Javascript闭包 - 变量的生命周期

时间:2013-11-24 18:14:26

标签: javascript closures

对于Javascript和c#背景相当新,我一直在磕磕绊绊。我很快就知道,我需要了解函数本身就是对象这一事实,而且JS闭包通常是导致混淆的原因。

我想了解这段代码

// Function which returns object with function properties
function myFunc() {

    value = 42;

    var result = {
        value: value,
        getValue: getValue,
        incrementValue: incrementValue,
        setValue: setValue,
    };
    return result; 

    function setValue(y) {
        value = y;
    };

    function getValue() {
         return value;   
    }; 

    function incrementValue() {
        value++;
    };
};

// Helper function to print out results
function printResults(m,x){
    $('#output').append(m + ': ' + x).append('<br/>');
};

var myObject = myFunc();  // returns the object 
printResults('Inital call to getValue',myObject.getValue());

myObject.setValue(59);
printResults('Called changeValue',myObject.getValue());

printResults('Value property of object',myObject.value);
printResults('Called getValue again',myObject.getValue());

myObject.incrementValue();
printResults('Call increment value',myObject.getValue());
printResults('Value property of object',myObject.value);

在jsFiddle

中运行时,我得到以下结果
Inital call to getValue: 42
Called changeValue: 59
Value property of object: 42
Called getValue again: 59
Call increment value: 60
Value property of object: 42

这些表明函数在它们的闭包中使用变量value,这在内部函数的调用之间持续存在。但是,value的值在返回的对象中不会改变。

我认为我得到的基本观点是使用在定义时生效的范围链来执行函数。

问题 我可以使返回对象的value属性以相同的方式运行 - 或者是通过函数返回它的唯一方法,因为后者在其闭包中保留变量吗?

,仅用于确认,对于myFunc()的每次调用,我假设我将获得一个对象,其函数属性将具有自己的作用域链,因此独立于每次调用。< / p>

3 个答案:

答案 0 :(得分:5)

首先,在声明变量时不要忘记var关键字。在value = 42内声明myFunc时,实际上是在全局命名空间而不是函数范围中创建变量。它应该像这样开始:

function myFunc() {
    var value = 42;

现在,myObject.result返回42,因为myFunc返回您的result对象,其中包含函数内声明的value变量的副本。

您的功能setValuegetValueincrementValue正在更改value的值,而不是result.value。当您调用myObject.value时,您将从返回的对象中获取值,而不是函数的内部变量。

你可以使用这样的东西让它工作:

function myFunc() {
    var value = 42;
    var result = {
        value: value,
        getValue: getValue,
        incrementValue: incrementValue,
        setValue: setValue
    };

    return result;

    function setValue(y) {
        result.value = y;
    }

    function getValue() {
        return result.value;
    }

    function incrementValue() {
        result.value++;
    }
}

然而,有比这更好的设计模式。您可以使用new关键字和prototype来定义可用于从函数返回的对象的方法。举个例子:

function myFunc() {
    this.value = 42;
}

myFunc.prototype.setValue = function(y) {
    this.value = y;
}

myFunc.prototype.getValue = function(y) {
    return this.value;
}

myFunc.prototype.incrementValue = function(y) {
    this.value++;
}

var myObject = new myFunc();
console.log(myObject.getValue()); // 42
myObject.setValue(30);
myObject.incrementValue();
console.log(myObject.getValue()); // 31

答案 1 :(得分:1)

  

我可以使返回对象的value属性以相同的方式运行

如果您的意思是它显示更新的值,是的,您可以这样做。您只需更改代码以更新value属性:

function myFunc() {

    var value = 42; // don't forget var!

    var result = {
        value: value,
        getValue: getValue,
        incrementValue: incrementValue,
        setValue: setValue,
    };
    return result; 

    function setValue(y) {
        result.value = value = y;
    }

    function getValue() {
         return value;   
    }

    function incrementValue() {
        value++;
        result.value = value;
    }
}

我选择同时使用valueresult.value的原因是为了防止通过result.value修改值。如果您注意到,我不会在result.value内部读取,我只会写信给它。这意味着从外部代码分配给result.value没有效果。这符合您现有代码的工作方式。

  

而且,为了确认,对于myFunc()的每次调用,我假设我将得到一个对象,其函数属性将具有自己的作用域链,因此独立于每次调用。

是的,myFunc的每次调用都会创建一个新对象和新函数,它们完全独立于先前调用创建的对象/函数。

答案 2 :(得分:1)

是的,你可以:

var result = {
    get value() {
        return value;
    },
    getValue: getValue,
    incrementValue: incrementValue,
    setValue: setValue,
};

对于ECMAScript 5的万岁。当然,这不适用于IE&lt; 8。

&lt;旁边&gt; value = 42;应为var value = 42;。&lt; / aside&gt;

顺便说一句,这与变量的生命周期没有多大关系 - 它只是赋值的工作原理。 JavaScript中有引用,但没有“引用变量”或“引用属性”。该对象包含当时value所有内容的副本;像这样创建一个getter就像创建一个隐式调用的函数。