Является ли JavaScript "новым" ключевым словом считается вредным?

В другом question пользователь указал, что ключевое слово new было опасно для использования и предложило решение для создания объекта, которое не использовало new. Я не верил, что это правда, главным образом потому, что я использовал Prototype, Scriptaculous и другие превосходные библиотеки JavaScript, и каждый из них использовал ключевое слово new.

Несмотря на это, вчера я смотрел, как Дуглас Крокфорд разговаривал в театре YUI, и он сказал то же самое, что он больше не использовал ключевое слово new в своем коде (Crockford on JavaScript - Действие III: Функция Ultimate - 50:23 минут).

"Плохо" использовать ключевое слово new? Каковы преимущества и недостатки его использования?

Ответ 1

Крокфорд много сделал для популяризации хороших методов JavaScript. Его упрямая позиция по ключевым элементам языка вызвала много полезных обсуждений. Тем не менее, существует слишком много людей, которые берут каждое провозглашение "плохого" или "вредного", как евангелие, отказываясь смотреть за пределы мнения одного человека. Иногда это может немного расстраивать.

Использование функциональных возможностей, предоставляемых ключевым словом new, имеет ряд преимуществ перед построением каждого объекта с нуля:

  • Наследование прототипов. В то время как часто просматривались с подозрением и издевательством со стороны тех, кто привык к языкам OO на основе классов, технология наследования родного языка JavaScript - это простой и удивительно эффективный способ повторного использования кода. И новое ключевое слово - это каноническое (и только доступное кросс-платформенное) средство его использования.
  • Производительность. Это побочный эффект # 1: если я хочу добавить 10 методов для каждого создаваемого объекта, я мог бы просто написать функцию создания, которая вручную назначает каждый метод каждому новому объекту... Или я мог бы назначить их создайте функцию prototype и используйте new для удаления новых объектов. Мало того, что это быстрее (без кода, необходимого для каждого метода на прототипе), он избегает раздувания каждого объекта отдельными свойствами для каждого метода. На медленных машинах (или, особенно, медленных интерпретаторах JS), когда создается много объектов, это может означать значительную экономию времени и памяти.

И да, new имеет один решающий недостаток, умело описываемый другими ответами: если вы забудете его использовать, ваш код сломается без предупреждения. К счастью, этот недостаток легко смягчается - просто добавьте немного кода к самой функции:

function foo()
{
   // if user accidentally omits the new keyword, this will 
   // silently correct the problem...
   if ( !(this instanceof foo) )
      return new foo();

   // constructor logic follows...
}

Теперь вы можете иметь преимущества new, не беспокоясь о проблемах, вызванных случайным использованием. Вы даже можете добавить утверждение к проверке, беспокоит ли вас мысль о сломанном коде, который молча работает. Или, как прокомментировал some, используйте проверку, чтобы ввести исключение во время выполнения:

if ( !(this instanceof arguments.callee) ) 
   throw new Error("Constructor called as a function");

(Обратите внимание, что этот фрагмент кода позволяет избежать жесткого кодирования имени функции конструктора, поскольку в отличие от предыдущего примера ему не нужно фактически создавать экземпляр объекта, поэтому он может быть скопирован в каждую целевую функцию без изменений.)

Джон Ресиг подробно описывает эту технику в своем Простой "Класс" Instantiation post, а также включает средства построения такого поведения в ваши "классы" по умолчанию. Определенно стоит прочитать... как и его будущая книга, Секреты JavaScript Ninja, которая находит скрытое золото в этом и многих других "вредных" особенности языка JavaScript ( глава на with особенно интересны для тех из нас, кто первоначально отклонил эту широкозахваченную функцию как трюк).

Ответ 2

Я только что прочитал некоторые части своей книги Крокфордов "Javascript: The Good Parts". У меня возникает ощущение, что он считает все, что когда-либо укусило его как вредное:

Об отключении переключателя:

Я никогда не позволяю случаям переключения до следующего случая. Я однажды нашел ошибка в моем коде, вызванная непреднамеренное падение после энергичной речи о том, почему падать иногда полезно. (стр. 97, ISBN 978-0-596-51774-8)

О ++ и -

++ (приращение) и - (декремент) операторы были известны способствовать плохому коду, поощряя излишняя хитрость. Они являются вторыми только для ошибочной архитектуры в включение вирусов и другой безопасности угроз. (стр. 122)

О новом:

