在函数内创建的对象的Javascript变量范围

时间:2016-02-18 00:20:10

标签: javascript scope javascript-objects

考虑这段代码,

var getPerson = function() {
    var _name = "";
    var person = {};

    person.getName = function() {
        return _name;
    }

    person.setName = function(value) {
        _name = value;
    }

    return person;
}


p1 = getPerson();
p1.setName("foo");

p2 = getPerson();
p2.setName("bar");

console.log(p1.getName()); // logs "foo"
console.log(p2.getName()); // logs "bar"

有人可以在这里解释_name的范围吗? p1._name和p2._name不存在。该变量真正存储在哪里?

3 个答案:

答案 0 :(得分:1)

_name的范围限定为getPerson函数,因为它是使用var关键字声明的。

嵌套在getPerson中的任何函数都将继承getPerson中声明的变量,并且它们可以覆盖它以声明它们自己的变量。 This concept is called closure并且在进行任何认真的JavaScript开发之前都值得了解。

在JavaScript中,范围不是由块决定的(如在Java中),而是function scoped

因此,_name函数中的setName_name中声明的getPerson相同。

答案 1 :(得分:1)

_name的范围限定为getPerson()函数中的任何代码。

在Javascript中,函数中的局部变量的范围限定在函数的特定调用范围内,并且函数的每次调用都会创建一组新的局部变量。此外,这些局部变量就像其他变量一样被垃圾收集。它们不会严格存储在函数退出时被销毁的堆栈上。

由于垃圾收集,只要仍可访问的代码具有对该变量的引用,函数内的变量就会存在。因此,在getPerson()函数中,_name变量的范围是getPerson()函数的特定调用,但变量的生命周期只要一些代码仍然可以达到变量,即使在getPerson()的调用完成后很长时间。

这在Javascript中被称为闭包,是一个非常有价值和有用的工具。

因为person.getName()person.setName()_name函数执行完毕后仍然有getPerson()变量的引用,所以它将保持活动状态并且不会是垃圾由语言收集。

因此,scope由变量的声明决定。 _name的范围限定在它声明的函数和该函数中也存在的任何其他子范围内。 Javascript具有lexical范围(这意味着范围由声明的位置/方式决定)。但是,范围内变量的生命周期由垃圾收集器单独确定,并且范围内的变量可以远远超出创建范围的函数的执行,只要其他一些可访问代码仍然具有对该变量的引用即可。

我喜欢将函数作用域本身视为Javascript中的垃圾收集实体。虽然Javascript的某些实现甚至可能比整个范围更精细(例如,垃圾收集范围内的各个变量尽可能),但是垃圾收集而不是基于堆栈的范围的概念为您提供了理解它们之间差异的框架。像Javascript这样的语言和像C ++这样的语言,它使用显式堆栈帧来确定局部变量的生命周期。

答案 2 :(得分:1)

我认为你可能是从Java背景来的JavaScript。

var _name = "";定义匿名函数范围内的_name变量(分配给getPerson的函数)。该变量不附加到任何对象,它只是一个普通变量。

如果你真的想做p1._name和p2._name,你可以做一两个解决方法。

使其成为返回对象的属性

var getPerson = function() {
    var person = {
      _name: ''
    };

    person.getName = function() {
        return this._name;
    }

    person.setName = function(value) {
        this._name = value;
    }

    return person;
}


p1 = getPerson();
p1.setName("foo");

p2 = getPerson();
p2.setName("bar");

console.log(p1.getName()); // logs "foo"
console.log(p2.getName()); // logs "bar"

console.log(p1._name); // logs "foo"
console.log(p2._name); // logs "bar"

在返回的对象上创建一个getter

var getPerson = function() {
    var _name = "";

    var person = {
        get _name () {
            return _name;
        }
    };

    person.getName = function() {
        return _name;
    }

    person.setName = function(value) {
        _name = value;
    }

    return person;
}


p1 = getPerson();
p1.setName("foo");

p2 = getPerson();
p2.setName("bar");

console.log(p1.getName()); // logs "foo"
console.log(p2.getName()); // logs "bar"

console.log(p1._name); // logs "foo"
console.log(p2._name); // logs "bar"

还有很多其他方法可以实现这一目标; JavaScript是一种动态语言。

奖金提示

你上面所说的是一个闭包。通过返回person,您可以公开方法person.getNameperson.setName。因为这些方法是在getPerson中定义的,所以它们也可以访问_name变量(因为_name是在同一个函数中定义的。)

getPerson以外的代码无法访问_name,但他们可以通过getName和setName方法(以受控方式)访问。这为_name变量创建了隐私。