理解JavaScript的作用域链

理解JavaScript的作用域链

2015/10/31 · JavaScript
·
作用域链

原文出处:
田小计划   

上一篇文章中介绍了Execution Context中的三个重要部分:VO/AO,scope
chain和this,并详细的介绍了VO/AO在JavaScript代码执行中的表现。

本文就看看Execution Context中的scope chain。

JavaScript 深入之闭包

2017/05/21 · JavaScript
· 闭包

原文出处: 冴羽   

一、作用域Scope和上下文Context

    在javascript中,作用域scope和上下文context是两个不同的概念。每个函数调用都会伴随着scope和context,从本质上来说,scope是和函数绑定的,而context是基于对象的。即scope用于在函数调用时提供变量访问,且每次函数调用时,都不同;而context始终是关键词this的值,它指向当前执行代码所属的对象。
scope 作用域
    在前一篇的“javascript变量”部分讨论了javascript的作用域,分为全局和局部,且javascript中不存在块作用域。

** ‘this’ context 上下文**
    context
经常被函数所调用的方式所决定。(1)当函数被作为一个对象的方法调用时,this
被设置为该函数所属的对象。如

var obj = {
    foo: function() {
        return this;   
    }
};
obj.foo() === obj; // true。 this指向obj对象

(2)当使用new关键字去创建一个新的函数对象时,this的值也被设置为新创建的函数对象。比如

function foo() {
    alert(this);
}
foo() // window
new foo() // foo

(3)当函数被普通调用时,this被为全局contex或者浏览器的window对象。比如

function foo() {
    alert(this);
}
foo() // window

作用域

开始介绍作用域链之前,先看看JavaScript中的作用域(scope)。在很多语言中(C++,C#,Java),作用域都是通过代码块(由{}包起来的代码)来决定的,但是,在JavaScript作用域是跟函数相关的,也可以说成是function-based。

例如,当for循环这个代码块结束后,依然可以访问变量”i”。

JavaScript

for(var i = 0; i < 3; i++){ console.log(i); } console.log(i); //3

1
2
3
4
5
for(var i = 0; i < 3; i++){
    console.log(i);
}
 
console.log(i); //3

对于作用域,又可以分为全局作用域(Global scope)和局部作用域(Local
scpoe)。

全局作用域中的对象可以在代码的任何地方访问,一般来说,下面情况的对象会在全局作用域中:

  • 最外层函数和在最外层函数外面定义的变量
  • 没有通过关键字”var”声明的变量
  • 浏览器中,window对象的属性

局部作用域又被称为函数作用域(Function
scope),所有的变量和函数只能在作用域内部使用。

JavaScript

var foo = 1; window.bar = 2; function baz(){ a = 3; var b = 4; } //
Global scope: foo, bar, baz, a // Local scope: b

1
2
3
4
5
6
7
8
9
var foo = 1;
window.bar = 2;
 
function baz(){
    a = 3;
    var b = 4;
}
// Global scope: foo, bar, baz, a
// Local scope: b

定义

MDN 对闭包的定义为:

闭包是指那些能够访问自由变量的函数。

那什么是自由变量呢?

自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量。

由此,我们可以看出闭包共有两部分组成:

闭包 = 函数 + 函数能够访问的自由变量

举个例子:

var a = 1; function foo() { console.log(a); } foo();

1
2
3
4
5
6
7
var a = 1;
 
function foo() {
    console.log(a);
}
 
foo();

foo 函数可以访问变量 a,但是 a 既不是 foo 函数的局部变量,也不是 foo
函数的参数,所以 a 就是自由变量。

那么,函数 foo + foo 函数访问的自由变量 a 不就是构成了一个闭包嘛……

还真是这样的!

所以在《JavaScript权威指南》中就讲到:从技术的角度讲,所有的JavaScript函数都是闭包。

咦,这怎么跟我们平时看到的讲到的闭包不一样呢!?

别着急,这是理论上的闭包,其实还有一个实践角度上的闭包,让我们看看汤姆大叔翻译的关于闭包的文章中的定义:

ECMAScript中,闭包指的是:

  1. 从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。
  2. 从实践角度:以下函数才算是闭包:
    1. 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)
    2. 在代码中引用了自由变量

接下来就来讲讲实践上的闭包。

二、函数生命周期

    函数生命周期可以分为创建和执行两个阶段。
    在函数创建阶段,JS解析引擎进行预解析,会将函数声明提前,同时将该函数放到全局作用域中或当前函数的上一级函数的局部作用域中。
    在函数执行阶段,JS解析引擎会将当前函数的局部变量和内部函数进行声明提前,然后再执行业务代码,当函数执行完退出时,释放该函数的执行上下文,并注销该函数的局部变量。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website