美文网首页
原型链与继承

原型链与继承

作者: talent_ray | 来源:发表于2020-03-24 14:20 被阅读0次

    创建对象的方法

    • 字面量创建
    • 构造函数创建
    • Object.create()
    var o1 = {name: 'value'};
    var o2 = new Object({name: 'value'});
    
    var M = function() {this.name = 'o3'};
    var o3 = new M();
    
    var P = {name: 'o4'};
    var o4 = Object.create(P)
    

    原型

    • JavaScript 的所有对象中都包含了一个 __proto__ 内部属性,这个属性所对应的就是该对象的原型
    • JavaScript 的函数对象,除了原型 __proto__ 之外,还预置了 prototype 属性
    • 当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型 __proto__

    原型链

    任何一个实例对象通过原型链可以找到它对应的原型对象,原型对象上面的实例和方法都是实例所共享的。

    一个对象在查找以一个方法或属性时,他会先在自己的对象上去找,找不到时,他会沿着原型链依次向上查找。

    注意: 函数才有prototype,实例对象只有有proto, 而函数有的proto是因为函数是Function的实例对象

    判断对象是哪个类的直接实例

    使用对象.construcor直接可判断

    构造函数,new时发生了什么?

       var obj  = {}; 
       obj.__proto__ = Base.prototype;
       Base.call(obj);  
    
    1. 创建一个新的对象 obj;
    2. 将这个空对象的proto成员指向了Base函数对象prototype成员对象
    3. Base函数对象的this指针替换成obj, 相当于执行了Base.call(obj);
    4. 如果构造函数显示的返回一个对象,那么则这个实例为这个返回的对象。 否则返回这个新创建的对象

    类的声明

    // 普通写法
    function Animal() {
      this.name = 'name'
    }
    
    // ES6
    class Animal2 {
      constructor () {
        this.name = 'name';
      }
    }
    

    继承

    借用构造函数法

    在构造函数中 使用Parent.call(this)的方法继承父类属性。

    原理: 将子类的this使用父类的构造函数跑一遍

    缺点: Parent原型链上的属性和方法并不会被子类继承

    function Parent() {
      this.name = 'parent'
    }
    
    function Child() {
      Parent.call(this);
      this.type = 'child'
    }
    

    原型链实现继承

    原理:把子类的prototype(原型对象)直接设置为父类的实例

    缺点:因为子类只进行一次原型更改,所以子类的所有实例保存的是同一个父类的值。
    当子类对象上进行值修改时,如果是修改的原始类型的值,那么会在实例上新建这样一个值;
    但如果是引用类型的话,他就会去修改子类上唯一一个父类实例里面的这个引用类型,这会影响所有子类实例

    function Parent() {
      this.name = 'parent'
      this.arr = [1,2,3]
    }
    
    function Child() {
      this.type = 'child'
    }
    
    Child.prototype = new Parent();
    var c1 = new Child();
    var c2 = new Child();
    c1.__proto__ === c2.__proto__
    

    组合继承方式

    组合构造函数中使用call继承和原型链继承。

    原理: 子类构造函数中使用Parent.call(this);的方式可以继承写在父类构造函数中this上绑定的各属性和方法;
    使用Child.prototype = new Parent()的方式可以继承挂在在父类原型上的各属性和方法

    缺点: 父类构造函数在子类构造函数中执行了一次,在子类绑定原型时又执行了一次

    function Parent() {
      this.name = 'parent'
      this.arr = [1,2,3]
    }
    
    function Child() {
      Parent.call(this);
      this.type = 'child'
    }
    
    Child.prototype = new Parent();
    

    组合继承方式 优化1:

    因为这时父类构造函数的方法已经被执行过了,只需要关心原型链上的属性和方法了

    Child.prototype = Parent.prototype;
    

    缺点:

    • 因为原型上有一个属性为constructor,此时直接使用父类的prototype的话那么会导致 实例的constructor为Parent,即不能区分这个实例对象是Child的实例还是父类的实例对象。
    • 子类不可直接在prototype上添加属性和方法,因为会影响父类的原型

    注意:这个时候instanseof是可以判断出实例为Child的实例的,因为instanceof的原理是沿着对象的proto判断是否有一个原型是等于该构造函数的原型的。这里把Child的原型直接设置为了父类的原型,那么: 实例.proto === Child.prototype === Child.prototype

    组合继承方式 优化2 - 添加中间对象【最通用版本】:

    function Parent() {
      this.name = 'parent'
      this.arr = [1,2,3]
    }
    
    function Child() {
      Parent.call(this);
      this.type = 'child'
    }
    
    Child.prototype = Object.create(Parent.prototype); //提供__proto__
    Child.prototype.constrctor = Child;
    

    Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto

    封装一个原生的继承方法

    /**
     * 继承
     * @param Parent
     * @param Child
     */
    function extendsClass(Parent, Child) {
      function F() {}
      F.prototype = Parent.prototype
      Child.prototype = new F()
      Child.prototype.constrctor = Child
      return Child
    }
    

    相关文章

      网友评论

          本文标题:原型链与继承

          本文链接:https://www.haomeiwen.com/subject/pguoyhtx.html