js的作用域与作用域链
js的作用域
作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
事例:
var scope="global";
function t(){
console.log(scope);
var scope="local"
console.log(scope);
}
t();
第一句输出的是: "undefined",而不是 "global"
第二句输出的是:"local"
我们可能会认为第一句会输出:"global",因为代码还没执行var scope="local",所以肯定会输出“global"。
这想法完全没错,只不过用错了对象。我们首先要区分Javascript的函数作用域与java等的块级作用域。
在java中,花括号内中的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的。
而Javascript压根没有块级作用域,而是函数作用域.
所谓函数作用域就是:
变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
所以根据函数作用域的意思,可以将上述代码重写如下:
var scope="global";
function t(){
var scope;
console.log(scope);
scope="local"
console.log(scope);
}
t();
第一句输出的是: "undefined",而不是 "global"
第二讲输出的是:"local"
t函数里面的 scope声明覆盖了全局的scope , 但是还没有赋值,所以输出:”undefined“。
为什么说Js没有块级作用域呢,有以下代码为证:
var name="global";
if(true){
var name="local";
console.log(name)
}
console.log(name);
都输出是“local", 如果有块级作用域,明显if语句将创建局部变量name,并不会修改全局name,可是没有这样,所以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
变量作用域
先看事例:
function t(flag){
if(flag){
s="ifscope"; //window.s 这个变量不加var 默认是全局变量的,而且是顶层对象的属性。
for(var i=0;i<2;i++) ;
}
console.log(i);
}
t(true);
console.log(s);
最后结果是:ifscope
这主要是js中没有用var声明的变量都是全局变量,而且是顶层对象的属性
所以用console.log(window.s) 也是会输出:ifcobfig
i = 2;
alert(window.i); // 没有,受制于函数的作用域,i不属window的属性
当使用var声明一个变量时候,创建的这个属性是不可配置的,也就是说无法通过delete运算符删除的
var name = 1 ->不可删除
sex = ‘girl‘ ->可删除
this.age = 22 ->可删除
比如:delete sex;//可删除变量sex
- 函数与变量声明区别
事例1:
console.log(add());//18;函数声明全局生效,全局有定义
function add(){
i = 18;
return i;
}
事例2:
console.log(add());//报错,找不到add这个方法,函数表达式,只有在声明的时候才定义。
var add=function(){
i = 18;
return i;
}
事例3:
console.log(n);//undefined ,变量被var声明以后,作用域中全局有定义,最多就是没有值。
var n = 100;
事例4:
console.log(n);//报错,没有被var声明的变量,作为属性存在的时候,它只有在出现的时候才有定义。
n = 100;
作用域链
事例:
name="lwy";
function t(){
var name="tlwy";
function s(){
var name="slwy";
console.log(name); //slwy
}
function ss(){
console.log(name); //tlwy
}
s();
ss();
}
t();
console.log(name); //lwy
当执行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.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“。
- 垃圾回收机制
内存管理机制就是分配内存管理,每种编程语言都有它的内存管理机制,JavaScript的内存管理机制是:内存基元在变量(对象,字符串等等)创建时分配,然后在他们不再被使用时“自动”释放。后者被称为垃圾回收。
(自动 是容易让人混淆,迷惑的,并给 JavaScript(和其他高级语言)开发者一个印象:他们可以不用关心内存管理。然而这是错误的。)
在js中的分配的内存一般有如下的生命周期:
内存分配(当我们声明变量,函数,对象时系统自动为他们分配内存)
内存使用(使用变量,函数等)
内存回收(使用完毕,由垃圾回收机制自动回收不再使用的内存)
全局作用域的回收
整个script标签里,页面打开时创建全局作用域,页面关闭的时候删除
函数作用域的回收
函数里面,函数调用的时候创建函数作用域,调用完删除
- with语句
说到作用域链,不得不说with语句。with语句的作用是将代码的作用域设置到一个特定的对象中。
它主要用来临时扩展作用域链,将语句中的对象添加到作用域的头部。
看下面代码
person={name:"yhb",age:22,height:175,wife:{name:"lwy",age:21}};
with(person.wife){
console.log(name);//此时可以直接使用person对象的name属性。
}
with语句将person.wife添加到当前作用域链的头部,所以输出的就是:“lwy".
with语句结束后,作用域链恢复正常。
with 语句用于设置代码在特定对象中的作用域。
语法:
with (expression) statement
提示:with 语句是运行缓慢的代码块,尤其是在已设置了属性值时。大多数情况下,最好避免使用它。
知识拓展
this指向问题:
this是一个关键字,不是变量,也不是属性名。JavaScript的语法不允许给this赋值(报错)。
JavaScript的 this 关键词指的是它所属的对象。
它是函数运行时,在函数体内部自动生成的一个对象,只能在函数体内部使用。
它拥有不同的值,具体是什么,取决于它的使用位置:
2 在方法中,this 指的是所有者对象。
2 单独的情况下,this 指的是全局对象。
2 在函数中,this 指的是全局对象。
2 在函数中,严格模式下,this 是 undefined。
2 在事件中,this 指的是接收事件的元素。
像 call() 和 apply() 这样的方法可以将 this 引用到任何对象。
总结一句话:这个方法属于哪个对象,它里面的this就指向谁(也就是说,是它所属的对象)。
事例:
var obj = {
foo: function () {
console.log(this);//this指的就是obj这个对象,因为这个方法foo是属于obj对象的
}
};
function test(){
console.log(this);//this指的就是window全局对象,test方法属于window对象的方法
}
test();
- obj.foo();
但是,
关键字this和变量有所不同,首先它没有作用域的限制(谁都可以使用this,但是它们不一定是同一个对象),再次嵌套的函数不会从调用它的函数中继承this。
事例:
var obj = {
foo: function () {
console.log(this);//this指向obj
function test(){
console.log(this);//this指向window
}
test();
},
bar: 1
};
obj.foo()