JS 的继承

213 阅读2分钟

1.JS基于原型的继承

声明一个对象obj1:

let obj1 = { name: 'jack' } 

发现obj1自动会有一些属性:

obj1.toString() //"[object Object]"
obj1.valueOf() //{name: "jack"}

这是js基于原型的继承的结果。

可以这样理解,js中一个实例对象的属性和方法,分成两种,一种是本地的, 另一种是继承的。

name: 'jack'是本地属性,toString()、valueOf()等则是继承属性。

toString()查找过程:

  • 看 obj1 对象本身有没有 toString 属性,没有就下一步
  • 看 obj._ _ proto_ _ 对象有没有 toString 属性,发现 obj._ _ proto_ _ 有 toString 属性,找到了
  • 如果 obj._ _ proto_ _ 没有,浏览器会继续查看 obj._ _ proto_ _ ._ _ proto_ _
  • 4如果 obj._ _ proto_ _ ._ _ proto_ _ 也没有,浏览器会继续查看 obj._ _ proto_ _ ._ _ proto_ _ . _ _ proto_ _
  • 直到找到 toString 或者 _ _ proto_ _ 为 null。

再看下面代码:

obj1.__proto__===Object.prototype //true
  • obj1._ _ proto_ _为obj1自身属性
  • Object.prototype为Object构造函数对象本身属性
  • 值相等,可以理解为obj1继承了Object函数对象的prototype属性中的方法,并把这些方法放到了自身属性_ _ proto_ _上。

再声明一个对象obj2:

let obj2 = { name: 'rose' } 

发现:

obj1.__proto__===obj2.__proto__  //true
obj1.toString()===obj2.toString() //true

实例对象共享同一个prototype对象,好像"继承"了prototype对象一样 obj1与obj2都继承了Object函数对象的prototype属性中的方法。 如果我们改写 obj2._ _ proto_ _.toString(),那么 obj1.toString() 也会变。

如果想让 obj1.toString() 和 obj2.toString() 不同,则要赋值:

obj1.toString = function(){ return '新的 toString 方法' }

这时,toString()方法则变成了obj1的本地方法。

2.JS基于class的继承

ES6中,Class 可以通过extends关键字实现继承。

class ColorPoint extends Point {
  constructor(x, y, color) {`
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }
  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}
  • super关键字,表示父类的构造函数,用来新建父类的this对象
  • 子类必须在constructor方法中调用super方法
  • ES5 的继承是先创造子类的实例对象this,然后将父类的方法添加到this上
  • ES6 的继承是先将父类实例对象的属性和方法加到this上面(所以必须先调用super 方法),然后再用子类的构造函数修改this,加上子类自己的实例属性和方法
  • 如果子类没有定义constructor方法,这个方法会被默认添加
class ColorPoint extends Point {
}
// 等同于
class ColorPoint extends Point {
  constructor(...args) {
    super(...args);
  }
}
  • 只有子类的构造函数调用super之后,才可以使用this关键字
  • 父类的静态方法,也会被子类继承
class A {
  static hello() {
    console.log('hello world');
  }
}
class B extends A {
}

B.hello()  // hello world