JavaScript: хорошие части - как вообще не использовать `new` - PullRequest
57 голосов
/ 07 марта 2011

Книга Крокфорда, JavaScript: The Good Parts , говорит (на странице 114), что функциям конструктора должны всегда быть имена с начальной заглавной буквой (т. Е. Точка), ичто имена функций с начальными заглавными буквами должны только использоваться с функциями конструктора (все остальное должно быть lowerCase).

Это соглашение помогает нам не забывать использовать оператор new с функциями конструктора.

Далее он говорит, что "[a] n еще лучшая стратегия преодоления - неиспользуйте new вообще. "

Мой вопрос: как нам программировать JavaScript без использования new вообще?

  • Мы можем избежать new Object() иnew Array() с буквальным {} и [].
  • Мы можем избежать new Number(), new Boolean() и new String() с 0, true и ''.
  • Мы можем избежать new RegExp() с чем-то вроде /pattern/.

Как нам избежать new Date()?

И, самое главное, как нам избежать использования new с нашими собственными объектами?

Ответы [ 9 ]

41 голосов
/ 07 марта 2011

Крокфорд приводит пример для функции создания объекта, которая должна была быть предоставлена ​​самим Дж. С. в одном из его выступлений на Javascript, доступных на http://developer.yahoo.com/yui/theater/

Однако сама команда YUI (3) использует «new», и они действительно следуют его рекомендациям (так как он является главным архитектором JS в Yahoo (ОБНОВЛЕНИЕ: он продолжил, но утверждение было верным, когда этот ответ был первоначально написан) Я понимаю, что это конкретное утверждение относится скорее к «академическому» уровню, то, что ДОЛЖНО быть, если бы язык был разработан «правильно», а не с некоторыми остатками основанного на классах наследования. Он (ИМХО справедливо) говорит, что путь оказалось, что JS конфликтует, основывается на прототипах, но с этим одним из языков наследования "классического класса".

Тем не менее, JS как есть, иди и используй «новый».

Вы можете найти его функцию создания объекта здесь: http://javascript.crockford.com/prototypal.html

 if (typeof Object.create !== 'function') {
     Object.create = function (o) {
         function F() {}
         F.prototype = o;
         return new F();
     };
 }
 newObject = Object.create(oldObject);


РЕДАКТИРОВАТЬ: Обновлено, чтобы использовать последнюю версию этой функции Крокфорда - их три.


ОБНОВЛЕНИЕ Июнь 2015: у нас уже довольно давно Object.create(...), которые поддерживаются всеми современными браузерами (включая IE 9 и выше), поэтому в этом не было необходимости использовать функцию Крокфорда.

Однако , получается, что если вы используете Object.create, вам следует убедиться, что вы не делаете это много: эта функция работает НАМНОГО медленнее, чем new Constructor()!

См. http://mrale.ph/blog/2014/07/30/constructor-vs-objectcreate.html для объяснения (для двигателя V8) и см. http://jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/62 для демонстрации производительности.

Другая причина не отворачиваться от new Constructor(...) заключается в том, что ES6 классы наверняка увидят широкое распространение, даже если только по той простой причине, что большинство разработчиков Javascript приходят из языков на основе классов.

Также ознакомьтесь с этой статьей, в которой утверждается для Object.create: http://davidwalsh.name/javascript-objects-deconstruction

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

ОБНОВЛЕНИЕ Сентябрь 2015: Для себя я начал использовать ES 2015 Javascript для всего - используя io.js и / или Babel. Я также не использую any new в своих проектах, за исключением встроенных Javascript, таких как new Error(...). Я предпочитаю использовать гораздо более мощный функциональный подход, я полностью игнорирую объектную систему. [my-object].prototype и this полностью исчезли из моих проектов. Долгое время я ОЧЕНЬ скептически относился к этим идеям «потому что объекты работают просто отлично». Но после того, как он очень неохотно попробовал в начале нового проекта (io.js), он «щелкнул», и я не понимаю, почему я потратил два десятилетия впустую. Ладно, не совсем, сегодня движки и аппаратные средства JS гораздо более благоприятны для этого стиля. Особенно в ES 2015, я рекомендую попробовать функциональный стиль, полностью свободный от любых this и class (новое ключевое слово ES 2015 или вся концепция, основанная на использовании constructorFn.prototype). Это может занять несколько недель, но как только это «щелкнет», я обещаю, что вы никогда не вернетесь - не добровольно. Это намного удобнее и мощнее.

ОБНОВЛЕНИЕ Февраль 2018: Хотя я все еще делаю то, что написал в предыдущем обновлении, теперь я хочу добавить, что иногда классы в порядке. Там нет абсолютов. : -)

12 голосов
/ 08 апреля 2011

Я не знаю, как избежать new Date() или new XMLHttpRequest(). Но я знаю, как избежать использования нового для своих типов.

Сначала я начну с Object.create(). Это метод ES5, поэтому он доступен не везде. Я добавляю его, используя es5-shim , тогда я готов к работе.

Мне нравится шаблон модуля, поэтому я начинаю с обертывания моего типа в самозапускающуюся анонимную функцию, var Xyz = (function() {...})(). Это означает, что у меня есть личное пространство, чтобы работать без беспорядка в глобальном пространстве имен. Я возвращаю объект с функцией create() и свойством prototype. функция create() предназначена для пользователей моего типа. Когда им нужен один, они вызывают Xyz.create() и возвращают новый инициализированный объект моего типа. Свойство prototype доступно, если люди хотят наследовать.

