函数和变量声明的提升
在JS中存在一个很重要的特性,函数和变量声明的提升,理解这一点对于理解我们编写的代码非常有帮助,那么什么是声明的提升呢?我们通过下面的代码来分析。
console.log(a);//①
var a = 123;
console.log(a);//②
console.log(f);//③
f();//④
function f() {
console.log("函数声明提升");
}
①处的代码如果按照我们以前的理解,代码从上而下执行,那么在执行这行代码的时候,a还没有被声明,所以直接访问一个没有被声明的变量,程序应该报错。
但是结果却大出所料,这里得到的结果是undefined。
③处的结果也和我们最初的认识是不一样的,结果为f对应的函数对象。
造成这个结果是因为变量和函数的作用域提升的原因,什么意思呢?
JS是解释性语言,JS引擎对代码的处理分为两步:
预解析处理:在代码执行之前,对代码做全局扫描,对代码中出现的变量或者函数提前声明处理;
解析之后我们的代码:
var a;//提前声明,但不初始化
console.log(a);//undefined
a = 123;
console.log(a);//123
//提前声明
function f() {
console.log("函数声明提升");
}
console.log(f);//函数对象
f();//函数声明提升
调用执行:自上而下的执行代码
变量提升和作用域的关系
题1:
f();
function f() {
console.log("1");
}
f();
function f() {
console.log("2");
}
f();
function f() {
console.log("3");
}
根据前面对函数声明提升的认识,结果三次都是 “3”。
预解析之后的代码:
function f() {
console.log("3");
}
f();
f();
f();
为什么解析之后只剩下一个函数,而且是最后那一个?
因为三个函数的名称一样,后面的函数会将前面的覆盖,所以最后只剩下最后一个函数了。
题2:
console.log(a);
var a = 123;
console.log(a);
function f1() {
console.log(a);
var a = 456;
console.log(a);
}
f1();
console.log(a);
直接先对代码做预解析,然后再做分析。
var a;//变量声明提升
function f1() {//函数声明提升
var a;//变量声明提升
console.log(a);
a = 456;
console.log(a);
}
console.log(a);
a = 123;
console.log(a);
f1();
console.log(a);
解析得到上面的代码结果就非常明显了,分别是:undefined 123 undefined 456 123
由于在函数内部有变量a,所以在函数中访问到的是这个局部变量,如果在函数作用域中没有变量a,那么就会跳出函数作用域来到全局作用域来查找。
声明提升的规则
声明提升是将变量或者函数的声明提升到当前作用域的最顶端。在具体使用的过程中存在以下需要注意的细节。
- 变量和变量同名,解析之后只存在一个当前变量的声明
console.log(a);
var a = 123;
console.log(a);
var a = 456;
console.log(a);
解析之后:
var a;
console.log(a);//undefined
a = 123;
console.log(a);//123
a = 456;
console.log(a);//456
- 函数和函数同名,后面的声明将前面的覆盖
f();
function f() {
console.log("1");
}
f();
function f() {
console.log("2");
}
f();
function f() {
console.log("3");
}
解析之后:
function f() {
console.log("3");
}
f();//3
f();//3
f();//3
- 函数和变量同名,函数声明提升,忽略变量的声明
console.log(a);
var a = 123;
console.log(a);
function a() {}
console.log(a);
function a() {}
console.log(a);
解析之后:
function a() {}
console.log(a);//函数a
var a = 123;//将前面的函数覆盖,a的值变为123
console.log(a);//123
console.log(a);//123
console.log(a);//123
- 如果是命名函数,则只将前面的变量声明提升,函数不动。
console.log(fn1);
function fn1() {
}
console.log(fn1);
console.log(fn2);
var fn2 = function () {
}
console.log(fn2);
解析之后:
function fn1() {
}
var fn2;
console.log(fn1);//fn1函数
console.log(fn2);//undefined
fn2 = function () {
}
console.log(fn2);//fn2函数
面试题1:
console.log(a,b); //undefined undefined
var a=12;
var b=13;
sum(); //1
function sum(){
console.log(1);
}
面试题2:
console.log(a); //undefined
var a=12;
function fn(){
console.log(a); //undefined
var a=13;
}
fn();
console.log(a); //12
面试题3:
console.log(a); //undefined
var a=12;
function fn(){
console.log(a);//12
a=13;
}
fn();
console.log(a)//13
面试题4:
console.log(a);//这个就直接报错了,因为没有变量提升 a is not defined
a=12;
function fn(){
console.log(a);
var a = 13;
}
fn();
console.log(a)
面试题5:
var foo=1;
function bar(){
//不管条件是否成立,都要进行变量提升
//这时的foo属于私有变量 给的默认值是undefined,undefined取反就为真
if(!foo){
var foo=10;
}
console.log(foo);
}
bar();//10
面试题6:
var n=13;
function fn(n) {
alert(n); // 13
var n=14;//有形参赋值了,就不在走变量提升了
alert(n); // 14
}
fn(n);
console.log(n);//13
面试题7:
console.log(a);// undefined
a=12;
function fn(a) {
console.log(a); //undefined
a=13;
}
fn();//注意这里没有传值
var a;
console.log(a); //12
面试题8:
function f1() {
if("a" in window){//带var的变量提升时不看条件成立不成立的都会提升。
var a = 10; //函数体中声明的变量是局部变量 所以不在window
}
console.log(a);//undefined
}
f1();
面试题9:
if(!"a" in window){//使用var声明的全局变量会变成window成员。
var a = 10;
}
console.log(a);//undefined
面试题10:
console.log(a, b, c);// undefined undefined undefined
var a=10,b=20,c=30;
function f(a) {
console.log(a, b, c);// 10 undefined 30
var b=a=c=100; //a 和 b 是局部变量,只有c是全局变量
console.log(a, b, c);//100 100 100
}
f(10,20);
console.log(a, b, c);// 10,20,100
面试题11:
function foo() {
var num = 123;
console.log(num);//123
}
foo();
console.log(num);//报错 num is not defined
面试题12:
var scope = "global";
foo();
function foo() {
console.log(scope);//undefined
var scope = "local";
console.log(scope);//local
}
面试题13:
var foo = 1;
function bar() {
if(!foo){ //foo变量提升完是undefined 取反条件成立
var foo = 10;
}
console.log(foo);//10
}
bar();
console.log(foo);//1
面试题14:
var arr = [1,2,3,4];
function fun(arr) {
arr[0] = 5;
arr = [0]; //arr开辟了一个新的地址,指针指向改变
arr[0] = 6;
console.log(arr);//[6]
return arr
}
var res = fun(arr);
console.log(res);//[6]
console.log(arr);//[5,2,3,4]