javascript核心(三)

本文深入探讨了JavaScript中的闭包概念,解释了闭包如何通过保存父级作用域链来访问自由变量,并解决了funarg问题。同时,还讨论了this值在不同执行上下文中的表现。

一、闭包

在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();



 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值