js之prototype与_proto_

prototype要点:

1:所有对象本身都可以有prototype属性,它本身也是对象。如此循环下去就形成一个prototype链,这个链当遇到链中的prototype为null时即中止。(Object的默认prototype是null)。

2:js的原型继承是引用原型,而不是复制原型。当修改原型时会导致所有的实例变化。反过来实例的变化不会对原型产生影响,但会对该实例的其他成员产生影响,如:

<script>  
    var ClassA = function() {    
        this.a = [];    
    };  
    var ClassB = function() {  
        this.b = "b";  
    };    
    ClassB.prototype = new ClassA();    
    var objB1 = new ClassB();   
    var objB2 = new ClassB();   
    objB1.a.push(1,2,3);   
    alert(objB2.a); // 1,2,3   
</script>
由此可以看出,在子类对象中访问原型的成员对象后,由于采用的是引用原型方式,其实它和ClassB.a.push实现的是同样的功能。所以其它的子类也会跟着变化。

3:构造子类时,原型的构造函数不会被执行,如:

<script>  
    var ClassA = function() {  
        alert("ClassA");   
        this.a = "a";  
    };  
    var ClassB = function() {    
        alert("ClassB");    
        this.b = "b";   
    };   
    ClassB.prototype = new ClassA();// ClassA  
    var objB1 = new ClassB();  // ClassB  
    var objB2 = new ClassB();  // ClassB  
</script>

_proto_要点:

1:所有的对象都有__proto__的内部属性,指向构造函数的prototype,即原型, 对象在调用一个方法时会首先在自身里寻找是否有该方法,若没有,则通过内部的__proto__属性去自己的构造函数原型上去寻找,依次层层递进,直到Object.prototype.__proto__=null为止,这样往上追溯的整个过程,即原型链。

分析:

var Person = function(){};
var p = new Person();

new的过程拆分成以下三步:
(1) var p={}; 也就是说,初始化一个对象p
(2) p.__proto__ = Person.prototype;
(3) Person.call(p); 也就是说构造p,也可以称之为初始化p

2:__proto__是一些浏览器提供的一个查看prototype的接口,也就是说prototype才是标准的原型、可用在代码中,而__proto__用于调试。:

3:__proto__指向的是当前对象原型对象,而prototype指向的,是以当前函数作为构造函数构造出来的对象原型对象

举个例子:

/构造一个对象
function NvShen () { 
this.name = "Alice"; 
}
//先定义一个hand对象再把hand赋值给NvShen的prototype
var hand = { whichOne: "right hand", 
someFunction: function(){ console.log("not safe for work."); } };
NvShen.prototype = hand;
//这个时候,我们可以用NvShen作为构造函数,构造出myObject对象
var myObject = new NvShen();
 console.log(myObject.__proto__ === NvShen.prototype) //true

分析:通过上面的代码,我们构建了一个女神对象myObject,而myObject的原型是hand对象,而刚好myObject的构造函数NvShen()的prototype属性也指向hand对象。现在我们知道,prototype与__proto__的关系就是:你的__proto__来自你构造函数的prototype

hand这种直接用赋值语句加花括号来创建的对象,叫做对象字面量,你可以想象JavaScript内置了一个叫Object()的构造函数,这个函数的prototype属性指向的是一个空对象:

console.log(Object.prototype) //输出{}

而所有对象字面量都是通过Object()构造出来的,换言之,对象字面量的__proto__属性都指向Object.prototype,

所以我们可以知道, hand.__proto__ 指向的是Object.prototype,而Object.prototype这个对象,它的__proto__指向的是null,

console.log(Object.prototype.__proto__);//输出null
来张图:

总结:

function Person (name) { this.name = name; }
function Mother () { }
Mother.prototype = {    //Mother的原型
    age: 18,
    home: ['Beijing', 'Shanghai']
};
Person.prototype = new Mother(); //Person的原型为Mother

//用chrome调试工具查看,提供了__proto__接口查看原型,这里有两层原型,各位还是直接看chrome好一点。
var p1 = new Person('Jack'); //p1:'Jack'; __proto__:{__proto__:18,['Beijing','Shanghai']}
var p2 = new Person('Mark'); //p2:'Mark'; __proto__:{__proto__:18,['Beijing','Shanghai']}

