JavaScript原型系列(二)什么是原型继承

將欲歙之,必固張之;將欲弱之,必固強之;——《道德經》javascript

JavaScript原型系列(一)构造函数、原型和原型链html

JavaScript原型系列(二)什么是原型继承java

JavaScript原型系列(三)Function、Object、Null等等的关系和鸡蛋问题编程

简介


JavaScript-prototype

在上一节上面介绍了原型和原型链,即每一个对象拥有一个原型对象,经过 __proto__ 指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null,这种关系被称为原型链(prototype chain)app

继承是面向对象编程语言的一大核心功能点,JavaScript是面向对象的只不过是比较特殊的面向对象的语言。它不像Java是基于类的面向对象,而javaScript是基于prototype的面向对象。编程语言

会用一篇文章来介绍什么面向对象,javascript是怎么实现继承封装多态javascript面向对象的特殊之处。函数

原型链继承


JavaScript 对象是动态的属性“包”(指其本身的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不单单在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。post

原型链继承的本质是重写原型对象,代之以一个新类型的实例。将父类的实例做为子类的原型。看下面代码:ui

function SubType () {
        this.name = 'subtype';
    }
    SubType.prototype.getName = function () {
        return this.name;
    }

    function Sub () {}
    // 将父类的实例做为子类的原型
    Sub.prototype = new SubType();
    var sub = new Sub();
    sub.constructor === SubType; // true
    Sub.prototype.constructor === SubType; // true
    sub.name = 'sub';
    console.log(sub.getName()); // sub
复制代码

优势this

  • 基于原型的方法全部子类均可以复用

缺点

  • 多个实例对引用类型的操做会被篡改
  • 子类型的原型上的 constructor 属性被重写了
  • 建立子类型实例时没法向父类型的构造函数传参

主要分析一下它的缺点暂时不分析它的优势。

引用类型被修改

由于本质上每一个实例的__proto__都会指向构造函数的prototype,实例上都是保存了一个引用地址,因此当prototype中的引用类型修改全部实例都会被改变。在上面代码基础上修改以下:

function SubType () {
        this.name = 'subtype';
    }
    SubType.prototype.getName = function () {
        return this.name;
    }

    SubType.prototype.Arr = ['sub', 'subtype', 'Sub'];
    function Sub () {}
    // 将父类的实例做为子类的原型
    Sub.prototype = new SubType();
    var sub = new Sub();
    var sub1 = new Sub();
    var sub2 = new Sub();

    sub2.Arr.push('push');

    sub1.Arr; // ["sub", "subtype", "Sub", "push"]
    sub2.Arr; // ["sub", "subtype", "Sub", "push"]
复制代码

在构造函数SubType.prototype新增Arr属性而且赋值为['sub', 'subtype', 'Sub'],经过new关键字实例化两个实例sub1sub1,当修改了sub2.Arr的时候,sub1.Arr的也会被影响。

实例constructor被重写

子类型原型上的 constructor 属性被重写, 执行 Sub.prototype = new SubType() 后原型被覆盖,Sub.prototype 上丢失了 constructor 属性, Sub.prototype 指向了 SubType.prototype,而 SubType.prototype.constructor 指向了 SubType,因此 Sub.prototype.constructor 指向了 SubType。 以下图所示:

JavaScript-prototype

function SubType () {
        this.name = 'subtype';
    }
    SubType.prototype.getName = function () {
        return this.name;
    }

    function Sub () {}
    // 将父类的实例做为子类的原型
    Sub.prototype = new SubType();
    // 全部涉及到原型链继承的继承方式都要修改子类构造函数的指向,不然子类实例的构造函数会指向SuperType。
    Sub.prototype.constrcutor = Sub;
    var sub = new Sub();

    Sub.prototype.constrcutor === Sub; // true
    sub.__proto__.constrcutor === Sub; // true
复制代码

经过Sub.prototype.constrcutor = Sub;Sub.prototype.constrcutor指向Sub,若是所示:

JavaScript-prototype

给子类型原型添加属性和方法必须在替换原型以后,缘由在第二点已经解释过了,由于子类型的原型会被覆盖。

属性遮蔽

Sub.prototype上添加getName方法,当调用sub上得getName时,访问到的是Sub.prototype.getName而不是访问到SubType.prototype.getName,这种状况称为属性遮蔽(property shadowing)

function SubType () {
        this.name = 'subtype';
    }
    SubType.prototype.getName = function () {
        return this.name;
    }

    function Sub () {}
    // 将父类的实例做为子类的原型
    Sub.prototype = new SubType();
    // 全部涉及到原型链继承的继承方式都要修改子类构造函数的指向,不然子类实例的构造函数会指向SuperType。
    Sub.prototype.constrcutor = Sub;
    // 添加getName属性
    Sub.prototype.getName = function () {
        return 'Sub.prototype.getName';
    }

    var sub = new Sub();

    sub.getName(); // Sub.prototype.getName
复制代码

能够经过__proto__调用原型链上的属性便可。

console.log(sub.__proto__.__proto__.getName()); // undefined
复制代码

实现一个new


function create () {
        // 建立一个空对象
        let obj = new Object();
        // 获取第一个参数,构造函数
        let Preson = [].shift.call(arguments);
        // 连接该对象(即设置该对象的构造函数)到另外一个对象;
        obj.__proto__ = Preson.prototype;
        // 绑定this指向,执行构造函数
        let result = Preson.apply(obj, arguments);
        return typeof result === 'object' ? result : obj;
    }
复制代码

在这里很少作赘述了,详细内容我另外一片博客javascript中实现一个本身的new

总结

  • 每一个对象拥有一个原型对象,经过__proto__ 指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null,这种关系被称为 原型链
  • 当访问一个对象的属性 / 方法时,它不单单在该对象上查找,还会查找该对象的原型,以及该对象的原型的原型,一层一层向上查找,直到找到一个名字匹配的属性 / 方法或到达原型链的末尾null
  • 原型链的构建依赖于 __proto__,一层一层最终连接到 null
  • instanceof 原理就是一层一层查找 __proto__,若是和 constructor.prototype 相等则返回 true,若是一直没有查找成功则返回 false
  • 原型链继承的本质是重写原型对象,代之以一个新类型的实例

参考

对象原型

继承与原型链

图解原型链及其继承优缺点

一篇文章理解 JS 继承

相关文章
相关标签/搜索