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

时间:2015-11-04 09:39:45

标签: javascript node.js

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?

1 个答案:

答案 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