一、闭包
在ECMAScript中,函数是first-class(囧,不知道怎么翻译)对象。first-class意味着函数能够作为一个参数传到另外的函数(这种情况被称为functional arguments,简称funargs)。接收funargs的函数被称为higher-order functions ,和运算、操作同一个概念。同样的函数能够从其他函数中被返回。这些被其他函数返回的函数被称为function valued函数(即有返回值的函数functions with functional value)。
由此引出和funargs和functional values相关的两个概念上的问题,这两个问题可以归纳为一个称之为“Funarg problem”的问题,为了精确解决这个问题,引入闭包的概念。(在ECMAScript中都是使用函数的[[Scope]] 属性来解决上面的问题)。
下面来详细介绍下两个问题,第一个问题"upward funarg problem"。这个问题就是在上一篇博客中提到的,当函数执行结束的时候,仍然使用之前的free variables。为了能够访问parent context的变量(甚至当parent context已经结束),在内部函数创建的同时就将它的[[Scope]] 属性保存在parent’s 的scope chain里面了。之后,当函数被调用,上下文的scope chain被组织到了AO和[[Scope]] 属性之中。
Scope chain = Activation object + [[Scope]]
再次注意,正是在创建的时刻,函数保存了parent’s 的 scope chain 。正是这个保存的scope chain 将会用来在之后函数调用的时候进行变量的查找。
function foo() {
var x = 10;
return function bar() {
console.log(x);
};
}
// "foo" returns also a function
// and this returned function uses
// free variable "x"
var returnedFunction = foo();
// global variable "x"
var x = 20;
// execution of the returned function
returnedFunction(); // 10, but not 20
第二个问题是 “downward funarg problem”,这种情况下,parent context 可能会存在,但变量有可能会有歧义:来自于哪一个scope的标示符的值将会被使用?是函数创建的时候静态保存的值还是在执行的时候动态保存的值?为了避免这个问题同时也为了组织好闭包,将会使用到静态作用域(static scope )。
// global "x"
var x = 10;
// global function
function foo() {
console.log(x);
}
(function (funArg) {
// local "x"
var x = 20;
// there is no ambiguity,
// because we use global "x",
// which was statically saved in
// [[Scope]] of the "foo" function,
// but not the "x" of the caller's scope,
// which activates the "funArg"
funArg(); // 10, but not 20
})(foo); // pass "down" foo as a "funarg"
我们可以总结出静态作用域是实现闭包的必要条件。
闭包的定义:闭包是一个以静态/文法(statically/lexically)方式,保存了所有parent scopes的代码块。因此,通过这些保存的scopes 函数能够轻而易举的访问到free variables(不属于自己范围内的变量)。
既然,每个函数在创建的时候都保存了[[Scope]] ,理论上说,所有的函数都是闭包。
另一个需要注意的地方是:某些函数可能会有相同的parent scope (我们有两个以上的内部/全局函数)。这种情况下相同的parent scope chain的所有的函数共享[[Scope]] 属性,一个闭包导致的属性改变会引起其他闭包的属性改变。
function baz() {
var x = 1;
return {
foo: function foo() { return ++x; },
bar: function bar() { return --x; }
};
}
var closures = baz();
console.log(
closures.foo(), // 2
closures.bar() // 1
);
比较下面两段代码:
段一:
var data = [];
for (var k = 0; k < 3; k++) {
data[k] = function () {
alert(k);
};
}
data[0](); // 3, but not 0
data[1](); // 3, but not 1
data[2](); // 3, but not 2
段二:
var data = [];
for (var k = 0; k < 3; k++) {
data[k] = (function (x) {
return function () {
alert(x);
};
})(k); // pass "k" value
}
// now it is correct
data[0](); // 0
data[1](); // 1
data[2](); // 2
二、This value
this value是与执行上下文相关的一个特殊对象,一次因此也被称为上下文对象。
在上下文中任何对象都可以被用作this value。this对象并不是一个VO的一个属性而是执行上下文的一个属性。this value直接从执行上下文中取值,而从不会从任何scope chain 中查找(this的值在一进入上下文的时候就已经被确定了)。
在全局上下文中,this value是全局对象自身,这意味着这里的this value 等同于 VO。
var x = 10;
console.log(
x, // 10
this.x, // 10
window.x // 10
);
思考下段代码:
// the code of the "foo" function
// never changes, but the "this" value
// differs in every activation
function foo() {
alert(this);
}
// caller activates "foo" (callee) and
// provides "this" for the callee
foo(); // global object
foo.prototype.constructor(); // foo.prototype
var bar = {
baz: foo
};
bar.baz(); // bar
(bar.baz)(); // also bar
(bar.baz = bar.baz)(); // but here is global object
(bar.baz, bar.baz)(); // also global object
(false || bar.baz)(); // also global object
var otherFoo = bar.baz;
otherFoo(); // again global object
以上三篇文章皆来自:http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
(暂时写到这,该下班了吃饭了)
嵌套函数作为函数调用时,this值不是全局对象(非严格模式下)就是undefined(严格模式)。如果要使用外部函数的this,需要将其先保存起来。代码如下:
//函数的调用
var o ={
m: function(){
var self = this;
console.log(this===o);//true
f();
function f(){
console.log(this===o);//false
console.log(self===o);//true
}
}
};
o.m();