Простое создание экземпляров класса Джона Резига и «строгий контроль - PullRequest
7 голосов
/ 30 января 2012

Ссылка: 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 ]

4 голосов
/ 30 января 2012

Насколько я понимаю, 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 будет заменено в соответствии с моим примером .

2 голосов
/ 11 июля 2013

Вышеприведенная идея 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, используя внутренний флаг.

1 голос
/ 10 июля 2013

Сбой исходного кода Джона Ресига в конструкторе без параметров.

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 );
...