深入理解javascript作用域系列第二篇
前面的话
大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找。再加上this机制的干扰,使得变量查找极易出错。这实际上是由两种作用域工作模型导致的,作用域分为词法作用域和动态作用域,分清这两种作用域模型就能够对变量查找过程有清晰的认识。本文是深入理解javascript作用域系列第二篇——词法作用域和动态作用域
词法作用域
第一篇介绍过,编译器的第一个工作阶段叫作分词,就是把由字符组成的字符串分解成词法单元。这个概念是理解词法作用域的基础
简单地说,词法作用域就是定义在词法阶段的作用域,是由写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变
关系
无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定

function foo(a) {
var b = a * 2;
function bar(c) {
console.log( a, b, c );
}
bar(b * 3);
}
foo( 2 ); // 2 4 12

在这个例子中有三个逐级嵌套的作用域。为了帮助理解,可以将它们想象成几个逐级包含的气泡

作用域气泡由其对应的作用域块代码写在哪里决定,它们是逐级包含的
气泡1包含着整个全局作用域,其中只有一个标识符:foo
气泡2包含着foo所创建的作用域,其中有三个标识符:a、bar和b
气泡3包含着bar所创建的作用域,其中只有一个标识符:c
查找
作用域气泡的结构和互相之间的位置关系给引擎提供了足够的位置信息,引擎用这些信息来查找标识符的位置
在代码片段中,引擎执行console.log(...)声明,并查找a、b和c三个变量的引用。它首先从最内部的作用域,也就是bar(...)函数的作用域开始查找。引擎无法在这里找到a,因此会去上一级到所嵌套的foo(...)的作用域中继续查找。在这里找到了a,因此引擎使用了这个引用。对b来讲也一样。而对c来说,引擎在bar(...)中找到了它
[注意]词法作用域查找只会查找一级标识符,如果代码引用了foo.bar.baz,词法作用域查找只会试图查找foo标识符,找到这个变量后,对象属性访问规则分别接管对bar和baz属性的访问

foo = {
bar:{
baz: 1
}
};
console.log(foo.bar.baz);//1

遮蔽
作用域查找从运行时所处的最内部作用域开始,逐级向外或者说向上进行,直到遇见第一个匹配的标识符为止
在多层的嵌套作用域中可以定义同名的标识符,这叫作“遮蔽效应”,内部的标识符“遮蔽”了外部的标识符

var a = 0;
function test(){
var a = 1;
console.log(a);//1
}
test();

全局变量会自动为全局对象的属性,因此可以不直接通过全局对象的词法名称,而是间接地通过对全局对象属性的引用来对其进行访问

var a = 0;
function test(){
var a = 1;
console.log(window.a);//0
}
test();

通过这种技术可以访问那些被同名变量所遮蔽的全局变量。但非全局的变量如果被遮蔽了,无论如何都无法被访问到
动态作用域
javascript使用的是词法作用域,它最重要的特征是它的定义过程发生在代码的书写阶段
那为什么要介绍动态作用域呢?实际上动态作用域是javascript另一个重要机制this的表亲。作用域混乱多数是因为词法作用域和this机制相混淆,傻傻分不清楚
动态作用域并不关心函数和作用域是如何声明以及在任何处声明的,只关心它们从何处调用。换句话说,作用域链是基于调用栈的,而不是代码中的作用域嵌套

var a = 2;
function foo() {
console.log( a );
}
function bar() {
var a = 3;
foo();
}
bar();

