原型
每一个对象都有一个显式原型(prototype)和一个隐式原型(proto)。隐式原型引用了创建这个对象的函数的原型 prototype
|
|
一个对象实例的 proto 和 创建这个对象实例的函数的 prototype 是一样的
|
|
- Object.proto 指向的是 Function.prototype (Object 是一个对象构造函数,是被函数 Function 创建的)
- Foo.proto 指向的也是 Function.prototype (Foo 是一个普通的构造函数,是被函数 Function 创建的)
- Function.proto 指向的还是 Function.prototype (因为 Function 是被自身创建的)
Function.prototype 的 proto 指向的是 Object.prototype
typeof Function.prototype === ‘function’
Object.prototype.proto 指向 null
原型链
Instanceof 运算符的第一个变量是一个对象,暂时称为A;第二个变量一般是一个函数,暂时称为B。
Instanceof 的判断队则是:沿着A的proto这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false。
访问一个对象的属性时,先在自身基本属性中查找,如果没有,再沿着proto这条链向上找,这就是原型链。
call/apply 等方法是在 Function.prototype 中创建的
原型以及原型链图解
继承
利用原型以及原型链等方法,实现子类对于父类的继承
|
|
原型链继承
将父类的实例作为子类的原型
|
|
构造继承
使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
|
|
实例继承
为父类实例添加新特性,作为子类实例返回
|
|
组合继承
通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
|
|
寄生组合继承
通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
|
|
详细请看之前一篇博文红宝书系列读书笔记(二)
多重继承
多重继承,指的是一个子类对象继承了多个父类对象,主要是利用 call/apply 实现多重继承
|
|
上述代码中,inheritPrototype() 接收两个参数:父类构造函数和父类对象。通过 call 方法将父类构造函数绑定到子类中,然后再将另一个父类对象自身的属性全都复制给子类。
如果通过多个父类构造函数继承多个父类对象,那么代码如下
|
|
ES5 与 ES6 继承的区别
ES5 和 ES6 实现的继承底层还是利用了原型链
ES5 的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到 this 上(Parent.apply(this))
ES6 的继承机制完全不同,实质上是先创建父类的实例对象this(所以必须先调用父类的super()方法),然后再用子类的构造函数修改 this。
ES5 的继承时通过原型或构造函数机制来实现。
ES6 通过 class 关键字定义类,里面有构造方法,类之间通过 extends 关键字实现继承。子类必须在 constructor 方法中调用 super 方法,否则新建实例报错。因为子类没有自己的 this 对象,而是继承了父类的 this 对象,然后对其进行加工。如果不调用 super 方法,子类得不到 this 对象。
super 关键字指代父类的实例,即父类的 this 对象。