Function.bind.bind(Function.call)如何解决?

时间:2014-05-05 22:29:02

标签: javascript functional-programming this function-binding

我的代码库中有这一行:

var uncurryThis = Function.bind.bind(Function.call);

我正在努力解决这个问题。据推测,它是不可靠的。我该如何解决这个问题?

我猜这是Function.bind的一个版本,其this绑定到Function.call。对我没有帮助。而且我没有找到任何用途,所以我甚至不确定你是单独调用它还是需要将它称为“作为一种方法”,只是,你知道,首先绑定它。

4 个答案:

答案 0 :(得分:15)

它将call函数传递给bind函数,bind函数本身就是this的值。因此,您可以在bind函数周围获得一个包装器,当您调用this函数时,该函数会安排call成为call函数。反过来,那个是一个函数,它允许你围绕Function.bind.bind函数创建一个包装器,该函数绑定到你传递它的某个参数。

如果您今天早上醒来后不能一直喝咖啡,请一步一步:

  • bind是对bind函数的引用。引用是从 - 混淆点1 - bind函数本身的属性生成的。请记住,this函数在使用某个函数作为对象调用时,用于创建围绕该函数的包装器,并将Function.call.bind(something)绑定到传入的第一个参数。
  • 因此,函数调用会为您提供函数。该功能就像您调用randomFunction.call(whatever)
  • 一样
  • 如果你将一些随机函数作为参数传递给那个函数,那么你会得到一个围绕随机函数的包装器,当被调用时,它会像function random() { alert(this.foo); } var bb = Function.bind.bind(Function.call); var randomcall = bb(random); randomcall({ foo: "hello world" }); // alerts "hello world" 一样。

所以:

this

最终的一点是:您已经拥有了一个功能,并且在功能内部有代码,希望this拥有一些属性,并在一个代码中使用random.call(someObject); 方式或其他。你真的希望能够在这里使用某个对象的那个函数,那里有一些对象。显然你可以用

做到这一点
.call()

但这个魔术" bind-bind-call"技巧为您提供了一种在函数上创建变体的便宜方法,可以避免显式编码的this调用。它还允许您将您的高级前端开发人员位置延长一段时间。

编辑 - 我打算破坏上面的妙语,因为我只是想到了一个很好的理由来使用bind + call技巧来获得一个安排调用的函数期望通过var uc = ["Hello", "World"]; var lc = uc.map(function(s) { return s.toLowerCase(); }); 在某些"所有者"上运行的某些所需功能宾语。我们假设您已经拥有了一系列字符串,并且您希望以小写字母获得这些字符串的版本。你可以这样写:

var uc = ["Hello", "World"];    
var tlc = bb(String.prototype.toLowerCase);
var lc = uc.map(tlc);

但是有魔力" bb"功能我们也可以写:

bb()

用这种方式编写的改进并不多,但如果要制作一组{{1}} - 所有方便的String原型方法的包装器,它可能更有意义。当然,一切都有代价,而且这种封装器可能会产生一些性能影响。 (如果这样的做法很常见,那么运行时可能会得到改善。)

答案 1 :(得分:6)

行。你知道bind的作用吗?它是一个函数的方法来修复它们的this参数,并返回一个新函数。它可以简化为:

function bind(context) {
    var fn = this;
    return function() {
        return fn.apply(context, arguments);
    };
}

我将使用具有大量部分应用的更多功能样式的上下文缩写函数调用:bind fn (context) - &gt; FN <子>上下文。带参数:(bind fn (context))(...)等于fn context (...)。

同样地,call确实取this值但不是返回函数,而是立即应用它:call fn (context,...) - &gt; FN <子>上下文(...)。

现在让我们来看看您的代码:bind.call(bind, call)。在此,您在bindbind使用call作为此值:bind bind (call)。让我们扩展这个(使用上面的规则)来绑定 call 。如果我们现在提供一些参数怎么办?

  

bind bind (call)(fn)(context,...)

     

bind call (fn)(context,...)

     

调用 fn (context,...)

     

FN <子>上下文(...)

一步一步,我们可以做到

  

uncurryThis = bind bind (call)// bind call

     

func = uncurryThis(方法)//调用方法

     

result = func(context,...)// method context (...)

实际用例是任何&#34;类&#34;应该转换为静态函数的方法,将对象(将在其上调用该方法)作为第一个参数:

var uncurryThis = Function.bind.bind(Function.call);
var uc = uncurryThis(String.prototype.toUpperCase);
uc("hello") // in contrast to "hello".toUpperCase()

如果您不能进行方法调用但需要静态函数,这会很有用;例如如在

["hello", "world"].map(uc) // imagine the necessary function expression

此外,您要调用的方法可能不是对象本身的方法,如

var slice = uncurryThis(Array.prototype.slice);
slice(arguments) // instead of `Array.prototype.slice.call(arguments)` everywhere

如果它有帮助,这里也是一个显式实现,没有任何绑定:

function uncurryThis(method) {
    return function(context/*, ...*/)
        return method.apply(context, Array.prototype.slice.call(arguments, 1));
    };
}

答案 2 :(得分:0)

当我们在函数上调用bind时,它返回new函数,并将其替换为context:

function random() {
  alert(this.foo);
}
var newRandom = random.bind({foo:"hello world"}) //return new function same as //`random` with `this` is replaced by object {foo:"hello world"}
我们也一样:

Function.bind.bind(Function.call)
// return new Function.bind with its `this` is replaced by `Function.call`

