作用域在JavaScript中是非常重要的概念,理解了它对更深入地理解闭包等概念都有很大的帮助,这篇文章就来谈谈我对作用域的理解。

一、全局作用域与局部作用域

      在JavaScript中没有块级作用域的概念,它的作用域都是以函数作为划分的。JavaScript的作用域分为全局作用域和局部作用域。能在代码中的任何地方访问到的变量具有全局作用域,只能在固定代码段,例如函数内部,访问到的变量具有局部作用域。

     全局作用域主要包括:1. 定义在最外层的变量和函数 2. 没经过声明,直接定义的变量,及不包含var关键字的变量。3. window对象的属性都具有全局作用局。例如:window.setTimeout, window.location

     局部作用域主要包括: 1. 函数内部通过var关键字声明的变量  2.函数传入的参数

var foo = 'global';  //全局变量
function(bar) {
var goo = 'inside'; //局部变量
env = 1; //全局变量
}

 

二、作用域链

    作用域链定义了函数执行时可访问到的变量的范围。一个函数的作用域链创建分为两个步骤:一是在函数定义时创建的[[scope]]属性中包含的对象,二是在函数执行时创建的活动对象。

1. [[scope]]属性

    函数的[[scope]]属性中包含的是函数在创建时所在的作用域中可访问的对象。只有JavaScript引擎可以访问到。例如:

var foo = 1;
function add(num1, num2) {
var sum = num1 + num2;
return sum;
}
add(foo, 1);

    在函数add定义的时候,创建了[[scope]]内部属性,因为add定义在全局作用域中,所以[[scope]]属性中包含了所有的全局变量。

2. 活动对象

    函数每次调用都会创建一个执行上下文,这个执行上下文会有自己的作用域链,也就是函数的作用域链。作用域链被创建时,会初始化为[[scope]]属性中包含的对象。接下来,将函数传入的参数arguments,以及函数中声明的局部变量集合起来,组成了活动对象。并把活动对象压入作用域链的最前端。在本例中add函数的活动对象主要包括传入的参数num1、num2以及局部变量sum。

     此后在进行变量名解析时,会从作用域链的顶端开始往下查找,找到则返回对应变量,找不到返回错误信息。

    当函数执行完毕,执行上下文会被销毁,活动对象也随之销毁。函数的每次调用都会创建新的执行上下文,并关联新的作用域链。

三、改变作用域

    要想人为地改变函数的作用域链有两种方法:with语句和try-catch中的catch语句。

1. width语句

    当运行到width语句时,会把width的对象的所有属性包裹成一个对象压入作用域链的最前端。

function create() {
width(document) {
var list = createElement('li');
list.onclick = function() {
//dosomething
}
};
}

 

    在这个例子中会在当前create函数的作用域链的最前端加入document包含的所有属性,例如例子中用到的createElement方法。这样写貌似代码更简洁了,可是却造成了性能问题。因为作用域链的最前端是with对象的所有属性方法,这样函数内部的局部变量都变成作用域链中的第二级了,要查找到局部变量需要先遍历第一级,使查找时间变长。所以一般来讲程序中尽量不要出现with语句。

2. catch 语句

    使用try-catch语句当遇到异常跳到catch语句中时,会把catch到的错误对象压入作用域链的最前端。

try{
dosomething
} catch(er) {
alert(er);
}

 

    如上例,当运行到catch语句时,会把er对象压入作用域链的最前端。

三、变量声明提升

    JavaScript会提升变量声明,被提升的声明包括var表达式和function函数声明,它们会被提升到当前作用域的最顶部。

bar();
var bar = function() {};
var someValue = 42; test();
function test(data) {
if(false) {
goo = 1;
} else {
var goo = 2;
}
for(var i=0; i<100; i++) {
var e = data[i];
}
}

 

如上例子,在变量提升后会变成:

var bar, someValue;  //var 表达式声明被提升,但是赋值语句没有跟着提升,现在它们的值是undefined

//函数声明被提升
function test(data) {
var goo, i, e; //局部变量的声明也会被提升,
if(false) {
goo = 1;
} else {
goo = 2;
}
for(i=0; i<100; i++) {
e = data[i];
}
} bar(); //出错,因为bar的值还是undefined
bar = function() {}; //赋值语句没有提升
someValue = 42;
test();

 

    变量声明提升有时会带来一些不容易发现的问题,例如在没有提升前函数foo的false语句看起来是要改变全局变量goo,但是提升后可以清晰地看到它其实改变的是局部变量goo。对于这些情况一定到多注意。我认为能减少这类错误的方法是在函数内部将要用到的局部变量都先声明在最前面,手动提升。而函数声明尽量使用function声明方式而不是赋值语句,这样无论调用语句在前面还是后面都不会出错。

