日期:2014-05-16  浏览次数:20398 次

What's "new" in JavaScript?
老调重弹,在JavaScript中,遇到new操作符时执行了什么操作?
也就是说new到底创造了什么,过程是怎样的?
能否为Function对象增加一个原型方法,模拟new的操作?
Function.prototype.__new__ = function(){
	var emptyFunction = function(){};
	emptyFunction.prototype = this.prototype;
	var newObject = new emptyFunction();
	newObject.__proto__ = this.prototype;
	newObject.constructor = this.prototype.constructor;
	var returnedObject = this.apply(newObject, arguments);
	return (typeof returnedObject === 'object' && returnedObject) || newObject;
};

多数代码来自老道的<<JavaScript: the Good Parts>>
第5,6行是额外的一点补充:
第5行:所有JS对象都有__proto__属性,指向 -- 构造器的prototype. (很多浏览器是可以访问到__proto__的,ie不行.)

第6行:对象的constructor指向不一定是构造器,而是构造器的prototype对象的构造器,用一段代码说明:
function Person(name){
	this.name = name;
}
Person.prototype = {
	getName:function(){
		return this.name;
	}
}
var p = new Person("a");
alert(p.constructor === Person); //false
alert(p.constructor === Object); //true 从第4行看到Person.prototype.constructor === Object 即Person.prototype = new Object(); Person.prototype.getName = function(){..}

这样的constructor指向与constructor(构造器)的字面意义不搭,并不是我们期待的,往往会造成困扰.所以常见OO支持(YUI Kissy)通过显示的重写constructor维持对象的constructor的指向其构造器.
但在对new的模拟中,需要保持与浏览器中new的做法一致:newObject.__proto__ = this.prototype;

第2~4行:首先获取一个空的Function实例,获取构造器的prototype的引用,这就滤掉了构造器内定义的特有属性和特权方法.首要保障prototype的纯正血统.再用一段代码说明
function Person(name,age){
	this.name = name;
	this.age = age;
	this.getAge = function(){//特权方法(privileged methods) 避免其被子类继承
		return this.age
	}
}
Person.prototype.getName = function(){ //原型方法 需要被其子类集成 
	return this.name;
}//BTW: 通过这种方式为Person扩充原型方法,没有通过"="操作符为Person.prototype赋予新的对象(= new Objecct) 所以Person.prototype.constructor没有出现混乱.


第7行:在保证了新创建对象的prototype的无增无损后,这一行代码保障特有属性和特权方法是新创建对象中依然是特有的,而且特有属性和特权方法和传入构造器的参数相关,通过apply方法将构造函数功能和传入参数作用于新构建的对象之上.将构造函数的返回值放入returnedObject暂存.

第8行:新的对象已经创建好,如果returnedObject是非空对象则返回returnedObject,否则返回新创建的对象.returnedObject的这个判断也不难理解,我们可以利用这个特性创建单件(Singleton).比如这样写:
function Singleton(){
	if(Singleton.inst){
		return Singleton.inst;
	}
	Singleton.inst = this;
}
var s1 = new Singleton();
var s2 = new Singleton();
alert(s1===s2);//true


还有一个困扰,一旦Singleton前面没有new,Singleton.inst将指向window.
其实所有类都需要判断是否是通过new进入的函数体.判断的方法不复杂,可以参照YUI使用instanceof判断:
function Singleton(){
	if(this instanceof arguments.callee){ //如果没有通过new进入函数体,这里的this会指向window
		return new arguments.callee;
	}
	if(Singleton.inst){
		return Singleton.inst;
	}
	Singleton.inst = this;
}
var s1 = new Singleton();
var s2 = Singleton();
alert(s1===s2);//true

这就是为Function增加原型方法__new__来做对new操作符的模拟,可能还不完善,但对理解一些JS内部机制有帮助.要是有错误,请一定帮忙指出~
最后,推荐看一下ECMA-262-3 in Detail系列文章.