Вот пример:

var Vehicle = (function(){
        var exports = {};
        exports.prototype = {};
        exports.prototype.init = function() {
                this.mph = 5;
        };
        exports.prototype.go = function() {
                console.log("Going " + this.mph.toString() + " mph.");
        };

        exports.create = function() {
                var ret = Object.create(exports.prototype);
                ret.init();
                return ret;
        };

        return exports;
})();

и наследование выглядит так:

var Car = (function () {
        var exports = {};
        exports.prototype = Object.create(Vehicle.prototype);
        exports.prototype.init = function() {
                Vehicle.prototype.init.apply(this, arguments);
                this.wheels = 4;
        };

        exports.create = function() {
                var ret = Object.create(exports.prototype);
                ret.init();
                return ret;
        };

        return exports; 

})();
11 голосов
/ 07 марта 2011

Не использовать new и слепо следовать Крокфорду - глупо.

Понимать JavaScript и писать хороший код. Использование ключевого слова new - Cornerstone JavaScript OO.

Вы пропустите много хорошего кода JavaScript, избегая new.

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

Крокфорд имеет привычку говорить, что все в JavaScript, которое когда-либо давало ему ошибку в его коде, является плохим.

Я бы лично сказал, что "[еще] лучшая стратегия выживания - быть компетентным."

6 голосов
/ 07 марта 2011

Вы можете избежать new, создав фабричные функции:

var today = Date.getToday();

(Если вам интересно, вы не можете избежать этого насама фабричная функция:)

Date.getToday = function() { return new Date(); };

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

5 голосов
/ 07 марта 2011

На этот вопрос уже задавали и отвечали: Считает ли "новое" ключевое слово JavaScript вредным?

Как сказал Рейнос, слепо следовать Крокфорду (или кому-либо еще в этом отношении), не понимая почему они говорят, что делают, глупо.

3 голосов
/ 07 марта 2011

Я думаю, что его совет не использовать new вообще является концептуальным (академическим) и не должен восприниматься буквально. Класс Date является прекрасным исключением из правила, потому что как еще можно получить объект текущей (или произвольной) даты, используя стандартный ECMAScript?

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

var newCar = function(o) {
  o = o || {};
  // Add methods and properties to "o"...
  return o;
}
1 голос
/ 03 июля 2017

Вы застряли с использованием 'new' для создания экземпляров объектов других людей.Но для вашего собственного кода вы можете избежать ошибок как «this», так и «new».

(Кстати, проблема на самом деле не в самом «new». Но в создании объекта с помощью«new», вероятно, использует «this» внутри. Использование «this» приводит к частым и тонким ошибкам, потому что javascript «this» требует, чтобы caller выполнял дополнительную работу, чтобы привязать вызываемый метод к нужному объектуи неправильные привязки трудно помешать или иным образом обнаружить до выполнения.)

Короткая версия:

Чтобы избежать использования 'new' для создания экземпляров объектов, которые вы пишете, просто вернитеобъект из любой функции.Внутри этой функции присоедините любые методы к этому объекту и выполните любую инициализацию.Вы сделали - эта функция является и вашим определением класса, и конструктором.

Длинная версия, с примером:

Следующий шаблон 'self' позволяет избежать использования как 'new', так и 'this'.Хотя шаблон хранит копии методов в каждом объекте, это не имеет значения, если вы не создаете много объектов во время выполнения.Если это так, то вы всегда можете использовать шаблон «flyweight» из http://justjs.com/posts/this-considered-harmful. (хотя в примерах на этой странице все еще используется «this», это небольшая настройка, чтобы также адаптировать их к следующему шаблону.)

// this function defines a "class" -- the whole function is the constructor
function Foo(x) {
    // create/init whatever object type you want here
    var self = {x: x};
    // if subclassing, for instance, you can do:
    // var self = Baz(x);

    // public method
    self.foo = function() {
        return self.x;
    };

    // public method
    self.bar = function() {
        console.log(self.x);
        logger();
    };

    // private method
    function logger() {
        console.log(self.x);
    }

    // ...more constructor bits can go here

    // don't forget to return self
    return self;
}

var f = Foo(1); 
var g = Foo(2); 
setTimeout(f.bar, 1000);
setTimeout(g.bar, 1000);

console.log(g.foo()); // 2
g.x = 5;
console.log(f.foo()); // 1
console.log(g.foo()); // 5

// ...then, 1 second later:
// 1  (from f.bar)
// 1  (from f.logger)
// 5  (from g.bar)
// 5  (from g.logger)

// blows up if uncommented
// f.logger();
1 голос
/ 07 марта 2011

Вы можете избежать «нового», возвращая анонимный объект и используя замыкание в своем конструкторе. Это также поможет вам скрыть личные данные.

Рассмотрим:

function SomeCounter(start) {

    var counter = start;

    return {
        getCounter : function() {
            return counter;
        },

        increaseCounter : function() {
            counter++;
        }

    };
}

Теперь, чтобы использовать это, все, что вам нужно сделать, это

var myCounter = SomeCounter(5);
myCounter.increaseCounter();
console.log(myCounter.getCounter()); //should log 6

Прелесть этого в том, что вам не нужно помнить, чтобы использовать "новый", но если вы это сделаете, это не повредит вам.

var myCounter = new SomeCounter(5); //still works
1 голос
/ 07 марта 2011
function F() { return { /* your fields and methods here */ } }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...