1.函数作用域
以前做项目经常会用到Js,但是很多知识点都没有深入的研究,JS是一门非常难得语言(对于做后端的来说确实),今天看了下Js的作用域,感触非常深,在这里记录下。
首先看一段js代码:
var num="one";
function t(){
console.log(num);
var num="two"
console.log(num);
}
t();
这段代码应该有人会认为输出:"one" "two" 。不过最后的结果是
:
"undefined" 和 "true" 。为什么呢?
应为JavaScript没有块级作用域,而是函数作用域,换句话说:JavaScript只有函数提供作用域。
再来看一段代码:
var num="one";
if(true){
var num="two";
console.log(num)
}
console.log(num);
都输出"two"。如果有块级作用域,if中的语句是不会改变全局变量第一个num的值得,所以说js是没有
块级作用域的。
所以到这里第一段代码应该很容易理解了。
那么下面这段代码应该也很容易理解:
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"
2.变量作用域
function t(flag){
if(flag){
s="ifscope";
for(var i=0;i<2;i++)
;
}
console.log(i);
}
t(true);
console.log(s);
这段代码就是上面代码的修改版,其中把var申明给去掉了,但是这对结果肯定是没有影响的,
写过js的应该都知道没有var申明的变量都是全局变量,而且还是 顶层对象的属性。所以用console.log(window.s)也是输出"ifscope"。
当使用var声明一个变量时,创建的这个属性是不可配置的,也就是说无法通过delete运算符删除
var name=1 ->不可删除
sex=”girl“ ->可删除
this.age=22 ->可删除
3.作用域链
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>
<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.οnlοad=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“。
4.with语句
说到作用域链,不得不说with语句。with语句主要用来临时扩展作用域链,将语句中的对象添加到作用域的头部。
看下面代码:
person={name:"yhb",age:22,height:175,wife:{name:"lwy",age:21}};
with(person.wife){
console.log(name);
}
with语句将person.wife添加到当前作用域链的头部,所以输出的就是:“lwy".
with语句结束后,作用域链恢复正常。
再看另外一段代码:
person={name:"yhb",age:22,height:175,wife:{name:"lwy",age:21}};
with(person.wife){
console.log(name);
}
console.log(name);
这段代码网上有人测试说会输出2个 "lwy",我试了下也是,后来发现原因:因为上面有个例子定义了全局变量nam="lwy",而且name 是
顶层对象的属性 这里取得之前那个name的值,真是巧啊。正确答案第二个输出应该是空!!!