JavaScript作用域链、预解析、闭包

作用域及执行环境

变量的作用域有全局变量和局部变量两种,这一点与其他语言(如C)的概念是非常相似的。

我们先了解一下几个概念:

  • 执行环境(execution context)定义了变量或者函数有权访问其他数据,决定他们各自的行为。
  • 每个执行环境都有关联的变量对象(VO),环境中定义的变量和函数都会保存在这个对象中。
  • 作用域链(scope chain)保证对执行环境有权访问的所有变量和函数的有序访问
  • 全局执行环境(GO)在web浏览器中通常为window对象,始终处于作用域链的最后一位。
  • 执行函数的时候,会创建一个活动对象(AO)作为变量对象置于作用域链的前端。

JS没有块级作用域

JS中没有花括号块级作用域

if(true) {
var color = "red" ;
}

alert (color) ; //red

花括号执行完毕之后并不会销毁变量。

尤其记住for语句中声明的变量for(var i=0;i<10;i++) { } ,变量i在for执行结束的时候仍然会存在于循环外部的执行环境

来看看以下代码:

function foo() {
    var x = 1;
    function bar() {
        var y = x + 1; // bar可以访问foo的变量x!
    }
    var z = y + 1; // ReferenceError! foo不可以访问bar的变量y!
}

我们知道嵌套函数中,内部函数可以访问外部函数的变量,而外部函数不能访问内部函数的变量。

那么我们又说没有块级作用域,要怎么理解?

在JavaScript中,把这个东西叫做,执行环境。注意,执行环境只分为两种,全局执行环境除函数之外的环境)和局部执行环境只有函数内部的区域是局部的。)每个执行环境都有一个与之相关联的变量对象(所有的变量与函数保存在里面)
对于全局执行环境来说,对象就是window对象,而对于局部(函数)环境来说,称为活动对象(最开始至包含arguments对象)

为了解决块级作用域,ES6引入了新的关键字letlet替代var可以申明一个块级作用域的变量

for (let i=0; i<100; i++) 
{
     sum += i;
}

预解析

预解析的简单规则:

  1. 把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。

  2. 把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。

  3. 先提升函数,再提升变量

理解函数提升的关键,就是理解函数声明与函数表达式之间的区别。 ——《JavaScript高级程序设计》

sayHi(); //错误:函数还不存在
var sayHi = function(){
    alert("Hi!");
}

这段代码在执行的时候因为预解析,会先声明sayHi这个变量,然后再提升匿名函数的声明,然后是执行sayHi,最后才是赋值

//变量声明
var sayHi;
//函数声明
function(){
  alert("Hi!");
}
//函数执行,报错
sayHi(); 
//赋值
var sayHi = function(){
    alert("Hi!");
}

预解析的详细步骤:

以上提到的GO和AO,GO是始终存在并处于作用域底端的,当一个函数开始预解析的时候严格按照以下顺序:

  1. 创建AO对象AO{ } (AO也称执行期上下文)
  2. 找形参和变量声明;将变量和形参名作为AO的属性名,值为undefined
  3. 将实参值与形参统一
  4. 在函数体里面找函数声明,值赋予函数体

a函数最开始被调用时,会创建一个执行环境,还有它的作用域链

AO对象的创建


也就是说,在作用域顶端创建了AO,于是函数在执行查找变量的时候,按照作用域顺序开始查找,从0开始,从上到下。

一般来讲,当函数执行完毕之后,局部活动对象AO就会被销毁,内存中仅保存全局作用域。但是嵌套函数的情况又有所不同(在函数中定义另外一个函数),于是闭包出现了。

闭包

先不着急说什么是闭包,上文提到,在函数内部定义函数,内部函数会将外部函数的AO添加到自己的作用域中,因此内部函数的作用域链包含了外部函数的AO。即使外部函数被销毁,倘若内部函数被返回到了外面,在其他地方被调用了,那么它仍然可以访问外部函数的AO中的变量,直到内部函数也被销毁。

作用域链

**注意上图,a的AO被销毁后,作用域链的指针不再指向AO,但是b仍然包含a的AO。换句话说,a函数执行完毕,其执行环境的作用域链会被销毁,但它的活动对象仍然会留在内存中。 **

说到这里,已经对闭包的概念有所认识了,我们给出闭包的定义:

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式就是在一个函数内部创建另一个函数。

1. 把匿名函数作为返回值,保存在外面的变量,不立即执行

//函数作为返回值
function a(){
    var name = "aaa";
    return function(){  //匿名函数
        return name;
    }
}
var b = a();
console.log(b()); //aaa

2.把变量保存到全局执行环境,fn1()每次被调用会重新创建n,而num是全局变量。

function fn() {
    var num = 3;
    return function(){
    var n = 0;
    console.log(++n);
    console.log(++num);
    }
}
var fn1 = fn();
fn1(); //1 4
fn1(); //1 5

3.匿名函数立即执行

(function (x) {
    return x * x;
})(3); // 9

闭包的用途

  1. 可以读取函数内部的变量,闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
  2. 让变量的值始终保持在内存中

延长作用域链

有些语句可以在作用域链前端添加一个变量对象,该变量对象会在代码执行后被移除。

  • try-catch语句的catch块

对于catch语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。

  • with语句

with的作用是将代码的作用域设置到一个特定的对象中。

这一部分以后在详细说。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356