本文详解JavaScript中不使用new关键字创建对象的两种常见模式。第一种通过直接调用原型方法实现,但会导致所有实例共享同一对象状态;第二种结合new操作符与原型指向修正,在保持无new调用便捷性的同时,确保每个实例拥有独立的作用域和属性,是jQuery等库的核心实现思路。
JavaScript无new构建实例详解
在研读jQuery源码时,其对象创建机制起初令人费解,经过深入分析后理清了思路,现将相关原理整理如下。
第一种无new构建方式分析
第一种实现方式如下:
var A = function(){
return A.prototype.init();
}
A.prototype = {
init:function(){
this.age = 50;
console.log(this);
return this;
},
age:100
}
console.log(A() === new A());

首先分析为何该方式返回结果为true。
函数A()内部实际执行的是A.prototype.init()方法。
当使用new A()时,内部会调用构造函数,该构造函数定义为function(){return A.prototype.init();},因此最终同样执行A.prototype.init()方法。
接下来分析A.prototype.init()函数的返回值。
关键在于this的指向。由于该方法作为原型对象的属性被调用,此时this指向原型对象A.prototype本身。
采用这种创建方式,无论调用多少次A(),返回的都是同一个对象实例。因此,对变量b的修改会直接影响变量a,具体表现见下图。
var a = A();
var b = A();
console.log(a.age);
console.log(b.age);
b.age = 22;
console.log(a.age);
console.log(b.age);

第二种无new构建方式及原理
为解决上述共享状态的问题,下面介绍第二种方式,这也是jQuery所采用的方案。
第二种实现方式如下:
var A = function(){
return new A.prototype.init();//①
}
A.prototype = {
init:function(){
this.age = 50;
console.log(this);
return this;
},
age:100
}
A.prototype.init.prototype = A.prototype;//②
var a = new A();
var b = new A();
console.log(a===b);
console.log(a.age);
console.log(b.age);
b.age = 22;
console.log(a.age);
console.log(b.age);

对代码中标记①和②的部分进行详细分析。
语句①中new A.prototype.init()主要执行了三个步骤。
第一步是创建一个空对象,即var obj = {};。
第二步是将obj对象的__proto__属性指向函数A.prototype.init的prototype属性。
第三步是将A.prototype.init函数的this上下文替换为obj对象,执行A.prototype.init.call(obj),并返回新创建的对象。
由于语句①返回的对象原型是A.prototype.init.prototype,这与A.prototype并无直接关联。为了使新返回的对象能够继承自A.prototype,语句②将A.prototype.init.prototype重新指向A.prototype。
因此,方式二既成功创建了实例,又保证了各个实例间作用域的独立性。
以上介绍了JavaScript中无new构建对象的两种实例详解,希望对读者有所帮助。如需了解更多相关信息,敬请关注本站后续更新。