更新
如果无法做到这一点,请随时提供解释原因的答案。我很乐意在接受时加以标记。
我想略微简化以下代码(对象“声明”的两个步骤,我想要一个):
var Masher = function(opts) {
this._name = opts.name;
};
Masher.prototype = Object.create(Object.prototype, {
_name: { writable: true },
name: { get: function() { return this._name; }}
});
// Note: (new Masher({name: 'bar'})).name == 'bar'
我想一次性创建整个函数原型,构造函数出现在Object.create中的某个地方。也许,这样的事情:
var Basher = Object.create(Function.prototype, {
_name: { writable: true },
name: { get: function() { return this._name; }},
constructor: { value: function(opts) { this._name = opts.name; }}
});
但是,当我调用new Basher()
时,我得到:'TypeError:object不是函数'。
虽然我意识到我可以用语法糖(一个辅助库)来做这个,但我的目标是让事情尽可能简单,并对JS对象,原型,构造函数内部结构有所了解。我尽可能多地阅读:SO related questions,Crockford,Ben Nadel,Joost Diepenmaat。
也许我没有找到正确的表述,或者我正在与Object.create的设计理念作斗争,或者语言不允许这样做。也许,这实际上只是一种风格,因此是一种自负。
当然,我可以忍受两步过程(Masher)。有一些东西可以一次性打包,感觉很合适(Basher)。
有办法做到这一点吗?感谢。
答案 0 :(得分:3)
如果你想使用基于类的方法来调用new
的构造函数,你将总是有两个部分:
如果您不想完全删除原型,那么没有JavaScript语法可以同时执行函数创建和原型设置(当然,除了新的ES6 class
语法之外)仍然保持从函数到原型对象的.prototype
链接。当然,一个简单的辅助函数(不需要是一个完整的库)会做:
function Class(p) {
return (p.constructor.prototype = p).constructor;
}
var Casher = Class({
constructor: function(opt) { this._name = opt.name },
get name() { return this._name }
});
var foo = new Casher({name:'bar'});
这种模式与Object.create
根本没有多大关系(除非你希望你的原型继承自另一个)。
所以是的,也许你正试图与Object.create
的哲学作斗争,即只使用对象并从中获取其他对象(阅读Wikipedia article并确保查看一些例如来自JS以外的语言)。你没有构造函数,也没有new
运算符 - 而是在你的对象上调用create
方法:
var Proto = { // some helper methods (usually native in more prototype-focused languages)
clone: function() {
return Object.create(this);
},
create: function(opt) {
var derived = this.clone();
derived.init(opt);
return derived;
},
init: function(opt) {
Object.getOwnPropertyNames(opt).forEach(function(p) {
Object.defineProperty(this, p, Object.getOwnPropertyDescriptor(opt, p));
}, this);
}
};
var Pasher = Proto.create({ // "subclass" Proto
init: function(opt) {
if ("name" in opt) this._name = opt.name;
},
_name: "",
get name() { return this._name; }
});
var foo = Pasher.create({name:'bar'});
答案 1 :(得分:0)
Object.create()
返回对象,而非函数,并定义了特定的原型继承链。
var Basher = Object.create(Function.prototype, {
_name: { writable: true },
name: { get: function() { return this._name; }},
constructor: { value: function(opts) { this._name = opts.name; }}
});
> undefined
Basher
> Object {_name: undefined, name: (...)}_name: undefinedconstructor: function (opts) { this._name = opts.name; }name: (...)get name: function () { return this._name; }__proto__: function Empty() {}
> typeof Basher
"object"
但是,您可以将Object.create()
和构造函数结合起来,让您将对象文字重用为API,这使代码看起来更清晰:
var basherAPI = {
name: function () {
return this._name;
}
};
function Basher(name) {
var inst = Object.create(basherAPI);
// assign instance *state* here; the API is
// reusable but each object needs its own state
inst._name = name;
return inst;
}
var basher = new Basher('Tom');
basher.name() // Tom
编辑:在这种情况下,使用new
关键字纯粹是惯例;它与构造函数中发生的事情无关。人们还可以写:var basher = Basher('Tom');
。
答案 2 :(得分:0)
我在这里给出答案,但我不确定这完全回答了我的问题,因此我不会将其标记为 答案,因为我我不相信我完全理解JS语言的这一部分。
我相信我能得到的最接近的是:
var Lasher;
(Lasher = function (opts) {
this._name = opts.name;
}).prototype = Object.create(Object.prototype, {
_name: { writable: true },
name: { get: function() { return this._name; }},
});
这个公式揭示了正在发生的事情的一部分,这是@Bergi所提到的,尽管是短暂的。首先,请注意它不回答我的问题,因为它仍然需要两个步骤(变量声明然后值赋值)。它确实触及了我的风格问题的核心,即共同定位所有对象的声明代码。它通过捕获Lasher中的匿名函数定义(构造函数),然后引用prototype
属性以便为其分配原型对象("类"声明的其余部分)来实现此目的。 p>
虽然组合在一起作为一个计算单元,但在视觉上它更难以解析而且相当丑陋,坦率地说。此外,匿名函数甚至可能在其范围内捕获Lasher(我不确定)。
顺便说一句,我尝试了这种有缺陷的方法。这是一个声明,虽然仍然有点难看:
(function Crasher(opts) {
this._name = opts.name;
}).prototype = Object.create(Object.prototype, {
_name: { writable: true },
name: { get: function() { return this._name; }},
});
太糟糕了,它不起作用。 Crasher的范围是第一个()
并且不在外部范围内,这就是为什么早期的代码必须将Crasher声明为变量才能捕获匿名函数。
问题是,从线数或可读性的角度来看,Lasher并不比Masher(上图)更简单。因此,原始配方是最好的(到目前为止)。
另外,虽然我理解为什么Object.create
无法使用,因为它创建了一个Object,但我想我不了解内部JS解释器如何创建函数与对象。我的意思是,一个函数是一个对象,但我想没有办法创建一个可以直接从Object.create
返回的函数。这是主要问题。
此外,无论原型赋值内部和构造函数赋值是什么,在Object.create中添加构造函数属性都不起作用。我们可以看到这段代码:
var Masher = function(){ console.log("default");};
Masher.prototype = Object.create(Object.prototype, {
_name: { writable: true },
name: { get: function() { return this._name; }},
constructor: { value: function(opts) {
console.log("ctor");
this._name = opts.name;
return this;
}}
});
// "new Masher()" outputs:
// default
// undefined
所以,如果我理解其中的一些,那么函数创建和原型创建之间在JS解释器/编译器中有一些深层关系,并且语言不提供做我想做的事情的方法。 / p>