关于从构造函数返回的闭包

时间:2013-11-29 18:09:43

标签: javascript closures

之前已经问过这个问题,但没有得到明确的答案,所以我会再问一遍:

function complex(x, y) {
    return {
        real: function () {
            return x;
        },
        imaginary: function () {
            return y;
        },
        add: function (c) {
            return complex(x + c.real(), y + c.imaginary());
        }
    };
}

这是使用原型方法的“复杂”类的骨架。问题是:使用此函数创建的每个复杂对象是否都有三个内部函数的代码副本?

有些人说没有;解释器/编译器将检测这种情况并对其进行优化。有些人说是的,构造函数返回的每个对象都会有一个单独的副本。有些人甚至说不可能这样做。

在我看来,只要函数的“代码”与定义函数的“上下文”分开,就不会是不可能的。也就是说,构造函数返回的每个对象都有三个属性'real','imaginary','add',但这些属性将是特殊的闭包对象{'context','code'};在任何返回的对象'c','c.real.context'==='c.imaginary.context'===等;在任何两个对象'c1','c2','c1.real.code'==='c2.real.code'等。

任何人都可以放弃任何光明吗?

修改

上面的“使用原型方法”这个短语(错误的原因)比我的问题更受关注:每个内部函数的CODE是否会被“复杂”创建的实例共享?显然,每个对象都有不同的属性,c1.real!== c2.real(注意我不是真的, c1.real()可能等于或不等于c2.real())。问题是c1.real和c2.real是否会共享相同的CODE。

该模式旨在将x和y保密。我只是想知道它是否会带来严重的内存损失,特别是在对象中返回的函数很大的情况下。这至少意味着每个对象具有每个方法的属性,每个方法可以为每个对象提供几个字节。但是,更糟糕的是,每个方法的CODE是否在每个对象中都重复了?

结束编辑

2 个答案:

答案 0 :(得分:1)

在你的设计中绝对没有原型(除Object.prototype之外)。你目前所做的与基本相同:

var complexObject1 = { someFn: function () {} },
    complexObject2 = { someFn: function () {} };

正如您在上面的示例中所看到的,对象根本不共享函数或任何其他成员(Object.prototype成员除外)。

以下是使用原型方法实现所需功能的方法。

var complex = {

    init: function (x, y) {
        this.x = x;
        this.y = y;
        return this;
    },

    real: function () {
        return this.x;
    },

    imaginary: function () {
        return this.y;
    },

    add: function (c) {
        return Object.create(Object.getPrototypeOf(this)).init(
            this.x + c.real(), 
            this.y + c.imaginary());
    }
};

var c1 = Object.create(complex).init(1, 2),
    c2 = Object.create(complex).init(3, 4),
    c3 = c1.add(c2);

但是,您可能更好地使用与具有构造函数的经典模型更相似的东西。至少在Chrome中由于hidden classes

function Complex(x, y) {
    this.x = x;
    this.y = y;
}

Complex.prototype = {
    constructor: Complex,
    //other functions...
};

var c1 = new Complex(1, 2);
//...

另外,为了更好地阐述你的主要问题......使用你的方法,没有可能的方法在对象之间共享函数,因为函数将依赖于父作用域上的闭包来访问xy个变量。您无法在执行时切换函数的闭包范围。可能存在一些编译器优化,但是在这种特定情况下,我怀疑编译器是否足够智能以在内部创建单个函数实例并在调用时使用预期范围调用它。

答案 1 :(得分:1)

  

使用此函数创建的每个复杂对象是否都有三个内部函数的代码副本?

没有。所有函数都将共享代码(和要解释的字节码)。

但是,当人们说每个对象确实包含自己的3个函数对象和1个范围结构(包含xy)时,人们就是对的。必须为每个complex“实例”重新实例化这三种方法,并引用不同的父作用域,但它们将共享其代码。