折戟 Arguments 对象😔

139 阅读4分钟

今天在浏览面试题的时候遇到了一道百思不得其解的题目,话不多说,直接上题目,看看几人能做对

function side(arr) {
    arr[0] = arr[2];
}
function func1(a, b, c = 3) {
    c = 10;
    side(arguments);
    console.log(a + b + c);
}
function func2(a, b, c) {
    c = 10;
    side(arguments);
    console.log(a + b + c);
}
func1(1, 1, 1);
func2(1, 1, 1);

正确输出是:

func1(1, 1, 1); // 12

func2(1, 1, 1); // 21

我想应该有人和我有一样的疑惑,这是个怎么回事呢?抱着打破砂锅问到底的学习态度,今天是一定要给他整明白的。 于是我便找资料,终于发现了有人问出了同样的问题,并且评论区给到了回答 [原文链接] (ac.nowcoder.com/discuss/721…)

image.png

于是我顺着它的解释,前往了mdn进行查阅,发现确实是这样说的 MDN链接 image.png

不包含剩余参数、默认参数和解构赋值

顺着它的话,我对严格模式下和非严格模式下的同样的代码进行测试,如下:

"use strict";
function func(a) {
  arguments[0] = 99; 
  console.log(arguments);
  console.log(arguments[0]); 
  console.log(a);  
}
func(10);

// [Arguments] { '0': 99 }
// 99
// 10


function func(a) {
  arguments[0] = 99; 
  console.log(arguments);
  console.log(arguments[0]); 
  console.log(a);  
}
func(10);

// [Arguments] { '0': 99 }
// 99
// 99

发现严格模式下对arguments对象进行操作,值确实会被改变,但是并没有影响到a,但是非严格模式下对arguments对象进行操作会同步影响到a。那么如果是对入参a直接进行操作会不会影响arguments呢

"use strict";
function func(a) {
  a=99
  console.log(arguments);
  console.log(arguments[0]); 
  console.log(a);  
}
func(10);

// [Arguments] { '0': 10 }
// 10
// 99

function func(a) {
  a=99
  console.log(arguments);
  console.log(arguments[0]); 
  console.log(a);  
}
func(10);

// [Arguments] { '0': 99 }
// 99
// 99

😯发现严格模式下对a进行修改并不会影响arguments对象,但是在非严格模式下对a的修改会映射到arguments对象。从而得出结论:

在严格模式下,函数没有包含剩余参数、默认参数和解构赋值,参数的改变并不会改变arguments对象,同时arguments对象的改变也无法改变参数的值。

当非严格模式下,函数没有包含剩余参数、默认参数和解构赋值,那么arguments对象中的值会跟踪参数的值,二者存在映射关系,会同步修改。

包含剩余参数、默认参数和解构赋值

以默认参数为例, 对严格模式和非严格模式再次进行测试:

"use strict";
function func(a=55) {
  arguments[0] = 99; 
  console.log(arguments);
  console.log(arguments[0]); 
  console.log(a);  
}
func(10);

// [Arguments] { '0': 99 }
// 99
// 10


function func(a=55) {
  arguments[0] = 99; 
  console.log(arguments);
  console.log(arguments[0]); 
  console.log(a);  
}
func(10);

// [Arguments] { '0': 99 }
// 99
// 10

可以看到,二者的答案是一样的,先再来看下直接操作a参数会怎样再得出结论:

"use strict";
function func(a=55) {
  a = 99; 
  console.log(arguments);
  console.log(arguments[0]); 
  console.log(a);  
}
func(10);

// [Arguments] { '0': 10 }
// 10
// 99


function func(a=55) {
  a = 99; 
  console.log(arguments);
  console.log(arguments[0]); 
  console.log(a);  
}
func(10);

// [Arguments] { '0': 10 }
// 10
// 99

😯对比二者可以看到,从而得出结论:

当严格模式中的函数有包含剩余参数、默认参数和解构赋值,参数的改变并不会改变arguments对象,同时arguments对象的改变也无法改变参数的值。

当非严格模式中的函数有包含剩余参数、默认参数和解构赋值,那么arguments对象中的值不会跟踪参数的值,二者不存在映射关系,不会同步修改。

原题解析

function side(arr) {
    arr[0] = arr[2];
}
function func1(a, b, c = 3) {
    c = 10;
    side(arguments);  // arguments此时传递是 [Arguments] { '0': 1, '1': 1, '2': 1 }
    console.log(a + b + c);
}
function func2(a, b, c) {
    c = 10;
    side(arguments); // arguments此时传递是 [Arguments] { '0': 1, '1': 1, '2': 10 }
    console.log(a + b + c);
}
func1(1, 1, 1);  // 12
func2(1, 1, 1);  // 21

在func1中,因为包含默认参数c,所以不被arguments对象追踪,不会同步更新,对c=10,不会影响到arguments的值,所以在进行side函数中arr[2]的值仍然是1,同时因为arr[0]也就是对应映射的a并不属于剩余参数、默认参数和解构赋值, 它的赋值会影响到a的值, 因此最后相加是1+1+10 = 12

在func2中,因为不包含默认参数,所以会被arguments对象追踪,会同步更新,对c=10,会让arguments[2]=10,所以在进行side函数中arr[2]的值10赋值给了arr[0],同时因为arr[0]也就是对应映射的a并不属于剩余参数、默认参数和解构赋值, 它的赋值会影响到a的值,因此最后相加是10+1+10 = 21

总结:

1.严格模式下,无论什么参数, 参数的改变并不会改变arguments对象,同时arguments对象的改变也无法改变参数的值。

2.非严格模式下,函数没有包含剩余参数、默认参数和解构赋值,那么arguments对象中的值会跟踪参数的值,二者存在映射关系,会同步修改。反之亦然。

3.好记性不如烂笔头,这是第一篇笔记,但不会是最后一篇.