前端面试的一些知识点(JavaScript) - 函数和作用域

函数

JavaScript中函数是一等公民

区别

函数声明函数表达式在语法上相同,如何区分?

区分函数声明和表达式最简单的方式是看 function 关键字出现在声明中的位置
如果 function 是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。
函数声明和函数表达式之间最重要的区别是他们的名称标识符将会绑定在何处

提升

只有声明本身会被提升,而赋值或其他运行逻辑会留在原地,每个作用域都会进行提升操作。
这就是为什么函数声明会被提升,而函数表达式不会被提升的原因。
即使是具名的函数表达式,名称标识符在赋值之前也无法在所在的作用域中使用。

函数声明的提升优先级最高,一个普通块内部的函数声明通常会被提升到所在作用域的顶部。

名称标识符

函数声明的名称标识符可以在函数体外使用,不能缺省。

函数表达式的名称标识符只能在函数体内部使用,缺省则成为匿名函数表达式。

意外转换

函数声明会意外转换为函数表达式,视具体位置而定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 函数声明
function foo() {}

// 函数表达式
(function bar() {})

// 函数表达式
x = function hello() {}

if (x) {
// 函数表达式
function world() {}
}

// 函数声明
function a() {
// 函数声明
function b() {}
if (0) {
//函数表达式
function c() {}
}
}

立即执行函数表达式(IIFE)

函数被包含在一对 () 括号内部变成表达式,末尾加上另一个 () 可以立即执行这个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 两种形式都可以
(function () {...}());
(function () {...})();
// 进阶用法
var a = 2;
(function IIFE(global) {
var a = 3;
console.log(a); //3
console.log(global.a); //2
})(window);

// 变化,倒置代码的运行顺序,将需要运行的函数放在第二位,在IIFE执行之后当作实参传递进去。
var a = 2;
(function (def) {
def(window);
}(function (global) {
var a = 3;
console.log(a); //3
console.log(global.a); //2
});

词法作用域

有2个欺骗词法作用域的方法:eval 和 with,知道就好,不建议使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function foo(obj) {
with(obj) {
a = 2;
}
}
var o1 = {
a: 3
}
var o2 = {
b: 3
}

foo(o1);
console.log(o1.a); // 2

foo(o2);
console.log(o2.a); // undefined
console.log(a); // 2, with 中的 a 被泄漏到全局作用域上。

Demo 1:

1
2
3
4
5
6
7
8
9
10
11
function foo() {
function bar(a) {
i = 3;
console.log(a+i);
}

for(var i=0; i<10; i++) {
bar(i*2);
}
}
foo(); // 会无限循环

分析:

1,for 循环内的 var i 会提升到 foo 函数体的顶部。

2,当 bar 函数运行时, i = 3 语句会赋值给 foo 函数作用域内的 i 变量。

3,第一次循环时,i = 0,第一次调用 bar 函数时,a = 0,i = 3,输出3,循环后 i++,i = 4;

4,第二次循环开始,传入 bar 的 a 恒等于 4 * 2 = 8,i 被 初始化为 3,输出 11,无限循环。

解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function foo() {
function bar(a) {
i = 3;
console.log(a+i);
}

for(let i=0; i<10; i++) {
bar(i*2);
}
}
// 或者
function foo() {
function bar(a) {
var i = 3;
console.log(a+i);
}

for(var i=0; i<10; i++) {
bar(i*2);
}
}

下一集:闭包