Scope & Closure in JavaScript

March 23 2013 , Category: JavaScript

最近一直在关注@lifesinger的博客,以issues的形式写的,还是比较有创意,直接watch就可以”订阅”,免掉了RSS(如果我的GR中的各位大牛都用这种方式写博客倒也不错,也就不必伤感GR的关闭了)。
其中有一篇文章牵扯到了”原生”JavaScript和jQuery之间的话题,关于文章的内容,可以去拜读一下原文.

让我这种菜鸟眼前一亮的是:

具体对 JavaScript 语言来说,会用就好。搞清楚数据类型、作用域、闭包、原型链等基本概念,足矣。再深入进去,对绝大部分人来说,除了能满足下心理上的优越感,对实际工作不会有任何实质性帮助。

“不明觉厉”!我之前有一篇博文说到过自己是一个”野生”程序员,所以也来”研究”一下这些基本概念.

作用域 Scope

作用域的概念很简单:

Scope in a programming language controls the visibility and lifetimes of variables and parameters.

举一个JavaScript: the good part里的例子:

var foo = function () {
  var a = 3, b = 5;
  //
  var bar = function () {
    var b = 7, c = 11;
	//
	a += b + c;
	// a = 21, b = 7, c = 11
  };
  // a = 3, b = 5, c undefined
  bar();
  // a = 21, b = 5
};

当然,这个例子还是有点简单了,理解起来应该没有任何问题.

var x = 10;
function foo() {
  alert(x);
}
(function () {
  var x = 20;
  foo();
  // alert 10, not 20
}) ();

JavaScript函数运行在被定义的作用域中,而不是执行它们的作用域中. 所以即使调用foo()函数前又声明了一个变量x,变量还是会从foo()函数创建时的作用域中去查找x的值.

在StackOverFlow上看到一个关于作用域的问题,其中有个回答得到了572票:

// a globally-scoped variable
var a=1;

// global scope
function one(){
    alert(a);
}

// local scope
function two(a){
    alert(a);
}

// local scope again
function three(){
    var a = 3;
	alert(a);
}

// Intermediate: no such thing as block scope in javascript
// JavaScript不支持块级(block scope)作用域
// 如果支持,那么var a=4;的作用域将被局限在if这个语句块内
function four(){
    if(true){
	    var a=4;
	}
	alert(a); // alerts '4', not the global value of '1'
}

// Intermediate: object properties
function Five(){
    this.a = 5;
}

// Advanced: closure
var six = function(){
    var foo = 6;

	return function(){
	    // javascript "closure" means I have access to foo in here,
	    // because it is defined in the function in which I was defined.
	    alert(foo);
	}
}()

// Advanced: prototype-based scope resolution
function Seven(){
    this.a = 7;
}

// [object].prototype.property
// loses to [object].property in the scope chain
Seven.prototype.a = -1;
// won't get reached, because 'a' is set in the constructor above.
Seven.prototype.b = 8;
// Will get reached, even though 'b' is NOT set in the constructor.

// These will print 1-8
one();
two(2);
three();
four();
alert(new Five().a);
six();
alert(new Seven().a);
alert(new Seven().b);

尽管有关原型链的概念还不是很了解,但是从上面这个例子中,我们应该能很进一步的理解有关JavaScript作用域的知识.当然,从这些代码的角度来研究作用域还是比较浅显的,想进一步了解的同学可以参考一下ECMA深入理解.

闭包 Closure

在上文中的最后一个例子中有一个是关于解释闭包的,我们可以单独拎出来看看:

// Advanced: closure
var six = function(){
    var foo = 6;

	return function(){
	    // javascript "closure" means I have access to foo in here,
	    // because it is defined in the function in which I was defined.
	    alert(foo);
	}
}()

这个例子中的注释已经解释了closure的概念,再来看看JavaScript: the good parts中的说法:

var quo = function (status) {
  return {
    get_status: function() {
	  return status;
	}
  };
};

var myQuo = quo("amazed");

document.writeln(myQuo.get_status());

创建变量myQuo的时候,我们调用了quo,它返回包含get_status方法的一个新对象.该对象的引用包含在myQuo中。即使quo已经返回了,get_status方法仍然可以访问quo对象的status属性.

The function has access to the context in which it was created. This is called closure.

其实也可以这样理解:当内部函数在定义它的作用域的外部被引用时(比如quomyQuo引用时),就创建了该内部函数的一个闭包(引用可访问该内部函数quo的属性和变量).
这方面的知识这样梳理下来也是只知道了一个梗概(强烈欢迎指出错误,以及更为简便的理解方式),更多的还是要持续学习啊.

以前尝试过在Emacs中用Shell,最近在终端里用Emacs,两者一比,我反而更喜欢后者. 命令:emacs -nw nw应该是no window的意思吧. 不过有很dt的事情,就是快捷键会有冲突, 比如C-y不能粘贴,反而要用终端里的粘贴快捷键C-S-v,又不想改掉快捷键,所以……还是改邪归正吧!

–EOF–

支持作者 | 文章采用 CC BY-NC-SA 4.0,转载请注明出处