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

Шаблон для применения нового в javascript

Я читал книгу шаблонов JavaScript Stoyan Stefanov, и один из шаблонов для принудительного применения нового оператора для функций конструктора выглядит следующим образом

function Waffle() {
if (!(this instanceof Waffle)) {
return new Waffle();
}
this.tastes = "yummy";
}
Waffle.prototype.wantAnother = true;

при написании этого способа вы можете вызвать Waffle либо один из этих способов.

var first = new Waffle(),
second = Waffle(); 

Я думаю, что это полезная функция, не уверенная в том, что она реализована в будущих версиях ecma/javascript

Я придумал что-то само по себе, которое, как я думал, может просто копировать и вставлять каждый раз при создании функции-конструктора

что-то вроде этого

function checkInstance (name) {
    if (name.constructor.name === undefined) {
       return "construct it"
    } else {
       return false;
    }
}

function Waffle() {
    var _self = checkInstance.call(this, this);
    if (_self === "construct it") {
       return new Waffle()
    }
    this.tastes = "yummy"
}

var waffle = Waffle()
waffle

Поэтому я могу вызвать Waffle в любом случае с помощью новой Waffle или Waffle() и все равно вернуть объект

Моя проблема, что у меня есть, здесь

  if (_self === "construct it") {
       return new Waffle()
       }

В любом случае, я могу ссылаться на new Waffle(), не ссылаясь на фактическое имя функции конструктора, поэтому я мог бы копировать и вставлять это каждый раз и ничего не менять. Значение Я мог бы сохранить Waffle() как переменную и сделать что-то вроде

return new var

Я бы хотел использовать this.name, но это не работает либо до его вызова.

У меня есть чувство, что я не могу не хотеть, по крайней мере, спросить некоторых людей здесь о переполнении стека, если это была возможность

Снова ваши комментарии и отзывы приветствуются

4b9b3361

Ответ 1

У меня лучшее решение. Это то, что вы сейчас делаете:

function Waffle() {
    if (!(this instanceof Waffle))
        return new Waffle;
    this.tastes = "yummy";
}

Waffle.prototype.wantAnother = true;

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

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

Function.prototype.new = (function () {
    return function () {
        functor.prototype = this.prototype;
        return new functor(this, arguments);
    };

    function functor(constructor, args) {
        return constructor.apply(this, args);
    }
}());

Эта функция позволяет вам создать экземпляр функции следующим образом:

var waffle = Waffle.new();

Однако мы вообще не хотим использовать new. Поэтому, чтобы покончить с этим, мы создадим функцию, которая обертывает конструктор следующим образом:

function constructible(constructor) {
    function functor() { return Function.new.apply(constructor, arguments); }
    functor.prototype = constructor.prototype;
    return functor;
}

Теперь мы можем определить функцию Waffle следующим образом:

var Waffle = constructible(function () {
    this.tastes = "yummy";
});

Waffle.prototype.wantAnother = true;

Теперь вы можете создавать объекты с использованием или без использования new:

var first = new Waffle;
var second = Waffle();

Примечание. Функция constructible выполняется довольно медленно. Вместо этого используйте следующую версию constructible - это немного быстрее:

function constructible(constructor) {
    constructor = Function.bind.bind(constructor, null);
    function functor() { return new (constructor.apply(null, arguments)); }
    functor.prototype = constructor.prototype;
    return functor;
}

Лично я бы не использовал ни один из этих двух методов. Я бы просто забыл написать new, или (более вероятно), я бы реструктурировал свой код следующим образом:

var waffle = {
    create: function () {
        var waffle = Object.create(this);
        waffle.tastes = "yummy";
        return waffle;
    },
    wantAnother: true
};

var first = waffle.create();
var second = waffle.create();

Если вы хотите узнать больше об этом шаблоне, прочитайте следующий ответ: fooobar.com/questions/17945/...

Ответ 2

Вы можете использовать что-то вроде этого:

var Waffle = (function() {
    function Waffle() {
        this.tastes = "yummy"
    }

    return exportCtor( Waffle );
})();


var waffle = Waffle();

alert(waffle.tastes);

console.log(Waffle);

/*
function ConstructorProxy() {
    "use strict";
    return new Constructor();
}
*/

http://jsfiddle.net/ywQJF/

Он также обрабатывает переменные аргументы

Ответ 3

arguments.callee, который относится к текущей функции, является самым простым решением. Однако он не рекомендуется, поэтому используйте его на свой страх и риск.

function Waffle() {
    if (!(this instanceof arguments.callee))
        return new arguments.callee();

    this.tastes = 'yummy';
}

Это тяжелая проблема, потому что вы, вероятно, хотите сохранить аргументы, которые вы передаете, как упоминал Винотбабу. Но если вы действительно намерены использовать new, вы можете просто выбросить ошибку, которая представляет собой простую две строки кода:

if (!(this instanceof Waffle))
    throw new Error('Constructor called without new');

Вы можете даже обернуть его в функцию:

function cons(C) {
    var c = function () {
        if (!(this instanceof c))
            throw new Error('Constructor called without new');

        C.apply(this, arguments);
    };
    c.prototype = C.prototype;
    return c;
}

var Waffle = cons(function () {
    this.tastes = 'yummy';
});
Waffle.prototype.wantAnother = function () {
    return true;
};

new Waffle(); // { tastes: 'yummy', 'wantAnother': true }
Waffle(); // throws error

Теперь Waffle должен быть вызван с помощью new - в противном случае он выдает ошибку.

