(1)作用域
一个变量的作用域(scope)是程序源代码中定义的这个变量的区域。
1. 在JS中使用的是词法作用域(lexical scope)
不在任何函数内声明的变量(函数内省略var的也算全局)称作全局变量(global scope)
在函数内声明的变量具有函数作用域(function scope),属于局部变量
局部变量优先级高于全局变量
var name="one"; function test(){ var name="two"; console.log(name); //two } test();
函数内省略var的,会影响全局变量,因为它实际上已经被重写成了全局变量
var name="one"; function test(){ name="two"; } test(); console.log(name); //two
函数作用域,就是说函数是一个作用域的基本单位,js不像c/c++那样具有块级作用域 比如 if for 等
function test(){ for(var i=0;i<10;i++){ if(i==5){ var name = "one"; } } console.log(name); //one } test(); //因为是函数级作用域,所以可以访问到name="one"
当然了,js里边还使用到了高阶函数,其实可以理解成嵌套函数
function test1(){ var name = "one"; return function (){ console.log(name); } } test1()();
test1()之后将调用外层函数,返回了一个内层函数,再继续(),就相应调用执行了内层函数,所以就输出 ”one"
嵌套函数涉及到了闭包,后面再谈..这里内层函数可以访问到外层函数中声明的变量name,这就涉及到了作用域链机制
2. JS中的声明提前
js中的函数作用域是指在函数内声明的所有变量在函数体内始终是可见的。并且,变量在声明之前就可以使用了,这种情况就叫做声明提前(hoisting)
tip:声明提前是在js引擎预编译时就进行了,在代码被执行之前已经有声明提前的现象产生了
比如
var name="one"; function test(){ console.log(name); //undefined var name="two"; console.log(name); //two } test();
上边就达到了下面的效果
var name="one"; function test(){ var name; console.log(name); //undefined name="two"; console.log(name); //two } test();
再试试把var去掉?这是函数内的name已经变成了全局变量,所以不再是undefined
var name="one"; function test(){ console.log(name); //one name="two"; console.log(name); //two } test();
3. 值得注意的是,上面提到的都没有传参数,如果test有参数,又如何呢?
function test(name){ console.log(name); //one name="two"; console.log(name); //two } var name = "one"; test(name); console.log(name); // one
之前说过,基本类型是按值传递的,所以传进test里面的name实际上只是一个副本,函数返回之后这个副本就被清除了。
千万不要以为函数里边的name="two"把全局name修改了,因为它们是两个独立的name
(2)作用域链
上面提到的高级函数就涉及到了作用域链
function test1(){ var name = "one"; return function (){ console.log(name); } } test1()();
1. 引入一大段话来解释:
每一段js代码(全局代码或函数)都有一个与之关联的作用域链(scope chain)。
这个作用域链是一个对象列表或者链表,这组对象定义了这段代码中“作用域中”的变量。
当js需要查找变量x的值的时候(这个过程称为变量解析(variable resolution)),它会从链的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,如果第一个对象中没有名为x的属性,js会继续查找链上的下一个对象。如果第二个对象依然没有名为x的属性,则会继续查找下一个,以此类推。如果作用域链上没有任何一个对象含有属性x,那么就认为这段代码的作用域链上不存在x,并最终抛出一个引用错误(ReferenceError)异常。
2. 作用域链举例:
在js最顶层代码中(也就是不包括任何函数定义内的代码),作用域链由一个全局对象组成。
在不包含嵌套的函数体内,作用域链上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。
在一个嵌套的函数体内,作用域上至少有三个对象。
3. 作用域链创建规则:
当定义一个函数时(注意,是定义的时候就开始了),它实际上保存一个作用域链。
当调用这个函数时,它创建一个新的对象来储存它的参数或局部变量,并将这个对象添加保存至那个作用域链上,同时创建一个新的更长的表示函数调用作用域的“链”。
对于嵌套函数来说,情况又有所变化:每次调用外部函数的时候,内部函数又会重新定义一遍。因为每次调用外部函数的时候,作用域链都是不同的。内部函数在每次定义的时候都要微妙的差别---在每次调用外部函数时,内部函数的代码都是相同的,而且关联这段代码的作用域链也不相同。
(tip: 把上面三点理解好,记住了,最好还要能用自己的话说出来,不然就背下来,因为面试官就直接问你:请描述一下作用域链...)
举个作用域链的实用例子:
var name="one"; function test(){ var name="two"; function test1(){ var name="three"; console.log(name); //three } function test2(){ console.log(name); // two } test1(); test2(); } test();
上边是个嵌套函数,相应的应该是作用域链上有三个对象
那么在调用的时候,需要查找name的值,就在作用域链上查找
当成功调用test1()的时候,顺序为 test1()->test()->全局对象window 因为在test1()上就找到了name的值three,所以完成搜索返回
当成功调用test1()的时候,顺序为 test2()->test()->全局对象window 因为在test2()上没找到name的值,所以找test()中的,找到了name的值two,就完成搜索返回
还有一个例子有时候我们会犯错的,面试的时候也经常被骗到。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <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); //都是 Button4 },false); } } window.onload=buttonInit; </script> </head> <body> <button id="button1">Button1</button> <button id="button2">Button2</button> <button id="button3">Button3</button> </body> </html>
为什么?
根据作用域链中变量的寻找规则:
b.addEventListener("click",function(){ alert("Button"+i); },false);
这里有一个函数,它是匿名函数,既然是函数,那就在作用域链上具有一个对象,这个函数里边使用到了变量i,它自然会在作用域上寻找它。
查找顺序是 这个匿名函数 -->外部的函数buttonInit() -->全局对象window
匿名函数中找不到i,自然跑到了buttonInit(), ok,在for中找到了,
这时注册事件已经结束了,不要以为它会一个一个把i放下来,因为函数作用域之内的变量对作用域内是一直可见的,就是说会保持到最后的状态
当匿名函数要使用i的时候,注册事件完了,i已经变成了4,所以都是Button4
那怎么解决呢?
给它传值进去吧,每次循环时,再使用一个匿名函数,把for里边的i传进去,匿名函数的规则如代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <script type="text/javascript"> function buttonInit(){ for(var i=1;i<4;i++){ (function(data_i){ var b=document.getElementById("button"+data_i); b.addEventListener("click",function(){ alert("Button"+data_i); },false); })(i); } } window.onload=buttonInit; </script> </head> <body> <button id="button1">Button1</button> <button id="button2">Button2</button> <button id="button3">Button3</button> </body> </html>
这样就可以 Button1..2..3了
4.上述就是作用域链的基本描述,另外,with语句可用于临时拓展作用域链(不推荐使用with)
语法形如:
with(object)
statement
这个with语句,将object添加到作用域链的头部,然后执行statement,最后把作用域链恢复到原始状态
简单用法:
比如给表单中各个项的值value赋值
一般可以我们直接这样
var f = document.forms[0]; f.name.value = ""; f.age.value = ""; f.email.value = "";
引入with后(因为使用with会产生一系列问题,所以还是使用上面那张形式吧)
with(document.forms[0]){ f.name.value = ""; f.age.value = ""; f.email.value = ""; }
另外,假如 一个对象o具有x属性,o.x = 1;
那么使用
with(o){ x = 2; }
就可以转换成 o.x = 2;
假如o没有定义属性x,它的功能就只是相当于 x = 2; 一个全局变量罢了。
因为with提供了一种读取o的属性的快捷方式,但他并不能创建o本身没有的属性。
要理解变量的作用域范围就得先理解作用域链
用var关键字声明一个变量时,就是为该变量所在的对象添加了一个属性。
作用域链:由于js的变量都是对象的属性,而该对象可能又是其它对象的属性,而所有的对象都是window对象的属性,所以这些对象的关系可以看作是一条链
链头就是变量所处的对象,链尾就是window对象
看下面的代码:
function t() {
var a;
function t2() {
var b;
}
}
js中函数也是对象,所以变量a所在的对象是t,t又在window对象中,所以a的作用域链如下
t--window
那么b所以在的对象即t2,t2又包含在t中,t又在window对象,所以b的作用域链如下
t2--t--window
明白了作用域链下面就开始变量的作用域分析了
1 javascript 没有var的变量都为全局变量,且为window对象的属性
function test1() {
//执行这个句的时候它会找作用域对象,这个函数就是作用域链中的第一个对象,但这个对象中没有相关的var语句
//于里就找作用域链的第二个对象,即全局对象,而全局对象中也没有相关的var语句
//由于没有相关的var语句,js隐式在函数地声明了变量即var all;
all = 30;
alert(all);
}
test1();
alert(all);
alert(window.all);
2 函数内(函数内的函数除外)定义的变量在整个函数内部都有效
function test2() {
var t = 0;
//在for的条件里定义变量,这个变更的作用域链对象是这个函数
//因此在整个的函数里它是有效的
for (var i = 0; i t += i;
}
alert(i);
}
test2();
3 函数内部的变量取代全局同名变量
var t = "bb";
function test() {
//执行t的时候,它会先找作用域链对象,由于它定义在函数内部,所以这个函数就是它的作用域链的第一个对象
//而在这个对象里又有t的定义,所以t就是局部变量了,它替换了全局变量t
//t只是此时有定义,但并没有赋值,赋值在下一行,所以这里输出了undefined
alert(t);
var t = "aa";
alert(t);
}
test();
4 没块的作用域
if (true) {
//在块中定义了一个变量,它的作用域链的第一个对象就是全局对象window
var tmp = 0;
}
//tmp的作用域链的第一个对象就是全局对象window,而上面又有全局对象中相关的var语句,因此输出0
alert(tmp);
以下内容来自读网上博客的总结,当笔记使用,只记重点,同时非常感谢乐于分享的博主们,是你们让我站在了巨人的肩旁上!
1、
var temp = (function(){
var name ="test";
return function(){
alert(name);
}
})();
以上代码片断是我们jser经常见到的写法,是传说中的闭包。 众所周知:调用 temp();会弹出 “ test”;该过程可以有以下三条理论作为依据来解释:
1)js 作用域只和函数的界定符相关,函数与函数的嵌套形成了作用域链;
2)作用域链的创建规则是复制上一层环境的作用域链,并将指向本环境变量对象的指针放到链首;
3)在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。
如果看了以上3条还不明白,可看接下来结合理论对代码的详细解释:
首先外层函数执行完,被销毁;但是外层函数的作用域链被复制到内层函数的作用域链里,组成内层函数的作用域链的一部分,记住是复制,不是引用(依据第2条),所以内层函数仍然可以访问到 name;由于 返回的内层函数被 temp 引用,所以当外层函数执行完被销毁后,内层函数虽然作为外层函数的一部分,但是依然存在,正如第3条依据那样,它被第三者引用了;传说中的闭包也就是这个理

在Linux多线程编程中,锁是一种非常重要的机制,可以避免线程间的竞争和死锁。然而,如果不正确使用锁,可能会导致性能下降和不稳定的行为。本文将介绍Linux中的常见锁类型,如何正确使用它们,以及如何避免竞争和死锁等问题。在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为”互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。Linux实现的互斥锁机制包括POSIX互斥锁和内核互斥锁,本文主要讲POSIX互斥锁,即线程间互斥锁。信号量用在多线程

packagemainimport"fmt"funcmain(){x:=10switchx{case0:y:='a'fmt.Printf("%c\n",y)case1://y='b'//thiscan'tcompile,y:='b'fmt.Printf("%c\n",y)default:y:=

Golang函数中的变量作用域详解在Golang中,变量的作用域指的是变量的可访问范围。了解变量的作用域对于代码的可读性和维护性非常重要。在本文中,我们将深入探讨Golang函数中的变量作用域,并提供具体的代码示例。在Golang中,变量的作用域可以分为全局作用域和局部作用域。全局作用域指的是在所有函数外部声明的变量,即在函数之外定义的变量。这些变量可以在整

掌握JavaScript函数的嵌套和作用域,需要具体代码示例在JavaScript编程中,函数是非常重要的概念。函数的嵌套和作用域能够极大地提高代码的可读性和灵活性。本文将介绍如何正确地使用嵌套函数和作用域,并提供具体的代码示例。函数的嵌套可以理解为在一个函数中定义了另一个函数。这种嵌套的方式能够将代码分成多个小块,使得程序的逻辑更加清晰。同时,嵌套函数还可

pythonLambda表达式是一个小的匿名函数,它可以将一个表达式存储在变量中并返回它的值。Lambda表达式通常用于执行简单的任务,这些任务可以通过编写一个单独的函数来完成,但Lambda表达式可以使代码更简洁和易读。Lambda表达式的语法如下:lambdaarguments:expressionarguments是Lambda表达式接收的参数列表,expression是Lambda表达式的体,它包含需要执行的代码。例如,以下Lambda表达式将两个数字相加并返回它们的和:lambdax,

JavaScript中const的作用和用法JavaScript是一种广泛应用于网页开发的编程语言,其具有灵活性和动态性是其特点之一。在JavaScript中,我们可以使用const关键字来声明一个常量。本文将介绍const关键字的作用和用法,并提供一些具体的代码示例来帮助读者更好地理解。const的作用const(常量)是一种用于声明不可更改的变量的关键字

c语言static的作用和用法:1、变量作用域;2、生命周期;3、函数内部;4、修饰全局变量;5、修饰函数;6、其他用途;详细介绍:1、变量作用域,当一个变量前有static关键字,那么这个变量的作用域被限制在声明它的文件内,也就是说,这个变量是“文件级作用域”,这对于防止变量的“重复定义”问题很有用;2、生命周期,静态变量在程序开始执行时初始化一次,并在程序结束时销毁等等。

Python是一种高级编程语言,它的易用性和流行程度使得它成为了众多程序员的首选语言。与其他语言一样,Python也存在一些常见的错误类型,例如变量未定义错误。当我们在Python中使用一个未定义的变量时,程序就会抛出一个名为“NameError”的异常。这种错误通常出现在以下几种情况下:拼写错误:可能是因为变量名拼写错误导致了变量未定义错误,我们需要仔细检


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

Dreamweaver Mac版
视觉化网页开发工具

SublimeText3 英文版
推荐:为Win版本,支持代码提示!

记事本++7.3.1
好用且免费的代码编辑器