var和function声明提升的区别

288 阅读3分钟

问题的提出

我们先来看两段代码,猜猜会输出什么?

a = 2
var a 
console.log(a)
console.log(a)
var a = 2

正确输出分别是2undefined 为什么会这样呢?这里就涉及到JavaScript的声明提升。

什么是声明提升

包括变量的和函数在内的所有声明都会在任何代码执行前首先被处理。

注意,只有声明本身会被提升,而赋值或其他运行逻辑会留在原地。如果提升改变了代码执行的顺序,会造成非常严重的破坏。

当你看到var a = 2时,可能会认为这是一个声明,但JavaScript实际上会将其看出两个声明var a;a = 2;。第一个定义声明是在编译阶段进行的。第二个赋值声明会被留在原地等待执行阶段。

所以上面的第一个代码片段会以如下形式进行处理:

var a 
a = 2
console.log(a)

第二个代码片段:

var a
console.log(a)
a = 2

再来看两个片段:

foo()
function foo() {
  console.log('foo')
}
foo()
var foo = function() {
  console.log('foo')
}

第一个代码片段会正常输出foo 第二个代码片段,报错:Uncaught TypeError: foo is not a function

函数声明会被提升,但是函数表达式却不会被提升。

var和function分别定义函数的区别

代码片段1:

var foo = function () {
  console.log("foo -- var");
};
function foo() {
  console.log("foo -- function");
}
foo();

分析代码执行如何执行的?

  1. 首先遇到var声明
var foo = function () {
  console.log("foo -- var");
};

变成:

var foo
foo = function () {
  console.log("foo -- var");
};
  1. 接着看到函数声明,会提升
function foo() {
  console.log("foo -- function");
}

变成:

var foo
function foo() {
  console.log("foo -- function");
}
foo = function () {
  console.log("foo -- var");
};
  1. 所以上面代码片段实际代码执行:
var foo
function foo() {
  console.log("foo -- function");
}
foo = function () {
  console.log("foo -- var");
};
foo();

所以输出:foo -- var

代码片段2:

console.log(a);
if (false) {
  var a = 1;
}

当使用 var 声明变量时,它写在哪里都是一样的,因为它们最终都会函数作用域和块作用域 属于外部作用域。 所以 上面代码片段输出undefined,而不是报错。

代码片段3:

var foo = 2;
function foo() {}
console.log(typeof foo);

结果输出number。 分析结果:

var foo;
function foo() {}
foo = 2;
console.log(typeof foo);

foo = 2,所以输出number

函数优先

函数声明和变量声明都会被提升。但是一个值得注意的细节是函数会首先被提升,然后才是变量。

foo(); // 1
var foo;
function foo() {
  console.log(1);
}
foo = function () {
  console.log(2);
};
foo(); // "b"
var a = true;
if (a) {
  function foo() { console.log("a"); }
}
else {
  function foo() { console.log("b"); }
}

面试题分析

题目1

代码片段如下,给出正确的输出结果

var foo = function () {
  console.log("foo1");
};
foo();

var foo = function () {
  console.log("foo2");
};
foo();

function foo() {
  console.log("foo1");
}
foo();

function foo() {
  console.log("foo2");
}
foo();

经分析,实际执行如下:

var foo
function foo() {
  console.log("foo1");
}
function foo() {
  console.log("foo2");
}
foo = function () {
  console.log("foo1");
};
foo()
foo = function () {
  console.log("foo2");
};
foo();
foo();
foo();

正确输出如下: foo1foo2foo2foo2

题目2

代码片段如下:

var a = 0;
console.log("第一次输出a: ", a);
if (true) {
  a = 1;
  console.log("第二次输出a: ", a);
  function a() {}
  a = 2;
  console.log("第三次输出a: ", a);
}
console.log("第四次输出a: ", a);

分析如下:

var a = 0 // 外部a
console.log("第一次输出a: ", a) //输出外部a
if (true) {
    // 这里js隐式的把function a的定义放到这里来了,此刻这里有一个内部a
    a = 1 // 将内部a由函数修改为1
    console.log("第二次输出a: ",a) // 此时输出的是内部a
    function a() {} // 执行到function a的声明处,此时会将if块中的a提升到外部去,也就是把内部a赋值给了外部a, 外部a此刻被修改了
    a = 2 // 此处修改的是内部a, 内部a被由1修改为2
    console.log("第三次输出a: ", a) // 输出内部a 也就是a
}
console.log("第四次输出a: ",a) // 输出外部a,由于if块中的声明同步,将外部a从0同步成了1