本文所有图的黑色框框表示全局执行上下文,其他颜色框框表示私有上下文,圆角框框表示堆内存。
ARG和形参的映射机制「非严格模式」
var a = 4;
function b(x, y, a) {
console.log(a);
arguments[2] = 10;
console.log(a);
}
a = b(1, 2, 3);
console.log(a);
// 输出 3 10 undefined
看下真正的执行过程:
// EC(G) 进栈
// a --> 4 --> undefined
// b --> 0x001 [[scope]]: EC(G)
// 变量提升
// function b(x, y, a) { ... }
// var a;
var a = 4;
function b(x, y, a) {
// EC(b) 进栈
// x --> 1,
// y -- > 2
// a --> 3 --> 10
// 作用域链: <EC(B),EC(G)>
// 初始化 ARG(实参集合): { 0: 1, 1: 2, 2: 3, length: 3 } 类数组结构
// 形参赋值: x = 1, y = 2, a = 3
// 变量提升: ----
console.log(a); // 3
arguments[2] = 10;
console.log(a); // 10
}
a = b(1, 2, 3); // 全局的 a 变为 undefined
console.log(a); // undefined
在非严格模式下,初始化 ARG 和 形参赋值后,会建立一个映射机制,一个改另一个也改,但是在严格模式下,是没有这个机制的,严格模式输出 3 3 undefined
闭包来了
var test = (function(i) {
return function() {
alert(i *= 2);
}
})(2);
test(5);
// 4
这道题画图理解即可,考验闭包的基础,输出 4.
闭包作用域
var x = 4;
function func() {
return function(y) {
console.log(y + (--x));
}
}
var f = func(5);
f(6);
func(7)(8);
f(9);
console.log(x);
// 输出:9 10 10 1
这道题建议画图理解,或者参考前面写过的另一篇文章 (一道闭包作用域面试题)[blog.yangshuaiweb.com/2021/05/25/…]
一道关于惰性思想的题(函数重构)
var x = 5, y = 6;
function func() {
x += y;
func = function(y) {
console.log(y + (--x));
}
console.log(x, y);
}
func(4);
func(3);
console.log(x, y);
// 输出: 11,6 13 10,6
这道题需要注意的点是,第一次 func(4) 被调用时,执行的是大函数。
我们可以看到,并非大函数返回小函数才能形成闭包,而是函数执行形成一个全新的私有的上下文,在私有上下文中的某些东西,被上下文以外的地方引用,那么当前上下文是不能被释放的,供下级上下文调取使用,这样的保护 + 保存机制,称之为闭包,所以函数重构也能形成闭包。
一道套娃面试题(复杂)
function fun(n, o) {
console.log(o);
return {
fun: function(m) {
return fun(m, n);
}
}
}
var c = fun(0).fun(1);
c.fun(2);
c.fun(3);
// 0 1 1
这个题有点难度,还是老规矩,一定要画图理解,需要注意每一次返回的对象中的函数的父作用域究竟是谁。
匿名函数具名化引发的事情
var b = 10;
(function b() {
b = 20;
console.log(b); // func
})();
console.log(b); // 10
自执行函数本身就是自执行的匿名函数,函数表达式的函数名不能修改,且只能内部访问。
一般使用场景是,匿名递归函数自己调用自己,arguments.callee 也可以访问函数本身,但在严格模式下,就访问不到了。
但是具名化的函数名权重较低,但凡我们在函数内部重新基于 let/var/function/const等声明过同名的变量,都可以把它替换掉。
(function b() {
let b = 20;
console.log(b); // 20
})();
(function h() {
console.log(h); // undefined
var h = 20;
console.log(h); // 20
})();