Если вы забыли включить новый префикс при вызове конструктора функции, то это не будет связанный с новым объектом. К сожалению, это будет привязана к глобальному объекту, поэтому вместо увеличения вашего нового объекта, вы будете сбивать глобальные переменные. Это действительно плохо. Там не компилирует предупреждение, и нет предупреждение о запуске. (стр. 49)

Есть больше, но я надеюсь, что вы получите картину.

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

Обновление

Примерно через год после написания этого ответа было выпущено 5-е издание ECMAScript с поддержкой строгий режим. В строгом режиме this больше не привязан к глобальному объекту, а к undefined.

Ответ 3

Javascript - динамический язык, там есть миллионы способов испортить, где другой язык остановит вас.

Избегая основной функции языка, такой как new, на основе того, что вы можете испортить, немного напоминает удаление ваших блестящих новых ботинок перед тем, как пройти через минное поле, на случай, если вы можете получить туфли грязными.

Я использую соглашение, в котором имена функций начинаются с буквы нижнего регистра и "функций", которые на самом деле являются определениями классов, начинаются с буквы верхнего регистра. Результат - довольно убедительный визуальный ключ, который не соответствует синтаксису: -

var o = MyClass();  // this is clearly wrong.

В дополнение к этим хорошим навыкам именования помогают. После того, как все функции выполняют вещи, и поэтому в его имени должен быть глагол, тогда как классы представляют объекты и являются существительными и прилагательными без глагола.

var o = chair() // Executing chair is daft.
var o = createChair() // makes sense.

Интересно, как синтаксическая раскраска SO интерпретировала вышеприведенный код.

Ответ 4

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

Я пришел из мира С#, где использование ключевого слова "новый" настолько естественно, что дизайн шаблона factory выглядит странным для меня.

Когда я первый код в Javascript, я не понимаю, что есть "новое" ключевое слово и код, как в шаблоне YUI, и мне не нужно долго бежать в катастрофу. Я теряю информацию о том, что должна делать конкретная строка, когда я оглядываюсь на код, который я написал. Более хаотичным является то, что мой разум не может действительно проходить между границами объектов, когда я "сухим" кодом.

Затем я нашел ключевое слово "новое", которое для меня, это "отдельные" вещи. С новым ключевым словом он создает вещи. Без нового ключевого слова я знаю, что я не буду путать его с созданием вещей, если только функция, которую я вызываю, не дает мне убедительных подсказок.

Например, с var bar=foo(); у меня нет никаких подсказок, как может быть бар... Является ли это возвращаемым значением или это вновь созданный объект? Но с var bar = new foo(); я точно знаю, что бар - это объект.

Ответ 5

Другой случай для нового - это то, что я называю Pooh Coding. Винни-Пух следует за своим животиком. Я говорю " с языком, который вы используете, а не против).

Скорее всего, поддерживающие языки будут оптимизировать язык для идиом, которые они пытаются поощрять. Если они помещают новое ключевое слово в язык, они, вероятно, думают, что имеет смысл быть ясным при создании нового экземпляра.

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

EDIT: И это выходит далеко за рамки производительности. Я не могу сосчитать время, которое я слышал (или сказал) "Почему, черт возьми, они сделали , что?" при поиске странно выглядящего кода. Часто оказывается, что в то время, когда был написан код, для него была "хорошая" причина. После Дао этого языка ваша лучшая страховка за то, что ваш код высмеял несколько лет спустя.

Ответ 6

Я написал сообщение о том, как уменьшить проблему вызова конструктора без нового ключевого слова.
Он в основном дидактичен, но показывает, как вы можете создавать конструкторы, которые работают с или без new, и не требуют, чтобы вы добавляли шаблонный код в test this в каждом конструкторе.

http://js-bits.blogspot.com/2010/08/constructors-without-using-new.html

Здесь суть метода:

/**
 * Wraps the passed in constructor so it works with
 * or without the new keyword
 * @param {Function} realCtor The constructor function.
 *    Note that this is going to be wrapped
 *    and should not be used directly 
 */
function ctor(realCtor){
  // This is going to be the actual constructor
  return function wrapperCtor(){
    var obj; // object that will be created
    if (this instanceof wrapperCtor) {
      // Called with new
      obj = this;
    } else {
      // Called without new. Create an empty object of the
      // correct type without running that constructor
      surrogateCtor.prototype = wrapperCtor.prototype;
      obj = new surrogateCtor();
    }
    // Call the real constructor function
    realCtor.apply(obj, arguments);
    return obj;
  }

  function surrogateCtor() {}
}

