Должен ли я использовать прототип или нет?
Я создаю класс 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);
};
}
Ответы
Ответ 1
Это в точности ситуация с использованием прототипа. Для этого я вижу два основных преимущества:
- Функции не создаются несколько раз. Если вы определяете функции внутри конструктора, для каждой определяемой вами функции создается всякая анонимная функция при каждом вызове конструктора. Прототипы являются статическими объектами, и каждый экземпляр Vector3D будет просто ссылаться на функции прототипа.
- Прототип - это единственный объект, с которым можно легко манипулировать. Это дает большую гибкость; к сожалению, я могу лишь привести несколько примеров того, что это может предложить:
- Если вы хотите создать дочерний класс, например Vector3DSpecial, вы можете просто клонировать
Vector3D.prototype
и назначать его Vector3DSpecial.prototype
. Хотя вы также можете сделать это с помощью конструкторов Vector3DSpecial.prototype = new Vector3D();
, конструкторы могут содержать побочные эффекты, которые будут выполняться в этом простом назначении прототипа, и поэтому его следует избегать. С прототипами вы можете даже выбрать только определенные функции в прототипе для копирования в новый класс.
- Добавление методов в
Vector3D
- это просто вопрос добавления свойств прототипа и позволяет более легко разделить/упорядочить ваш код на несколько файлов или позволить динамически добавлять методы в другие части кода. Конечно, вы можете сделать комбинацию добавления методов в конструкторе и прототипе, но это противоречиво и, скорее всего, приведет к большей сложности в дальнейшем по пути.
Когда я использовал не прототип? Для одноэлементных объектов, например, контроллер, который взаимодействует со страницей и может делегировать работу другим объектам. Одним из таких примеров является глобальное "уведомление". Здесь расширение маловероятно, и объект создается только один раз, что делает прототип дополнительной (концептуальной) сложностью.
Ответ 2
Прототипные методы будут работать только для общедоступных свойств, если вы будете отслеживать x, y, z как переменные "private", прототип не будет работать.
Я бы использовал последнее, потому что вам могут понадобиться методы, которые работают только с частными/внутренними переменными, но все зависит от контекста.
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);
}
}
Ответ 3
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);
};