Простое создание экземпляров класса Джона Ресига и "строгое использование"
Ссылка: http://ejohn.org/blog/simple-class-instantiation/
// makeClass - By John Resig (MIT Licensed)
function makeClass(){
return function(args){
if ( this instanceof arguments.callee ) {
if ( typeof this.init == "function" )
this.init.apply( this, args.callee ? args : arguments );
} else
return new arguments.callee( arguments );
};
}
Мне было интересно, есть ли какой-либо ECMAScript 5 совместимый способ реализовать ту же функциональность. Проблема в том, что доступ arguments.callee
не рекомендуется в строгом режиме.
3 ответа
Как я понимаю arguments.callee
не рекомендуется в строгом режиме, и в этом случае вы можете продолжать его использовать; скорее он был удален, и попытка его использования вызовет (или должна) вызвать исключение.
Обходной путь - использовать именованные анонимные функции, если вы простите оксюморон. На самом деле я должен сказать "выражений именованных функций". Пример:
function someFunc(){
return function funcExpressionName(args){
if (this instanceof funcExpressionName) {
// do something
} else
return new funcExpressionName( arguments );
};
}
Имя, которое вы предоставляете, в моем примере funcExpressionName
Предполагается, что он не будет доступен нигде, кроме как внутри функции, к которой он применяется, но, к сожалению, у IE есть другие идеи (как вы можете видеть, если вы используете Google).
Для примера в вашем вопросе я не уверен, как справиться с args.callee
так как я не знаю, как это устанавливается вызывающей функцией, но использование arguments.callee
будет заменен в соответствии с моим примером.
Вышеприведенная идея, данная nnnnnn, довольно хороша. И чтобы избежать проблем с IE, я предлагаю следующее решение.
function makeClassStrict() {
var isInternal, instance;
var constructor = function(args) {
// Find out whether constructor was called with 'new' operator.
if (this instanceof constructor) {
// When an 'init' method exists, apply it to the context object.
if (typeof this.init == "function") {
// Ask private flag whether we did the calling ourselves.
this.init.apply( this, isInternal ? args : arguments );
}
} else {
// We have an ordinary function call.
// Set private flag to signal internal instance creation.
isInternal = true;
instance = new constructor(arguments);
isInternal = false;
return instance;
}
};
return constructor;
}
Обратите внимание, как мы избегаем ссылки на args.callee
в // do something
часть с помощью внутреннего флага.
Исходный код Джона Резига не работает с конструктором без параметров.
var Timestamp = makeClass();
Timestamp.prototype.init = function() {
this.value = new Date();
};
// ok
var timestamp = Timestamp();
alert( timestamp.value );
// TypeError: args is undefined
var timestamp = new Timestamp();
alert( timestamp.value );
Но это можно исправить, используя следующую строку
this.init.apply( this, args && args.callee ? args : arguments );