在 JavaScript 中, 当我们调用对象的某个方法时, 其实不用去关心该对象原本是否被设计为拥有这个方法, 这是动态类型语言的特点可以通过反柯里化 (uncurrying) 函数实现, 让一个对象去借用一个原本不属于他的方法
通常让对象去借用一个原本不属于它的方法, 可以用 call 和 apply 实现, 如下
更常见的场景之一是让类数组对象去借用 Array.prototype 的方法;
- (function(){
- Array.prototype.push.call(arguments,4)
- console.log(arguments);//[1, 2, 3, 4]
- })(1,2,3)
扩展: 为什么类数组对象能够借用数组的方法呢? 不妨理解下 V8 的引擎源码, 就以 Array.prototype.push 为例:
- function ArrayPush() {
- CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push");
- var array = TO_OBJECT(this);
- var n = TO_LENGTH_OR_UINT32(array.length);
- var m = %_ArgumentsLength();.......
- for (var i = 0; i < m; i++) {
- array[i + n] = %_Arguments(i);
- }
- var new_length = n + m;
- array.length = new_length;
- return new_length;
- }
通过这段代码大致可以看出, Array.prototype.push 实际上是一个属性复制的过程, 把参数按照下标依次添加到被 push 的对象上面, 顺便修改了这个对象的 length 属性, 这个对象到底是数组还是类数组并不重要从源码可以看出, 只要对象本身可以存取属性, 且 length 属性可读写, 就可以借用 Array 原型的 push 方法
这样一来, 方法中用到 this 的地方, 就不在局限原本的对象, 而是加以泛化并得到更广的适用性那么有没有办法把泛化 this 的过程提取出来呢? 那么反柯里化 (uncurrying) 就是解决这个问题的反科里化 (uncurrying) 的话题来自 JavaScript 之父 Brendan Eich 在 2011 年发表的一篇文章, 以下代码是实现方式之一:
- Function.prototype.uncurrying = function() {
- var self = this;
- return function() {
- var obj = Array.prototype.shift.call(arguments);
- return self.apply(obj, arguments);
- };
- };
然后就可以定义一个 push 函数, 更加简洁和明了的实现了一个不在局限于数组的 push 方法如下:
- var push = Array.prototype.push.uncurrying();
- (function(){
- push(arguments,4);
- console.log(arguments);//[1,2,3,4]
- })(1,2,3)
除了刚刚的一种反柯里化实现, 还有另一种实现方式:
- Function.prototype.uncurrying = function() {
- var self = this;
- return function() {
- return Function.prototype.call.apply(self, arguments)
- };
- }
来源: http://www.bubuko.com/infodetail-2499084.html