我之前用过闭包,用过this,虽然很多时候知道是这么一回事,但是确实理解上还不够深入。再一次看javascript高级程序设计这本书时,发现一起很多疑难问题竟然都懂了,所以总结一下一些理解,难免有错,所以希望有大神可以指出或者补充。

一开始都是从作用域链开始的,要理解作用域链,首先要先说几个名词,一开始把我搞晕的几个名词。执行环境(execution context),变量对象(variable object),作用域链(scope chain)。

执行环境,里面有是运行时的环境,包括了当前作用域有权访问的数据。在运行期间,环境的切换类似于堆栈,当从一个环境进入另一个作用域,那么就会把这个新的环境压入栈顶,当环境执行完毕,环境弹出,若有必要,改变量对象也会被摧毁。

变量对象,顾名思义,这是一个对象,保存了当前执行环境中的所有变量以及函数。

作用域链,上面的变量对象中,当一个环境被压入堆栈时,变量对象就会创建一个作用域链,这个作用域链的最前端,保存的是自己的变量对象,往后依次保存外部的每一个作用域的活动对象,而最后一个,当然就是全局对象,可以认为就是window。作用域链保证了在一个作用域变量的搜索的有序进行。

当然,这样是很抽象的,这只是概念而已,没什么用,看下面这张图(感觉好low,不要建议)。

如图所示,在js代码的执行过程中,我们知道,js是没有块级作用域的(可以通过模拟块级作用域),当从全局环境进入Fun1时,fun1环境压入栈中,它获得了当前的控制权,fun1的活动对象获得该作用域内的变量和属性,例如:this,arguments等等,Fun1环境的活动对象获得创建作用域链,这里只标明了作用域链,作用域链的最前端是自己的活动对象,也就是0,1是上级活动对象,在fun1中是window,也就是全局对象。同理,如果,fun1进入fun2,。。。到funN,funN的作用域链,而作用域链的收集是由上到下一级一级搜索。我们看看实际的例子:

var name = 'window';
console.log(name);
function base()
{
var name = 'base';
console.log(name);
(function innerFun()
{
var name = 'in';
console.log(name);
})(); //自动运行方式,应该不难理解
}
base();

  我在不同的作用域内分别console各自的name,结果当然是不断获得当前的name:

这段代码的运行过程可以抽象为这张图:

即使没有这张图,我相信我们也能理解,但是它方便我们知道解释器做了什么,实际上这是在运行的时候发生的,一开始只有全局环境(也叫全局上下文),然后创建环境内的变量和函数,这里我们说创建Base的时候发生了什么,也就是执行到Base函数的时候,再看一张图:

参考了书上的图,画的好丑哈哈。

执行到创建Base函数的时候,穿件一个[[scope]]对象保存作用域链,这个与运行时不同,它没有包含自己的活动对象,当然,此时函数并未运行当然没有活动对象,但是它会搜索所有上级作用域,依次赋值到作用域链中。而到Base函数真正运行的时候,那就是上面那张图,解释器生成了一个运行时环境(context),压入栈中,用创建Base的作用域链初始化运行时作用域链,然后生成当前运行环境,压入作用域链前端,也就是当前作用域了,同理当在base中执行innerFun时,过程也是这样,只是这个时候,作用域链会更长了。

实际上,了解这么细并不是很有必要,但是这有助于理解this和闭包,也有助于避免错误代码。

关于this

函数运行时,第一个初始化的变量时arguments,然后是this,我图中没画出来。this的值是函数运行时的对象。最简单的代码如下:

function b()
{
this.name = "theSbWindow";
}
b();
console.log(window.name);

运行结果:

用构造函数形式:

有new运算符来new一个函数,结果当然不一样:

function b()
{
this.names = "theSbWindow"; //我改成了names原因是这样更好理解,window有name的属性,但是没有names属性
}
new b();
console.log(window.names);

 结果:

很明显,此时this不是window,那是什么呢,new运算符发生了什么,其实是用已有的函数代码复制了一个对象,并且返回了这个对象,this指向了这个对象,我们只需要打印出来就知道了。

function b()
{
this.names = "theSbWindow";
}
console.log(new b()); //改了这里
console.log(window.names);

结果:

就是那个b了。

当然,这有什么难,我们经常这有来创建对象,还用这种方式实现面向对象编程,额,没错。

很好的说明了一切,this赋值给了window。如果是某个对象里面:

name  = 'window';
var me = {
name:"wython",
sayName: function ()
{
console.log(this.name); }
};
me.sayName();

结果:wython

当然,这有什么难。

关于游离的函数中的This:


什么叫游离的this,也就是没有明确是哪个对象的函数中的this,解释器会赋值给它window:

name  = 'window';
var me = {
name:"wython",
sayName: function ()
{ (function()
{
console.log(this.name);
})(); //自动运行
}
};
me.sayName();

结果当然是window,为什么是这样,你可以想象在函数创建过程中,this赋值了window,活动对象中的this取得是window的值。

name  = 'window';
var me = {
name:"wython",
sayName: function ()
{
console.log(this.name);
var b = {
name : "b",
sayName:function(){
console.log(this.name);
}
};
b.sayName();
}
};
me.sayName();

如果是这样呢,当然,函数有明确的b对象,所以,运行结果是b。

关于闭包:

最简单的代码:

function out()
{
var outName = "outter";
return function(){
return outName;
}
}
var getOut = out();
console.log(getOut());

结果当然是:outter

理解了作用域链在解释闭包就好解释了,我们只需要知道为什么out执行完毕以后竟然outName不被摧毁,根据上面的抽象图,当out执行完毕,out执行环境当然会被pop出栈

