美文网首页ECMAScript 6程序员
Javascript: 当我们在重写原型链时,我们在干嘛?

Javascript: 当我们在重写原型链时,我们在干嘛?

作者: mary_s | 来源:发表于2016-04-16 22:55 被阅读205次

    JS中的原型链,曾经困惑过我。理论上说,当访问一个对象的属性时,首先在本身的属性里去找,如果没有找到,就去这个对象的原型找。如果原型有,就是它了,如果没有继续在往原型的原型找下去。。。

    那么问题来了,假设这里的访问是指“写属性”,在搜索这个实例对象原型的时候,咦,找到了这个属性,那么现在重写这个属性,究竟是在本实例对象内新写了这个属性,还是在原型上重写了此属性呢?

    区别在于,若是在本实例对象写属性,那么虽然以后访问(不论是读还是读写)的都是本实例的该属性,但其实原型上的这个属性也并没有消失也没有改变,只是访问的时候屏蔽它了。

    而若是直接改写了原型上这个属性,那么以后任何这个原型的其它实例,它们的这个属性都会改变(前提是这些实例并没有自身的同名属性)。

    到底是哪种呢,我们把代码跑起来看看!

    function SuperType () {    
        this.property = true;
    }
    SuperType.prototype.getSuperValue = function () {    
        return this.property;
    };
    function SubType () {    
        this.subproperty = false;
    }
    SubType.prototype = new SuperType();
    
    //给新实例写方法 这方法到顶级原型都没有 所以写在新实例里面了
    SubType.prototype.getSubValue = function () {    
        return this.subproperty;
    }
    
    //给新实例写方法 这方法顶级原型有 
    //所以究竟是给实例添加了同名属性,还是直接改写了顶级原型??
    SubType.prototype.getSuperValue = function () {    
        return false;
    }
    var instance = new SubType();
    console.log(instance.getSubValue()); //false
    console.log(instance.getSuperValue()); //false
    console.log(SuperType.prototype.getSuperValue()); //undefined
    

    undefined!!看到了没,顶级原型上的这个getSuperValue并没有被改写!如果改写了,那就是false,而不是undefined,是undefined的意思就是,还是原型的这个属性还是那个return this.property的函数,因为没有实例化,所以不存在this, 所以结果是undefined。也就是说,给对象写一个自身并不具有的属性,而原型却具有的属性,那么结果不是改变它原型对象上的改属性,而是给它自身添加了这个同名属性!原型上的该属性,并不会改变!只是在访问实例的时候被屏蔽!

    最后,通过prototype其实是通过指针来继承属性,所以如果链断了,或者原型指针重新指向另一个对象,那么之前通过prototype继成的任何属性全部消失。但如果是通过contructor构造函数去实例化的对象,是没有这个问题的,因为通过constructor产生的属性,全部是副本,而不是指针。prototype可以通过指针一直引,a的prototype指向b对象,b的prototype指向c对象,而constructor也有类似这样的方法可以满足链式继成,例如a的constructor是bCon函数,而bCon函数里,调用cCon函数去call(this),即cCon.call(this)就是在这个constructor函数里面。

    接下来又发生了一件怪事,以上的重写都通过"="来直接赋值,当然简洁明了,毫无疑问是真正的重写。那么有时候我们对原型上某属性值是数组,我们对它的操作又是另外一番光景了。看以下代码。

    //object(o) 函数作用: 以o为原型实例化一个对象
    function object(o) {    
        function F () {};    
        F.prototype = o;    
        return new F();
    }
    var person = {    
        name: 'Nicholas',    
        friends: ['shelby','Court','Van']
    };
    var anotherPerson = object(person);//anotherPerson是以person为原型的一个实例对象
    anotherPerson.name = 'Greg';
    console.log(anotherPerson.name); //'Greg'
    console.log(person.name); //'Nicholas'
    
    anotherPerson.friends = 'mary'; 
    console.log(anotherPerson.friends); //mary
    console.log(person.friends); // ['shelby','Court','Van']
    
    anotherPerson.friends.push('Mary'); 
    console.log(anotherPerson.friends); // ['shelby','Court','Van','Mary']
    console.log(person.friends); // ['shelby','Court','Van','Mary']
    

    当你给实例写新属性值,这个新属性名在实例自身上并没有,但是该实例的原型上有这个属性的时候,有以下两种情况:

    1. 这里的“写”属性,是指用''=" 赋值,其实是重新建立一个副本,那么原型上的该属性值不会改变或消失,只是实例自身拥有了这个属性,那么以后对该实例上这个属性的读写,都是发生在自身这个属性上,不涉及原型。比如上例对name属性的重写。原型中的name还是‘Nicholas’,而实例的name则是自己重写过后的值。
    2. 这里的“写”属性,是引用的方式,比如上例,使用的是anotherPerson.friends.push('Mary'); , 这里其实是使用指针,指向原型上这个属性,通过指针去重写的属性,都是指向同一个内存上的属性,并没有副本。一旦改变,所有指向它的引用,全部都会发生改变。

    相关文章

      网友评论

        本文标题:Javascript: 当我们在重写原型链时,我们在干嘛?

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