javascript构造函数和object.create可以组合使用吗
Can javascript constructor function and object.create be combined?
更新
如果无法做到这一点,请随时提供解释原因的答案。我很乐意标记它已被接受。
我想稍微简化以下代码(对于对象"声明",我希望有两个步骤):
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相关的问题,Crockford,Ben Nadel,Joost Diepenmaat。
也许我还没有找到正确的公式,或者我正在与Object.create的设计理念作斗争,或者语言不允许这样做。也许,这真的只是一种风格上的东西,因此,是一种自负。
当然,我可以接受两步流程(Masher)。把所有东西都包装在一个镜头里,感觉很好(巴舍尔)。
有办法做到这一点吗?谢谢
如果您想使用基于类的方法,可以用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
的哲学,即只使用对象并从中派生其他对象(阅读维基百科上关于它的文章,并确保从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'});
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
EDIT:在这种情况下,使用new
关键字纯粹是惯例;它与构造函数函数中发生的事情无关。也可以写:var basher = Basher('Tom');
。
我在这里输入了一个答案,但我不确定这是否完全回答了我的问题,所以我不会将其标记为答案,因为我不相信我完全理解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
属性,为其分配原型对象("类"声明的其余部分)来实现这一点。
尽管将其组合为一个计算单元,但从视觉上看,它更难解析,坦率地说,相当丑陋。此外,匿名函数甚至可能在其作用域中捕获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
在创建对象时不能使用的原因,但我想我不明白内部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解释器/编译器中有一些深层次的关系,并且该语言不能提供我想要做的事情。
- ES6构造函数返回基类的实例
- 使用Google Visualization动态调用构造函数
- javascript中对象构造函数中的var属性与this.properties
- 理解typescript中的构造函数接口
- 为什么构造函数不是构造函数
- 如果在构造函数中有“返回”,则在 JavaScript 中的新运算符中做了什么
- 拦截对构造函数的调用
- 使用闭包共享构造函数参数
- 文本表示法VS.构造函数,用于在JavaScript中创建对象
- 从js引擎的角度来看闭包和构造函数是如何工作的
- 如何使用此从对象访问构造函数
- Javascript:为什么是构造函数's __proto__属性Empty(){}
- 当一个重要的构造函数参数丢失时应该发生什么
- Chrome Javascript日期构造函数错误
- 如何使用构造函数's的输出,以便将值插入到对象中
- 为什么在调用父构造函数时在[]中发送数据
- 构造函数函数闭包变量
- javascript构造函数和object.create可以组合使用吗
- 使用ViewModel构造函数击倒虚拟组合
- 为什么组合构造函数/原型模式返回对象类型