js之原型链、继承

178 阅读4分钟

原型链

基本概念

  • prototype: 显式原型
  • __ proto__: 隐式原型

规则

  • 实例拥有 new时默认是 this 构造函数的属性和构造函数原型上的方法
  • 引用类型,都具有对象特性,即可自由扩展属性。
  • 引用类型,都有一个隐式原型 __proto__ 属性,属性值是一个普通的对象
  • 引用类型,隐式原型 __proto__ 的属性值指向它的构造函数的显式原型 prototype 属性值。
  • 当你试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型 __proto__(也就是它的构造函数的显式原型 prototype)中寻找。

原型 原型对象 实例 关系图(掘金上找的)

image.png

  function People (name = 'People') {
      this.name = name
    }
    People.prototype.say = function () {
      console.log('我是一个', this.name)
    }
    let people = new People()
    console.log(People.prototype.constructor === People) // true
    console.log(People.prototype === people.__proto__) // true

原型链 关系图(掘金上找了几张好理解的)

image.png

instanceof操作符 、isPrototypeOf方法

instanceof  检测实例(instance)的原型链中 出现过的 构造函数的原型对象(prototype)

function Foo(name) {
    this.name = name; 
} 
let f = new Foo('nick') 
f instanceof Foo // true 
f instanceof Object // true

isPrototypeOf() 原型链中出现过的原型

    Foo.prototype.isPrototypeOf(f) // true

继承

基础代码 (后面代码在此基础上实现基础)

     function People (name= 'People') {
          this.name = name
    }
    People.prototype.say = function () {
      console.log('我是一个', this.name)
    }
    
    function Women (age,name) {
         this.age = age
    }
   

1、原型链继承

     let people = new People()
     Women.prototype = people
     let women = new Women(18,'women')

缺点

  • 无法传值给父类
  • 当原型链中包含引用类型值的原型时,该引用类型值会被所有实例共享

2、借用构造函数

    function Women (age,name) {
         this.age = age
         Peple.call(this,name)
    }

缺点

  • 子级 只能 获取 父级 构造函数内定的属性和方法 ,获取不到父级原型对象的方法
  • 原型链上关系不清晰 ,不可使用 instanceof 、isPrototypeOf方法检测

3、组合式继承

原型链继承  + 借用构造函数继承

缺点 两次调用 父级 构造函数 造成不必要的消耗

4、原型式继承

在object()函数内部, 先创建一个临时性的构造函数, 然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例

function object(obj){ 
    function F(){} 
    F.prototype = obj; 
    return new F(); 
}
  let women = object(new People('women'))

缺点 对父级仅仅是浅复制

在 ECMAScript5 中,通过新增 object.create()  方法规范化了上面的原型式继承.

object.create()  只有一个参数时功能与上述object方法相同, 它的第二个参数与Object.defineProperties()方法的第二个参数格式相同

var person = { name : "Ammy" }; 
var anotherPerson = Object.create(person, { 
    name : { value : "Luci" } 
});
alert(anotherPerson.name);//"Luci"

5、寄生式继承

function createAnother(original){
  var clone = object(original); // 通过调用 object() 函数创建一个新对象
  clone.sayHi = function(){  // 以某种方式来增强对象
    alert("hi");
  };
  return clone; // 返回这个对象
}

缺点

  • 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。

6、寄生组合式继承

 function inheritPrototype (Sub, Super) {
      // Object.create 可以将subPrototype的原型(__proto__) 设置成 Super的原型对象(prototype);
      var subPrototype = Object.create(Super.prototype)
      // 因为Object.prototype.constructor会返回对象本身,所以这里设置为Sub本身;
      subPrototype.constructor = Sub

      // 这里将Sub的原型对象(prototype)设置成subPrototype,实现Sub对Super基于原型链的继承;
      Sub.prototype = subPrototype
    }

    function Super (name) {
      this.name = name
    }
    Super.prototype.show = function () {
      console.log(this.name)
    }
    function Sub () {
      // 这里 Sub 将自己的上下文传给Super并执行,真可谓一个懒字了得;
      Super.apply(this, arguments)
    }

    // 调用寄生组合式继承;
    inheritPrototype(Sub, Super)

    var instance = new Sub('我是Ammy')

    // instance 本身并没有show方法,
    // 但是通过inheritPrototype函数把instance对象的原型的原型设置成了Super的原型对象,
    // 所以,基于JavaScript 原型链的特性,实际上是这样调用的 instance.__proto__.__proto__.show();
    // 打印一下便知道了;
    instance.show()
    console.log(instance.__proto__.__proto__ === Super.prototype)
  }

7、語法糖 ES6 Class extends继承

 class People {
      constructor (name) {
        this.name = name
      }
      say () {
        console.log('我是一个', this.name)
      }
    }
    class Women extends People {
      constructor (age, name) {
        super(name)
        this.age = age
      }
    }
  }

函数基础 与 类继承区别

  1. 函数声明会提升,类声明不会。
  2. ES5继承和ES6继承的区别
  • ES5的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上(Parent.call(this)).

  • ES6的继承有所不同,实质上是先创建父类的实例对象this,然后再用子类的构造函数修改this。因为子类没有自己的this对象,所以必须先调用父类的super()方法,否则新建实例报错。

new 运算符

var obj = {}; 
obj.__proto__ = F.prototype; 
F.call(obj);

以 new 操作符调用构造函数的时候,函数内部实际上发生以下变化:

  1. 创建了一个空对象
  2. 空对象的proto 指向 该函数的原型prototype
  3. 函数对象的this指向该对象。
  4. 返回 该对象