我一直在阅读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但是在调用之前它不起作用。
我有一种感觉,我不得不至少问一些有关堆栈溢出的人,如果有可能的话
再次感谢您的意见和反馈
答案 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();
}
*/
它也处理变量参数
答案 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()
表达式解决方法:原型和参数
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();
}
这有两个问题......
使用更多通用方法可能看起来更像这样:
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());
答案 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);
是您获得有保障的新情况的地方。