5

更新

如果这是不可能的,请随时提供解释原因的答案。我很乐意标记它已接受。


我想稍微简化以下代码(对象“声明”的两个步骤,我想要一个):

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 is not a function'。

虽然我意识到我可以使用语法糖(一个帮助程序库)来做到这一点,但我的目标是让事情尽可能简单,并对 JS 对象、原型、构造函数内部结构有所了解。我试图尽可能多地阅读:SO相关问题CrockfordBen NadelJoost Diepenmaat

也许我还没有找到正确的表述,或者我正在与 Object.create 的设计理念作斗争,或者语言不允许这样做。也许,这真的只是一种风格上的东西,因此,一种自负。

当然,我可以忍受两步过程(捣碎器)。一次将所有内容打包在一起感觉不错(巴舍尔)。

有没有办法做到这一点?谢谢。

4

3 回答 3

4

如果你想使用基于类的方法,你可以用 调用构造函数new,你总是有两个部分:

  • 构造函数本身(初始化实例)
  • 原型对象(用于共享属性)

如果您不想完全放弃原型,则没有 JavaScript 语法可以一次性完成函数创建和原型设置(class当然,除了新的 ES6 语法)同时仍然保持.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 文章,并确保查看一些来自 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'});
于 2014-10-06T19:42:58.007 回答
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');

于 2014-10-06T14:42:44.503 回答
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属性以便为其分配原型对象(“类”声明的其余部分)来实现这一点。

虽然作为一个计算单元组合在一起,但坦率地说,它在视觉上更难解析并且相当丑陋。此外,匿名函数甚至可能在其范围内捕获 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 解释器/编译器中具有一些深厚的关系,并且该语言并没有提供一种方法来做我想做的事情。

于 2014-10-06T19:35:35.623 回答