💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ## 变量提升 在**当前**作用域当中,把所有带var和function关键字的提前声明或则定义 ### 声明和定义 > 在`当前作用域`中,js代码自上而下执行之前,浏览器首先会把所有带`var/function`关键字的进行提前的`声明或则定义` es6中的`let/const`并不会 >声明(declare): var num;在当前作用域中吼一嗓子 我有num这个名了 > 定义(defined):num=12;把声明的名字赋一个值(先有个值,再把这个值和声明的名字建立链接) ### var和function >[warning]带var关键字的只是提前的声明一下;带function关键字的在变量提升阶段把声明和定义都完成了 ``` console.log(num); console.log(fn); var numb = 13; function fn(){ console.log(a); var a =10; console.log(a); } fn(); console.log(numb) ``` ![](https://box.kancloud.cn/136bf7faf8fae03bc3f278ba98323d72_1732x724.png) > **注意:** 除了全局作用域执行时,函数执行时也会执行自己的变量提升 ### 带var和不带var的区别 在项目中,如果目的是创建变量,最好不要省略var #### 带var是声明变量(顺道在全局下定义一个属性) 在当前作用域中声明一个变量,如果当前是全局作用域,也相当于给全局作用域设置了一个属性叫做a。 ``` //=>变量提升:var a; <=> window.a=undefined; console.log(a); //->undefined var a = 12; //定义了一个变量a并且将12这个值赋给了它,另外顺道在 window.a=12 console.log(a); //->12 console.log(window.a); //->window['a'] 在”全局作用域“中,我们声明一个变量相当于给全局对象window增加了一个属性名 ``` #### 不带var是全局属性 在全局作用域中,如果不带var,仅仅只是给全局对象设置了一个新的属性名(相当于window.a=xx) ``` console.log(a); //=>Uncaught ReferenceError:a is not defined //先看是不是变量,如果没有,在window下看有没有这个属性,如果都没有,就会报错(浏览器会认为你在忽悠它) //如果输出的window.a,不会报错,只会输出undefined a = 12; //<=>window.a = 12 console.log(a); //=>12 console.log(window.a); //=>12 ``` ### 只对等号左边的进行变量提升 ~~~ 等号= 左边是变量 右边都应该是值 ~~~ 匿名函数:函数表达式(把函数当做一个值赋值给变量或则其它内容) ``` oDiv.onclick = function(){ } //<=> oDiv.onclick = aaafff000 ``` 变量提升只会提升等号左边的,右边是值不会提前声明。 ``` console.log(fn);//undefined var fn = function(){ } console.log(fn);//->函数本体 //谷歌浏览器中用小f 表示一个function ``` ``` sum(); //报错 Uncaught TypeError:sum is not a function var sum = function(){} ``` 真实项目中,应用这个原理,我们创建函数的时候可以使用函数表达式的方式。 为了在项目中使代码更加严谨,我们一般都通过函数表达式的方式来定义一个函数(先定义后执行) 因为: 1. 只能对等号左边的进行提升,所以变量提升完成后,当前函数只是声明了,没有定义,想要执行函数只能放在赋值的代码之后(放在前面执行相当于让undefined执行,会报错的) 2. 这样让我们的代码逻辑更加严谨,以后想要知道一个执行的函数做了什么功能,只需要向上查找定义的部分即可(不会存在定义的代码在执行下面的情况) ``` var fn = function sum(){ console.log(sum);//->函数本身 console.log(1); console.log(arguments.callee);//->函数本身 } sum(); //Uncaught TypeError:sum is not a function fn(); ``` ### 不管条件是否成立,都要进行变量提升 不管条件是否成立,**判断体**当中出现的`var`或则`function`都会进行变量提升,但是在最新版本的浏览器当中,function声明的变量只能提前声明不能定义了。(前提:函数是在判断体中) 老版本将浏览器的function是声明加定义,新版本只会声明(说的是放置在判断体中时的情况) ``` console.log(num); //->undefined console.log(fn); //->undefined if(1 !== 1){ //不会走 console.log(num); console.log(fn); var num = 12; function fn(){} console.log(num); console.log(fn); } console.log(fn); //=>undefined ``` ``` console.log(num); //->undefined console.log(fn); //->undefined if(1 === 1){ console.log(num); //->undefined console.log(fn); //->函数体本身 var num = 12; function fn(){} console.log(num); //->12 console.log(fn); //=>函数体本身 } console.log(fn); //=>函数体本身 ``` ``` console.log(fn); //undefined for(var i=0;i<9;++i){ function fn(){} } ``` > 代码执行到条件判断的地方 >[条件不成立] >进入不到判断体中,此时之前声明的变量或则函数依然是undefined > >[条件成立] >进入条件判断体中的第一件事情不是代码执行,而是把之前变量提升没有定义的函数首先定义了(进入到判断体中函数就定义了,为了迎合ES6中的块级作用域) ``` f = function(){ return true; } g = function(){ return false; } ~function(){ //=>[私有作用域] //变量提升:g //虽然g提升了但在新版本中,因为这货在判断体里,只提升不会定义 //故g()报错 if(g()&&[]==![]){ f=function(){ return false; }; function g(){ return true; } } }(); console.log(f()); // console.log(g()); //false ``` ### 重命处理 ``` fn(); //4 function fn(){ console.log(1); } fn(); //4 function fn(){ console.log(2); } fn(); //4 var fn = 13; fn(); //报错 function fn(){ console.log(3); } fn(); function fn(){ console.log(4); } fn(); ``` 在变量提升阶段,如果名字重复了,不会重新的进行声明,但是会重新进行定义(后面赋的值会把前面赋的值替换掉) ## 作用域链 ``` function fn(){ a = 123; // <=>window.a = 123; console.log(a); //=>123 } fn(); console.log(a); //=>123 ``` >函数执行形成一个私有的作用域(保护私有变量),进入到私有作用域中,首先变量提升(声明过的变量是私有的),接下来代码执行 >1. 执行的时候遇到一个变量,如果这个变量是私有的,变量不受外界干扰 >2. 如果当前这个变量不是私有的,我们需要向它的上级作用域进行查找,上级也没有,则继续向上查找,直到找到window全局作用域为止,我们把这种查找机制叫做**作用域链** >1)如果上级作用域有,我们当前操作的都是上级作用域中的变量(假如我们在当前作用域把值改了,相当于把上级作用域中的这个值给改了) >2)如果上级作用域中没有这个变量(找到window也没有) >变量 = 值:相当于给window设置了一个属性,以后再操作window下就有了 >alert(变量):想要输出这个变量,但此时是没有的,所以会报错 ``` console.log(x,y);//undefined undefined var x = 10, y = 20; //var x=10;var y=20; function fn(){ console.log(x,y); //undefined 20 var x = y = 100; //var x = 100;y=100; console.log(x,y);// 100 100 } fn(); console.log(x,y);//10,100 ``` 作用域(开辟)->变量提升->逐行运行