(this github repo中的完整代码)
在You don't know JS书系列(特别是“this& Object Prototypes”标题)以及许多SO答案(例如this one)中,经常会提到“没有”这样的事情。构造函数“而是通过”构造函数调用“调用的普通函数。我试图通过创建不能用new
调用来创建我的对象的vanilla函数来解决这一问题。
第一次尝试有效:
var assert = function(condition, message) {
if (!condition)
throw new Error(message||'assertion error');
};
var Counter1 = function() {
var count = 0;
return {get: function() {return count;},
inc: function() {count++;}};
};
var c2a = Counter1();
c2a.inc();
assert(c2a.get()===1);
var c2b = Counter1();
assert(c2b.get()===0);
assert(c2a.get()===1); // previous counter is a separate object
现在我试图通过不每次重新创建getter / setter函数来改进上面的代码,而只是简单地将它们分配给原型(这里我失败了):
var Counter2 = function Counter2_() {
var count = 0;
Counter2_.prototype.get = function() {return count;};
Counter2_.prototype.inc = function() {count++;};
assert(Counter2_.prototype.constructor === Counter2_);
var rv = {};
rv.__proto__ = Counter2_.prototype;
return rv;
};
var c = Counter2();
c.inc();
assert(c.get()===1);
assert(Object.getPrototypeOf(c)===Counter2.prototype);
var cb = Counter2();
assert(Object.getPrototypeOf(cb)===Counter2.prototype);
assert(cb.get()===0);
assert(c .get()===1, 'expecting c to be 1 but was:'+c.get());
上一行代码在最后一行失败。
我的理解是上面的代码没有成功维护单独的计数器,因为每次调用Counter2
函数时,先前对象的原型get
被设置为新创建的函数,该函数在词法上绑定到新的count
(再次初始化为0)。 Plus 代码愚蠢,因为每次调用Counter2
函数时都会再次创建函数,并在原型上反复重新分配(带有灾难性的结果)无法维护单独的计数器。)
然而,尝试将作业放在Counter2
函数之外的原型也失败了,因为count
变量不再在范围内:
var Counter3 = function Counter3_() {
var count = 0;
var rv = {};
rv.__proto__ = Counter3_.prototype;
return rv;
};
Counter3.prototype.get = function() {return count;}; // this fails - I no longer have access to count's lexical scope
Counter3.prototype.get = function() {return this.count;}; // this fails too
我的问题是:
1)我读到为什么Counter2
无法保持单独的计数器正确无误?
2)有没有办法使用这个习惯用法(即使用不想用new
调用的“vanilla”函数)但是每次调用函数时都避免重新创建getter / setter?
答案 0 :(得分:0)
1)是的,原型的get和inc每次都被重新定义。原型不是“封闭”的,原型继承的链式行为使这成为可能。
function Counter2(name) {
var count = 0;
Counter2.prototype.get = function() {return count;};
Counter2.prototype.inc = function() {count++;};
var rv = {};
if(name) {
rv.get = function() {console.log(name); return count;};
}
console.log(count)
rv.__proto__ = Counter2.prototype;
return rv;
}
var a = Counter2('a');
var b = Counter2('b');
var c = Counter2();
在以下示例中,a和b不继承get
函数,因为它们的get
键未定义并指向不同的函数实例。请记住,如果传入一个名称,并且知道每次你猜出以下输出将会重新定义原型时,count变量仍然会被分配和唯一绑定。
a.inc();a.inc();
[a.get(), b.get(), c.get()]
是:
[0,0,2]
2)是和否。您可以轻松返回具有每次都不会分配的函数的对象,但如果绑定了变量,它们将在它们之间共享。奇怪的是,通过使用原型继承和new
运算符,这可以很容易地完成。
例如,以下代码不会每次都分配新函数,但a.inc()
会增加b
的值以及其他值(如果被调用)。
var count = 0;
function inc() {++count;}
function get() {return count;}
function Count() {
return {inc: inc, get: get};
};
var a = Count();
var b = Count();
通过使用new
运算符,可以实现唯一绑定的“变量”,而无需每次都分配新函数。
function Count(){}
Count.prototype.count = 0;
Count.prototype.get = function() {return this.count;};
Count.prototype.inc = function() {this.count++;};
var a = new Count();
var b = new Count();
a.inc();
a.get();
>>1
b.get();
>>0
a.inc === b.inc
>>true