Подтвердить что ты не робот

Как связать аргументы функции, не связывая это?

В 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)?

Edit:

Извините за путаницу. Я хочу связать аргументы, а затем позвоню позже и буду вести себя так, как если бы я вызвал исходную функцию и передал ей связанные аргументы:

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?

Я все еще новичок в этом, извините за путаницу.

Спасибо!

4b9b3361

Ответ 1

Вы можете сделать это, но лучше не думать об этом как о "привязке", поскольку это термин, используемый для установки значения "this". Возможно, подумайте об этом как о "переносе" аргументов в функцию?

Что вы делаете, так это создать функцию, в которой есть встроенные в нее аргументы через закрытие:

var withWrappedArguments = function(arg1, arg2)
    {
        return function() { ... do your stuff with arg1 and arg2 ... };
    }(actualArg1Value, actualArg2Value);

Надеюсь, я получил синтаксис прямо там. То, что он делает, это создать функцию, называемую withWrappedArguments() (чтобы быть педантичной, это анонимная функция, назначенная переменной), которую вы можете вызвать в любое время в любом месте и всегда будете действовать с actualArg1Value и actualArg2Value, и все, что вы хотите вставить там. При желании вы также можете принять дополнительные аргументы во время вызова. Секрет - круглые скобки после окончательной скобки закрытия. Это приводит к немедленному выполнению внешней функции с переданными значениями и генерированию внутренней функции, которая может быть вызвана позже. Пропущенные значения затем замораживаются во время создания функции.

Это эффективно то, что связывает, но таким образом явно, что завернутые аргументы - это просто замыкания на локальные переменные, и нет необходимости изменять поведение этого.

Ответ 2

В нативном 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;
};

Ответ 3

В ES6 это легко сделать с использованием параметров отдыха в сочетании с оператором спреда.

Таким образом, мы можем определить функцию bindArgs которая работает как bind, за исключением того, что связаны только аргументы, но не контекст (this).

Function.prototype.bindArgs =
    function (...boundArgs)
    {
        const targetFunction = this;
        return function (...args) { return targetFunction.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 указанный в вызове, используется, а дополнительные аргументы добавляются после связанных аргументов. Возвращаемое значение просто перенаправляется.

Ответ 4

Еще одна крошечная реализация просто для удовольствия:

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");

Ответ 5

var b = function() {
    return c(1,2,3);
};

Ответ 6

Немного сложно точно сказать, что вы в конечном итоге хотите сделать, потому что этот пример является произвольным, но вы можете посмотреть на частичные (или карри): 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(){})

Ссылка на статью Джона Ресига: http://ejohn.org/blog/partial-functions-in-javascript/

Ответ 7

Используйте 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);
}

В принципе, функция, которую вы хотите передать параметрам, становится прокси-сервером, который вы можете вызвать для передачи переменной, и возвращает функцию, которую вы действительно хотите делать.

Надеюсь, это поможет!

Ответ 8

Анонимный пользователь добавил дополнительную информацию:

Основываясь на том, что уже было предоставлено в этом посте, самое элегантное решение, которое я видел, - это Curry ваши аргументы и контекст:

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']);

Ответ 9

Хорошо для примера, который вы дали, это будет делать

var b= function(callback){
        return obj.c(1,2,3, callback);
};

Если вы хотите, чтобы оболочка параметров:

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)

Но если это так, вы должны просто придерживаться своего решения:

var b = obj.c.bind(obj, 1, 2, 3);

Это лучший способ.

Ответ 10

Простой пример?

var b = (cb) => obj.c(1,2,3, cb)
b(function(){}) // insidde object

Более общее решение:

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

Ответ 11

Почему бы не использовать обертку вокруг функции, чтобы сохранить это как мифику?

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);

Ответ 12

Используя 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.

Ответ 13

jQuery 1.9 привел именно эту функцию с функцией прокси.

Начиная с jQuery 1.9, когда контекст имеет значение null или undefined, проксированная функция будет вызываться с тем же объектом, что и с прокси-сервером. Это позволяет использовать $.proxy() для частичного применения аргументов функции без изменения контекста.

Пример:

$.proxy(this.myFunction, 
        undefined /* leaving the context empty */, 
        [precededArg1, precededArg2]);

Ответ 14

Может быть, вы хотите привязать ссылку этого в последнем, но вашем коде: -

var c = function(a, b, c, callback) {};
var b = c.bind(null, 1, 2, 3); 

Уже применяется привязка, например, это, а затем вы не можете ее изменить. Я предлагаю использовать ссылку на использование также как параметр:

var c = function(a, b, c, callback, ref) {  
    var self = this ? this : ref; 
    // Now you can use self just like this in your code 
};
var b = c.bind(null, 1, 2, 3),
    newRef = this, // or ref whatever you want to apply inside function c()
    d = c.bind(callback, newRef);

Ответ 15

Случай использования 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));
}