您好,欢迎访问一九零五行业门户网

Javascript之旅 对象的原型链之由来_javascript技巧

以问题开始:
function base(){}var base = new base()
上面两行代码会创建几个对象(object)?
要回答这个问题,先明确一下javascript里object的概念。
objects
在javascript里,几乎一切都是object(arrays、functions、numbers、objects……),而没有c#里的class的概念。object的本质是一个name-value pairs的集合,其中name是string类型的,可以把它叫做“property”,value包括各种objects(string,number,boolean,array,function…),指的是property的值。
typeof
既然object包含arrays、functions、numbers、objects……,那怎么区分这些呢?答案是typeof。 typeof返回一个字符串,如typeof(null) = “object”,typeof(false) = “boolean”, typeof(1) = “number”。既然总是返回字符串,那么对于typeof (typeof x),不管x是什么,总是返回”string”。
constructor
js里没有class,也就没有class里的构造函数,那么object是怎么被创建的呢?用构造器:constructor。constructor其实就是function,因此本身也是object。开头的function base(){}就是一个构造器,var b = new base()就是用这个构造器(通过关键字new)创建了一个叫b的object。至此我们可以得出结论,开头的两行代码至少创建了2个object:一个是base,类型为function的object,一个是base,类型为object的object。
function()和object()
这是两个重要的预定义好的构造器。一切function(比如开头的base())都是由function()构造出来的;而object的prototype将会被所有object继承,下面会讲到。
function的创建过程
当执行function base(){this.a = 1}时,相当于var base = new function(“this.a = 1”),也就是说,这行代码本身,将使用预定义好的function() constructor,来构造一个function型object(即base)出来。在这个创建过程中,js将做哪些事呢?
1, 首先当然会创建一个object起来,base指向这个object。typeof 这个object = “function”
2, 给base附上__proto__属性,让它等于function这个构造器的prototype(也是预定义好的)。这是很重要的一步,也是规律性的一步。(规律:)在执行任意类似varx = new x()时,都会把x的prototype赋给x的__proto__,也就是说,x.__proto__和x.prototype此时会指向同一个对象。
3, 为base创建call属性,该属性是个function。因此我们可以这样写:base.call()
4, 为base创建construct属性,该属性也是个function。在执行var base = new base()时,即会调用这个construct属性。
5, 为base创建scope,length等属性,略。
6, 为base创建prototype属性:先用new object()创建一个对象,为这个对象创建一个属性叫constructor,该属性值设置为base。再把base的prototype设置为这个新创建的对象。伪代码如下:
var x = new object();
x.constructor = base;
base.prototype = x;

先把关注点放到2和6。
__proto__和prototype
从2可以看出来,任意一个用构造器构造出来的object(包括objects和functions),都会有__proto__属性,指向该构造器的prototype属性。注意__proto__是个私有属性,在ie上是看不到的,我用的是chrome,可以看到。
从6可以看出,任意一个用new function()构造出来的functions,都会有prototype属性,该属性是用new object()构建出来的,初始公开属性只有一个constructor。
原型链
再来分析下第6步的伪代码,也就是为function创建prototype的这一步:
var x = new object(); // 参见2中的规律,会有x.__proto__= object.prototype。
x.constructor = base;
base.prototype = x;

此时我们用base()构造一个对象出来:
var base= new base(); // 参见2中的规律,会有base.__proto__ = base.prototype,也就是 = x。
  // 因此有base.__proto__.__proto__ = x.__proto__
// 而x.__proto__ = object.prototype(见上一个代码片段)
// 所以,base.__proto__.__proto__ = object.prototype.

__proto__.__proto__,这就是传说中js对象的原型链!由于用function()创建构造器时的关键的第6步,保证了所有object的原型链的顶端,最终都指向了object.prototype。
property lookup
而我们如果要读某个object的某个属性,js会怎么做呢?
比如有个object叫xxx,我们执行alert(xxx.a),也就是读取xxx的a属性,那么js首先会到xxx本身去找a属性,如果没找到,则到xxx.__proto__里去找a属性,由此沿着原型链往上,找到即返回(没找到,则返回undefined)。可以来看个例子:
上图得知:base本身是没有constructor属性的,但是base.constructor确实能返回base这个函数,原因就在于base.__proto__有这个属性。(base.__proto__是啥?就是base.prototype,上面构建function的第6步的伪代码里,为base.prototype.constructor赋值为base本身。)
object作为“基类”
另外,由于任意object的原型链的顶端都是object.prototype。所以,object.prototype里定义的属性,就会通过原型链,被所有的object继承下来。这样,预定义好的object,就成了所有对象的“基类”。这就是原型链的继承。
看上图,object.prototype已经预定义好了一些属性,我们再追加一条属性叫propertya,那么这个属性和预定义属性一样,都可以从base上读到。
原型继承
已经得知,
对于 var xxx =new object(); 有xxx.__proto__= object.prototype;
对于 var xxx =new base(); 有xxx.__proto__.__proto__= object.prototype;
看上去很像什么呢?从c#角度看,很像base是object的子类,也就是说,由base()构造出来的object,比由object()构造出来的object,在原型链上更低一个层级。这是通过把base.prototype指向由object()创建的对象来做到的。那么自然而然,如果我想定义一个继承自base的构造器,只需把改构造器的prototype指向一个base()构造出来的对象。
function derived(){}
var base = new base();
derived.prototype = base;
var d = newderived(); //很容易推算出:d.__proto__.__proto__.__proto__ = object.prototype.

推算过程:d.__proto__指向derived.prototype,也就是base;则__proto__.__proto__指向base.__proto__,也就是base.prototype,也就是某个new object()创建出来的东东,假设是o;则__proto__.__proto__.__proto__指向o.__proto__,也就是object.prototype。
回答开头的问题,以及几个新的问题
那两行代码至少创建了三个对象:base、base、base.prototype。顺便说说,base是没有prototype属性的,只有function类型的object,在被构建时才会被创建prototype属性。
d.constructor会返回什么呢?
构造器base()和derived()里都是空的,如果有代码,将会怎么被执行呢?
……
待续。见下篇
其它类似信息

推荐信息