13

我一直在阅读 Stoyan Stefanov 的 JavaScript 模式书,其中一种为构造函数强制执行 new 运算符的模式是这样的

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

因此,我可以通过 new Waffle 或 Waffle() 调用 Waffle,并且仍然让它返回一个对象

我遇到的问题就在这里

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

无论如何我可以参考new Waffle()而不参考构造函数的实际名称含义,所以我可以每次复制和粘贴它而不必更改任何内容。这意味着我可以将 Waffle() 保存为变量并执行类似的操作

return new var

我希望我可以使用 this.name ,但这在调用它之前也不起作用。

我有一种感觉,我不能,但至少想问问堆栈溢出的一些人是否有可能

再次感谢您的评论和反馈

4

7 回答 7

8

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

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();

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

于 2013-06-11T04:03:55.310 回答
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/

它也处理可变参数

于 2013-06-10T21:04:37.963 回答
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-- 调用,否则会引发错误。

于 2013-06-10T22:52:34.730 回答
2

即使没有:也有更简单的方法可以强制创建新对象new

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

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

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

解释

使用newwith 函数,有两种可能:

  • 函数返回对象:对象是new function()表达式的结果
  • 函数不返回对象:返回具有新上下文的函数本身

请参阅ECMA 脚本文档

解决方法:原型和参数

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 中标准化。

于 2013-06-10T23:13:30.117 回答
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;
}
于 2013-06-11T03:06:47.450 回答
1
if (!(this instanceof Waffle)) {
    return new Waffle();
}

这有两个问题...

  1. 它不能在没有名称的匿名函数中工作
  2. 它丢失了发送给构造函数的所有参数。

使用更通用的方法可能看起来更像这样:

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());
于 2013-06-10T22:39:58.633 回答
0

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

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

于 2013-06-15T04:31:58.747 回答