JS高级程序设计笔记(四):类和继承

来源:互联网 发布:淘宝客能赚钱吗 编辑:程序博客网 时间:2024/06/13 13:31

一些方法

  1. Person.prototype.isPrototypeOf(实例); //返回布尔值

  2. Object.getPrototypeOf(实例); //返回原型对象

  3. 实例.hasOwnProperty(属性名) ;返回布尔值

  4. for in 和 in会访问实例和原型属性;for in只遍历可枚举属性

  5. Object.keys(o)返回可枚举实例属性名组成的数组,与for in中出现的顺序一致。要遍历所有实例属性(包括不可枚举的)使用Object.getOwnPropertyNames()。

  6. 如果通过这样的方式给原型对象添加属性和方法:

Person.prototype = {    XXX: XXX,    ...};

那么实际上是重写了原型对象,则该原型对象中的constructor 属性不再指向构造函数Person, 而是指向Object。除非重新更改constructor 的值,使其指向Person.
重写了原型,仍然是实例仍然是可以用instanceof来检测类型的。对于原型链上的对象以及构造函数都是返回true
7. 如果在创建了一个实例之后再重写原型对象,原来的实例的[prototype]属性仍然是指向之前的原型对象的(因为构造函数会为实例添加一个指向最初原型对象的属性[prototype]),因此应当在重写原型对象之后再创建实例:

function Person() {}Person.prototype = {    constructor: Person,    .....};var p = new Person();

创建类

  • 构造函数中创建实例属性,原型中创建共享属性和方法。
function Person(name, age) {    this. name = name;    this.age = age;    this.hobby = ['swimming', 'tennis'];};person.prototype = {    constructor: Person,    sayName: function() {        alert(this.name);    }};
  • 动态原型模式:
function Person(name, age, gender) {    this.name = name;    this.age = age;    this.gender = gender;    if (typeof this.sayName != 'function') {        Person.prototype.sayName = function() {};        Person.prototype......    }

该方式的优点是将原型的添加也放到了构造函数里,封装性更好;原型的添加也只有在第一次new对象的时候才会执行,只需检测一个要添加的原型属性是否存在即可。
需注意的是该方式不能重写原型,否则实例指向的原型仍然是原来的原型。

  • 寄生构造函数
function SpecialArray() {    var arr = new Array();    arr.push.apply(arr, arguments);    arr.toPipeString = function() {        return this.join('|');    };    return arr;}var names = new SpecialArray('lily', 'anna', 'mike');console.log(names.toPipeString());console.log(names instanceof SpecialArray);  //false

传统的构造函数会默认返回生成的实例,如果在构造函数中返回一个对象,则会重写返回的对象。
返回的对象和构造函数以及构造函数关联的原型没有半毛钱关系,不能用instanceof来确定对象的类型。最好不要使用这种方式。

  • 稳妥构造函数
    没有this和new,适合在需要安全性的环境中使用。并且没有公用属性。
function Person(name, age) {    //定义私有变量    var o = new Object();    o.sayName = function() {        alert(name);    };    return o;}var obj = Person("lily", 10);

只有在o.sayName中可以访问到name。避免修改。

继承

继承依赖原型链,主要思想是将超类的实例赋值给子类的原型,由于实例中持有对超类原型的引用,因此可以访问到超类中的原型属性(所有属性)。

  • 采用构造函数和原型组合的方式

如果仅采用原型,由于SuperType的实例属性会成为SubType的原型属性,如果SuperType中包含引用类型数据,那么子类的多个实例会共享这些引用类型属性,这是不希望看到的。
而如果仅采用构造函数的方式,则不能函数复用,因此采用两者结合的组合继承方式:

function SuperType(name, hobby) {    this.name = name;    this.hobby =  hobby;}SuperType.prototype.getSuperValue = function() {    console.log(this.name + ' ' + this.hobby);};function SubType(age, name, hobby) {    SuperType.call(this, name, hobby);//父类实例属性成为子类实例属性    this.age = age;}SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function() {    console.log(this.age);};

由于父类实例中有一个不可访问的属性[prototype]指向SuperType.prototype,而子类的原型对象是父类的实例,因此形成了一条原型链。
采用这种方式,超类构造函数会被调用两次,超类的实例属性在子类中会以两种形式出现,第一次是在:

SubType.prototype = new SuperType(); 

超类的实例属性成为子类的原型属性。第二次是:

SuperType.call(this, name, hobby);

在构造函数中,子类的同名实例属性屏蔽了原型属性。可以采用寄生组合继承来解决这个问题。

  • 原型继承
    不使用构造函数。新对象的原型是某个已知的对象。相当于创建了一个已知对象的副本,修改新对象,也会影响到已知对象。例如:
function create(o) {    function F(){}    F.prototype = o;    return new F();}

在ES5中提供了更为规范的函数Object.create(o[, {}]) 后面的可选参数是一个对象,包含要为新对象添加的属性。

  • 寄生组合式继承

组合继承的缺点是原型那里有冗余的属性,我们希望子类的原型是这么一个对象:它不拥有超类的实例属性,但持有超类原型对象的引用。因此不能直接以超类的实例来作为子类的原型对象。

//原型继承,适用于没有自定义类型,仅想继承ofunction object(o) {    function F() {}    F.prototype = o;    return new F();}function inheritPrototype(SubType, SuperType) {    var prototype = object(SuperType.prototype);    prototype.constructor = SubType;    SubType.prototype = prototype;}//寄生组合继承function SuperType(name, hobby) {    this.name = name;    this.hobby =  hobby;}SuperType.prototype.getSuperValue = function() {    console.log(this.name + ' ' + this.hobby);};function SubType(age, name, hobby) {    SuperType.call(this, name, hobby);//父类实例属性成为子类实例属性    this.age = age;}inheritPrototype(SubType, SuperType); SubType.prototype.getSubValue = function() {    console.log(this.age);};

上述代码中,返回的F实例其原型对象就是SuperType.prototype,再把这个没有任何实例属性的F实例作为子类的原型。即子类的原型对象中的[prototype] 属性指向一个纯粹的父类的原型对象,而不是包含冗余的父类实例属性的父类实例。同时也只调用了一次父类的构造函数。刚好符合我们的两个要求。
寄生组合继承是目前最理想的继承方式。

0 0
原创粉丝点击