美文网首页
再看原型链

再看原型链

作者: GrowthCoder | 来源:发表于2018-09-27 19:26 被阅读0次

    开场白

    输出?

    var A= function(){}
        A.prototype.n=1
        var b=new A()
        A.prototype={
            n:2,
            m:3
        }
    var c=new A()
    console.log(b.n, b.m, c.n, c.m)
    console.log(A.prototype.__proto__)
    

    prototype

    每个构造函数都有一个prototype属性,指向自己的原型对象;

    什么是原型?每个对象创建的时候,都会与之关联另一个对象,这个就是原型对象,会从原型“对象”继承属性和方法。


    __proto__

    每个实例对象(object)都有一个私有属性__proto__指向它的原型对象(prototype)。该原型对象也有一个自己的原型对象,层层向上直到一个对象的原型对象为null

    function Person(name) {
      this.name = name
    }
    
    Person.prototype.getName = function () {
      return this.name
    }
    
    var person = new Person();
    person.name = 'gaoting';
    console.log(person.name) // gaoting
    console.log(Person.prototype) 
    console.log(person.__proto__) 
    console.log(person.__proto__ === Person.prototype) // true
    
    WX20180926-161836.png

    constructor

    每个原型对象,都有一个constructor属性,用来指向自己关联的函数,默认指向关联的构造函数。
    constructor 引用同样被委托给了 Person.prototype,而 Person.prototype.constructor 默认指向 Person

    3.png

    Person.prototype 的 .constructor 属性只是 Person函数在声明时的默认属性。
    如果 你创建了一个新对象并替换了函数默认的 .prototype 对象引用,那么新对象并不会自动获 得 .constructor 属性。

    function Person(name) {
      this.name = name
    }
    
    Person.prototype.getName = function () {
      return this.name
    }
    
    var person = new Person();
    person.name = 'gao';
    console.log(person.constructor === Person, Person.prototype.constructor === Person)
    //test constructor
    Person.prototype = {
      age: 18,
      company: 'yck'
    }
    
    var gao = new Person()
    console.log(gao.constructor === Person) // false
    // 获取对象原型
    console.log(Object.getPrototypeOf(gao)) // { age: 18, company: 'yck' }
    

    gao 并没有 .constructor 属性,所以它会委托 [[Prototype]] 链上的 Person. prototype。但是这个对象也没有 .constructor 属性(不过默认的 Person.prototype 对象有这 个属性!),所以它会继续委托,这次会委托给委托链顶端的 Object.prototype。这个对象 有 .constructor 属性,指向内置的 Object(..) 函数。

    以上为构造函数、实例、实例原型之间的关系


    基于原型链的继承

    JavaScript对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

    4.png
    function Person(name) {
      this.name = name
    }
    
    Person.prototype.getName = function () {
      return this.name
    }
    
    function Man (sex) {
      this.sex = sex
    }
    Man.prototype = new Person()
    var man = new Man('male')
    man.name = 'qi'
    console.log(man.getName())
    console.log(man instanceof Man, man instanceof Person) // true true
    

    问题一

    即在通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做原型指向了另一个对象

    问题二

    在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了

    function SuperType(){
        this.colors = ["red", "blue", "green"];
    }
    function SubType(){
    }
    //继承了 SuperType
    SubType.prototype = new SuperType(); // SuperType的原型对象将拥有colors属性,之后的实例对colors的修改,将直接作用到原型上
    var instance1 = new SubType(); 
    instance1.colors.push("black");
    console.log(instance1.colors); //"red,blue,green,black"
    var instance2 = new SubType(); 
    console.log(instance2.colors); //"red,blue,green,black"
    

    错误做法

    //和你想要的机制不一样! 
    Bar.prototype = Foo.prototype;
    

    只会让Bar.prototype直接引用Foo.prototype对象,当执行类似Bar.prototype.getName的时候,会直接修改Foo.prototype对象本身。这不是想要的结果。

    // 基本上满足你的需求,但是可能会产生一些副作用 :( 
    Bar.prototype = new Foo();
    

    借用构造函数

    为了解决原型中包含引用类型值的问题,可以使用借用构造函数,在子类型构造函数的内部调用超类型构造函数。

    function SuperType () {
      this.colors = ['red', 'blue', 'green']
    }
    SuperType.prototype.getColors = function(){
        return this.colors.join(',')
    }
    function SubType () {
      //继承了 SuperType,在新构造函数中调用SuperType初始化对象的代码,每个实例保存colors副本
      SuperType.call(this)
    }
    
    var ins = new SubType()
    ins.colors.push('black')
    var ins2 = new SubType()
    console.log(ins2) // ['red', 'blue', 'green']
    ins2.getColors() // 报错
    

    缺点,更为致命,无法访问超类型SubType.prototype中定义的函数,仅仅获取了超类型构造函数中定义的属性,没有用到继承的思想。


    组合继承

    将原型链和组合继承技术组合,通过原型链实现对原型属性和方法的继承,通过借用构造函数,让实例拥有自己的属性。

    function SuperType (name) {
      this.name = name
      this.colors = ['red', 'blue', 'green']
    }
    
    function SubType (name, age = 18) {
      SuperType.call(this, name)
      this.age = age
    }
    SubType.prototype = new SuperType()
    SubType.prototype.getAge = function () {
      return this.age
    }
    var ins = new SubType('seven', 22)
    ins.colors.push('black')
    var ins2 = new SubType('six', 11)
    console.log(ins2)
    

    原型式继承

    Object.create()

    function Foo(name) { 
        this.name = name;
    }
    Foo.prototype.myName = function() { 
        return this.name;
    };
    function Bar(name,label) { 
        Foo.call( this, name ); 
        this.label = label;
    }
    // 我们创建了一个新的 Bar.prototype 对象并关联到 Foo.prototype 
    
    Bar.prototype = Object.create( Foo.prototype );
    
    Bar.prototype.myLabel = function() {
     return this.label;
    };
    var a = new Bar( "a", "obj a" );
    a.myName(); // "a"
    a.myLabel(); // "obj a"
    

    参考链接

    JavaScript深入之从原型到原型链

    相关文章

      网友评论

          本文标题:再看原型链

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