加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员  
 
x
 
 本帖最后由 沉滞的剑 于 2016-8-26 15:03 编辑  
 
原型是js和c++之类面向对象的语言本质区别所在。 
在js中一切都是对象,js本身并没有类这样的概念,与其说一个对象是类的一个实例,不如说一个对象是另一个对象的克隆。 
具体的,就需要了解prototype属性和__proto__属性了。 
 
1.Function 对象 
 
函数对象是很特殊的一类对象, 它比一般的Object对象多出来很多属性。 
对比一下: 
例子1: 
var fun = new Function(); var obj = new Object(); 
 
 var fun = new Function();  
var obj = new Object();  
 
  
用控制台就能发现Function对象拥有比Object对象更多的成员。 
那这些多出来的成员是从哪里来的呢?当然是从Function对象那里找来的。 
在new Function的时候fun肯定从Function那里克隆了一部分内容。 
但是,它是克隆的Function的成员么?乍一看上去Function的成员和fun的成员是一致的, 
但其实真正的源头是Function.prototype,用控制台查看Function.prototype的话就会发现这面也有和Function一样的成员。 
推理一下,如果拷贝的是Function的话,那么fun也应该有何Function一样的prototype,但结果fun的prototype是空的。 
所以说fun在创造的时候是从Function.prototype处拷贝的成员。 
那么如果这个推理是正确的,那么对其他函数对象(比如fun)来说这个克隆规则也一样适用。 
例子2: 
var people = new Function(); people.age = 80; people.prototype.age = 24; var li = new people(); li.age; 
 
 var people = new Function();  
people.age = 80;  
people.prototype.age = 24;  
var li = new people();  
li.age;  
 
  
输出: 24; 
结论: a = new b的时候, a会拷贝b的prototype属性中的成员 
 
2. new的原理 
那么我们可以分析一下new的工作原理 
1. fun拷贝了Function.prototype里的成员 
2. fun修改了这些成员方法中的this指针(这些方法的指针原先是指向Function.prototype的,现在指向了fun) 
3. fun执行了构造器中的内容比如: 
var people = function(age){this.age = age}; people.age = 80; people.prototype.age = 24; var li = new people(44); li.age; 
 
 var people = function(age){this.age = age};  
people.age = 80;  
people.prototype.age = 24;  
var li = new people(44);  
li.age;  
 
  
那么new出来的成员和原本的成员有关系么 
例子3: 
var people = new Function(); people.age = 80; people.prototype.age = 24; var li = new people(); console.log(li.age); li.age = 10; console.log(people.prototype.age); people.prototype.age = 30; console.log(li.age); 
 
 var people = new Function();  
people.age = 80;  
people.prototype.age = 24;  
var li = new people();  
console.log(li.age);  
li.age = 10;  
console.log(people.prototype.age);  
people.prototype.age = 30;  
console.log(li.age);  
 
  
输出: 24,24,10 
例子4: 
var people = new Function(); people.age = 80; people.prototype.makeCall = function(){console.log('Hello')}; people.prototype.makeCall2 = function(){console.log('Hello 2')}; var li = new people(); li.makeCall = function(){console.log('Hello 3')}; li.makeCall(); people.prototype.makeCall(); people.prototype.makeCall2 = function(){console.log('Hello 4')}; li.makeCall2(); 
 
 var people = new Function();  
people.age = 80;  
people.prototype.makeCall = function(){console.log('Hello')};  
people.prototype.makeCall2 = function(){console.log('Hello 2')};  
var li = new people();  
li.makeCall = function(){console.log('Hello 3')};  
li.makeCall();  
people.prototype.makeCall();  
people.prototype.makeCall2 = function(){console.log('Hello 4')};  
li.makeCall2();  
 
  
输出:Hello 3, Hello, Hello 4; 
结论: 通过上面两个例子你会发现,对于在li中重新定义过的成员(比如 makeCall和age)不会受到people.prototype改变的影响 
而未在li中定义过的成员MakeCall2就依然会跟随者people.prototype的改变而改变。 
那这是为什么呢?拷贝的和重新定义的属性有什么不同呢? 
 
3.原型链 
先介绍一个函数:hasOwnProperty, 这个函数用来判断一个成员是否是这个对象自有的。 
例子5: 
li.hasOwnProperty('makeCall2'); li.hasOwnProperty('makeCall'); 
 
 li.hasOwnProperty('makeCall2');  
li.hasOwnProperty('makeCall');  
 
  
输出: false, true; 
结论: 显然, makeCall2并没有真正复制到了li上,而只是被li引用了而已。 
那么js又是怎么找到makeCall2的呢? 
这就要谈到__proto__属性了。 
所有的对象都有一个__proto__属性。 
在函数对象的情形下li.__proto__ 可以等价于 people.prototype 
当你访问li的一个成员的时候,js首先会在li的所有自有的成员中寻找。 
比如你在寻找MakeCall2, 但是li并没有这个成员。 
那么js则会转到li.__proto__里去寻找 
假设还没找到则会到li.__proto__.__proto__里寻找,一直到找到或找不到为止。 
这就是js最重要的原型链的原理。 
 
 
结论: 
1.a = new b 拷贝的是b.prototype的属性而不是b的属性 
2.a = new b 会导致 a.__proto == b.prototype,__proto__代表了对象的原型, 如果在对象中找不到指定的成员, js会在对象的原型中寻找。 |