Здесь, как его использовать:

// Create our point constructor
Point = ctor(function(x,y){
  this.x = x;
  this.y = y;
});

// This is good
var pt = new Point(20,30);
// This is OK also
var pt2 = Point(20,30);

Ответ 7

Обоснование не использования нового ключевого слова просто:

Не используя его вообще, вы избегаете ловушки, которая приходит с случайно опуская ее. Шаблон построения, который использует YUI, является примером того, как вы можете полностью исключить новое ключевое слово "

var foo = function () {
    var pub= { };
    return pub;
}
var bar = foo();

В качестве альтернативы вы можете так:

function foo() { }
var bar = new foo();

Но при этом вы рискуете тем, что кто-то забыл использовать ключевое слово new, а оператор this - весь fubar. AFAIK нет никакого преимущества для этого (кроме того, что вы привыкли к нему).

В конце дня: Это о защите. Можете ли вы использовать новый оператор? Да. Это делает ваш код более опасным? Да.

Если вы когда-либо писали С++, это похоже на указание указателей на NULL после их удаления.

Ответ 8

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

Ответ 9

Случай 1: new не требуется и его следует избегать

var str = new String('asd');  // type: object
var str = String('asd');      // type: string

var num = new Number(12);     // type: object
var num = Number(12);         // type: number

Требуется 2: new, в противном случае вы получите сообщение об ошибке

new Date().getFullYear();     // correct, returns the current year, i.e. 2010
Date().getFullYear();         // invalid, returns an error

Ответ 10

Вот краткое резюме, которое я мог бы сделать из двух самых сильных аргументов для и без использования оператора new:

Аргумент против new

  • Функции, предназначенные для создаются как объекты, используя Оператор new может иметь катастрофические если они ошибочно вызывается как нормальные функции. функциональный код в таком случае будет выполняются в области, в которой функция вызывается, а не в область действия локального объекта как предназначена. Это может привести к глобальному переменные и свойства для получения перезаписанный катастрофическим последствия.
  • Наконец, написав function Func(), и затем вызывая Func.prototype и добавляя к нему материал, чтобы вы может вызвать new Func() для построения ваш объект кажется уродливым для некоторых программистов, которые предпочли бы использовать другой стиль наследования объектов для архитектурных и стилистических причины.

Подробнее об этом аргументе посмотрите Douglas Crockford великую и лаконичную книгу Javascript: The Good Parts. В любом случае, проверьте это.

Аргумент в пользу new

  • Используя оператор new вместе с Прототипное назначение выполняется быстро.
  • Что-то случайно запуск функции-конструктора кода в глобальном пространстве имен легко предотвратить, если вы всегда добавьте немного кода в свой функции конструктора для проверки видеть, вызваны ли они правильно, и в тех случаях, когда они не справляются с вызовом по желанию.

См. пост Джона Ресига для простого объяснения этой техники и для общего более глубокого объяснения модели наследования, которую он защищает.

Ответ 11

Я согласен с pez и некоторыми здесь.

Мне кажется очевидным, что "новое" - это создание самоописательного объекта, где описывает шаблон YUI, который описывает Грег Дин, полностью затенен.

Возможность, которую кто-то мог написать var bar = foo; или var bar = baz();, где baz не метод создания объекта, выглядит гораздо более опасным.

Ответ 12

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

JavaScript основан на прототипе объектно-ориентированной архитектуры. Следовательно, каждый объект ДОЛЖЕН быть создан из другого объекта, такого как var newObj=Object.create(oldObj). Здесь oldObj называется прототипом newObj (отсюда и "прототип" ). Это означает, что если свойство не найдено в newObj, то оно будет искать в oldObj. newObj по умолчанию будет пустым объектом, но из-за его цепи прототипов он, как представляется, имеет все значения oldObj.

С другой стороны, если вы делаете var newObj=new oldObj(), прототипом newObj является oldObj.prototype, который излишне трудно понять.

Фокус в том, чтобы использовать

Object.create=function(proto){
  var F = function(){};
  F.prototype = proto;
  var instance = new F();
  return instance;
};

Он находится внутри этой функции и только здесь должен использоваться новый. После этого просто используйте метод Object.create(). Метод решает проблему прототипа.