美文网首页
第5章 继承

第5章 继承

作者: 不系流年系乾坤 | 来源:发表于2016-11-07 15:33 被阅读9次

    对象继承是最简单的继承类型。你唯需要做的是指定哪个对象是新对象的 [[Prototype]]。对象字面量形式会隐式指定 Object.prototype 为其 [[Protoype]]。当然我们可以用 ES5 的 Object.create() 方法显式指定。该方法接受两个参数,第一个是新对象的的 [[Prototype]] 所指向的对象。第二个参数是可选的一个属性描述对象,其格式与 Object.definePrototies()一样。

    var obj = {
        name: "Ljc"
    };
    
    // 等同于
    var obj = Object.create(Object.prototype, {
        name: {
            value: "Ljc",
            configurable: true,
            enumberable: true,
            writable: true
        }
    });
    

    下面是继承其它对象:

    var person = {
        name: "Jack",
        sayName: function(){
            console.log(this.name);
        }
    }
    
    var student = Object.create(person, {
        name:{
            value: "Ljc"
        },
        grade: {
            value: "fourth year of university",
            enumerable: true,
            configurable: true,
            writable: true
        }
    });
    
    person.sayName(); // "Jack"
    student.sayName(); // "Ljc"
    
    console.log(person.hasOwnProperty("sayName")); // true
    console.log(person.isPrototypeOf(student)); // true
    console.log(student.hasOwnProperty("sayName")); // false
    console.log("sayName" in student); // true
    
    Paste_Image.png

    当访问一个对象属性时,JavaScript引擎会执行一个搜索过程。如果在对象实例存在该自有属性,则返回,否则,根据其私有属性 [[Protoype]] 所指向的原型对象进行搜索,找到返回,否则继承上述操作,知道继承链末端。末端通常是 Object.prototype,其 [[Prototype]]null

    当然,也可以用 Object.create() 常见一个 [[Prototype]]null 的对象。

    var obj = Object.create(null);
    
    console.log("toString" in obj); // false
    

    该对象是一个没有原型对象链的对象,即是一个没有预定义属性的白板。

    5.3 构造函数继承

    JavaScript 中的对象继承也是构造函数继承的基础。
    第四章提到,几乎所有函数都有 prototype 属性,它可被修改或替换。该 prototype 属性被自动设置为一个新的继承自 Object.prototype 的泛用对象,该对象(原型对象)有一个自有属性 constructor。实际上,JavaScript 引擎为你做了下面的事情。

    // 你写成这样
    function YourConstructor(){
        // initialization
    }
    
    // JavaScript引擎在背后为你做了这些处理
    YourConstructor.prototype = Object.create(Object.prototype, {
        constructor: {
            configurable: true,
            enumerable: true,
            value: YourConstructor,
            writable: true
        }
    })
    

    你不需要做额外的工作,这段代码帮你把构造函数的 prototype 属性设置为一个继承自 Object.prototype 的对象。这意味着 YourConstructor 创建出来的任何对象都继承自 Object.prototype

    由于 prototype 可写,你可以通过改变它来改变原型对象链。

    MDN:instanceof 运算符可以用来判断某个构造函数的 prototype 属性是否存在另外一个要检测对象的原型链上。

    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 + "]";
    }
    
    
    // inherits from Rectangle
    function Square(size){
        this.length = size;
        this.width = size;
    }
    
    Square.prototype = new Rectangle(); // 尽管是 Square.prototype 是指向了 Rectangle 的对象实例,即Square的实例对象也能访问该实例的属性(如果你提前声明了该对象,且给该对象新增属性)。
    // Square.prototype = Rectangle.prototype; // 这种实现没有上面这种好,因为Square.prototype 指向了 Rectangle.prototype,导致修改Square.prototype时,实际就是修改Rectangle.prototype。
    console.log(Square.prototype.constructor); // 输出 Rectangle 构造函数
    
    Square.prototype.constructor = Square; // 重置回 Square 构造函数
    console.log(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()); // "[Rectangle 5 * 10]", 但如果是Square.prototype = Rectangle.prototype,则这里会"[Square 5 * 10]"
    console.log(square.toString()); // "[Square 6 * 6]"
    
    console.log(square instanceof Square); // true
    console.log(square instanceof Rectangle); // true
    console.log(square instanceof Object); // true
    
    Paste_Image.png

    Square.prototype 并不真的需要被改成为一个 Rectangle 对象。事实上,是 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
        }
    })
    

    在对原型对象添加属性前要确保你已经改成了原型对象,否则在改写时会丢失之前添加的方法(因为继承是将被继承对象赋值给需要继承的原型对象,相当于重写了需要继承的原型对象)。

    5.4 构造函数窃取

    由于JavaScript中的继承是通过原型对象链来实现的,因此不需要调用对象的父类的构造函数。如果确实需要在子类构造函数中调用父类构造函数,那就可以在子类的构造函数中利用 callapply方法调用父类的构造函数。

    // 在上面的代码基础上作出修改
    // inherits from Rectangle
    function Square(size){
        Rectangle.call(this, size, size);
        
        // optional: add new properties or override existing ones here
    }
    

    一般来说,需要修改 prototyp 来继承方法并用构造函数窃取来设置属性,由于这种做法模仿了那些基于类的语言的类继承,所以这通常被称为伪类继承。

    5.5 访问父类方法

    其实也是通过指定 callapply 的子对象调用父类方法。

    相关文章

      网友评论

          本文标题:第5章 继承

          本文链接:https://www.haomeiwen.com/subject/oosouttx.html