p1.age = 20;  
/* 实例不能改变原型的基本值属性,正如你洗剪吹染黄毛跟你妈无关
 * 在p1实例下增加一个age属性的普通操作,与原型无关。跟var o={}; o.age=20一样。
 * p1:下面多了个属性age,而__proto__跟 Mother.prototype一样,age=18。
 * p2:只有属性name,__proto__跟 Mother.prototype一样
 */

p1.home[0] = 'Shenzhen'; 
/* 原型中引用类型属性的共享,正如你烧了你家,就是烧了你全家的家
 * 这个先过,下文再仔细唠叨一下可好?
 * p1:'Jack',20; __proto__:{__proto__:18,['Shenzhen','Shanghai']}
 * p2:'Mark';    __proto__:{__proto__:18,['Shenzhen','Shanghai']}
 */

p1.home = ['Hangzhou', 'Guangzhou']; 
/* 其实跟p1.age=20一样的操作。换成这个理解: var o={}; o.home=['big','house']
 * p1:'Jack',20,['Hangzhou','Guangzhou']; __proto__:{__proto__:18,['Shenzhen','Shanghai']}
 * p2:'Mark';                             __proto__:{__proto__:18,['Shenzhen','Shanghai']}
 */

delete p1.age;    
/* 删除实例的属性之后,原本被覆盖的原型值就重见天日了。正如你剃了光头,遗传的迷人小卷发就长出来了。
 * 这就是向上搜索机制,先搜你,然后你妈,再你妈他妈,所以你妈的改动会动态影响你。
 * p1:'Jack',['Hangzhou','Guangzhou']; __proto__:{__proto__:18,['Shenzhen','Shanghai']}
 * p2:'Mark';                          __proto__:{__proto__:18,['Shenzhen','Shanghai']}
 */


Person.prototype.lastName = 'Jin'; 
/* 改写原型,动态反应到实例中。正如你妈变新潮了,邻居提起你都说你妈真潮。
 * 注意,这里我们改写的是Person的原型,就是往Mother里加一个lastName属性,等同于Mother.lastName='Jin'
 * 这里并不是改Mother.prototype,改动不同的层次,效果往往会有很大的差异。
 * p1:'Jack',['Hangzhou','Guangzhou']; __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai']}
 * p2:'Mark';                          __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai']}
 */

Person.prototype = { 
    age: 28, 
    address: { country: 'USA', city: 'Washington' }
};
var p3 = new Person('Obama'); 
/* 重写原型!这个时候Person的原型已经完全变成一个新的对象了,也就是说Person换了个妈,叫后妈。
 * 换成这样理解:var a=10; b=a; a=20; c=a。所以b不变,变得是c,所以p3跟着后妈变化,与亲妈无关。
 * p1:'Jack',['Hangzhou','Guangzhou']; __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai']}
 * p2:'Mark';                          __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai']}
 * p3:'Obama';__proto__: 28 {country: 'USA', city: 'Washington'}
 */


Mother.prototype.no = 9527;
/* 改写原型的原型,动态反应到实例中。正如你妈他妈变新潮了,邻居提起你都说你丫外婆真潮。
 * 注意,这里我们改写的是Mother.prototype,p1p2会变,但上面p3跟亲妈已经了无瓜葛了,不影响他。
 * p1:'Jack',['Hangzhou','Guangzhou']; __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai'],9527}
 * p2:'Mark';                          __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai'],9527}
 * p3:'Obama';__proto__: 28 {country: 'USA', city: 'Washington'}
 */

Mother.prototype = { 
    car: 2, 
    hobby: ['run','walk']
};
var p4 = new Person('Tony');
/* 重写原型的原型!这个时候Mother的原型已经完全变成一个新的对象了!人他妈换了个后妈!
 * 由于上面Person与Mother已经断开联系了,这时候Mother怎么变已经不影响Person了。
 * p4:'Tony';__proto__: 28 {country: 'USA', city: 'Washington'}
 */
 
Person.prototype = new Mother(); //再次绑定
var p5 = new Person('Luffy');
// 这个时候如果需要应用这些改动的话,那就要重新将Person的原型绑到mother上了
// p5:'Luffy';__proto__:{__proto__: 2, ['run','walk']}

p1.__proto__.__proto__.__proto__.__proto__ //null,你说原型链的终点不是null?
Mother.__proto__.__proto__.__proto__    //null,你说原型链的终点不是null?


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值