作用域
<script> var outerVar = "outer"; function fn(){
console.log(outerVar); } fn();//result:outer
script>
<script> function fn(){
var innerVar = "inner"; } fn(); console.log(innerVar);// ReferenceError: innerVar is not defined
script>
需要注意的是,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
<script> function fn(){
innerVar = "inner"; } fn(); console.log(innerVar);// result:inner
script>
再来看一个代码:
<script> var scope = "global"; function fn(){
console.log(scope);//result:undefined var scope = "local"; console.log(scope);//result:local; } fn();
script>
很有趣吧,第一个输出居然是undefined,原本以为它会访问外部的全局变量(scope=”global”),但是并没有。这可以算是javascript的一个特点,只要函数内定义了一个局部变量,函数在解析的时候都会将这个变量“提前声明”:
<script> var scope = "global"; function fn(){
var scope;//提前声明了局部变量 console.log(scope);//result:undefined scope = "local"; console.log(scope);//result:local; } fn();
script>
for(int i = 0; i < 10; i++){ //i的作用范围只在这个for循环 } printf("%d",&i);//error
但是javascript不同,并没有所谓的块级作用域,javascript的作用域是相对函数而言的,可以称为函数作用域:
<script> for(var i = 1; i < 10; i++){ //coding } console.log(i); //10
script>
作用域链(Scope Chain)
执行环境(execution context)
每个函数运行时都会产生一个执行环境,而这个执行环境怎么表示呢?js为每一个执行环境关联了一个变量对象。环境中定义的所有变量和函数都保存在这个对象中。
全局执行环境是最外围的执行环境,全局执行环境被认为是window对象,因此所有的全局变量和函数都作为window对象的属性和方法创建的。
js的执行顺序是根据函数的调用来决定的,当一个函数被调用时,该函数环境的变量对象就被压入一个环境栈中。而在函数执行之后,栈将该函数的变量对象弹出,把控制权交给之前的执行环境变量对象。
举个例子:
<script> var scope = "global"; function fn1(){
return scope; } function fn2(){
return scope; } fn1(); fn2();
script>
标识符解析是沿着作用域链一级一级地搜索标识符地过程。搜索过程始终从作用域链地前端开始,然后逐级向后回溯,直到找到标识符为止(如果找不到标识符,通常会导致错误发生)—-《JavaScript高级程序设计》
<script> function outer(){
var scope = "outer"; function inner(){
return scope; } return inner; } var fn = outer(); fn();
script>
像上面这种内部函数的作用域链仍然保持着对父函数活动对象的引用,就是闭包(closure)
闭包
<script> function outer(){
var result = new Array(); for(var i = 0; i < 2; i++){
//注:i是outer()的局部变量 result[i] = function(){
return i; } } return result;//返回一个函数对象数组 //这个时候会初始化result.length个关于内部函数的作用域链 } var fn = outer(); console.log(fn[0]());//result:2 console.log(fn[1]());//result:2
script>
<script> function outer(){
var result = new Array(); for(var i = 0; i < 2; i++){ //定义一个带参函数 function arg(num){
return num; } //把i当成参数传进去 result[i] = arg(i); } return result; } var fn = outer(); console.log(fn[0]);//result:0 console.log(fn[1]);//result:1
script>
<script> function outer(){
var result = new Array(); for(var i = 0; i < 2; i++){ //定义一个带参函数 function arg(num){
function innerarg(){
return num; } return innerarg; } //把i当成参数传进去 result[i] = arg(i); } return result; } var fn = outer(); console.log(fn[0]()); console.log(fn[1]());
script>
function outer(){
var result = new Array(); for(var i = 0; i < 2; i++){ //定义一个带参函数 result[i] = function(num){
function innerarg(){
return num; } return innerarg; }(i);//预先执行函数写法 //把i当成参数传进去 } return result; }
关于this对象
关于闭包经常会看到这么一道题:
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){
return function(){
return this.name; }; } }; alert(object.getNameFunc()());//result:The Window
《javascript高级程序设计》一书给出的解释是:
this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象调用时,this等于那个对象。不过,匿名函数具有全局性,因此this对象同常指向window
参考资料
- Js作用域与作用域链详解
- 阮一峰博文《学习Javascript闭包(Closure)》
- JavaScript 开发进阶:理解 JavaScript 作用域和作用域链
- 结合代码图文讲解JavaScript中的作用域与作用域链
- 《Javascript高级程序设计》
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/205734.html原文链接:https://javaforall.net
