Javascript工厂模式变量范围

Javascript factory pattern variable scoping

我正在按照教程显示工厂模式以在javascript中创建对象。 下面的代码让我难以理解它的工作原理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6-2.htm
</head>
<body>
<script type="text/javascript">
function createAddress(street, city, state, zip) {
  var obj = new Object();
  obj.street = street;
  obj.city = city;
  obj.state = state;
  obj.zip = zip;
  obj.showLabel = function() {
    //alert(this.street +"
" + this.city +"," + this.state +"" + this.zip);
    //var obj;
    alert(obj.street +"

" + obj.city +"," + obj.state +"" + obj.zip);
  };
  return obj;
};

var JohnAddr = createAddress("
12 A St.","Johnson City","TN", 37614);
var JoeAddr = createAddress("
10061 Bristol Park","Pensacola","FL", 32503);

JohnAddr.showLabel();
JoeAddr.showLabel();

</body>
</html>

第一个注释行似乎对我来说(在showLabel函数中使用this关键字)。 我不确定在它的位置使用obj是如何工作的。 obj必须在某处引用一个全局变量,因为在该函数中运行时没有定义obj,对吧? 因为我制作了2个对象,在这种情况下不仅仅是运气都能很好地显示,所以obj内容的旧值被正确存储和引用。 但怎么样? 如果我取消注释第二个评论然后它打破了我理解为什么,现在我明确地告诉js我正在谈论一个局部变量而且没有。


欢迎来到封闭世界。你是正确的感觉到你的行为,就像它是一个全球但不是全球的。这就是闭包的行为方式。

基本上在javascript中,当函数返回时,并非所有局部变量都必须像Java或C那样被垃圾收集/释放。如果存在对该变量的引用,则该变量在定义它的函数范围内存活。

从技术上讲,机制是不同的,有些人试图以这种方式解释它,并最终混淆了许多其他人。对我来说,闭包是一种"私有"全局变量,就像全局变量一样,它们在函数中共享,但它们并未在全局范围内声明。就像你在遇到这个功能时所描述的那样。

这里有一些关于与javascript闭包相关的stackoverflow的其他答案,我认为值得一读:

JavaScript的隐藏功能?

请解释在循环中使用JavaScript闭包

或者你可以谷歌短语"javascript closure"来探索这个主题。

补充答案。

至于为什么this在你的代码中起作用的解释(而不是* cough *试图纠正你的代码,以便this即使它在未修正的版本* cough * ;-)中工作也能工作:

Javascript有后期绑定。很晚,非常晚。 this不仅在编译期间没有绑定,它甚至在运行时也没有绑定。它在执行时受到约束 - 也就是说,在调用函数之前,你无法知道它真正指向的是什么。调用者基本上决定this的值是什么,而不是使用this的函数。

一些时髦的javascript后期绑定动作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function foo () {
    alert(this.bar);
}

var bar ="hello";
var obj = {
    foo : foo,
    bar :"hi"
};
var second_obj = {
    bar :"bye"
};

foo(); // says hello, 'this' refers to the global object and this.bar
       // refers to the global variable bar.

obj.foo(); // says hi, 'this' refers to the first thing before the last dot
           // ie, the object foo belongs to

// now this is where it gets weird, an object can borrow/steal methods of
// another object and have its 'this' re-bound to it

obj.foo.call(second_obj); // says bye because call and apply allows 'this'
                          // to be re-bound to a foreign object. In this case
                          // this refers to second_obj

在你的代码中,this方便地引用调用函数作为其方法的对象,这就是为什么它工作的原因,即使你显然没有使用假设正确的构造函数语法。


objshowLabel函数内部工作的原因是因为obj是局部变量。每次调用create address时都会声明该函数。在JavaScript中,我们称之为闭包。

一般而言,原型对象创建优先于此工厂模式。

现在来看看这个原型示例:

1
2
3
4
5
6
7
8
9
10
11
var Address = function(street, city, state, zip){
    this.street = street;
    this.city = city;
    this.state = state;
    this.zip= zip;
};

Address.prototype.showLabel = function(){
    alert(this.street +"
"
+ this.city +"," + this.state +"" + this.zip);
}

现在,当我使用新关键字创建新地址时:

1
2
3
// create new address
var address = new Address('1', '2', '3', '4');
address.showLabel(); // alert

代码的行为与您期望的完全一样。但是如果我不在构造函数中使用new关键字this实际上是window对象。

1
2
3
// create new address
var address = Address('1', '2', '3', '4'); // address == undefined
window.showLabel(); // address was added to window

我希望这可以解决一些问题。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Address(street, city, state, zip) {

  this.street = street;
  this.city = city;
  this.state = state;
  this.zip = zip;
  this.showLabel = function() {
    //alert(this.street +"
" + this.city +"," + this.state +"" + this.zip);
    //var obj;
    alert(this.street +"

" + this.city +"," + this.state +"" + this.zip);
  };
};

var JohnAddr = new Address(...);
JohnAddr.showLabel();


这是js古怪的一部分,showLabel是一个闭包并且可以访问obj,因为它在创建时在范围内 - 每次调用createAddress时都会创建一个新的闭包。

要按照您期望的方式使用"this",您需要使用new运算符,这样:

var foo = new createAddress(...

并将成员变量分配给'this'。

在没有使用new的情况下,"this"是全局对象。