如何绑定函数参数而不绑定这个?
在Javascript中,如何绑定参数到一个函数而不绑定this
参数?
例如:
//Example function. var c = function(a, b, c, callback) {}; //Bind values 1, 2, and 3 to a, b, and c, leave callback unbound. var b = c.bind(null, 1, 2, 3); //How can I do this without binding scope?
我怎样才能避免绑定函数的作用域的副作用(例如设置this
= null)呢?
编辑:
对困惑感到抱歉。 我想要绑定参数,然后能够稍后调用绑定的函数,并使其行为完全一样,如果我调用原始函数并传递给绑定的参数:
var x = 'outside object'; var obj = { x: 'inside object', c: function(a, b, c, callback) { console.log(this.x); } }; var b = obj.c.bind(null, 1, 2, 3); //These should both have exact same output. obj.c(1, 2, 3, function(){}); b(function(){}); //The following works, but I was hoping there was a better way: var b = obj.c.bind(obj, 1, 2, 3); //Anyway to make it work without typing obj twice?
对此我还是新的,对于混淆抱歉。
谢谢!
你可以做到这一点,但最好避免把它看作是“绑定”,因为这是用来设置“this”值的术语。 也许把它想象成“包装”一个函数的参数?
你所做的是创build一个函数,通过闭包内置所需的参数:
var withWrappedArguments = function(arg1, arg2) { return function() { ... do your stuff with arg1 and arg2 ... }; }(actualArg1Value, actualArg2Value);
希望我在那里得到了语法。 它所做的就是创build一个名为withWrappedArguments()的函数(这是一个迂回的函数,它是一个赋予variables的匿名函数),你可以在任何地方任何时候调用它,并且总是会用actualArg1Value和actualArg2Value以及其他任何你想要的东西那里。 如果需要,您也可以在通话时接受更多的参数。 最后的大括号之后的秘诀是括号。 这些使外部函数立即执行,并传递值,并生成稍后可调用的内部函数。 传递的值在函数生成时被冻结。
这实际上是绑定所做的事情,但是这种方式明确地说明,绑定的参数只是局部variables的闭包,并且不需要改变这种行为。
在本地bind
方法中,结果函数中的this
值将丢失。 但是,您可以轻松地重新编码通用垫片,以避免在上下文中使用参数:
Function.prototype.arg = function() { if (typeof this !== "function") throw new TypeError("Function.prototype.arg needs to be called on a function"); var slice = Array.prototype.slice, args = slice.call(arguments), fn = this, partial = function() { return fn.apply(this, args.concat(slice.call(arguments))); // ^^^^ }; partial.prototype = Object.create(this.prototype); return partial; };
在ES6中,使用rest参数和扩展运算符很容易完成。
所以我们可以定义一个像bind
一样工作的bindArgs
函数,除了只绑定参数,而不是上下文( this
)。
Function.prototype.bindArgs = function (...boundArgs) { let context = this; return function(...args) { return context.call(this, ...boundArgs, ...args); }; };
然后,对于指定的函数foo
和对象obj
,该语句
return foo.call(obj, 1, 2, 3, 4);
相当于
let bar = foo.bindArgs(1, 2); return bar.call(obj, 3, 4);
其中只有第一个和第二个参数绑定到bar
,而使用调用中指定的上下文obj
,并在绑定的参数后面附加额外的参数。 返回值只是简单的转发。
另一个小实现只是为了好玩:
function bindWithoutThis(cb) { var bindArgs = Array.prototype.slice.call(arguments, 1); return function () { var internalArgs = Array.prototype.slice.call(arguments, 0); var args = Array.prototype.concat(bindArgs, internalArgs); return cb.apply(this, args); }; }
如何使用:
function onWriteEnd(evt) {} var myPersonalWriteEnd = bindWithoutThis(onWriteEnd, "some", "data");
要确切地说明你最终想做的事情有点难,因为这个例子有些武断,但是你可能想看看partials(或currying): http : //jsbin.com/ifoqoj/1/edit
Function.prototype.partial = function(){ var fn = this, args = Array.prototype.slice.call(arguments); return function(){ var arg = 0; for ( var i = 0; i < args.length && arg < arguments.length; i++ ) if ( args[i] === undefined ) args[i] = arguments[arg++]; return fn.apply(this, args); }; }; var c = function(a, b, c, callback) { console.log( a, b, c, callback ) }; var b = c.partial(1, 2, 3, undefined); b(function(){})
链接到John Resig的文章: http : //ejohn.org/blog/partial-functions-in-javascript/
使用protagonist
!
var geoOpts = {...}; function geoSuccess(user){ // protagonizes for 'user' return function Success(pos){ if(!pos || !pos.coords || !pos.coords.latitude || !pos.coords.longitude){ throw new Error('Geolocation Error: insufficient data.'); } var data = {pos.coords: pos.coords, ...}; // now we have a callback we can turn into an object. implementation can use 'this' inside callback if(user){ user.prototype = data; user.prototype.watch = watchUser; thus.User = (new user(data)); console.log('thus.User', thus, thus.User); } } } function geoError(errorCallback){ // protagonizes for 'errorCallback' return function(err){ console.log('@DECLINED', err); errorCallback && errorCallback(err); } } function getUserPos(user, error, opts){ nav.geo.getPos(geoSuccess(user), geoError(error), opts || geoOpts); }
基本上,你想传递params函数成为一个代理,你可以调用传递一个variables,它返回你真正想做的东西的function。
希望这可以帮助!
匿名用户发布了这个额外的信息:
基于这篇文章中已经提供的内容,我所见过的最优雅的解决scheme是咖喱你的论点和上下文:
function Class(a, b, c, d){ console.log('@Class #this', this, a, b, c, d); } function Context(name){ console.log('@Context', this, name); this.name = name; } var context1 = new Context('One'); var context2 = new Context('Two'); function curryArguments(fn) { var args = Array.prototype.slice.call(arguments, 1); return function bindContext() { var additional = Array.prototype.slice.call(arguments, 0); return fn.apply(this, args.concat(additional)); }; } var bindContext = curryArguments(Class, 'A', 'B'); bindContext.apply(context1, ['C', 'D']); bindContext.apply(context2, ['Y', 'Z']);
那么你给的例子,这将做
var b= function(callback){ return obj.c(1,2,3, callback); };
如果你想保护参数的shell:
var b= (function(p1,p2,p3, obj){ var c=obj.c; return function(callback){ return c.call(obj,p1,p2,p3, callback); } })(1,2,3,obj)
但如果是这样,你应该坚持你的解决scheme:
var b = obj.c.bind(obj, 1, 2, 3);
这是更好的方法。
var b = function() { return c(1,2,3); };
为什么不使用函数的包装来保存这个mythis呢?
function mythis() { this.name = "mythis"; mythis = this; function c(a, b) { this.name = "original"; alert('a=' + a + ' b =' + b + 'this = ' + this.name + ' mythis = ' + mythis.name); return "ok"; } return { c: c } }; var retval = mythis().c(0, 1);
这样简单吗?
var b = (cb) => obj.c(1,2,3, cb) b(function(){}) // insidde object
更一般的解决scheme
function original(a, b, c) { console.log(a, b, c) } let tied = (...args) => original(1, 2, ...args) original(1,2,3) // 1 2 3 tied(5,6,7) // 1 2 5
使用LoDash,你可以使用_.partial
函数。
const f = function (a, b, c, callback) {} const pf = _.partial(f, 1, 2, 3) // f has first 3 arguments bound. pf(function () {}) // callback.
jquery使用案例:
代替:
for(var i = 0;i<3;i++){ $('<input>').appendTo('body').click(function(i){ $(this).val(i); // wont work, because 'this' becomes 'i' }.bind(i)); }
用这个:
for(var i = 0;i<3;i++){ $('<input>').appendTo('body').click(function(e){ var i = this; $(e.originalEvent.target).val(i); }.bind(i)); }