javascript – “构造函数”函数,不应与构造函数调用一起使用

(完整代码在this github repo)

You don’t know JS系列丛书(特别是“this& Object Prototypes”标题)以及许多SO答案(例如this one)中,经常会发现没有“构造函数”这样的东西,而是通过调用的普通函数. “构造函数调用”.我试图通过创建vanilla函数来解决这一问题,这些函数并不是用new来调用来创建我的对象.

第一次尝试有效:

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被设置为新创建的函数,该函数在词法上绑定到新计数(再次初始化为0).此外,代码是愚蠢的,因为每次调用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?

最佳答案
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键未定义并指向不同的函数实例.请记住,如果传入一个名称,并且每次猜到以下输出将被重新定义时,计数变量仍然会被分配和唯一绑定.

a.inc();a.inc();
[a.get(), b.get(), c.get()]

是:

  [0,0,2]

2)是和否.您可以轻松返回具有每次都不会分配的函数的对象,但如果绑定了变量,它们将在它们之间共享.奇怪的是,通过使用原型继承和新的运算符,这可以很容易地完成.

例如,以下代码不会每次都分配新函数,但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

转载注明原文:javascript – “构造函数”函数,不应与构造函数调用一起使用 - 代码日志