2014-04-18 2 views
1

Я пытаюсь сделать небольшую функцию полезности toPromise, которая преобразует функцию с помощью обратного вызова в функцию обещания (используя Q). Вот утилита:Aplating аргументы в javascript не работают

Object.defineProperty(Function.prototype, "toPromise", { 
    enumerable: false, 
    configurable: false, 
    writable: false, 
    value: function(self) { 
    var $this; 
    $this = this; 
    return function() { 
     var args, deferred; 
     deferred = Q.defer(); 
     args = arguments; 
     args[args.length] = deferred.resolve; 
     console.dir(args); 
     $this.apply(self, args); 
     return deferred.promise; 
    }; 
    } 
}); 

Здесь использование:

var f = function(nickname, name, callback){ 
    console.log(arguments) 
    callback(undefined, name+" "+nickname); 
}; 
f.toPromise(this)("a", "b").then(function(str){ 
    console.log(str) 
}); 

Но выход:

{ '0': 'a', '1': 'b', '2': [Function] } // This is debugging from utils 
{ '0': 'a', '1': 'b' } // and from the function f 

C:\test.js:4 
    callback(undefined, name+" "+nickname); 
^
TypeError: undefined is not a function 

Так почему же третий аргумент (в утилитах), функция , не прошел в apply?

+1

Случайная догадка: конвертировать 'аргументы' в массив перед назначением' args' – Kos

+0

@Kos спасибо! Это сработало. Отправьте ответ, пожалуйста ... – Vinz243

+2

Не указывайте свойства прототипа объекта. Чтобы преобразовать функцию в Promise, все, что вам нужно сделать, это вызвать на ней 'nfbind'. –

ответ

2

Объект arguments не реальный массив:

>>> function getArguments() { return arguments; } 
>>> var a = getArguments(1,2,3), b = [1,2,3]; 
>>> a[3] = 4; b[3] = 4; 
>>> a 
[1, 2, 3] 
>>> b 
[1, 2, 3, 4] 
>>> a.length 
3 
>>> b.length 
4 

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

var args = Array.prototype.slice.call(arguments); 
+0

Обратите внимание, если на самом деле многообещающий, какой OP делает это очень медленно, и вызов '.slice.call (arguments)' также намного медленнее, чем альтернатива. –

2

Вы назначили обратный вызов arguments object, но так как это не фактический массив, который автоматически не увеличивал его свойство length. С length все еще было 2, ваш обратный вызов был просто пропущен в apply.

Исправление прост:

Function.prototype.toPromise = function(self) { 
    var fn = this; 
    return function() { 
     var deferred = Q.defer(); 
     arguments[arguments.length++] = deferred.resolve; 
//        ^^ 
     fn.apply(self||this, arguments); 
     return deferred.promise; 
    }; 
}; 

Вы также можете использовать Array.prototype.push.call(arguments, deferred.resolve). Обратите внимание на то, что Q already provides such similar1 a functionality (с, вероятно, оптимизированным КПД), так что если вы просто хотите, чтобы использовать его на прототипе вы бы лучше сделать

Function.prototype.toPromise = function(self) { 
    return Q.nbind(this, self); 
}; 

1: Обратите внимание на то, что resolve() не принимает аргумент ошибки, как «nodebacks» обычно делают

+0

Я понятия не имел, что push.call работает на 'arguments'. Хотя примечание, OP обещает что-то, что выглядит не очень узким. –

+0

Ну, ['push'] (http://es5.github.io/#x15.4.4.7) является одним из этих« намеренно общих »методов Array и явно увеличивает свойство' length' :-) re right с nodeback/callback, хотя использование OP 'callback (undefined, name +" "+ nickname);' обманул меня в это :-) Не уверен, что он действительно хочет передать 'def.resolve', а точнее' def.makeNodeResolver() '. – Bergi

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