🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
作用域的作用:隔离变量 js没有块作用域(ES5有啦) ## **全局作用域(全局执行上下文)** 全局作用域在打开页面时创建,关闭页面时销毁 在全局作用域中有一个全局对象window,它代表的是一个浏览器窗口,他由浏览器创建我们可以直接使用 在全局作用域中创建的变量都会作为window对象的属性保存 首先对全局数据进行预处理: * 将 var定义的变量添加为window属性 * 将 function申明的函数添加为window方法 * 将this指向window(即将this赋值为window) ``` var a=1; //此时a===window.a console.log(a);//1 console.log(window.a)//1 console.log(window.a==a)//true ``` 在全局作用域中创建的函数都会作为window对象的方法保存 ``` function demo(){ console.log("我是自定义的demo函数") } demo();//我是自定义的demo函数 window.demo();//我是自定义的demo函数 ``` **变量声明提前(变量提升):** 使用var 声明的变量,会在所有代码执行之前声明(但是不会赋值) ``` console.log(a); //undefined var a=1; //等效于 var a; console.log(a);//undefined a=1; //变量没有声明提前 console.log(b)//Uncaught ReferenceError: b is not defined ``` **函数的声明提前(函数提升):** ``` //使用函数的声明形式创建的函数,他会在所有代码执行之前创建,所以我们可以在定义函数前调用函数 demo(); function demo(){} //其他函数声明方式则不能提前调用函数 demo1(); var demo1 =function(){ console.log("haha");// } ``` 注意:js中一个{}代表一组代码块,他只有分组作用,没有作用域等特性 ## ## **函数作用域(函数执行上下文)** 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的 在函数作用域中可以访问到全局作用域的变量在全局作用域中无法访问到函数作用域的变量 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有则直接使用,没有就去上一级的作用域查找知道全局作用域 ***** 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(存在与内存栈中)对局部数据进行预处理 * 形参变量==>赋值(实参)=>添加为执行上下文的属性 * arguments==>赋值(实参列表),添加为执行上下文的属性 * var定义的局部变量==>undefined,添加为执行上下文的属性 * function声明的函数==>赋值(fun),添加为执行上下文的方法 * this==>赋值(调用函数的对象) 开始执行函数件代码 ``` function fn(a1){ console.log(a1);//2 console.log(a2);//undefined a3();//"a3()" console.log(this);//window console.log(arguments);//伪数组(2,3) var a2=3; function a3(){ console.log("a3()'); } } fn(2,3) ``` ``` var g=1; a=2; var b=10; function demo(){ //局部变量 var c="yinli"; //全局变量 f="dash"; var b=3; console.log(g); console.log(a); console.log(b); } demo(); console.log(f); console.log(c);//c is not defined ``` ``` var a=123; function demo(){ alert(a);//123 } demo(); ``` ``` var a=123; function demo(){ alert(a);//undefined //变量提前声明 var a=456; } demo(); alert(a);//123 ``` ``` var a=123; function demo(){ //a变量此时不存咋,想上一级作用域查找 alert(a);//123 //全局变量 a=456; } demo(); alert(a)//456 ``` ``` var a=123; function demo(a){ //参数 alert(a);//undfined //修改参数 a=456; } demo(); alert(a);//123 ``` ``` var a=123; function demo(a){ //参数 alert(a);//123 //修改参数 a=456; } demo(123); alert(a);//123 ``` ## ## **执行上下文栈** 栈:后进先出 函数的执行上下文对象在函数调用的时候产生, 下例:var a=10 是一个全局上下文 bar(10);与 foo(x+b)是函数上下文 下例的执行上下文总数:n+1=2+1=3 如果在调用一次bar(10);就是4+1=5 ``` var a=10;//1、进入全局执行上下文 var bar=function(x){ var b=5; foo(x+b);//3、进入fn执行上下文 } var fn =function(y){ var c=5; console.log(a+c+y); } bar(10);//2、进入bar执行上下文 ``` ![](https://img.kancloud.cn/d6/a7/d6a7d90b40426d8018e3b93e665545fa_601x115.png) 1,在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象 2,在全局执行上下文(window)确定后,将其添加到栈中(压栈) 3·在函数执行上下文创建后,将其添加到栈中(压栈) 4,在当前函数执行完后,将栈顶的对象移除(出栈) 5,当所有的代码执行完后,中只剩下window 递归: ``` console.log(i); var i=1; foo(1); function foo(i){ if(i==4){ return; } console.log("fb:"+i); foo(i+1);//递归调用,在内部调用自身 console.log("fe:"+i); } console.log(i); ``` ![](https://img.kancloud.cn/e9/6b/e96be6098255ec14e45105ca4449ee8d_1014x753.png) 结果: ``` undefined fb:1 fb:2 fb:3 fe:3 fe:2 fe:1 1 ``` 先执行变量提升,在执行函数提升 ``` //先执行变量提升,在执行函数提升 function a(){} var a; console.log(typeof a);//"function" if (!(b in window)) { var b=1; } console.log(b);//undefined var c=1; function c(c){ console.log(c); } c(3);//报错 ``` 区别1 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时 全局执行上下文环境是在全局作用域确定之后,js代码马上执行之前创建 函数执行上下文是在调用函数时,函数体代码执行之前创建 区别2, 作用域是静态的,只要函数定义好了就一直存在,且不会再变化 执行上下文是动态的,调用函数时创建,函数调用结束时就会自动释放 联系: 执行上下文环境(对象)是从属于所在的作用域全局上下文环境==>全局作用域 函数上下文环境=>对应的函数使用域 作用域链 ``` var a=2; function fn1(){ var b=3; function fn2(){ var c=4; console.log(c); console.log(b); console.log(a); console.log(d); } fn2() } fn1() ``` ``` var x=10; function fn(){ console.log(x) } function show(f){ var x=20; f(); } show(fn); 输出10 ``` ``` var fn=functio(){ console.log(fn)//打印此函数 } var obj={ fn2:function(){ console.log(fn2)//undefined console.log(this.fn2)//打印此方法 } } obj.fn2() ```