在javascript中强制执行新的模式

时间:2013-06-10 20:58:56

标签: javascript

我一直在阅读Stoyan Stefanov撰写的JavaScript Patterns一书,其中一个强制构造函数的新运算符的模式就像这样

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但是在调用之前它不起作用。

我有一种感觉,我不得不至少问一些有关堆栈溢出的人,如果有可能的话

再次感谢您的意见和反馈

7 个答案:

答案 0 :(得分:8)

我有更好的解决方案。这就是您目前正在做的事情:

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

Waffle.prototype.wantAnother = true;

这种模式并不是很好,因为您正在混合代码以使用代码构建新对象,以检查是否正在使用new关键字。

我之前提到你shouldn't use the new keyword in 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();

如果您想了解有关此模式的更多信息,请阅读以下答案:https://stackoverflow.com/a/17008403/783743

答案 1 :(得分:3)

您可以使用以下内容:

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/

它也处理变量参数

答案 2 :(得分:3)

arguments.callee是指当前函数,是最简单的解决方案。不过,它已被弃用,因此使用它需要您自担风险。

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

    this.tastes = 'yummy';
}

这也是一个难题,因为你可能想要保留你所传递的论据,正如Vinothbabu所说。但是如果你真正意图强制执行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 < - >,否则会引发错误。

答案 3 :(得分:2)

即使没有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 documentation

解决方法:原型和参数

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

这值得a fiddle

注意: constructor.name(您在模式中使用的)是not standard

注2: __proto__也不是标准配置,但现代浏览器支持,并将在ES6中进行标准化。

答案 4 :(得分:2)

在我看来,最好的方法是不要让自己错误地调用错误:

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;
}

答案 5 :(得分:1)

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

这有两个问题......

  1. 一个在一个没有名字的匿名函数中工作的人
  2. 它丢失了发送给构造函数的所有参数。
  3. 使用更多通用方法可能看起来更像这样:

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

    此方法可确保使用constructor调用new,而无需state the function' s nameadds 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());
    

答案 6 :(得分:0)

我没有意识到这是客户端还是服务器端,但我使用的模式有时如下。我在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);

是您获得有保障的新情况的地方。