Javascript 核心对象 执行上下文 Execution context 作用域链 Scope Chain 闭包 Closure

js 写了好几年,总是对其用法懵懂,恨其没有标准语法书籍,这次下很心看了ECMA-262-3标准。

整理自己比较模糊的核心概念

 

Execution context 运行上下文,avascript 是单线程语言,同一时间只有一个任务被执行。当javascript 开始解析运行代码时,默认先进入全局运行上下文(global execution context),然后在全局上下文中每调用一次函数生成新的运行上下文。也可以调用eval 方法进入 eval 上下文 (eval execution context)。这个过程如下

将运行上下文栈(Execution Context Stack): 所有的运行上下文存储到栈中如下

 

运行上下文可以抽象成一个对象

 

variable object :变量对象,该对象是一个与运行上下文相关的的容器对象。用于存储 variable .function 声明定义

注意函数表达式不存储在 变量对象中

var foo = 10;

function bar() {} // function declaration, FD

(function baz() {}); // function expression, FE

console.log(

this.foo == foo, // true

window.bar == bar // true

);

console.log(baz); // ReferenceError, "baz" is not defined

以上式全局上下文 global context

在全局上下文中 Variable Object 是全局对象本身

 

在eval context eval 上下文中 variable object 即可以用全局的variable object 也可用调用它的function的variable object

 

Activation object: 函数上下文中 variable Object 叫做 Activation object,其除变量variable 与函数funciton的声明定义外,还有形参及arguments 对象

function foo(x, y) {

var z = 30;

function bar() {} // FD

(function baz() {}); // FE

}

foo(10, 20);

函数声明预先读写 hoisting of function variable

 

alert(sum(10,10));//20;因为预先读取到了sum()函数的申明 alert(sum1(10,10))//报错,因为找不到sum1()函数; function sum(num1,num2){ return num1+num2; } var sum1=function(num1,num2){ return num1+num2; }

 

scope chain 作用域链: 在当前运行上下文可访问的变量。即一个可以内部函数可以访问它父函数上下中变量variable object及全局上下中的变量 global variable。

 

var 作用域 是 function scope

function f(shouldInitialize: boolean) 
{ 
if (shouldInitialize) { var x = 10; } return x;
 } f(true); // returns '10' 
f(false); // returns 'undefined'

var 作用域问题是相同变量名可以重复声明

function sumMatrix(matrix) {

var sum = 0;

for (var i = 0; i < matrix.length; i++) {

var currentRow = matrix[i];

for (var i = 0; i < currentRow.length; i++) {

sum += currentRow[i];

}

}

return sum;

}

sumMatrix([[1,2],[3,6]]) //return 3

var 作用域链

var x = 10;

(function foo(i) {

var y = 20;

(function bar() {

var z = 30;

// "x" and "y" and "i" are "free variables"

// and are found in the next (after

// bar's activation object) object

// of the bar's scope chain

console.log(x + y + z + i);

})();

})(40); //return 100

 

其外还有一些特殊的动态添加作用域

如 with-objects 或 catch-clauses.

Object.prototype.x = 10;

var w = 20;

var y = 30;

// in SpiderMonkey global object

// i.e. variable object of the global

// context inherits from "Object.prototype",

// so we may refer "not defined global

// variable x", which is found in

// the prototype chain

console.log(x); // 10

(function foo() {

// "foo" local variables

var w = 40;

var x = 100;

// "x" is found in the

// "Object.prototype", because

// {z: 50} inherits from it

with ({z: 50}) {

console.log(w, x, y , z); // 40, 10, 30, 50

}

// after "with" object is removed

// from the scope chain, "x" is

// again found in the AO of "foo" context;

// variable "w" is also local

console.log(x, w); // 100, 40

// and that's how we may refer

// shadowed global "w" variable in

// the browser host environment

console.log(window.w); // 20

})();

不建议使用with,使作用域链多了查找维度

 

注意不是所有 global vo 都 prototype object

 

Closures 闭包: 由于javascript函数作为第一公民的语言。可以将函数作为参数,也可以作为返回值。函数在创建时[[scopes]] 属性会保存parent funtion 作用域链 scope chain,然后当函数激活时,合并作用域链

Scope chain = Activation object + [[Scope]]

下面是函数作为返回值,它的作用域链是向上寻找父函数作用域

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

 

上图在作用域叫静态作用域 static scope or lexical scope,其他语言也有dynamic 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"

 

上图 foo 作用域是函数创建时生成[[scopes]]。

总结: 静态作用域是作为语言拥有闭包closure 特性的一种必要条件。有些语言可以同时拥有静态作用域,动态作用域,并可以开发的时候选择。

 

闭包的定义 是代码块作用域(var 是 function scope let {} scope )与静态作用域的集合,改静态作用域保存父作用域链variable object 的引用。

共享父作用域链的情况

function baz() {

var x = 1;

return {

foo: function foo() { debugger; return ++x; },

bar: function bar() { debugger; 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 () {

console.log(k);

};

}

data[0](); // 3, but not 0

data[1](); // 3, but not 1

data[2](); // 3, but not 2

解决方法 使用function 隔离作用域

var data = [];

for (var k = 0; k < 3; k++) {

data[k] = (function (x) {

return function () {

console.log(x);

};

})(k); // pass "k" value

}

// now it is correct

data[0](); // 0

data[1](); // 1

data[2](); // 2

但是ES6 let 块作用域

let data = [];

for (let k = 0; k < 3; k++) {

data[k] = function () {

console.log(k);

};

}

data[0](); // 0

data[1](); // 1

data[2](); // 2

context object 上下文 this对象的值,属于运行上下文execute context的属性,

与variable object相比this 不存在作用域链问题 scope chain,这个值是运行上下文激活是被赋值即函数调用时,且只能被赋值一次

通常由函数的调用者决定, 一下三种不同context

// the code of the "foo" function

// never changes, but the "this" value

// differs in every activation

function foo() {

console.log(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/

转载于:https://my.oschina.net/fsilence/blog/1581545

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值