2012-07-15 3 views
6

Я использую следующие функции для создания экземпляров функций в JavaScript из массива аргументов:функции Instantiate JavaScript с пользовательскими прототипами

var instantiate = function (instantiate) { 
    return function (constructor, args, prototype) { 
     "use strict"; 

     if (prototype) { 
      var proto = constructor.prototype; 
      constructor.prototype = prototype; 
     } 

     var instance = instantiate(constructor, args); 
     if (proto) constructor.prototype = proto; 
     return instance; 
    }; 
}(Function.prototype.apply.bind(function() { 
    var args = Array.prototype.slice.call(arguments); 
    var constructor = Function.prototype.bind.apply(this, [null].concat(args)); 
    return new constructor; 
})); 

Используя описанную выше функцию можно создавать экземпляры следующим образом (см fiddle):

var f = instantiate(F, [], G.prototype); 

alert(f instanceof F); // false 
alert(f instanceof G); // true 

f.alert(); // F 

function F() { 
    this.alert = function() { 
     alert("F"); 
    }; 
} 

function G() { 
    this.alert = function() { 
     alert("G"); 
    }; 
} 

Приведенный выше код работает для пользовательских конструкторов, таких как F. Однако это не работает для встроенных конструкторов, таких как Array по очевидным соображениям безопасности. Вы всегда можете создать массив, а затем изменить его свойство __proto__, но я использую этот код в Rhino, поэтому он не будет работать там. Есть ли другой способ добиться того же результата в JavaScript?

ответ

6

Вы не можете fully subclass an array.

Для того, чтобы удалить какую-либо сложность с вашего текущего кода, можно использовать как: Object.create (ex).

+0

Как бы вы использовали 'Object.create' удалить сложность из моего кода? Я был бы признателен, если бы вы могли написать демоверсию. –

+0

Я добавил пример ссылки на мой ответ. Обратите внимание, что он не работает на версии носорога Ideone (но он отлично работает с моим, 1.7r3). –

+0

Ваш метод работает для большинства конструкторов. Однако это вызывает [проблемы] (http://jsfiddle.net/MMp5u/2/), когда фабричная функция используется как конструктор (например, «Массив»). Использование 'Object.create (Array.prototype)' создает объект, который является 'instanceof' конструктором' Array'. Однако это не массив. Моя функция может показаться немного более сложной, однако она [избегает] (http://jsfiddle.net/MMp5u/1/) проблем, представленных вашей функцией. –

3

Я не думаю, что вы достигаете того, что вы собираетесь здесь. Сначала в ваших функциях F и G вы определяете функцию оповещения об этом объекте . Это означает, что каждый раз, когда вы создаете объект, создается новый объект функции и назначается для предупреждения. Это не то, что вы хотите, вы должны определить предупреждение о прототипе F и G.

function F() { } 

F.prototype.alert = function() { 
    alert("F"); 
}; 

function G() { } 

G.prototype.alert = function() { 
    alert("G"); 
}; 

Однако вы по-прежнему есть проблема в функции создания экземпляра. Если вы называете это так, как вы есть

var f = instantiate(F, [], G.prototype); 

все, что вы делаете, это установка прототипа F, чтобы G.prototype, что не то, что я думаю, что вы хотите. Я предполагаю, что если вы создаете экземпляр объекта F, вы хотели бы иметь возможность вызывать все функции , определенные на F.prototype, но, как все выглядит, это не так.

function F() { } 

F.prototype.alert = function() { 
    alert("F"); 
}; 

F.prototype.foo = function() { 
    alert("F foo"); 
}; 

function G() { } 

G.prototype.alert = function() { 
    alert("G"); 
}; 


var f = instantiate(F, [], G.prototype); 
f.foo(); // error! 

Причина ошибки здесь, как я сказал, вы просто присвоить прототип F, чтобы G.prototype и G.prototype не имеет функцию Foo определено.

Если вы хотите сделать наследование таким образом взглянуть на блог Резиг, он имеет приятный implemantation: http://ejohn.org/blog/simple-javascript-inheritance/

Также Дуглас Крокфорд поставил вместе некоторые хорошие примеры: http://www.crockford.com/javascript/inheritance.html

+0

Ха-ха. На самом деле это именно то, что я хочу. Я создал функцию 'instantiate', которая принимает функцию' constructor' и список 'args' и возвращает' instance' 'constructor'. Если вы передаете необязательный объект «prototype» в качестве третьего параметра, он устанавливает внутреннее свойство '[[proto]]' экземпляра 'этому' prototype'. Это предполагаемое поведение. Поэтому, когда я называю 'instantiate (F, [], G.prototype)', тогда он должен создать «экземпляр» 'F', но вместо' F.prototype' вместо 'F.prototype' должен использоваться' G.prototype' как 'prototype' «экземпляра». –

+0

Причина, по которой я написал функцию 'alert' внутри' F' и 'G', заключается в том, чтобы показать разницу: хотя' f' создается 'F' (и, следовательно,' f.alert() 'отображает' F'), это Свойство inner [[proto]] 'установлено на' G.prototype'. Следовательно, это 'instanceof' функция' G'. –

+0

Если вы думаете об этом, было бы противно интуитивно иметь дополнительный третий параметр «prototype», если бы я никогда не хотел изменять внутреннее свойство '[[proto]]' 'f' из' F.prototype' на 'G .prototype'. Поскольку это необязательно, если бы я оставил его, то 'f' наследовал бы от' F.prototype' (это поведение, которое вы предложили, правильно). Однако целью является изменение внутреннего свойства '[[proto]]' 'экземпляра без необходимости вручную изменять' __proto__'. –

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