Подтвердить что ты не робот

JavaScript: Хорошие детали - как не использовать `new` вообще

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

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

Далее он говорит, что "еще лучше стратегия совладания заключается в том, чтобы не использовать new вообще".

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

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

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

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

4b9b3361

Ответ 1

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

Однако команда YUI (3) сама использует "новую", и они выполняют свои рекомендации (поскольку он главный архитектор JS Yahoo (UPDATE: он перешел, но утверждение было правдой, когда этот ответ был изначально написан) Я понимаю, что это конкретное утверждение больше похоже на "академический" уровень, что СЛЕДУЕТ было понять, что язык был разработан "правильно", а не с некоторыми остатками наследования на основе классов. Он (ИМХО справедливо) говорит, что путь оказалось, что 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);


EDIT: Обновлено, чтобы использовать последнюю версию этой функции Crockford - есть три.


UPDATE Июнь 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: для себя я начал использовать Javascript ES 2015 для всего - используя либо io.js и/или Babel. Я также не использую никаких new в моих проектах, кроме встроенных Javascript, таких как new Error(...). Я предпочитаю использовать гораздо более мощный функциональный подход, я полностью игнорирую объектную систему. [my-object].prototype и this полностью исчезли из моих проектов. В течение долгого времени я был очень скептически относился к этим идеям "потому что объекты работают очень хорошо". Но после того, как он неохотно дал ему попытку в начале нового проекта (io.js), он "нажал", и я не понимаю, почему я потратил два десятилетия. Хорошо, не совсем, сегодня двигатели и аппаратные средства JS намного более благоприятны для этого стиля. Особенно с ES 2015, я рекомендую дать функциональный стиль, полностью свободный от любых this и class (новое ключевое слово ES 2015 или целая концепция, основанная на использовании constructorFn.prototype). Это может занять несколько недель, но как только он "щелкнет", я обещаю, что вы никогда не вернетесь - не добровольно. Это намного удобнее и мощнее.

Ответ 2

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

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

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

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

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

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

Ответ 3

Я не знаю, как избежать 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; 

})();

Ответ 4

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

var today = Date.getToday();

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

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

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

Ответ 6

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

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

var newCar = function(o) {
  o = o || {};
  // Add methods and properties to "o"...
  return o;
}

Ответ 7

function F() { return { /* your fields and methods here */ } }

Ответ 8

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

Рассмотрим:

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

Ответ 9

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

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

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

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

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

Следующий шаблон "self" позволяет избежать использования как "нового", так и "w654". Хотя шаблон хранит копии методов в каждом объекте, это не имеет значения, если вы не создаете много объектов во время выполнения. Если это так, то вы всегда можете использовать шаблон "мухи" от 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();