为什么我们需要在JavaScript中定义构造函数

为什么我们需要在JavaScript中定义构造函数,javascript,constructor,Javascript,Constructor,我正在读这段代码 // Shape - superclass function Shape() { this.x = 0; this.y = 0; } // superclass method Shape.prototype.move = function(x, y) { this.x += x; this.y += y; console.info('Shape moved.'); }; // Rectangle - subclass function Rectangle

我正在读这段代码

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

// superclass method
Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  console.info('Shape moved.');
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); // call super constructor.
}

// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

rect instanceof Rectangle; // true
rect instanceof Shape; // true

rect.move(1, 1); // Outputs, 'Shape moved.'
我被这个片段弄糊涂了

Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
为什么我们不使用Rectangle.prototype=Shape.prototype,这是
Object.create()
所做的任何特殊功能?
如果Rectangle.prototype.constructor=Rectangle怎么办;未调用?

如果执行
Rectangle.prototype=Shape.prototype
操作,则对
Rectangle.prototype
执行的任何修改都将反映在
Shape.prototype
上,因为它们是相同的对象

为了避免这种情况,我们使用
object.create(Shape.prototype)
创建一个新对象,该对象的prototype链接指向
Shape.prototype

至于
Rectangle.prototype.constructor=Rectangle
,这是为了确保
newshape().constructor
指向
Rectangle
,而不是
Shape

函数的本机
原型
具有指向该函数的
构造函数
属性

e、 g


现在,当我们执行
Rectangle.prototype=…
时,我们会破坏该引用,然后需要修复它。

如果您将
Rectangle.prototype
设置为
Shape.prototype
,那么
Rectangle
Shape
将与prototype共享同一个对象。这意味着,您添加到
矩形中的所有内容。prototype
也将在
形状上可用。prototype
矩形
将不再从
形状
继承

Object.create(parent)
创建从传入的父对象继承的子对象。这样,父对象的所有属性也可用于子对象,而子对象的属性不影响父对象

要回答您的问题(“为什么我们需要在JavaScript中定义构造函数?”),我们不需要这样做。也可以只使用普通对象:

// Shape - superclass
function Shape() {
  return {
    x: 0, 
    y: 0,
    // superclass method
    move: function(x, y) {
      this.x += x;
      this.y += y;
      console.info('Shape moved.');
    }
  };
}

// Rectangle - subclass
function Rectangle() {
  var rectangle = Shape();
  // rectangle-specific things could be added here
  return rectangle;
}

var rect = Rectangle();

rect instanceof Rectangle; // false, instanceof does not work here
rect instanceof Shape; // false

rect.move(1, 1); // Outputs, 'Shape moved.'

您的非构造函数示例并不是那么纯粹,而且具有误导性。下面是您应该做的
var shape={…};var rectangle=Object.create(shape)//添加矩形stuff var r1=Object.create(矩形)请原谅我写了一些不那么纯粹的例子。我只是想证明,在JavaScript中,我们并没有绑定到“类”继承。纯洁不是我最关心的。不过,我不同意这个例子有误导性
Shape()
在每次调用时返回一个新的形状。使用
Object.create()
从中继承不会获得任何结果。直接扩展那个对象是完全可能的,也是安全的。好吧,这是一种误导,因为有一种广泛的命名约定,规定大写函数应该是构造函数,但即使你解决了这个问题,你的代码在内存方面仍然是低效的,因为你没有通过原型共享任何东西。它的执行效率也很低,例如,每次创建矩形实例时,都必须对新创建的形状对象进行变异。所有这些都可以通过使用
对象来解决。像我建议的那样创建
,这是避免构造函数的标准方法。您可以通过不为每个对象创建新方法来共享方法,而只是通过引用来分配方法。我这样做并不是为了让这个例子简单。这样,我们就可以通过不分配原型来节省内存。变异:在新创建的实例上调用超级构造函数也会变异它们。每次代码都会经历相同的变化,JIT为此进行了优化。顺便说一句,在这里使用
Object.create()
不会节省任何内存,因为示例没有使用原型继承
Object.create()
对我的示例没有任何好处。我没有提到构造函数/初始化的变异,这是不可避免的。我想说的是,在你的实现中,每一个新的形状都必须变异成一个矩形,这是不必要的。是的,
Object.create
将节省内存,因为不必为每个实例复制默认值和函数。只需尝试这两种方法并进行分析,在多个实例中这将是显而易见的。最后,一些引擎如V8正在优化构造函数中设置的expando属性。您好,感谢您的回复。对我总是对变量在什么情况下是引用(如本例中的矩形.prototype)以及何时是副本感到困惑。你对此有什么见解,或者你能给我指出一些好文章吗?@Blake对象是被引用的,但原始值是被复制的(没有选择,因为它们是不可变的)。
为什么我们不使用Rectangle.prototype=Shape.prototype
矩形是一个形状,但不是每个形状都是矩形。更改矩形.prototype将更改形状.prototype。若有人想使用构造函数(以及在Chrome中登录控制台),则会修复该构造函数的一致性。有关构造函数、原型、继承、混合插件等的更多信息,请参见:
// Shape - superclass
function Shape() {
  return {
    x: 0, 
    y: 0,
    // superclass method
    move: function(x, y) {
      this.x += x;
      this.y += y;
      console.info('Shape moved.');
    }
  };
}

// Rectangle - subclass
function Rectangle() {
  var rectangle = Shape();
  // rectangle-specific things could be added here
  return rectangle;
}

var rect = Rectangle();

rect instanceof Rectangle; // false, instanceof does not work here
rect instanceof Shape; // false

rect.move(1, 1); // Outputs, 'Shape moved.'