• JavaScript 面向对象编程(三)如何写类和子类


    JavaScript面向对象编程(一)原型与继承JavaScript面向对象编程(二)构造函数和类中,我们分别讨论了JavaScript中面向对象的原型和类的概念。基于这两点理论,本篇文章用一个简单的例子来阐述如何在JavaScript中写类与子类。

    几个面向对象的概念

     实例属性:是每个对象所拥有的属性。比如对于一个Person类的对象而言,name、age等属性是每一个person所拥有的。而且,不同person的age和name可能不同。所以,在JavaScript中我们必须把实例属性加在对象上面。

    实例方法:是类的实例所共享的方法。这些方法通过实例进行调用。比如所有的Person类的对象共享一个getName()方法,通过person对象调用这个方法获取对象的name属性的值。这个方法是实例对象共享的,所以把实例方法加在类的prototype上。

    类属性与方法:这与传统面向对象语言中静态属性和方法类似。类属性与方法是类所拥有的,而非对象所拥有。在JavaScript所实现的类属性和类方法是通过“构造函数对象”获取。

    基类方法调用:在子类的构造函数或者是子类所重载的方法中,调用基类相应方法。

    现在用Person类和其子类Student类来阐述上面的概念。

    定义一个类

     1 var Person = function(name,age){
     2     //instance property, which owns by the instance.
     3     this.name = name;
     4     this.age = age;
     5 };
     6 //Class methods and properties, owned by Class not by the instances. They are "static"
     7 Person.YOUNG = 18;
     8 //instance methods, which are shared by all the instances
     9 Person.prototype.sayHello = function(){
    10     console.log("Hello!");
    11     console.log("I am " + this.name);
    12 };
    13 Person.prototype.isAdult  = function() {
    14     if(this.age >= Person.YOUNG){
    15         return true;
    16     }
    17     else{
    18         return false;
    19     }
    20 };
    21 Person.prototype.grow = function(){
    22     this.age = this.age + 1;
    23 };

    在Person类中,name和age是实例属性,它们是每个实例对象所拥有的。所以将name和age放在构造函数中。前一篇文章已经讨论过,new关键字生成一个对象,并且使用new后面的函数来初始化这个对象。所以name和id这两个实例属性已经通过this.name = name 和this.age = age这两行代码加在了实例对象上面了。

    Person.YOUNG是类属性,它是由Person类所拥有的。isAdult中通过与它比较来判断一个Person是否为成年人。如果YOUNG的值改变了,那么所有isAdult的行为也会发生变化。

    sayHello,isAdult和grow是实例方法。通过对象进行调用,实例方法中经常会带有this关键字,用来指代调用方法的对象。前一篇文章已经讨论过,通过new关键字的对象以构造函数的prototype属性为原型。所以根据原型链的概念,所有对Person类的对象都能调用这些方法。对象可以通过调用grow方法来增长其age的值,grow方法的this指代的是调用它的对象。

    那么就能使用这个类了:

    1 var p = new Person("Jack", 17);
    2 p.sayHello();//Hello!I am Jack
    3 p.isAdult();//false
    4 p.grow();
    5 p.isAdult();//true

    定义子类

    这里介绍定义子类的基本方法。定义子类的关键是,将基类(父类)的prototype属性作为子类的prototype属性的原型。这样就能构成一个原型链:实例->子类.prototype->父类.prototype。那么根据原型链的概念,就能通过实例对象访问父类定义的方法。同样,子类也可以覆盖父类中的方法。

    在传统面向对象编程语言中,子类(基类)在构造函数中会调用父类的构造函数。子类在覆盖父类方法中也能调用父类的方法。因为在定义子类时,我们往往希望对父类的行为进行修改或补充,而不一定是完全替换它们。

     1 //define a sub class
     2 var Student = function(major,name,age){
     3     this.major = major;
     4     Person.prototype.constructor.call(this,name,age);
     5 };
     6 Student.prototype = Object.create(Person.prototype);
     7 Student.prototype.constructor = Student;
     8 Student.baseClass = Person;
     9 //override the sayHello method
    10 Student.prototype.sayHello = function(){
    11     //call the method in the base class
    12     Student.baseClass.prototype.sayHello.call(this);
    13     console.log("I am a " + this.major + " student!");
    14 };
    15 //add a method
    16 Student.prototype.doHomework = function(){
    17     console.log("I am doing " + this.major + " homework!");
    18 };

    Student类是Person类的子类。它的每个实例对象除了拥有name和age之外还拥有一个major属性,用来表示每个student的专业。因为Person类的构造函数已经对name和age进行初始化了,所以在Student类的构造函数中可以调用其父类的构造函数(第4行)。

    为了让Student的实例也能访问Person类的实例方法,就要将Person.prototype作为Student.prototype的原型。这样就构成了原型链:实例->Student.prototype->Person.prototype。代码的第6行实现了这一点。需要补充的是:Student.prototype = new Person() 同样可以将Person.prototype作为Student.prototype的原型,但是此时Student.prototype却拥有了两个实例属性name和age,而这两个实例属性应该是实例所拥有而非类的prototype拥有。所以在这里使用Object.create(Person.prototype)比较好。(具体使用哪种方法要根据实现类的步骤决定)

    第7行的作用是将Student类的构造函数纠正为Student.

    第8行的作用是将Student类的基类(父类)赋值为Person,方便之后重定义父类方法时调用。

    Student类重定义了父类中的sayHello方法,不仅说出了自己的name而且说出了自己的major是什么。因为sayHello中只是补充了说出major的行为,所以调用了父类的方法。

    最后Student类增加了一个doHomework的方法(因为学生都得做作业)。

    这样就能使用这个类了:

    1 var s = new Student("Computer Science","Mike",17);
    2 s.sayHello();//Hello!I am Mike
    3             //I am a Computer Science student!
    4 s.isAdult();//false
    5 s.grow();
    6 s.isAdult(); //true
    7 s.doHomework();//I am doing Computer Science homework!

    面向对象编程的作用是:封装,继承和多态。我会在相关日志中不断从这三个角度更新日志。

    参考文章:

    1)《JavaScript The Definitive Guide》第九章

    2)CoolShell:http://coolshell.cn/articles/6441.html

    3)MDN:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript

  • 相关阅读:
    B树与B+详解
    SQLite占用资源少原因
    iOS SQLite详解
    YTKNetwork网络封装
    YTKNetwork源码详解
    AFNetworking封装-项目使用
    iOS网络请求-AFNetworking源码解析
    PHP 不使用第三个变量实现交换两个变量的值
    PHP public private protected 三种修饰符的区别
    PHP 汉字转拼音
  • 原文地址:https://www.cnblogs.com/zhenchaoni/p/JavaScript_Class_SubClass.html
Copyright © 2020-2023  润新知