2016-08-20 6 views
-1

Я изучаю функциональное программирование и node.js, и я столкнулся с этой странной проблемой при использовании Function.prototype.apply и .bind.Функциональные аргументы не обязательно являются объектами?

function Spy(target, method) { 
    var obj = {count: 0}; 
    var original = target[method] 
    target[method] = function(){//no specified arguments 
     obj.count++ 
     original.apply(this, arguments)//only arguments property passed 
    } 
    return obj; 
} 
module.exports = Spy 

Этот код работает, успешно шпионит target.method.


//same code here 
    target[method] = function (args){//args specified 
     obj.count++ 
     original.apply(this, args)//and passed here 
    } 
//same code here 

Этот код, однако, не делает. Он выдает сообщение об ошибке: TypeError: CreateListFromArrayLike called on non-object.


И вот самый большой сюрприз, этот метод работает отлично.

//same code here 
    target[method] = function (args){ 
     obj.count++ 
     original.bind(this, args) 
    } 
//same code here 

Почему именно эта ошибка возникает? Это потому, что аргументы функции не обязательно являются объектами? Или это потому, что apply имеет более строгие описания, чем bind?

+3

Ну, да, ['apply'] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) и [' bind'] (https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) имеют очень разные описания. 'apply' принимает в качестве второго параметра массив или объект' 'arguments' (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) - если вы не наняли, т один, жалуется. – Bergi

+0

Очень неясно. Во-первых, работы/не работают, фрагменты кода идентичны (если только я чего-то не хватает). Во-вторых, 'bind' не вызывает функцию, поэтому трудно поверить, что« работает ». – Amit

+0

@ Извините, я собираюсь отредактировать, так что это более очевидно. –

ответ

1

В этой версии:

target[method] = function (args){//args specified 
     obj.count++ 
     original.apply(this, args)//and passed here 
    } 

Здесь вы не принимаете все аргументы, но только один, названный args. Поскольку apply ожидает массив, подобный объекту, вы не можете использовать args, поскольку это только первый аргумент, переданный исходной цели.

Вы можете изменить его:

target[method] = function (arg){ //only one argument specified 
     obj.count++ 
     original.apply(this,[arg]) //one argument passed here 
} 

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

target[method] = function (arg){ //only one argument specified 
     obj.count++ 
     original.call(this,arg) //one argument passed here 
} 

Теперь bind это совершенно другое животное. Это функции partial applies, таким образом возвращая функции. Представьте, что вам нужно отправить обратный вызов, который не принимает аргументов, но вызывает функцию с некоторыми аргументами, которые вы имеете при ее создании. Вы видите код, похожий на:

var self = this; 
return function() { 
    self.method(a, b); 
} 

Хорошо. bind делает это для вас:

return this.method.bind(this, a, b); 

При вызове любой из этих возвращенных функций происходит то же самое. Метод method вызывается с аргументами a и b.Поэтому вызов bind на функцию возвращает частичную прикладную версию этой функции и не называет ее call или apply.

+0

Bind не карри. Если вообще, он выполняет частичное применение, но даже не является его основной целью. – Bergi

+0

@Bergi Вы правое частичное приложение - лучший термин для нетерпеливых языков, но я думаю, что вы ошибаетесь в отношении «основной» цели 'bind', поскольку это именно то, что он делает в контексте' call'. – Sylwester

1

bind называется так же, как call, хотя они делают совсем разные вещи.

Если вы действительно хотели использовать bind таким образом. Вы можете использовать спред оператор (ES2015), чтобы развернуть аргументы «массив» на отдельные аргументы:

original.bind(null, ...args); 

Это будет связывать функцию original со значениями массива как отдельные аргументы.

+1

Я понятия не имею, почему вы пытаетесь использовать срез, когда у вас уже есть оператор распространения. Мокрые обезьяны. – Sylwester

+0

@ Sylwester Это правда, я полностью забыл, что вы можете использовать оператор спредов и в массивах, и в массивах. Удалено преобразование перед использованием. – Snivels

Смежные вопросы