代码之家  ›  专栏  ›  技术社区  ›  Magnus

在JavaScript中实现Circle构造函数,无ES6类

  •  0
  • Magnus  · 技术社区  · 7 年前

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

    我在想:

    1. 他为什么要用 defineProperty ?我们不能直接将行为实现到构造函数中吗?例如: Circle.draw = function draw() {..}
    2. 为什么使用 get / set 而不只是让状态处于正常属性: Circle.circleCount ?
    3. 哪些属性应该直接在新实例对象上实现,通过 this 在构造函数中,vs 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());
    1 回复  |  直到 7 年前
        1
  •  1
  •   Mark    7 年前

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

    您还可以定义 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;
        }
    });
    

    这将允许您像访问每个实例上的属性一样访问区域:

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

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