Open aleen42 opened 7 years ago
其实我们很多时候在设计一个 JavaScript 函数时,都可能会遇到这样的一种情况:参数数量不定,且一般会传递有多个。此时,我们就会想该如何更优雅地设计函数来接收这些不定的参数?此 Issue 的提出就是为了谈谈不才及劣者所知道的一些见解。
对于这样的情况,我们肯定会想到一对函数:
Function.prototype.apply()
Function.prototype.call()
同样是为方法调用指定 this,两者的区别在于传递不定参数的方式。
this
为了方便记忆,我通常会采用一种巧妙的方法去区分两者。call() 首字母为 c,因而可看作以逗号(Comma)的方式来区分参数,而 apply() 首字母为 a,因而也可看作是以数组(Array)的方式来区分参数。
call()
apply()
这样一看,官方似乎已为我们预先提供了两种通用的方式:
然而,若不去亲身实现还不知道该怎么设计函数才能更为优雅?对此,不才与劣者认为应该先实现数组组合的方式,而后再实现逗号分隔的方式。为了能更好地说明,我将举例 underscore 中关于 _.without() 与 _.difference() 的实现。在讨论实现之前,我们先了解这两个函数到底有何作用?其实,它们主要用于过滤数组中的部分成员。因此,通过下面的代码片段我们就能清晰地看到:
_.without()
_.difference()
var arr = [1, 2, 3, 4, 5]; var result = _.without(arr, 1, 2, 3); console.log(result); /** => [4, 5] */
var arr = [1, 2, 3, 4, 5]; var result = _.difference(arr, [1, 2, 3], [5, 6]); console.log(result); /** => [4] */
过滤成员需要通过不定的参数来告知函数,而两者唯一的区别与前述例子类似,也就是传递不定参数的方式不同。那么,回到原来的问题,为何我们要先设计并实现以数组组合方式的函数呢?其实很简单,原因在于反过来实现会造成许多不必要的麻烦。
例如我们先实现数组方式传递的 _.difference(),我们就可以通过简单的数组组合来实现 _.without():
_.difference = function (array) { /** * 把后续的参数严格铺平成一个数组,即忽略不是包含在数组内的参数 * 如 _.difference(array, [1, 2], 3); 语句中的 3 */ var rest = flatten(arguments, true, true, 1); return _.filter(array, function (value) { return !_.contains(rest, value); }); }; _.without = function (array) { return _.difference(array, Array.prototype.slice.call(arguments, 1)); };
但倘若反过来,实现方式则变得更为复杂:
_.without = function (array) { /** 若经过 `_.difference()` 的调用,则还需要把参数进行一层铺平 */ var rest = (this == 'difference') ? _.flatten(arguments, false, false, 1) : Array.prototype.slice.call(arguments, 1); return _.filter(array, function(value) { return !_.contains(rest, value); }); }; _.difference = function(array) { var args = _.flatten(arguments, true, true, 1); return _.without.apply('difference', args.unshift(array)); };
综上所述,以 _.without() 和 _.difference() 为例其复杂点显然在于 _.without() 该如何区分到底是否经过 _.difference() 来调用自己?因为针对这样的两种情况,_.without() 都需要对参数进行不同的处理。简单来说,_.without() 对于来自 _.difference() 的调用需要再进行一次铺平(注:不才与劣者此处是通过指定 this 来提供一种区分的方式)。为何?细致想想就会发现,产生如此的复杂在于旧版的 JavaScript 语法只能通过数组组合来传递若干个不定的参数,而无法铺开成逗号分隔的形式来传递。
那么,既然语法存在缺陷,ES6 是否提供了新的方式去解决该问题呢?不才认为,这恰恰体现出展开操作符(...,Spread Operator)的魅力所在。有了它,你就可以直接展开若干个不定参数呢!
...
_.without = function (array) { var rest = Array.prototype.slice.call(arguments, 1); return _.filter(array, function(value) { return !_.contains(rest, value); }); }; _.difference = function(array) { var args = _.flatten(arguments, true, true, 1); args.unshift(array); return _.without.call(null, ...args); };
大牛啊。感觉很牛逼
@rockts 不才及劣者而已
其实我们很多时候在设计一个 JavaScript 函数时,都可能会遇到这样的一种情况:参数数量不定,且一般会传递有多个。此时,我们就会想该如何更优雅地设计函数来接收这些不定的参数?此 Issue 的提出就是为了谈谈不才及劣者所知道的一些见解。
对于这样的情况,我们肯定会想到一对函数:
Function.prototype.apply()
Function.prototype.call()
同样是为方法调用指定
this
,两者的区别在于传递不定参数的方式。这样一看,官方似乎已为我们预先提供了两种通用的方式:
然而,若不去亲身实现还不知道该怎么设计函数才能更为优雅?对此,不才与劣者认为应该先实现数组组合的方式,而后再实现逗号分隔的方式。为了能更好地说明,我将举例 underscore 中关于
_.without()
与_.difference()
的实现。在讨论实现之前,我们先了解这两个函数到底有何作用?其实,它们主要用于过滤数组中的部分成员。因此,通过下面的代码片段我们就能清晰地看到:过滤成员需要通过不定的参数来告知函数,而两者唯一的区别与前述例子类似,也就是传递不定参数的方式不同。那么,回到原来的问题,为何我们要先设计并实现以数组组合方式的函数呢?其实很简单,原因在于反过来实现会造成许多不必要的麻烦。
例如我们先实现数组方式传递的
_.difference()
,我们就可以通过简单的数组组合来实现_.without()
:但倘若反过来,实现方式则变得更为复杂:
综上所述,以
_.without()
和_.difference()
为例其复杂点显然在于_.without()
该如何区分到底是否经过_.difference()
来调用自己?因为针对这样的两种情况,_.without()
都需要对参数进行不同的处理。简单来说,_.without()
对于来自_.difference()
的调用需要再进行一次铺平(注:不才与劣者此处是通过指定this
来提供一种区分的方式)。为何?细致想想就会发现,产生如此的复杂在于旧版的 JavaScript 语法只能通过数组组合来传递若干个不定的参数,而无法铺开成逗号分隔的形式来传递。那么,既然语法存在缺陷,ES6 是否提供了新的方式去解决该问题呢?不才认为,这恰恰体现出展开操作符(
...
,Spread Operator)的魅力所在。有了它,你就可以直接展开若干个不定参数呢!