JS对象深入剖析
对象概述
Objects are mutable keyed collections.
An object is a container of properties, where a property has a name and a value.
A property name can be any string, including the empty string.
A property value can be any JavaScript value except for undefined.
一般对象
Object includes a prototype linkage feature that allows one object to inherit the properties of another.
这里,这个原型链接(a prototype linkage)是隐藏的(在Debugger里面看不见的),在某些实现中,它的属性名是__proto__
任何一个对象的__proto__链接都是不为空的,对于一个从Object Literal中创建的对象,其__proto__指向Object.prototype。只有Object的prototype的_proto_指向为null。
有了__proto__链接之后,就可以发挥一些面向对象的威力了,头一个就是Delegation。Delegation是指当从一个对象中获取一个属性失败时,JS会自动再尝试去其__proto__链接指向的对象中去查找,如果还找不到,就沿着__proto__一路找上去,指导找到Object.prototype为止。根据上述Delegation算法的描述,你可以很容易理解Delegation过程是动态的,这次可以从祖先中找到A属性,不代表下次一定能找到。
这里需要注意的是,__proto__链接只对查询有效,而对Update无效,也就是说,如果你给一个属性赋值的话,而这个属性在该对象中还不存在,而在__proto__祖先中已有同名属性,但这是JS会在该对象中再新建一个属性,而不是修改祖先的属性值
函数对象
函数对象首先是一个对象,所以,它也有__proto__链接,也有Delegation。函数对象还有一个特殊之处就是它会有一个叫做prototype的属性。
当用var a = function(){}定义一个函数时,JS会生成一个函数对象,这个函数对象的__proto__指针指向Function.prototype。同时还会有一个prototype属性,其值是一个对象,这个对象只有一个属性是构造函数,构造函数的值就是这个函数的定义function(){},而这个对象(prototype)的__proto__指针则指向Object.Prototype。
了解了函数对象,再看看它如何发挥作用:当用new激活一个函数时,一个新对象会被创建出来,其内容是执行函数的返回结果,而这个对象的__proto__链接指向函数的prototype属性对象。
创建对象Object.Create
1 Object.create = function (o) {
2 var F = function () {};
3 F.prototype = o;
4 return new F();
5 };
6 var b = Object.create(a);
首先,在执行var b = Object.create(a)之前,已经有了a对象,在执行了var F = function () {}之后,F函数对象被创建出来,F的prototype属性也被赋值指向一个新创建出来的对象,执行了F.prototype = o; 之后, F的prototype属性指向了对象A,执行new F();之后,即当用new激活一个函数时,一个新对象会被创建出来,其内容是执行函数的返回结果,而这个对象的__proto__链接指向函数的prototype属性所引用的对象。
最后,我们可以得出,对象的__proto__链接是不能直接修改的,而函数对象的prototype属性是可以修改的,因此,Object.create实际上是利用了这个特点,结合new来完成了一个拷贝创建的过程。
Scope和Closure
对于一个JS程序而言,都有一个Global Scope Object, 我们定义的全局变量,其实都是这个对象的属性,在主程序体中,可以用this来访问这个对象的内容。这时Scope Chain当中只有一个Global Scope Object。
1 var Counter = function() {
2 var value = 0;
3 return {
4 increment : function(inc) {
5 value += typeof(inc) === "number" ? inc : 1;
6 },
7 getValue : function() {
8 return value;
9 }
10 }
11 }
12 var counter1 = new Counter();
13 var counter2 = new Counter();
14 counter1.increment();
15 counter1.getValue();
16 counter2.getValue();
Scope原则1: 当函数Counter被定义时,JS会把当前有效的Scope Chain保存在函数对象当中备查。
Scope原则2:当一个函数被执行时,会创建一个新的Scope Object,再将函数内容完全分析一遍,将参数和局部变量都加成新Scope对象的属性,最后将新Scope Object作为Scope Chain的最后一节,更新Scope Chain。
执行完var Counter = function() {…}之后,触发原则1,执行var counter1 = new Counter;后触发原则2。
Scope Chain的作用在于之后查找属性时,如果本Scope查不到,就去父Scope里面查,指导Global Scope Object。感觉其实也是Delegation,所以说不定Scope Object之间也是用__proto__来连接的。
之后Counter的内容被执行,increment和getValue两个函数被定义,之后被返回给counter1。
一个函数F的内部函数G可以访问F定义的局部变量和参数,这就叫闭包,它是在JS中实现封装的基础。