связывание / применение конструкторов в JavaScript

Я искал решения для вызова конструкторов Javascript с произвольным числом аргументов и нашел несколько хороших сообщений SO, что заставило меня поверить, что эти три вызова должны работать одинаково. Однако, по крайней мере, в rhino и node.js они этого не делают:

1. f = Date.bind(Date, 2000,0,1)
2. g = Date.bind.call(Date, 2000, 0, 1)
3. h = Date.bind.apply(Date, [2000, 0, 1])

Первый имеет желаемый результат:

print(new f()) //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)

Но два других не делают:

print(new g()) //=> Thu Feb 01 1900 00:00:00 GMT-0500 (EST)
print(new h()) //=> Wed Jun 01 1904 00:00:00 GMT-0400 (EST)

Значит, где-то что-то пропало. Мысли о чем? Это просто плохая идея смешивать такие вещи, какapply, bind и / илиcall сnew?

 Mark Reed16 мая 2012 г., 06:12
@ RobG действительно. Я на самом деле пробовал свои силы в созданииnew метод (как в книге мистера Крокфорда, но не смотря на его реализацию), когда я натолкнулся на эту маленькую головоломку.
 RobG15 мая 2012 г., 08:15
Они все терпят неудачу в IE 8, потому что нетDate.bind. ; -)
 RobG15 мая 2012 г., 08:22
Мне кажется, что это неуместное использованиеbind. Он должен использоваться для создания функциональных объектов, но даты являются объектами Date, а не функциями и не могут быть вызваны. Кроме того, без использования привязки или вызова или применения,эт значениеСвидани в любом случае, какой смысл? Наконец, вы вызываете Date не как конструктор, а как функцию.
 Mark Reed15 мая 2012 г., 13:57
Конечно, даты - это объекты Date, но сама Date - это функция, и я пытаюсь связать ее с самой Date. Date - это функция, поэтому я связываю ее и получаю новую функцию, которую затем использую в качестве конструктора, вызывая ее с помощьюnew. Как я уже говорил в верхней части вопроса, реальная точка в том, чтобы иметь возможность вызывать конструктор со списком аргументов, определенных во время выполнения.
 RobG16 мая 2012 г., 04:35
Читая другие комментарии, я думаю, вы поняли, что в JavaScript нет классов. Это займет некоторое время, но в конце концов все доберутся до места. :-) Вы можете эмулировать множество вещей, основанных на классах, но Date (и, вероятно, Math) являются исключениями или, по крайней мере, делают это действительно трудным. Вот почему библиотеки Date используют функции, которые вызывают методы Date, а не пытаются «подклассить» Date. Даже Дуглас Крокфорд отказался от сложных «классовых» иерархий, предпочитая простые объект

Ответы на вопрос(2)

bind а такжеapply / call работает только вызов работы Функция но нетконструкто, поэтому в основном с нативными методами вы не можете сделать это, один из способов - написатьbindConstruct метод, но он может включать в себя дополнительную сложность:

    // since constructor always accepts a static this value
    // so bindConstruct cannot specify this
    var extraArgs = [].slice.call(arguments, 1);

    // create a 'subclass' of fn
    function sub() {
        var args = extraArgs.concat([].slice.call(arguments));
        fn.apply(this, args);
    }
    sub.prototype = fn.prototype;
    sub.prototype.constructor = sub;

    return sub;
}

Это на самом деле создает Подкласс вашему конструктору.

Затем твой код:

var MyClass = function(x, y) {
    console.log(arguments);
    console.log(x + y);
}
var BindedMyClass = bindConstruct(MyClass, 1, 2, 3);
var c = new BindedMyClass(4, 5);
console.log(c instanceof MyClass);
console.log(c instanceof BindedMyClass);

Вы также можете написать эту функцию вFunction.prototype или как расширение к родномуbind функция.

 Semicolon21 апр. 2014 г., 06:25
«связывать и применять / вызывать только рабочий вызов функции, но не конструктор» просто не соответствует действительности; увидеть ниже
 Mark Reed15 мая 2012 г., 14:00
Спасибо, но, к сожалению, это тоже не работает должным образом. Методы Date терпят неудачу на любых объектах, которые я создаю с помощью моей новой функции "Date subclass" сTypeError: Method called on incompatible object. Таким образом, еще раз продемонстрировав, что Javascript на самом деле не имеет подклассов. Или классы вообще, в этом отношении ...
Решение Вопроса

The ранее принятый ответ был неверным. Вы можете использовать bind, call и apply с конструкторами для создания новых конструкторов - единственная проблема в вашем тесте состоит в том, что вы забыли, что bind.apply и bind.call применяют и вызывают Связывать, а не сам конструктор, поэтому вы дали неправильные аргументы.

f = Date.bind(null, 2000,0,1)
g = Function.bind.call(Date, null, 2000, 0, 1)
h = Function.bind.apply(Date, [ null, 2000, 0, 1 ])

new f() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
new g() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
new h() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)

Все троеinstanceof Свидание

ргументы @ Call - это контекст выполнения, за которым следуют аргументы для применения. Аргументы Apply - это контекст выполнения и массив аргументов. Аргументы Bind - это контекст выполнения, за которым следуют аргументы Bind.

Например, аргументы, которые нужно применить, являются контекстом, для котороподать заявлени Связывать (Date), за которым следует массив с аргументамидл bind (поэтому первый член массива является аргументом контекста привязки). Вот почему сложно назвать или применить связывание; кажется странным предоставлять контекстные аргументы обоим.

Обратите внимание, что при использовании связывания с конструкторами аргумент контекста всегда игнорируется, поскольку «новый» явно создает новый контекст. Я использую нуль, когда аргумент контекста не имеет значения, чтобы сохранить это ясным, но это может быть что угодно.

В то же время для применения apply и call в этих примерах необходимо знать, что контекст, в котором они должны применяться / вызывать привязку, является функцией Date. Я переключил «Дата» на «Функция», где это возможно, чтобы помочь осветить то, что на самом деле обеспечивает контекст, где. Когда мы вызываем apply или вызываем Date.bind, мы действительно вызываем apply или вызываем метод bind, не привязанный к объекту Date. В этом случае метод связывания может происходить из любой функции. Это может быть Number.bind.call (Date, null, 2000, 0, 1), и результат будет точно таким же.

Если не понятно почему, рассмотрите разницу между следующими примерами:

context.method();

а такж

var noLongerAMethod = context.method;
noLongerAMethod();

Во втором случае метод был отделен от своего исходного контекста (... если только он не был предварительно связан) и будет вести себя иначе, если он полагался на 'this' внутри. Когда мы вытягиваем привязать любую функцию как собственность, вместо того, чтобы выполнять его напрямую, это просто еще один указатель на общий метод связывания в Function.prototype.

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

 Mark Reed09 сент. 2014 г., 15:00
Это был очень полезный ответ. Спасибо

Ваш ответ на вопрос