[置顶] Js作用域与作用域链详解
2016-06-23 12:36
615 查看
参考博客:http://blog.csdn.net/lmj623565791/article/category/2218967
http://www.cnblogs.com/libin-1/p/5820550.html
http://www.cnblogs.com/nihaorz/p/5584024.html
http://www.cnblogs.com/myqianlan/p/4421950.html
http://www.cnblogs.com/shuiyi/p/5343399.html http://www.cnblogs.com/shuiyi/p/5305435.html
弹出“Hello World”
也是弹出了“Hello World”
弹出了“undefined”
这里面隐藏了一个陷阱,就是JavaScript中的变量提升
它相当于
变量提升,简单的理解,就是把变量提升提到函数的最顶的地方。需要说明的是,变量提升只是提升变量的声明,并不会把赋值也提升上来,没赋值的变量初始值是undefined。所以上面就出现了声明为undefined的var,因为赋值在后面声明提升在了前面。
还有一点注意的是因为JavaScript是函数级作用域,只有函数才会创建新的作用域,而不像其他语言有块级作用域,例如:块,就像if语句,在上面例子中,不管会不会进入 if代码块,函数声明都会提升到当前作用域的顶部,得到执行,在js中并不会创建一个新的作用域(在C语言等语言中却是一个新的作用域,上面这个例子,两个函数实际上是一样的)。
从这里我们应该体会到,当我们在写js code的时候,我么需要把变量放在块级作用域的顶端,不然容易发生一些意想不到的错误。
注意:ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
还有一种就是函数提升
弹出“Hello World”
这里函数提升成功
报错:foo不是函数
函数提升失败
一个变量的查找,找这个变量最近定义
下面用代码实例说明:
实例1:
实例2
实例3:http://blog.csdn.net/libin_1/article/details/51367103
由于JavaScript的变量作用域实际上是函数内部,我们在
为了解决块级作用域,ES6引入了新的关键字
一直对Js的作用域有点迷糊,今天偶然读到JavaScript权威指南,立马被吸引住了,写的真不错。我看的是第六版本,相当的厚,大概1000多页,Js博大精深,要熟悉精通需要大毅力大功夫。
一:函数作用域
先看一小段代码:
[javascript] view
plain copy
var scope="global";
function t(){
console.log(scope);
var scope="local"
console.log(scope);
}
t();
(PS: console.log()是firebug提供的调试工具,很好用,有兴趣的童鞋可以用下,比浏览器+alert好用多了)
第一句输出的是: "undefined",而不是 "global"
第二讲输出的是:"local"
你可能会认为第一句会输出:"global",因为代码还没执行var scope="local",所以肯定会输出“global"。
我说这想法完全没错,只不过用错了对象。我们首先要区分Javascript的函数作用域与我们熟知的C/C++等的块级作用域。
在C/C++中,花括号内中的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的。而Javascript压根没有块级作用域,而是函数作用域.
所谓函数作用域就是说:-》变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
所以根据函数作用域的意思,可以将上述代码重写如下:
[javascript] view
plain copy
var scope="global";
function t(){
var scope;
console.log(scope);
scope="local"
console.log(scope);
}
t();
我们可以看到,由于函数作用域的特性,局部变量在整个函数体始终是由定义的,我们可以将变量声明”提前“到函数体顶部,同时变量初始化还在原来位置。
为什么说Js没有块级作用域呢,有以下代码为证:
[javascript] view
plain copy
var name="global";
if(true){
var name="local";
console.log(name)
}
console.log(name);
都输出是“local",如果有块级作用域,明显if语句将创建局部变量name,并不会修改全局name,可是没有这样,所以Js没有块级作用域。
现在很好理解为什么会得出那样的结果了。scope声明覆盖了全局的scope,但是还没有赋值,所以输出:”undefined“。
所以下面的代码也就很好理解了。
[javascript] view
plain copy
function t(flag){
if(flag){
var s="ifscope";
for(var i=0;i<2;i++)
;
}
console.log(i);
console.log(s);
}
t(true);
输出:2 ”ifscope"
二:变量作用域
还是首先看一段代码:
[javascript] view
plain copy
function t(flag){
if(flag){
s="ifscope";
for(var i=0;i<2;i++)
;
}
console.log(i);
}
t(true);
console.log(s);
就是上面的翻版,知识将声明s中的var去掉。
程序会报错还是输出“ifscope"呢?
让我揭开谜底吧,会输出:”ifscope"
这主要是Js中没有用var声明的变量都是全局变量,而且是顶层对象的属性。
所以你用console.log(window.s)也是会输出“ifconfig"
当使用var声明一个变量时,创建的这个属性是不可配置的,也就是说无法通过delete运算符删除
var name=1 ->不可删除
sex=”girl“ ->可删除
this.age=22 ->可删除
三:作用域链
先来看一段代码:
[javascript] view
plain copy
name="lwy";
function t(){
var name="tlwy";
function s(){
var name="slwy";
console.log(name);
}
function ss(){
console.log(name);
}
s();
ss();
}
t();
当执行s时,将创建函数s的执行环境(调用对象),并将该对象置于链表开头,然后将函数t的调用对象链接在之后,最后是全局对象。然后从链表开头寻找变量name,很明显
name是"slwy"。
但执行ss()时,作用域链是: ss()->t()->window,所以name是”tlwy"
下面看一个很容易犯错的例子:
[html] view
plain copy
<html>
<head>
<script type="text/javascript">
function buttonInit(){
for(var i=1;i<4;i++){
var b=document.getElementById("button"+i);
b.addEventListener("click",function(){ alert("Button"+i);},false);
}
}
window.onload=buttonInit;
</script>
</head>
<body>
<button id="button1">Button1</button>
<button id="button2">Button2</button>
<button id="button3">Button3</button>
</body>
</html>
当文档加载完毕,给几个按钮注册点击事件,当我们点击按钮时,会弹出什么提示框呢?
很容易犯错,对是的,三个按钮都是弹出:"Button4",你答对了吗?
当注册事件结束后,i的值为4,当点击按钮时,事件函数即function(){ alert("Button"+i);}这个匿名函数中没有i,根据作用域链,所以到buttonInit函数中找,此时i的值为4,
所以弹出”button4“。
四:with语句
说到作用域链,不得不说with语句。with语句主要用来临时扩展作用域链,将语句中的对象添加到作用域的头部。
看下面代码
[javascript] view
plain copy
person={name:"yhb",age:22,height:175,wife:{name:"lwy",age:21}};
with(person.wife){
console.log(name);
}
with语句将person.wife添加到当前作用域链的头部,所以输出的就是:“lwy".
with语句结束后,作用域链恢复正常。
http://www.cnblogs.com/libin-1/p/5820550.html
http://www.cnblogs.com/nihaorz/p/5584024.html
http://www.cnblogs.com/myqianlan/p/4421950.html
http://www.cnblogs.com/shuiyi/p/5343399.html http://www.cnblogs.com/shuiyi/p/5305435.html
JS变量提升的最佳实践
var v='Hello World'; alert(v);
弹出“Hello World”
var v='Hello World'; (function(){ alert(v); })()
也是弹出了“Hello World”
var v='Hello World'; (function(){ alert(v); var v='I love you'; })()
弹出了“undefined”
这里面隐藏了一个陷阱,就是JavaScript中的变量提升
它相当于
var v='Hello World'; (function(){ var v; alert(v); v='I love you'; })()
变量提升,简单的理解,就是把变量提升提到函数的最顶的地方。需要说明的是,变量提升只是提升变量的声明,并不会把赋值也提升上来,没赋值的变量初始值是undefined。所以上面就出现了声明为undefined的var,因为赋值在后面声明提升在了前面。
function foo() { if (false) { var x = 1; } return; var y = 1; } function foo() { var x, y; if (false) { x = 1; } return; y = 1; }
还有一点注意的是因为JavaScript是函数级作用域,只有函数才会创建新的作用域,而不像其他语言有块级作用域,例如:块,就像if语句,在上面例子中,不管会不会进入 if代码块,函数声明都会提升到当前作用域的顶部,得到执行,在js中并不会创建一个新的作用域(在C语言等语言中却是一个新的作用域,上面这个例子,两个函数实际上是一样的)。
从这里我们应该体会到,当我们在写js code的时候,我么需要把变量放在块级作用域的顶端,不然容易发生一些意想不到的错误。
注意:ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
还有一种就是函数提升
function myTest(){ foo(); function foo(){ alert("Hello World"); } } myTest();
弹出“Hello World”
这里函数提升成功
function myTest(){ foo(); var foo =function foo(){ alert("我来自 foo"); } } myTest();
报错:foo不是函数
函数提升失败
变量的作用域链
一个变量的查找,找这个变量最近定义var的地方,找到了就不会再继续向外找了.
下面用代码实例说明:
实例1:
var a = 10; function show(){ console.log(a); } show(); // 10
实例2
var a = 10; function show(){ console.log(a); a = 20; } show(); // 还是10
实例3:http://blog.csdn.net/libin_1/article/details/51367103
局部变量的误解
for循环
由于JavaScript的变量作用域实际上是函数内部,我们在
for循环等语句块中是无法定义具有局部作用域的变量的:
function foo() { for (var i= 0; i < 100; i++) { // } i += 100; // document.write(i); // 200 } foo();
if条件语句
if(true){ var i = 100; } console.log(i); // 100
为了解决块级作用域,ES6引入了新的关键字
let,用
let替代
var可以申明一个块级作用域的变量
一直对Js的作用域有点迷糊,今天偶然读到JavaScript权威指南,立马被吸引住了,写的真不错。我看的是第六版本,相当的厚,大概1000多页,Js博大精深,要熟悉精通需要大毅力大功夫。
一:函数作用域
先看一小段代码:
[javascript] view
plain copy
var scope="global";
function t(){
console.log(scope);
var scope="local"
console.log(scope);
}
t();
(PS: console.log()是firebug提供的调试工具,很好用,有兴趣的童鞋可以用下,比浏览器+alert好用多了)
第一句输出的是: "undefined",而不是 "global"
第二讲输出的是:"local"
你可能会认为第一句会输出:"global",因为代码还没执行var scope="local",所以肯定会输出“global"。
我说这想法完全没错,只不过用错了对象。我们首先要区分Javascript的函数作用域与我们熟知的C/C++等的块级作用域。
在C/C++中,花括号内中的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的。而Javascript压根没有块级作用域,而是函数作用域.
所谓函数作用域就是说:-》变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
所以根据函数作用域的意思,可以将上述代码重写如下:
[javascript] view
plain copy
var scope="global";
function t(){
var scope;
console.log(scope);
scope="local"
console.log(scope);
}
t();
我们可以看到,由于函数作用域的特性,局部变量在整个函数体始终是由定义的,我们可以将变量声明”提前“到函数体顶部,同时变量初始化还在原来位置。
为什么说Js没有块级作用域呢,有以下代码为证:
[javascript] view
plain copy
var name="global";
if(true){
var name="local";
console.log(name)
}
console.log(name);
都输出是“local",如果有块级作用域,明显if语句将创建局部变量name,并不会修改全局name,可是没有这样,所以Js没有块级作用域。
现在很好理解为什么会得出那样的结果了。scope声明覆盖了全局的scope,但是还没有赋值,所以输出:”undefined“。
所以下面的代码也就很好理解了。
[javascript] view
plain copy
function t(flag){
if(flag){
var s="ifscope";
for(var i=0;i<2;i++)
;
}
console.log(i);
console.log(s);
}
t(true);
输出:2 ”ifscope"
二:变量作用域
还是首先看一段代码:
[javascript] view
plain copy
function t(flag){
if(flag){
s="ifscope";
for(var i=0;i<2;i++)
;
}
console.log(i);
}
t(true);
console.log(s);
就是上面的翻版,知识将声明s中的var去掉。
程序会报错还是输出“ifscope"呢?
让我揭开谜底吧,会输出:”ifscope"
这主要是Js中没有用var声明的变量都是全局变量,而且是顶层对象的属性。
所以你用console.log(window.s)也是会输出“ifconfig"
当使用var声明一个变量时,创建的这个属性是不可配置的,也就是说无法通过delete运算符删除
var name=1 ->不可删除
sex=”girl“ ->可删除
this.age=22 ->可删除
三:作用域链
先来看一段代码:
[javascript] view
plain copy
name="lwy";
function t(){
var name="tlwy";
function s(){
var name="slwy";
console.log(name);
}
function ss(){
console.log(name);
}
s();
ss();
}
t();
当执行s时,将创建函数s的执行环境(调用对象),并将该对象置于链表开头,然后将函数t的调用对象链接在之后,最后是全局对象。然后从链表开头寻找变量name,很明显
name是"slwy"。
但执行ss()时,作用域链是: ss()->t()->window,所以name是”tlwy"
下面看一个很容易犯错的例子:
[html] view
plain copy
<html>
<head>
<script type="text/javascript">
function buttonInit(){
for(var i=1;i<4;i++){
var b=document.getElementById("button"+i);
b.addEventListener("click",function(){ alert("Button"+i);},false);
}
}
window.onload=buttonInit;
</script>
</head>
<body>
<button id="button1">Button1</button>
<button id="button2">Button2</button>
<button id="button3">Button3</button>
</body>
</html>
当文档加载完毕,给几个按钮注册点击事件,当我们点击按钮时,会弹出什么提示框呢?
很容易犯错,对是的,三个按钮都是弹出:"Button4",你答对了吗?
当注册事件结束后,i的值为4,当点击按钮时,事件函数即function(){ alert("Button"+i);}这个匿名函数中没有i,根据作用域链,所以到buttonInit函数中找,此时i的值为4,
所以弹出”button4“。
四:with语句
说到作用域链,不得不说with语句。with语句主要用来临时扩展作用域链,将语句中的对象添加到作用域的头部。
看下面代码
[javascript] view
plain copy
person={name:"yhb",age:22,height:175,wife:{name:"lwy",age:21}};
with(person.wife){
console.log(name);
}
with语句将person.wife添加到当前作用域链的头部,所以输出的就是:“lwy".
with语句结束后,作用域链恢复正常。
相关文章推荐
- 避免IE执行AJAX时,返回JSON出现下载文件
- JavaScript 字符串与数组互转,并保持数据去重、排序功能
- js实现多图上传和预览(包含表单上传、ajax上传)
- 同一个页面,多个script标签中Javascript执行问题
- 解决eclispe 编辑JS卡的问题
- 突然顿悟的Javascript中的this
- 火狐浏览器下载文件名为乱码
- list页面示例
- JS实现转动效果
- js 执行上下文环境
- JavaScript tips
- JS实现Clone
- 【JS】JSON
- session(登录注销)和cookie(记住用户名和密码)的实现
- jsp页面中两种方法显示当前时间的简单实例
- javascript参数传递
- Highcharts Stock 实时图表监控JS
- 最简洁的得到当时日期与时间的js方法
- JSONDB使用
- JS留言功能的简单实现案例(推荐)