我应该使用原型还是不使用?

IT技术 javascript
2021-02-23 03:46:21

我正在创建一个 Vector 类,它基本上可以保存三个数值。但是,可以对这样的向量进行很多操作 - 例如获取幅度、添加或减去另一个向量等。

我想知道这些函数是否应该被编码为 Vector 类的原型函数,或者我应该在构造函数中定义它们。

那么这两种方法中哪一种更可取呢?

function Vector3D(x, y, z) {
    this.x = x;
    this.y = y
    this.z = z;
}

Vector3D.prototype.magnitude = function() {
    return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
};

或者

function Vector3D(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;

    this.magnitude = function() {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
    };
}
3个回答

这正是使用原型的情况。我认为这样做有两个主要好处:

  1. 函数不会被多次创建如果您在构造函数中定义函数,则每次调用构造函数时,都会为您定义的每个函数创建一个新的匿名函数原型是静态对象,Vector3D 的每个实例都将简单地引用原型函数。
  2. 原型是一个易于操作的单一对象这提供了极大的灵活性;不幸的是,我只能提供一些示例来说明它可以提供的功能:
    1. 如果您想创建一个子类,例如 Vector3DSpecial,您可以简单地将其克隆Vector3D.prototype并分配给Vector3DSpecial.prototype. 虽然您也可以使用 byVector3DSpecial.prototype = new Vector3D();构造函数来做到这一点,但构造函数可能包含副作用,这些副作用将在那个简单的原型赋值中执行,因此应该避免。使用原型,您甚至可以只选择原型中的特定函数来复制到新类中。
    2. 添加方法Vector3D只是简单地向原型添加属性,并允许您的代码更容易地拆分/组织到多个文件中,或者允许在代码的其他部分动态添加方法。当然,您可以组合在构造函数中添加方法和通过原型添加方法,但这是不一致的,并且可能会导致进一步的复杂性。

什么时候不使用原型?对于单例对象,例如与页面交互并可能将工作委托给其他对象的控制器。全局“通知”对象就是这样的一个例子。在这里,扩展是不可能的,并且对象只创建一次,这使得原型具有额外的(概念上的)复杂性。

原型方法仅适用于公共属性,如果您将 x、y、z 作为“私有”变量进行跟踪,则原型将不起作用。

我会使用后者,因为您可能想要只使用私有/内部变量的方法,但这一切都取决于上下文。

function Vector3D(x, y, z) {
   // x, y, z is automatically in this scope now, but as private members.
   this.magnitude = function() {
        return Math.sqrt(x * x + y * y + z *z);
   }
}
我同意你的看法,因此我只有公共变量。在谷歌浏览器的开发者工具中,如果通过构造函数传递,函数也会显示为成员,而如果它们“隐藏”在原型中并且只有 x、y 和 z 会出现在那里会更实用。
2021-04-18 03:46:21
谢谢你的反应。x、y 和 z 也应该能够从 Vector3D 的实例中提取,因此它们应该是公开的。在这种情况下,原型会是最好的选择吗?
2021-04-22 03:46:21
@jishi 目前的约定是使用_someprivateJavaScript 不支持私有,但它确实很好地支持原型。因此,您正在破解该语言不支持的内容,以丢弃该语言支持的内容。支持我的意思是 JS 引擎会优化你的代码。
2021-04-24 03:46:21
嗯,“为自己隐藏它”在大多数情况下是正确的,但是有很多 js 框架可以从隐藏内部结构中受益,以防止滥用代码。我明白你的意思。
2021-04-28 03:46:21
+1 正确答案,但我更喜欢原型,因为 js 中的任何私有内容都是白日梦,通常只会让您更难对代码进行单元测试 - 除了您自己之外,您还向谁隐藏这些东西:)
2021-04-30 03:46:21

ECMA 6 http://es6-features.org/#BaseClassAccess

class Shape {
    …
    toString () {
        return `Shape(${this.id})`
    }
}
class Rectangle extends Shape {
    constructor (id, x, y, width, height) {
        super(id, x, y)
        …
    }
    toString () {
        return "Rectangle > " + super.toString()
    }
}
class Circle extends Shape {
    constructor (id, x, y, radius) {
        super(id, x, y)
        …
    }
    toString () {
        return "Circle > " + super.toString()
    }
}

ECMA 5

var Shape = function (id, x, y) {
    …
};
Shape.prototype.toString = function (x, y) {
    return "Shape(" + this.id + ")"
};
var Rectangle = function (id, x, y, width, height) {
    Shape.call(this, id, x, y);
    …
};
Rectangle.prototype.toString = function () {
    return "Rectangle > " + Shape.prototype.toString.call(this);
};
var Circle = function (id, x, y, radius) {
    Shape.call(this, id, x, y);
    …
};
Circle.prototype.toString = function () {
    return "Circle > " + Shape.prototype.toString.call(this);
};