【1】如果处于词法作用域,也就是现在的javascript环境。变量a首先在foo()函数中查找,没有找到。于是顺着作用域链到全局作用域中查找,找到并赋值为2。所以控制台输出2
【2】如果处于动态作用域,同样地,变量a首先在foo()中查找,没有找到。这里会顺着调用栈在调用foo()函数的地方,也就是bar()函数中查找,找到并赋值为3。所以控制台输出3
两种作用域的区别,简而言之,词法作用域是在定义时确定的,而动态作用域是在运行时确定的
深入理解javascript作用域系列第二篇的更多相关文章
- 深入理解javascript作用域系列第二篇——词法作用域和动态作用域
× 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极 ...
- 深入理解javascript函数系列第二篇——函数参数
× 目录 [1]arguments [2]内部属性 [3]函数重载[4]参数传递 前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传 ...
- 深入理解javascript作用域系列第一篇——内部原理
× 目录 [1]编译 [2]执行 [3]查询[4]嵌套[5]异常[6]原理 前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域 ...
- 深入理解javascript作用域系列第一篇
前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域貌似简单,实则复杂,由于作用域与this机制非常容易混淆,使得理解作用域的原 ...
- 深入理解javascript对象系列第二篇——属性操作
× 目录 [1]查询 [2]设置 [3]删除[4]继承 前面的话 对于对象来说,属性操作是绕不开的话题.类似于“增删改查”的基本操作,属性操作分为属性查询.属性设置.属性删除,还包括属性继承.本文是对 ...
- 深入理解javascript作用域系列第三篇——声明提升(hoisting)
× 目录 [1]变量 [2]函数 [3]优先 前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javasc ...
- 深入理解javascript作用域系列第三篇
前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javascript作用域系列第三篇——声明提升(hois ...
- 深入理解javascript作用域系列第四篇——块作用域
× 目录 [1]let [2]const [3]try 前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用 ...
- 深入理解javascript作用域系列第四篇
前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用其他类型的作用域单元甚至可以实现维护起来更加优秀.简洁的 ...
随机推荐
- 2015/9/28 Python基础(19):类的定制和私有性
用特殊方法定制类前面我们讲了方法的两个重要方面:首先,方法必须在调用前被绑定(到它们相应类的某个实例中):其次,有两个特殊方法可以分别作为构造器和解构器的功能,分别名为__init__()和__del ...
- SQL语句(二十二)—— 权限授予和回收(作业练习)
CREATE TABLE course ( Cno ) NOT NULL, Cname ) DEFAULT NULL, Cpno ) DEFAULT NULL, Ccredit smallint DE ...
- 关于Java泛型深入理解小总结
1.何为泛型 首先泛型的本质便是类型参数化,通俗的说就是用一个变量来表示类型,这个类型可以是String,Integer等等不确定,表明可接受的类型,原理类似如下代码 int pattern; //声 ...
- amcharts的一些用法
function chartdiv2() { var chart; var chartData = [ { "month" : "2015-08", " ...
- MongoDB中的基础概念:Databases、Collections、Documents
MongoDB以BSON格式的文档(Documents)形式存储.Databases中包含集合(Collections),集合(Collections)中存储文档(Documents). BSON是一 ...
- zedboard学习记录.2.PS+PL 流水灯与uart
1.建立一个工程. 硬件设计 2.IP integrator -> create block desgin;Run Block Automation. 3.add IP -> AXI GP ...
- android 图片旋转 移动 放大缩小
图片的变化主要是matrix的变化,对matrix不懂的可以先了解下matrxi. public class FunnyView extends View { /* * 手指按下时可能是移动 也可能是 ...
- xargs -i 和-I 的区别【转】
xargs与find经常结合来进行文件操作,平时删日志的时候只是习惯的去删除,比如 # find . -type f -name "*.log" | xargs rm -rf * ...
- “您查看的网页正在试图关闭窗口。是否关闭此窗口”的屏蔽方法(JavaScript)
原文:http://www.cnblogs.com/tigerhuolh/archive/2011/04/14/2015634.html 用JS代码关闭窗口时会提示“您查看的网页正在试图关闭窗口.是否 ...
- HBase 入门笔记-安装篇
一.前言 接触HBase已近半年,从一无所知到问题的解决,在数据落地方面也有了一定的了解,在此记录这半年来碰到的一些问题和对一些数据落地方面的见解,本篇主要介绍一下hbase安装方面的信息 二.安装环 ...