JavaScript作用域详解的更多相关文章

  1. javascript 作用域详解

    作用域理解:定义的变量.函数生效的范围.javascript 有全局作用域和函数作用域两种.注:es6实现let 块级作用域不是js原生的,底层同样是通过var实现的.如果想了解具体细节,请访问bab ...

  2. Javascript作用域详解。

    javascript的作用域 是按照   函数来划分的. 网址:http://www.cnblogs.com/rubylouvre/archive/2009/08/21/1551270.html

  3. javascript中的this作用域详解

    javascript中的this作用域详解 Javascript中this的指向一直是困扰我很久的问题,在使用中出错的机率也非常大.在面向对象语言中,它代表了当前对象的一个引用,而在js中却经常让我觉 ...

  4. JavaScript事件详解-jQuery的事件实现(三)

    正文 本文所涉及到的jQuery版本是3.1.1,可以在压缩包中找到event模块.该篇算是阅读笔记,jQuery代码太长.... Dean Edward的addEvent.js 相对于zepto的e ...

  5. JavaScript事件详解-Zepto的事件实现(二)【新增fastclick阅读笔记】

    正文 作者打字速度实在不咋地,源码部分就用图片代替了,都是截图,本文讲解的Zepto版本是1.2.0,在该版本中的event模块与1.1.6基本一致.此文的fastclick理解上在看过博客园各个大神 ...

  6. JavaScript正则表达式详解(一)正则表达式入门

    JavaScript正则表达式是很多JavaScript开发人员比较头疼的事情,也很多人不愿意学习,只是必要的时候上网查一下就可以啦~本文中详细的把JavaScript正则表达式的用法进行了列表,希望 ...

  7. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

  8. PHP常量、变量作用域详解(一)

    PHP 中的每个变量都有一个针对它的作用域,它是指可以在其中访问变量(从而访问它的值)的一个领域.对于初学者来说,变量的作用域是它们所驻留的页面.因此, 如果你定义了 $var,页面余下部分就可以访问 ...

  9. PHP变量作用域详解(二)

    学过C的人用PHP的时候一般会相当顺手,而且感到PHP太方便太轻松.但在变量作用域这方面却与C有不同的地方,搞不好会相当郁闷,就找不到错误所在.昨晚就与到这么一个问题,是全局变量在函数中的问题.今天搜 ...

随机推荐

  1. VRRP概述

    随着Internet的发展,人们对网络的可靠性的要求越来越高.对于局域网用户来说,能够时刻与外部网络保持联系是非常重要的. 通常情况下,内部网络中的所有主机都设置一条相同的缺省路由,指向出口网关(即图 ...

  2. Struts2结果页面配置(Result)

    1.全局页面配置 全局结果页面:针对一个包下的所有Action的页面跳转都可生效 <global-results> <result name="xxx">/ ...

  3. java算法 第七届 蓝桥杯B组(题+答案) 3.凑算式

    3.凑算式  (结果填空) B      DEFA + --- + ------- = 10     C      GHI         (如果显示有问题,可以参见[图1.jpg]) 这个算式中A~ ...

  4. Perl 获取时间函数

    Perl 时间日期 Perl中处理时间的函数有如下几种:    1.time() 函数:返回从1970年1月1日起累计的秒数    2.localtime() 函数:获取本地时区时间(多用这个)    ...

  5. 【LA 3989 训练指南】女士的选择 【稳定婚姻问题】

    我们先来学一下稳定婚姻问题 什么是稳定婚姻问题? 有n个女士和n个男士,他们要一一进行配对.每个男士心中对这n个女士都有一个排名,同理,每个女士心里对n个男性也有一个排名.我们要做的是,在他们配对完成 ...

  6. 143. Reorder List(List)

    Given a singly linked list L: L0→L1→…→Ln-1→Ln, reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You must do th ...

  7. Library not found for -lAPOpenSdk

    多人开发合作的时候 总是会遇见各种各样的问题 今天就来讲一个关于友盟的问题 在我的小伙伴 用cocoapods 中添加了这样一句话 pod ‘UMengSocialCOM’,  并且pod updat ...

  8. 746. Min Cost Climbing Stairs 最不费力的加权爬楼梯

    [抄题]: On a staircase, the i-th step has some non-negative cost cost[i] assigned (0 indexed). Once yo ...

  9. zookeeper和duboo 没用

    什么是dubbo Dubbo是阿里巴巴SOA服务化治理方案的核心框架,是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案. 测试和生产公用一套zookeep ...

  10. vs2012安装qt5.5.1

    https://blog.csdn.net/a6513806/article/details/80098057