JavaScript作用域详解
作用域在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作用域详解的更多相关文章
- javascript 作用域详解
作用域理解:定义的变量.函数生效的范围.javascript 有全局作用域和函数作用域两种.注:es6实现let 块级作用域不是js原生的,底层同样是通过var实现的.如果想了解具体细节,请访问bab ...
- Javascript作用域详解。
javascript的作用域 是按照 函数来划分的. 网址:http://www.cnblogs.com/rubylouvre/archive/2009/08/21/1551270.html
- javascript中的this作用域详解
javascript中的this作用域详解 Javascript中this的指向一直是困扰我很久的问题,在使用中出错的机率也非常大.在面向对象语言中,它代表了当前对象的一个引用,而在js中却经常让我觉 ...
- JavaScript事件详解-jQuery的事件实现(三)
正文 本文所涉及到的jQuery版本是3.1.1,可以在压缩包中找到event模块.该篇算是阅读笔记,jQuery代码太长.... Dean Edward的addEvent.js 相对于zepto的e ...
- JavaScript事件详解-Zepto的事件实现(二)【新增fastclick阅读笔记】
正文 作者打字速度实在不咋地,源码部分就用图片代替了,都是截图,本文讲解的Zepto版本是1.2.0,在该版本中的event模块与1.1.6基本一致.此文的fastclick理解上在看过博客园各个大神 ...
- JavaScript正则表达式详解(一)正则表达式入门
JavaScript正则表达式是很多JavaScript开发人员比较头疼的事情,也很多人不愿意学习,只是必要的时候上网查一下就可以啦~本文中详细的把JavaScript正则表达式的用法进行了列表,希望 ...
- JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解
二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...
- PHP常量、变量作用域详解(一)
PHP 中的每个变量都有一个针对它的作用域,它是指可以在其中访问变量(从而访问它的值)的一个领域.对于初学者来说,变量的作用域是它们所驻留的页面.因此, 如果你定义了 $var,页面余下部分就可以访问 ...
- PHP变量作用域详解(二)
学过C的人用PHP的时候一般会相当顺手,而且感到PHP太方便太轻松.但在变量作用域这方面却与C有不同的地方,搞不好会相当郁闷,就找不到错误所在.昨晚就与到这么一个问题,是全局变量在函数中的问题.今天搜 ...
随机推荐
- Adjacent Bit Counts(uvalive)
For a string of n bits x1, x2, x3,…, xn, the adjacent bit count of the string (AdjBC(x)) is given by ...
- Linux服务管理总结
简介与分类 系统的运行级别 运行级别 含义 0 关机 1 单用户模式,可以想象为windows的安全模式,主要用于系统修复 2 不完全的命令行模式,不含NFS服务 3 完全的命令行模式,就是标准字符界 ...
- Makefile里面打印信息
Makefile的规则相对来说还是比较复杂的,上手不容易,没有系统研究过,往往搞不清楚状况.如果掌握了基本的调试手段,那对我们写出正确的Makefile会非常有帮助.而在Makefile中,最重要的调 ...
- kubernetes 集群安全配置
版本:v1.10.0-alpha.3 openssl genrsa -out ca.key 2048 openssl req -x509 -new -nodes -key ca.key -subj & ...
- python gun readline
https://github.com/ludwigschwardt/python-gnureadline
- Linux实战教学笔记27:Nginx详细讲解
前言:nginx的特点 本节主要对Nginx Web服务软件进行介绍,涉及Nginx的基础,特性,配置部署,优化,以及企业中的日常运维管理和应用.作为HTTP服务软件的后起之秀,Nginx与它的老大哥 ...
- Spark scala和java的api使用
1.利用scala语言开发spark的worcount程序(本地运行) package com.zy.spark import org.apache.spark.rdd.RDD import org. ...
- WebAPI的路由规则
1.自定义路由 public static class WebApiConfig { public static void Register(HttpConfiguration config) { / ...
- [C++] How to prevent memory leaks
How to prevent memory leaks ? overload new/delete
- p2944 [USACO09MAR]地震损失2Earthquake Damage 2
传送门 分析 我们让s到1,关键点到t分别连流量为inf的边 于是我们可以考虑跑s到t的最小割 于是我们将所有点拆为两个点,关键点和1的两个点之间连inf,其余点连1 将原图的边也连上,流量为inf ...