out执行环境被摧毁,但是由于里面的函数引用了,作用域内的变量outName,所以暂时不被回收,也就是环境被摧毁,但是活动对象依然存活,仅仅是因为里面的函数的作用域链还保存着out的活动对象,使得他可以活到最后,当然,闭包也可能出现问题,但是只要注意下就行了。

深入javascript作用域链到闭包的更多相关文章

  1. 个人理解的javascript作用域链与闭包

    闭包引入的前提个人理解是为从外部读取局部变量,正常情况下,这是办不到的.简单的闭包举例如下: function f1(){ n=100; function f2(){ alert(n); } retu ...

  2. JavaScript 作用域链与闭包

    作用域链 在某个作用域访问某个变量或者函数时,会首先在自己的局部环境作用域中搜寻变量或者函数,如果本地局部环境作用域中有该变量或者函数,则就直接使用找到的这个变量值或者函数:如果本地局部环境作用域中没 ...

  3. javascript 作用域链及闭包,AO,VO,执行环境

    下面的文章内容会根据理解程度不断修正. js变量作用域: 定义:变量在它申明的函数体以及函数体内嵌套的任意函数体内有定义. function AA(){ var bb='我是AA内部变量'; func ...

  4. JavaScript作用域链与闭包的理解

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域 链的工作原理. 1. 全局作用域(Global Scope) (1)最外层函数和 ...

  5. 【进阶2-2期】JavaScript深入之从作用域链理解闭包(转)

    这是我在公众号(高级前端进阶)看到的文章,现在做笔记   https://github.com/yygmind/blog/issues/18 红宝书(p178)上对于闭包的定义:闭包是指有权访问另外一 ...

  6. 《浏览器工作原理与实践》<10>作用域链和闭包 :代码中出现相同的变量,JavaScript引擎是如何选择的?

    在上一篇文章中我们讲到了什么是作用域,以及 ES6 是如何通过变量环境和词法环境来同时支持变量提升和块级作用域,在最后我们也提到了如何通过词法环境和变量环境来查找变量,这其中就涉及到作用域链的概念. ...

  7. 在chrome开发者工具中观察函数调用栈、作用域链与闭包

    在chrome开发者工具中观察函数调用栈.作用域链与闭包 在chrome的开发者工具中,通过断点调试,我们能够非常方便的一步一步的观察JavaScript的执行过程,直观感知函数调用栈,作用域链,变量 ...

  8. 在chrome开发者工具中观察函数调用栈、作用域链、闭包

    在chrome的开发者工具中,通过断点调试,我们能够非常方便的一步一步的观察JavaScript的执行过程,直观感知函数调用栈,作用域链,变量对象,闭包,this等关键信息的变化.因此,断点调试对于快 ...

  9. JS详细图解作用域链与闭包

    JS详细图解作用域链与闭包 攻克闭包难题 初学JavaScript的时候,我在学习闭包上,走了很多弯路.而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战. 闭包有多重要?如果你 ...

随机推荐

  1. sql 中各种锁随记

    一. 为什么要引入锁    多个用户同时对数据库的并发操作时会带来以下数据不一致的问题:    丢失更新  A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系 ...

  2. phaser运用中,dota战术板

    首发:个人博客,更新&纠错&回复 还是没想好用phaser做个啥小游戏好,以每年春节打dota的这两伙人为基础是肯定的,但游戏具体咋做还没头绪. 暂时试着做了个卡通版dota地图,可以 ...

  3. 如何在视频处理控件TVideoGrabber中设置音频捕捉设备

    TVideoGrabber不仅可以捕捉视频,还可以作为多媒体播放器,并支持包括C#..NET.VB.NET.C++.Delphi.C++Builder和ActiveX平台,本文将剖析TVideoGra ...

  4. stc89c52开发板遥控器解码 红外线发射 内置 eeprom 存储 串口显示编码

    单片机,大概三年前,就买了一本 <爱上单片机> 最后就学会,用面包板了,编程书上基本没讲. 看原理图,看时序图,看数据手册, 都没讲. 而且书上自带的代码写的很烂. 1,缩近控制不好 2, ...

  5. 在keil 4中添加stc系列芯片的方法--【sky原创】

    在keil 4中添加stc系列芯片的方法: 1.从官网下载uv3.cdb的文件网址是:http://www.stcmcu.com/ 2.下载好后把uv3.cdb文件改成STC.cdb:3. 然后将[S ...

  6. Android开发的进阶之路

    客户端开发工程师,简单地从某几个方面描述一下个人理解里不同的等级: 1.初级的可以熟练使用系统框架提供的组件,搭建所需应用程序: 2.中级的,会对系统框架中如view绘制.broadcast机制.内存 ...

  7. 安装新版xampp后apache无法启动提示:Apache Service detected with wrong path解决方案

    我以前安装过xampp,因为学习thingPHP需要升级PHP5.0以上,所以我就卸掉了xampp,从新安装新版本的xampp其中PHP是最新版的,但是安装后启动xampp提示如下:Apache Se ...

  8. [转]AngularJS的$resource

    转自:http://blog.csdn.net/violet_day/article/details/17403207 $http $http服务是基于$q服务的,提供了promise封装,它接受一个 ...

  9. [转]微软SerialPort秘籍[SerialPort为什么死锁程序的分析]

    既然是秘籍,显然是写一些大家不常找到的,MSDN里遗漏提示大家注意的东西. 用过.net 2.0中,自带SerialPort的人,大多都遇到过.莫名其妙的执行Close的时候会死掉的问题.而Wince ...

  10. ffmpeg无法接收组播流问题处理

    问题:ffmpeg无法对IP组播进行处理,表现如下 [root@os01 /]# ffprobe udp://225.0.0.2:9000 ffprobe version Copyright (c) ...