创建同一个类的对象:Javascript原型、私有成员、继承

发布于 2024-08-21 09:49:35 字数 2394 浏览 5 评论 0原文

有些代码可能会说一千多个字:

/**
 * Represents an amount of a resource
 * @param {number} amount
 * @param {string} type
 */
function Resource(amount, type) 
{
    var nAmount = amount;
    var sType = type;

    if (amount < 0) 
    {
        throw new IllegalArgumentException("amount has to be positive");
    }

    /**
     * @method Resource
     * @return {number} amount of the resource
     */
    this.getAmount = function() 
    {
        return nAmount;
    };

    /**
     * @method Resource
     * @return {string} resource type
     */
    this.getType = function() 
    {
        return sType;
    };
}

/**
 * Addition of two resources produces a new resource with the sum amount
 * the new object uses the old one as prototype
 * @param {Resource} resource
 * @return {Resource} new Resource object
 */
Resource.prototype.plus = function(resource) 
{
    if (!(resource instanceof Resource && this.getType() == resource.getType())) 
    {
        throw new IllegalArgumentException("resources don't match.");
    }

    var newRes = Object.create(this); // create a new object based on the current one
    // execute the Resource constructor on it
    Resource.call(newRes, this.getAmount() + resource.getAmount(), this.getType());
    return newRes;
};

资源对象是被认为是不可变的ValueObject。它们在加法操作中返回一个新对象。现在,我不再只是调用“new Resource(args)”来创建要返回的新对象,而是基于旧对象创建了一个新对象。这也允许继承。

当我开始在所有 ValueObjects 上使用它时(以防万一我想在将来的某个时候继承它们),我开始更多地考虑这一点。

JavaScript 不允许不可变对象。然而,对象很容易通过直接方法覆盖或调用其构造函数而受到攻击。我所能做的就是认定这些都是坏习惯。不幸的是,ECMAScript5“冻结”还没有出现,尽管我的模式与它兼容。

现在我有这种在不可变对象上调用构造函数的“糟糕风格”以及此代码重复,我正在考虑创建一个新函数来封装此过程:

Object.recreate = function(proto, constructor, args) 
{
    var obj = Object.create(proto);
    constructor.apply(obj, args);
    return obj;
};

因此:

Resource.prototype.plus = function(resource) 
{
    // if ... {throw ...}
    return Object.recreate(this, Resource, 
            [this.getAmount() + resource.getAmount(), this.getType()]);
};

也许有人对名称有更好的想法这个功能。 “重新创造”是我的第一个想法。你觉得这个模式怎么样?是否过于抽象?我应该将其保存为我确定会继承的类吗?我错过了什么重要的事情吗?

编辑:我发现我忘记提及一些目前未在本文中反映出来的重要内容。 ValueObject 可以使用 Object.create 轻松克隆。他们的私人成员是不可更改的。但是可变的私人成员呢?如果在克隆上调用 set() ,它会在闭包中设置原始原型对象!当我的 Object.recreate 重新创建闭包时,这个问题就解决了。

那么有没有更好的方式用私有变量进行继承呢?为什么每个人都使用糖来创建类?我读了很多关于原型主义的书,但我仍然不明白它的窍门。

Some code may say more than a thousand words:

/**
 * Represents an amount of a resource
 * @param {number} amount
 * @param {string} type
 */
function Resource(amount, type) 
{
    var nAmount = amount;
    var sType = type;

    if (amount < 0) 
    {
        throw new IllegalArgumentException("amount has to be positive");
    }

    /**
     * @method Resource
     * @return {number} amount of the resource
     */
    this.getAmount = function() 
    {
        return nAmount;
    };

    /**
     * @method Resource
     * @return {string} resource type
     */
    this.getType = function() 
    {
        return sType;
    };
}

/**
 * Addition of two resources produces a new resource with the sum amount
 * the new object uses the old one as prototype
 * @param {Resource} resource
 * @return {Resource} new Resource object
 */
Resource.prototype.plus = function(resource) 
{
    if (!(resource instanceof Resource && this.getType() == resource.getType())) 
    {
        throw new IllegalArgumentException("resources don't match.");
    }

    var newRes = Object.create(this); // create a new object based on the current one
    // execute the Resource constructor on it
    Resource.call(newRes, this.getAmount() + resource.getAmount(), this.getType());
    return newRes;
};

Resource objects are ValueObjects considered immutable. They return a new object on an addition operation. Now instead of just calling "new Resource(args)" to create the new object to return, I created a new one based on the old object. This also permits inheritance.

As I'm starting to use this on all my ValueObjects (just in case I want to inherit from them at some point in the future), I started to think about this a bit more.

JavaScript doesn't allow immutable objects. Yet, Objects are vulnerable by direct method overwriting or calling their constructor. All I can do is to decide that these are bad habits. Unfortunately ECMAScript5 "freeze" is not yet here, though my pattern is compatible with it.

Now that I have this "bad style" of calling the constructor on an immutable object along with this code duplication, I'm thinking of creating a new function to encapsulate this procedure:

Object.recreate = function(proto, constructor, args) 
{
    var obj = Object.create(proto);
    constructor.apply(obj, args);
    return obj;
};

And therefor:

Resource.prototype.plus = function(resource) 
{
    // if ... {throw ...}
    return Object.recreate(this, Resource, 
            [this.getAmount() + resource.getAmount(), this.getType()]);
};

Maybe somebody has a better idea for the name of this function. 'Recreate' is what my first thought was. What do you think about this pattern? Is it an over-abstraction? Should I save this for classes I'm sure I'll inherit from? Did I miss anything important?

Edit: I see I forgot to mention something important which doesn't reflect currently in this article. ValueObject's are easily cloned with Object.create. Their private members are unchangeable. But what about changeable private members? If a set() is called on the clone, it sets the original prototype object within the closure! As my Object.recreate recreates the closure, this problem is solved.

So is there a better way for inheritance with private variables? Why is everyone using sugar for class creation? I've read so much about prototypalism, I still don't have the hang of it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

留蓝 2024-08-28 09:49:35

多年后回顾这一点,我可以说我当时编写的代码在混合概念方面存在缺陷。
JavaScript实际上可以通过闭包提供真正的私有变量。但由于 ValueObject 无论如何都是不可变的,因此私有状态无法更改,并且隐藏它会使事情变得过于复杂。

另外,巧妙的Object.recreate是一种过度抽象的“工厂方法”,如果需要的话,应该为每个类单独声明。

使用组合而不是继承!
Crockford 提供了很棒的 JavaScript 见解:https://crockford.com/javascript/prototypal.html

Coming back to this after years, I can say that the code I wrote then was flawed in mixing concepts.
JavaScript can actually provide true private variables by closure. But since ValueObjects are immutable anyways, that private state cannot be changed, and it overcomplicates things to hide it.

Also, the ingenious Object.recreate is a kind of over-abstracted "factory method", that one should declare seperatedly for each class if one needs it.

Use composition over inheritance!
Crockford gives great JavaScript insights: https://crockford.com/javascript/prototypal.html

世界等同你 2024-08-28 09:49:35

我认为复制或克隆是一个更好的名字。这篇文章解释了创建此类通用函数的机制。

I think copy or clone would be a better name. This article explains mechanisms for creating such generic functions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文