简析JavaScript中的作用域与作用域链

A.任何程序设计语言都有作用域的概念。作用域(scope)就是变量(variable)与函数(function)的可访问范围,换句话说,scope控制着variable和function的可见性和生命周期。

  1. 全局作用域
    所有的浏览器(如果用原生js,必须解决浏览器的兼容问题)都支持window对象,它表示着浏览器窗口。所以javacript中的全局对象 函数 变量都会自动成为window对象的成员,函数成为window对象的方法,变量成为window对象的属性。在代码任何地方都能访问到的对象拥有全局作用域,通常以下3种情形拥有全局作用域:

(1)最外层函数和在最外层函数外面定义的变量全局作用域,例如:

var a = 1;     //全局变量
function f1() {
    alert(a);     //alerts '1'
    function inner() {
     alert(a);    //alerts '1'
    }
}
f1();    //输出1 1;
inner();      //脚本错误

(2)在JS任何位置不使用var关键字声明的变量也拥有全局作用域

function f1() {
     var firstName = "James";
      lastName = "Camelo";
      alert(firstName);       //alerts 'James'
      alert(secondName);      //alerts 'Camelo'
}
f1();    //输出'James'和'Camelo'
alert(firstName);    //脚本错误
alert(secondName);    //输出'Camelo'

变量secondName拥有全局作用域,而firstName不能在函数外部被访问.

(3)所有window对象的属性拥有全局作用域.

总结:
全局变量存在于整个函数的生命周期中,然而其在全局范围内很容易被篡改;声明变量不带上var很容易造成混乱。少用全局变量,声明带上var。
全局变量存在于程序的整个生命周期,但并不是通过其引用我们一定可以访问到全局变量。

2.局部作用域/函数作用域
和全局作用域相反,函数作用域一般只在函数的代码片段内可访问到,外部不能进行变量访问。在函数内部定义的变量存在于函数作用域中,其生命周期随着函数的执行结束而结束。例如:

var name = 'James';
function getName(){
    var name = 'Camelo';
    alert(name);  //alerts 'Camelo'
}
alert(name);  //alerts ‘James'

3.词法作用域/静态作用域
词法作用域:函数是在定义它们的作用域里运行,而不是在执行它们的作用域里运行。换句话说,词法作用域是由书写代码时函数声明的位置来决定的(eval() 和 with 可以欺骗词法作用域)。还是通过一个例子来了解词法作用域:

var name = "James";
function f1(){
    alert(name);  //alerts 'undefined'
    var name = 'Camelo';
    alert(name);  //alerts 'Camelo'
}
f1();  //输出 'undefined'和'Camelo'

js解释器在执行任何代码之前都会先创建一个全局对象,全局变量相当于这个全局对象的一个属性。对于f1这个函数,就会生成一个叫做调用对象的东西,局部变量 函数参数 和 Arguments都是这个对象的一部分。
更致命的是,调用对象位于作用域链的前端,这就意味着全局对象的属性中与调用对象同名的属性将会被隐藏(变量的查询从最接近的绑定上下文开始,向外部逐渐扩展,直到查询到第一个绑定,一旦完成查找就结束搜索)。
所以代码片段中的,f1函数内部 "var name = 'Camelo';" 使得“var name = 'James';"被隐藏,并且第一个alert(name)处于"var name = 'Camelo';"之前,所以才会输出"undefined";函数定义完毕后,name就已经添加到作用域里了,所以alert()能找到name这个属性,但是恶心的事 name并未被赋值。
犀牛书中写道javascript函数“在定义它们的作用域里运行,而不是在执行它们的作用域里运行”,多么抽象而又精髓的一句总结。经过一番讲解后,上述代码等价于下述代码:

var name = "James";
function f1(){
    var name;
    alert(name);  //alerts 'undefined'
    name = 'Camelo';
    alert(name);  //alerts 'Camelo'
}
f1();  //输出 'undefined'和'Camelo'

javascript中的函数可以先写调用再写定义,但是这样做很不好,会养成不好的习惯 并且 难维护。

var name = "James";
f1();  //输出 'undefined'和'Camelo'
function f1(){
    var name;
    alert(name);  //alerts 'undefined'
    name = 'Camelo';
    alert(name);  //alerts 'Camelo'
}

4.动态作用域
与词法作用域不同于在定义时确定,动态作用域在执行时确定,其生存周期到代码片段执行为止。动态变量存在于动态作用域中,任何给定的绑定的值,在确定调用其函数之前,都是不可知的。

在代码执行的时候,对应的作用域通常是静态的,但是我们可以通过一些方法或者语句改变作用域链。例如with语句(with语句执行完毕后,会把作用域链恢复到原始状态,但是一般禁用with语句,因为太耽搁性能):

var name = 'James'
alert(name);  //alerts 'James'
with({name; 'Camelo'}){
    alert(name);  //alerts'Camelo'
}
alert(name);  //alerts'James'

当然还有call方法、apply方法和try-catch中的catch也能修改作用域链,但是很重要的一点就是,当作用域链中存在动态作用域时,this引用会变得更复杂,不再指向第一次创建的上下文,而是由调用者决定。

5.没有块级作用域(ES6新增的let能创建块级作用域)
不同于C那些编程语言,在JavaScript中没有块级作用域,换句话说,在块级语句内部里声明的变量在与外部声明的变量是一样的,在这些块级语句外部也能访问和修改这些变量的值。例如:

function f1(){
    if( 1 < 3 ){
        var name = 'James';
    }
    alert(name);  //alerts'James'
    name = 'Camelo';
    alert(name);  //alerts'Camelo'
}
f1();    //输出'James'和'Camelo'

B.作用域链
 两种模式:
词法作用域只关心函数和作用域是在哪里定义的,而不关心在哪执行。作用域链通常为嵌套作用域链。可以将作用域想成一条绳子,当前作用域位于绳头,全局作用域位于绳尾。RHS和LHS引用会从绳头开始进行查找直到绳尾,找到就用,找不到就返回异常(严格模式下)。
动态作用域并不关心函数和作用域是如何声明以及在哪里声明。作用域链是基于调用栈的,而不是代码中的作用域嵌套。

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

推荐阅读更多精彩内容