首页 > 解决方案 > 在 JavaScript 中实现 Circle 构造函数,没有 ES6 类

问题描述

Eric Faust 在以下关于 ES6 类的文章中键入了一个 Circle 构造函数:https ://hacks.mozilla.org/2015/07/es6-in-depth-classes/

我想知道:

  1. 他为什么用defineProperty?我们不能直接在构造函数中实现行为吗?例如:Circle.draw = function draw() {..}
  2. 为什么使用get/set而不是仅仅在正常属性中使用状态:Circle.circleCount
  3. 哪些属性应该直接在新实例对象上实现,通过this构造函数与 on Constructor.prototype(考虑到两者如何使属性可用于新实例)?

埃里克的代码:

function Circle(radius) {
  this.radius = radius;
  Circle.circlesMade++;
}

Circle.draw = function draw(circle, canvas) { /* Canvas drawing code */ }

Object.defineProperty(Circle, "circlesMade", {
  get: function() {
    return !this._count ? 0 : this._count;
  },

  set: function(val) {
    this._count = val;
  }
});

Circle.prototype = {
  area: function area() {
    return Math.pow(this.radius, 2) * Math.PI;
  }
};

Object.defineProperty(Circle.prototype, "radius", {
  get: function() {
    return this._radius;
  },

  set: function(radius) {
    if (!Number.isInteger(radius))
      throw new Error("Circle radius must be an integer.");
    this._radius = radius;
  }
});

let c1 = new Circle(10);
console.log(c1.area());
console.log(Circle.circlesMade);

我的版本:

function Circle(_radius) {
  this.radius = _radius;

  Circle.draw = function draw(circle, canvas) {
    /* Canvas drawing code */
  };

  !Circle.circleCount ?
    (Circle.circleCount = 1) //First construction
    :
    (Circle.circleCount = Circle.circleCount + 1);

  this.area = function area() {
    return Math.pow(this.radius, 2) * Math.PI;
  };
}

let c1 = new Circle(10);
console.log(Circle.circleCount);
console.log(c1.area());

let c2 = new Circle(20);
console.log(Circle.circleCount);
console.log(c2.area());

标签: javascriptecmascript-6

解决方案


我更喜欢将构造函数的原型对象用于将与实例共享的函数,而不是在每个实例上定义一个新函数。这可以节省内存,因为您只有一个函数实例,而不是为您创建的每个实例创建一个新副本。

您也可以circleCount在原型上定义,因为所有实例都需要相同的编号。您只需要稍微小心地更改它,以确保您不会在每个实例上创建阴影属性。然后每个实例可以直接通过原型链提供计数。

这样做会使函数复杂化,但会简化其余代码:

function Circle(_radius) {
  this.radius = _radius;
  // creating an instance increments the count for everyone
  Circle.prototype.circleCount++ // not this.circleCount++ which will create a new property on instance
}
Circle.prototype.draw = function draw(circle, canvas) {
  /* Canvas drawing code */
};
Circle.prototype.area = function() {
  return Math.pow(this.radius, 2) * Math.PI;
}
Circle.prototype.circleCount = 0


let c1 = new Circle(10);
console.log(c1.circleCount);
console.log(c1.area());

let c2 = new Circle(20);
console.log(c2.circleCount);
console.log(c2.area());

另外,关于Object.defineProperty. 看起来他正在使用它,因此他可以设置 getter 和 setter 函数,而不仅仅是返回属性。你可以这样做area

Object.defineProperty(Circle.prototype, "area", {
    get: function() {
       return Math.pow(this.radius, 2) * Math.PI;
    }
});

这将允许您访问 area ,就好像 ti 是每个实例的属性一样:

c2.area // instead of a function c2.area()

您可以直接将区域设置为属性,但是如果您更改半径,您还需要更改区域。我想哪个最好取决于半径是否会改变。


推荐阅读