从 arrow function 到 rest/spread syntax 到 void 到 IIFE

善良单纯的小阿板
创建时间: 2019年9月15日 庆丰七年
最后编辑: 2019年9月15日 5 年前
mdn 中,把 [`spread syntax`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) 翻译作 `展开语法`,我们有时候会叫 `展开表达式`, 甚至直接读 `点点点`,它同 `arrow function` 已经是被普遍地使用和普遍地做为面试题的东西了。

用法可以说已经深入骨髓了

const a = [42, 1729]
const b = [...a, ...a]
const f = (x, y) => x + y
f(...a)

在这种“正常”的用法中,我们用 ... 弹出了数组(iterable)的元素,使之可以被分割和拼接。

然后今天在读 mdn 的时候注意到了这段内容

Arrow functions do not have their own arguments object. Thus, in this example, arguments is simply a reference to the arguments of the enclosing scope:

In most cases, using rest parameters is a good alternative to using an arguments object.

function foo(n) { 
  var f = (...args) => args[0] + n;
  return f(10); 
}

foo(1); // 11

似乎不好理解 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters

function myFun(a, b, ...manyMoreArgs) {
  console.log("a", a); 
  console.log("b", b);
  console.log("manyMoreArgs", manyMoreArgs); 
}

myFun("one", "two", "three", "four", "five", "six");

例中给的也是常用的用法。

但是两个常用放在在一起,是不是有一种...套娃的感觉?

如果

function myFun(a, b, ...manyMoreArgs) {
    console.log("a", a); 
    console.log("b", b);
    console.log("manyMoreArgs", manyMoreArgs); 
}

const a = [42, 1729]
myFun(...a, ...a)

myFun(...[...a, ...a])//奇怪的写法

写法二虽然看起来的多余的一层展开,但是似乎又是最符合函数原则的(整个包装,整个展开参数)。

我觉得,这奇怪的写法,只是因为又有人偷懒了,restspread 使用了同一符号。

两种语法其实是...毫无关系的。标准 https://www.ecma-international.org/ecma-262/6.0/#sec-array-initializer 12.2.5

SpreadElement[Yield] :

... AssignmentExpression[In, ?Yield]

数组部分定义了元素弹出。

rest 方法仅可用于 function-definitions

https://www.ecma-international.org/ecma-262/6.0/#sec-function-definitions

https://www.ecma-international.org/ecma-262/6.0/#sec-function-definitions-static-semantics-expectedargumentcount 14.1.6

也就是说,仅能在函数声明的时候使用 rest,也就是弹“入”

emmmm 其实我在看 ecma 之前,也不懂其用意。先读《情商》,不是,拿错书,先读了 《圣经》,也不是,先读了《babel》

const f = (...a) => {
    console.log(a)
}

f(...[42, 1792])
f(42)
f(42, 1792)

=== babel7 es2015 ===>

var f = function f() {
  for (var _len = arguments.length, a = new Array(_len), _key = 0; _key < _len; _key++) {
    a[_key] = arguments[_key];
  }

  console.log(a);
};

f.apply(void 0, [42, 1792]);
f(42);
f(42, 1792); 

显而易见,只是把 arguments 压入,同时转换了箭头函数(这里其实是有问题的,因为...其实不 单纯 是一个 es6 的语法,es9 中对其对 iterable 的表现进行了修改,但是又不影响 reset 部分。因为箭头函数又没有 arguments,所以看起来也怪怪的)

a = [1,2,3]
const b = [...a, ...a]

=== babel7 es2015 ===>

function _toConsumableArray(arr) {
    return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
}

function _nonIterableSpread() {
    throw new TypeError("Invalid attempt to spread non-iterable instance");
}

function _iterableToArray(iter) {
    if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
}

function _arrayWithoutHoles(arr) {
    if (Array.isArray(arr)) {
        for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
            arr2[i] = arr[i];
        }
        return arr2;
    }
}

a = [1, 2, 3];
var b = [].concat(_toConsumableArray(a), _toConsumableArray(a));

一个判别拷贝(仅 es6 中的数组部分 并无 es9 中的对象展开)

很有意思的是,如果我们声明 a,会变成

const a = [1,2,3]//这里声明
const b = [...a, ...a]

=== babel7 es2015 ===>

var a = [1, 2, 3];
var b = [].concat(a, a); 

emmmm,这个编译原理就是另一个故事了

关注点回到前边

f.apply(void 0, [42, 1792]);

babel 使用 void operator 生成 undefined,其实这也是我面试的时候回答 “null 和 undefined 的区别”的时候一个技巧23333。很多初学者其实并不了解 undefined 的价值(对于v8的性能优化和垃圾回收)(说得跟我多知道一样),不过这就是另一个故事了。我们说到箭头函数,大家不会用 void 的很重要的一点原因是.....这部分的mdn没有被翻译成中文,当然我写完这篇博客之后已经提交了中文翻译,好久没给 mdn 提东西了23333。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void#Non-leaking_Arrow_Functions

Non-leaking Arrow Functions

button.onclick = () => void doSomething();

于是,就轻而易举的,扣到了题目。从箭头函数扯到了 js 的 void

Immediately Invoked Function Expressions

提到了 void 就不得不提一下 IIFE

如 mdn 例

void function iife() {
    var bar = function () {};
    var baz = function () {};
    var foo = function () {
        bar();
        baz();
     };
    var biz = function () {};

    foo();
    biz();
}();

同样也是面试考点,闭包和 IIFE ,还有其他 void 的用处也不赘述了,mdn 有很好的解释和中文翻译2333,成功地又一次扣到标题,然后开心 ending 啦~