Ответ 4

Существует более простой способ принудительного создания нового объекта даже без new:

function Waffle() {
    return {tastes:"yummy"};
}

var a = Waffle();
var b = new Waffle();

alert(a.tastes); // yummy
alert(b.tastes); // yummy

Объяснение

Используя new с функцией, есть две возможности:

  • функция возвращает объект: объект является результатом выражения new function()
  • функция не возвращает объект: возвращается сама функция с новым контекстом

См. документацию ECMA script

Обходной путь: прототип и аргументы

function Waffle(taste,how) {
    return {
        tastes: taste+" "+how,
        __proto__: Waffle.prototype
    }
}
Waffle.prototype.wantmore = "yes";

var a = Waffle("yummy","much");
var b = new Waffle("gummy","little");

console.log(a.tastes,b.tastes); // yummy much, gummy little
console.log(a.wantmore,b.wantmore); // yes, yes

Это заслуживает скрипку.

Примечание. constructor.name (который вы использовали в своем шаблоне) нестандартно

Примечание 2: __proto__ также не является стандартным, но поддерживается современными браузерами и будет стандартизоваться на ES6.

Ответ 5

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

function Waffle() {
  if (!(this instanceof Waffle)) {
    throw "Waffles need to be fresh or they're gross. Use 'new'.";
  }
}

Но если вы просто должны позволить себе писать непоследовательный код, сделайте инициализацию отдельным шагом.

function Waffle(options) {
  var o = options || {};
  if (this instanceof Waffle) {
    this.init = function() {
      /* this is really your constructor */
      console.log("initializing ... ");
    }

    if (!o.__do_not_initialize) {
      this.init(arguments);
    }
  } else {
    var rv = new Waffle( { __do_not_initialize: true } );
    rv.init(arguments);
    return rv;
  }
}

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

function BuildWaffle(options) {
  var o = options || {};

  if (this instanceof WaffleBuilder) {
    throw "BuildWaffle cannot be instantiated.";
  }

  var Waffle = function Waffle() { /* whatever */ }
  Waffle.prototype.doStuff = function() { /* whatever else */ }

  var rv = new Waffle(options);
  return rv;
}

Ответ 6

if (!(this instanceof Waffle)) {
    return new Waffle();
}

У этого есть две проблемы...

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

Использование более общего подхода может выглядеть примерно так:

if (!instanceExists(this, arguments)) {
    return requireInstance(this, arguments);
}

Этот подход гарантирует, что constructor вызывается с помощью new, без необходимости state the function' s name и adds all arguments sent to the constuctor so they aren 't lost during the process.

Здесь полный код для вышеперечисленного:

Function.prototype.callNew = function (args) {
    var a = [];
    for (var i = 0; i < args.length; i++) a.push("a[" + i + "]");
    var fn = new Function("var a=arguments;return new this(" + a.join(",") + ");");
    return fn.apply(this, args);
}

function instanceExists(t, args) {
    if (t instanceof args.callee) {
        return true;
    } else {
        return false;
    }
}

function requireInstance(t, args) {
    var fn = args.callee;
    if (!instanceExists(t, args)) {
        return fn.callNew(args);
    }
}

function Waffle(one, two, three) {
    if (!instanceExists(this, arguments)) {
        return requireInstance(this, arguments);
    }
    this.one = one;
    this.two = two;
    this.three = three;
}

Waffle.prototype.serve = function () {
    var out = [];
    for (var j in this) {
        if (!this.hasOwnProperty(j)) continue;
        out.push(j + ': ' + this[j]);
    }
    return ' {
    ' + out.join(",\n") + '
}
';
}

Сцена для игры. http://jsfiddle.net/RkPpH/

var waffle = Waffle(1, 2, 3);
alert(waffle.serve());

Ответ 7

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

Во-первых, я создаю что-то, что можно использовать по строкам традиционной базы OO или суперкласса:

//// Node:
//module.exports.Base = Base;

function Base(opts) {
    var self = this;
    if (!(self instanceof Base)) return new Base(opts);
    self.opts = opts || {};
}

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

// commonMethod is available to subclasses:
Base.prototype.commonMethod = function () {
    var self = this;
    //access self.opts to get the constructor arguments.
    //makes self always point to the right object.
}

// Provide abstractMethod, but subclass is responsible for implementation:
Base.prototype.abstractMethod = function () {
    //or throw an error if this should be implemented by subclasses:
    throw new Error('implement me');
}

Теперь вы можете сделать это:

//// If using Node:
//var inherits = require('util').inherits;
//var Parent = require('./Base').Base;

function Sub (opts) {
    var self = this;
    //// If using node and you  want super_ to be called prior to creating a new Sub:
    //if(Sub.super_) Sub.super_.call(this, opts);

    // Always do this:
    if (!(self instanceof Sub)) return new Sub(opts);

    //// If using node and you are ok with super_ called after creating new Sub:
    //if(Sub.super_) Sub.super_.call(this, opts);
    //// otherwise:
    parent(opts);
}

//// If using Node:
//inherits(Sub, Base);
//// Otherwise:
Sub.prototype.constructor = Base;
Sub.prototype.parent = Base.prototype;

//and provide the implementation of abstractMethod:
Sub.prototype.abstractMethod() {
    //...
}

И чтобы официально ответить на конкретный вопрос, все

if (!(self instanceof Sub)) return new Sub(opts);

где вы получаете гарантированную новую ситуацию.