function Parent1() {this.parent1 = 'parent1'this.arr = [1, 2, 3]}Parent1.prototype.walk = 'I can walk'function Child1() {Parent1.call(this)this.child1 = 'child1'}const child11 = new Child1()const child12 = new Child1()// 测试child11.parent1 // parent1child11.walk // undefinedchild11.arr.push(4)child11.arr // [1, 2, 3, 4]child12.arr // [1, 2, 3]
function Parent2() {this.parent2 = 'parent2'this.arr = [1, 2, 3]}Parent2.prototype.walk = 'I can walk'function Child2() {this.child2 = 'child2'}Child2.prototype = new Parent2()const child21 = new Child2()const child22 = new Child2()// 测试child21.parent2 // parent2child21.walk // I can walkchild21.arr.push(4)child21.arr // [1, 2, 3, 4]child22.arr // [1, 2, 3, 4]
------ 补充 ------
补充下 Object.create() 这个 api, 也将它归类到基于原型链的继承。当继承的对象属性没变化时, 可以使用它。
function Parent3() {this.parent3 = 'parent3'this.arr = [1, 2, 3]}Parent3.prototype.walk = 'I can walk'function Child3() {Parent3.call(this) // 这里执行第一次父类this.child3 = 'child3'}Child3.prototype = new Parent3() // 这里执行第二次父类, 原型继承发现只要把父子的原型对象绑定起来就好, 可以写成 Child3.prototype = new Parent3()>__proto__ 也正常const child31 = new Child3()const child32 = new Child3()// 测试child31.parent3 // parent3child31.walk // I can walkchild31.arr.push(4)child31.arr // [1, 2, 3, 4]child32.arr // [1, 2, 3]
function Parent4() {this.parent4 = 'parent4'this.arr = [1, 2, 3]}Parent4.prototype.walk = 'I can walk'function Child4() {Parent4.call(this)this.child4 = 'child4'}Child4.prototype = Parent4.prototypeconst child41 = new Child4()const child42 = new Child4()// 测试child41.parent4 // parent4child41.walk // I can walkchild41.arr.push(4)child41.arr // [1, 2, 3, 4]child42.arr // [1, 2, 3]child41.constructor // Parent4
function Parent5() {this.parent5 = 'parent5'this.arr = [1, 2, 3]}Parent5.prototype.walk = 'I can walk'function Child5() {Parent5.call(this)this.child5 = 'child5'}Child5.prototype = Object.create(Parent5.prototype) // Object.create() 创建了一个中间对象, 起到隔离子类和父类的作用。Child5.prototype.constructor = Child5const child51 = new Child5()const child52 = new Child5()// 测试child51.parent5 // parent5child51.walk // I can walkchild51.arr.push(4)child51.arr // [1, 2, 3, 4]child52.arr // [1, 2, 3]child51.constructor // Child5
Object.create() 的作用: Object.create(Parent5.prototype) 相当于一个空对象 {}, 这个空对象的 proto 等于 Parent5.prototype, 所以这时候我们修改 Child5.prototype.constructor 实际上是在空对象上加上 constructor 属性。
优点: 解决了不能判断实例的构造函数是子类还是父类的问题
function Parent(props) {this.props = props}class Child extends Parent {constructor() {super() // 这里}}var child = new Child({a: 1})console.log(child.props) // undefined
为什么这里 child.props 会输出 undefined 呢, 让我们接着看以下两个例子。
function Parent(props) {this.props = props}class Child extends Parent {constructor(props) {super(props)}}var child = new Child({a: 1})console.log(child.props) // {a: 1}
function Parent(props) {this.props = props}class Child extends Parent {}var child = new Child({a: 1})console.log(child.props) // {a: 1}
可以看到第二种情况和第三种情况是相等的。继承中, 当子类没有写 constructor 时, 它会自动加上如下代码:
constructor(props) {super(props)}
简单作个结论: 当 ES6 的类函数中有 constructor 和 super 时, 它们后面必须得跟个参数 props(名字随意), 否则原型链上的对象无法获取到相应属性。
将之翻译成 ES5, 代码如下:
function Parent(props) {this.props = props}function Child(props) {Parent.call(this, props) // 这里等价于 ES6 中 super(props)}Child.prototype = new Parent()var child = new Child({a: 1})
在下面的案例中, super 作为对象调用, 那么它指向什么呢?
class Parent {parentFn() {console.log('I am parentFn')}}class Child extends Parent {childFn() {console.log(super.constructor)super.parentFn()}}var child = new Child()child.childFn()// Class Parent { ... }// I am parentFn
打印结果已经作出了回答。super 在作为对象调用时, 它指向了父类对象的实例。