《JavaScript面向对象精要》--继承读后总结

157 阅读4分钟

对象继承

对象继承是最简单的继承类型。就是指定哪个对象是新对象的[[Prototyep]]。对象字面形式会隐式的指定Object.prototype为其[[Prototyep]],也可以用Object.create()方法显式指定。

Object.create()方法接受两个参数,第一个参数是需要被设置为新对象的[[Prototyep]]的对象;第二个是可选参数,是一个属性描述对象,使用格式和Object.defineProperties()方法一样。

var person1 = {
    name:'Nicholas',
    sayName:function(){
        console.log(this.name);
    }
}
var person2 = Object.create(person1,{
    name:{
        configurable:true,
        enumerable:true,
        value:'Greg',
        writable:true
    }
})
person1.sayName();//Nicholas
person2.sayName();//Greg
console.log(person1.hasOwnProperty('sayName'));//true
console.log(person1.isPrototypeOf(person2));//true
console.log(person2.hasOwnProperty('sayName'));//false

当访问一个对象的属性时,JavaScript引擎就会执行一个搜索过程,如果在对象实例上发现该属性(就是自有属性),改属性值就被使用;如果对象实例上没有该属性,就搜索[[Prototype]],如果还没有发现,则继续搜索该原型对象的[[Prototype]],直到继承链末端。末端通常是一个Object.prototype。其[[Prototype]]被置为空。 也可以通过Object.create()创建[[Prototype]]为null的对象。如下:

var nakedObj = Object.create(null);
console.log('toString' in nakedObj);//false
console.log('valueOf' in nakedObj);//false

nakedObj是一个没有原型对象链的对象,意味着toString()和valueOf()登内建方法都不在该对象上。

构造函数继承

一个简单的例子:

function YourConstructor(){
    //
}
YourConstructor.prototype = Object.create(Object.prototype,{
    constructor:{
        configurable:true,
        enumerable:true,
        value:YourConstructor,
        writable:true
    }
})

这段代码把构造函数的prototype属性设置为一个继承自Object.prototype的对象,这意味着YourConstructor创建出来的任何对象都继承自Object.prototype;YourConstructor是Object的之类,而Object是YourConstructor的父类。 思考下面这个例子:

function Rectangle(length,width){
    this.length = length;
    this.width = width;
}
// Rectangle.prototype.getArea = function(){
//     return this.length*this.width;
// }
// Rectangle.prototype.toString = function(){
//     return "[Rectangle"+this.length+"X"+this.width+"]";
// }
Rectangle.prototype = {
    getArea:function(){
        return this.length*this.width;
    },
    toString:function(){
        return "[Rectangle"+this.length+"X"+this.width+"]";
    }
}
//inherits from Rectangle
function Square(size){
    this.length = size;
    this.width = size;
}
Square.prototype = new Rectangle();
Square.prototype.constructor = Square;
Square.prototype.toString = function(){
    return "[Square"+this.length+"X"+this.width+"]";
}
var rect = new Rectangle(5,10);
var square = new Square(6);
console.log(rect.getArea());//50;
console.log(square.getArea());//36
console.log(rect.toString());//[Rectangle5X10]
console.log(square.toString());//[Square6X6]
console.log(rect instanceof Rectangle);//true
console.log(rect instanceof Object);//true
console.log(square instanceof Square);//true
console.log(square instanceof Object);//true
console.log(square instanceof Rectangle);//true

这段代码里创造了两个构造函数:Rectangle和Square。Square构造函数的prototype属性被改写为Rectangle的一个对象实例。此时不需要给Rectangle的调用提供参数,因为它们不需要被使用,而且如果提供了,那么所有的Square的对象实例都会共享同样的维度。

square和rect的原型对象链显示两者都继承自Rectangle.prototype和Object.prototype,但只有square继承自Square.prototype 上边代码,唯一相关的部分是Square.prototype需要指向Rectangle.prototype,使得继承得以实现,那就意味着可以用Object.create()简化上面的代码

//inherits from Rectangle
function Square(size){
    this.length = size;
    this.width = size;
}
Square.prototype = Object.create(Rectangle.prototype,{
    constructor:{
        configurable:true,
        enumerable:true,
        value:Square,
        writable:true
    }
})
Square.prototype.toString = function(){
    return "[Square"+this.length+"X"+this.width+"]";
}
var rect = new Rectangle(5,10);
var square = new Square(6);
console.log(rect.getArea());//50;
console.log(square.getArea());//36
console.log(rect.toString());//[Rectangle5X10]
console.log(square.toString());//[Square6X6]
console.log(rect instanceof Rectangle);//true
console.log(rect instanceof Object);//true
console.log(square instanceof Square);//true
console.log(square instanceof Object);//true
console.log(square instanceof Rectangle);//true

注意: 在对原型对象添加属性前要确保你已经修改了原型对象,否则在改写时会丢失之前添加的方法。 构造函数的窃取 构造函数的窃取,通常也称为伪类继承。构造函数窃取的关键就是在于this。只要在子类给构造函数中用**call()applay()**调用父类的构造函数,并将新创建的对象传进去即可。实际上、就是用自己的对象窃取父类的构造函数,如下例子:

function Square(size){
    Rectangle.call(this,size,size);
}
Square.prototype = Object.create(Rectangle.prototype,{
    constructor:{
        configurable:true,
        enumerable:true,
        value:Square,
        writable:true
    }
})
Square.prototype.toString = function(){
    return "[Square"+this.length+"X"+this.width+"]";
}
var square = new Square(6);
console.log(square.getArea());//36
console.log(square.toString());//[Square6X6]
console.log(square.length);//6
console.log(square.width);//6
console.log(square instanceof Square);//true
console.log(square instanceof Object);//true
console.log(square instanceof Rectangle);//true
 

总结 JavaScrip通过原型对象链支持继承。当将一个对象的[[Prototype]]设置为另一个对象时,就在这两个对象之间创建了一条原型对象链。所有的泛用对象都自动继承自Object.prototype。如果想要创建一个继承自其他对象的对象,可以用Object.create()指定[[Prototype]]为一个新对象。 可以在构造函数中创建原型对象链来完成自定义类型之间的继承。通过将构造函数的prototype属性设置为某一个对象,就像建立自定义类型对象和该对象的继承关系。构造函数的所有对象实例共享同一个原型对象,所以他们都继承自该对象。 为了正确继承自由属性,可以使用构造函数窃取。只需call()和apply()调用父类的构造函数,就可以在之类里完成各种初始化。结合构造函数窃取和原型对象链是JavaScript中最常见的继承手段,由于和基于类的继承相似,这个组合经常被称为为类继承。