它有以下来源(使用@Bergi给出的bind函数的简化版本):

var bb = function bind(context){
  var fn = Function.call;
  return function() {
        return Function.call.apply(context, arguments); //also replace fn here for easier reading
    };
}

请注意,此处的上下文将是函数,例如random,因此我们将newRandom函数称为bb(随机)函数:

newRandom = function(){
   return Function.call.apply(random, arguments); //also replace 
}
//`apply` function replace `this` of Function.call to `random`, and apply Function(now become `random`) with arguments in `arguments` array.

答案 3 :(得分:0)

我认为,如果您向后看,可以更清楚地解释这一点。

上下文:

假设我们要小写字符串数组。可以这样做:

[‘A’, ‘B’].map(s => s.toLowerCase())

让我们说,无论出于什么原因,我都希望使此调用更为通用。我不喜欢将s绑定到this并将粗箭头绑定到toLowerCase()的方式。

怎么样?

[‘A’, ‘B’].map(String.prototype.toLowerCase)

嗯,这行不通,因为map将元素作为第一个参数传递,但是String.prototype.toLowerCase没有参数。它期望输入字符串作为this传递。

所以一个问题是我们可以创建一个wrapper函数来实现此功能吗?

[‘A’, ‘B’].map(wrapper(String.prototype.toLowerCase))

wrapper返回一个函数,该函数将传递到this中的第一个参数转换为String.prototype.toLowerCase使用。

我声称您的uncurryThis === wrapper


证明:

因此,我们不要一次全部理解unCurryThis。相反,让我们使用一些公式将unCurryThis转换为更容易理解的内容。

首先使用某些公式:

instance.function(...args)
=== (instance.constructor.prototype).function.call(instance, ...args)
=== (Class.prototype).function.call(instance, ...args) [1]
=== (Class.prototype).function.bind(instance)(...args) [2]

例如,

Class === String
instance === 'STRING'
function === toLowerCase
args === []
---
'string'.toLowerCase()
=== ('STRING'.constructor.prototype).toLowerCase.call('STRING')
=== (String.prototype).toLowerCase.call('STRING')
=== (String.prototype).toLowerCase.bind('STRING')()

因此,让我们盲目地应用这些公式,而不必担心令人困惑的uncurryThis是什么样的:

'string'
=== (wrapper)(String.prototype.toLowerCase)('STRING')
=== (uncurryThis)(String.prototype.toLowerCase)('STRING')
=== (Function.bind.bind(Function.call))(String.prototype.toLowerCase)('STRING')

// Function.bind is not really the generic form because it's not using the prototype
// Here Function is an instance of a Function and not the constructor.prototype
// It is similar to calling Array.bind or someFunction.bind
// a more correct version would be
// someFunction.constructor.prototype.bind === Function.prototype.bind, so
=== (Function.prototype.bind.bind(Function.prototype.call))(String.prototype.toLowerCase)('STRING')

// Apply formula 2
// instance.function(...args) === (Class.prototype).function.bind(instance)(...args) [2]
// Class === Function
// function === bind
// instance === Function.prototype.call
// ...args === String.prototype.toLowerCase
=== instance.function(...args)('STRING')
=== (Function.prototype.call).bind(String.prototype.toLowerCase)('STRING')

// Apply formula 2 again
// Class == Function
// function == call
// instance === String.prototype.toLowerCase
// ...args === 'STRING'
=== instance.function(...args)
=== (String.prototype.toLowerCase).call('STRING')

// Apply formula 1
instance.function(...args) === (Class.prototype).function.call(instance, ...args) [1]
// Class === String
// function === toLowerCase
// instance === 'STRING'
// args === []
=== instance.function(...args)
=== 'STRING'.toLowerCase(...[])
=== 'STRING'.toLowerCase()

// So we have
(wrapper)(String.prototype.toLowerCase)('STRING')
=== (uncurryThis)(String.prototype.toLowerCase)('STRING')
=== 'STRING'.toLowerCase()
=== 'string'

反证明:

所以您可能想知道“那家伙是怎么得到uncurryThis函数的?”

您可以反转证明以得出它。我只是从上面复制方程式,但是反转了:

'STRING'.toLowerCase()
=== (String.prototype.toLowerCase).call('STRING') // apply formula [1]
=== (Function.prototype.call).bind(String.prototype.toLowerCase)('STRING') // apply formula [2]

// At this point, you might wonder why `uncurryThis !== (Function.prototype.call).bind)
// since it also takes (String.prototype.toLowerCase)('STRING')
// This is because passing in (Function.prototype.call).bind) as an argument
// is the same as passing in Function.prototype.bind
// `this` binding isn't done unless you call
// (Function.prototype.call).bind)(String.prototype.toLowerCase)
// at that exact moment.
// If you want to be able to pass unCurryThis as a function, you need to bind the
// Function.prototype.call to the Function.prototype.bind.

=== (Function.prototype.bind.bind(Function.prototype.call))(String.prototype.toLowerCase)('STRING') // apply formula 2
=== (Function.bind.bind(Function.call))(String.prototype.toLowerCase)('STRING') // un-generic-ize
=== (uncurryThis)(String.prototype.toLowerCase)('STRING')
=== (wrapper)(String.prototype.toLowerCase)('STRING')

=>

unCurryThis === wrapper === Function.bind.bind(Function.call)

仍然很难理解,但是每次我应用公式[1]和[时,尝试写出Classfunctioninstanceargs是什么2],这应该是有道理的。