用法可以说已经深入骨髓了
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])//奇怪的写法
写法二虽然看起来的多余的一层展开,但是似乎又是最符合函数原则的(整个包装,整个展开参数)。
我觉得,这奇怪的写法,只是因为又有人偷懒了,rest
和 spread
使用了同一符号。
两种语法其实是...毫无关系的。标准 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
也就是说,仅能在函数声明的时候使用 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。
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 啦~