4

拥有 Java 背景,当我切换到 Javascript 时,我(懒惰地)试图坚持我对 oop 的了解,即经典继承。我正在开发一个网络应用程序(我制作的),并使用了这种继承。但是,我正在考虑更改我的代码并重写 OOP 部分以进行原型继承(两个原因:我读了很多,它更好,其次,我需要尝试另一种方式以便更好地理解它)。

我的应用程序创建数据可视化(使用 D3js,但这不是主题),并以这种方式组织我的代码:

function SVGBuilder( dataset ) {
  this.data = dataset;
  this.xCoord;
  this.startDisplaying = function() {
    // stuff
    this.displayElement();
  }
}

displayElement()方法在继承自 SVGBuilder(或多或少是一个抽象类)的“类”中定义。然后我有:

function SpiralBuilder( dataset ) {
  SVGBuilder.call( this, dataset );
  this.displayElement = function() {
    // stuff
  };
}
SpiralBuilder.inheritsFrom( SVGBuilder );

我还有其他几个基于相同结构的“建设者”。

调用构建器的脚本看起来像这样(它有点复杂,因为它根据用户输入选择正确的构造器):

var builder = new SpiralBuilder( data );
builder.startDisplaying();

现在来“转换部分”。从Douglas Crockford 的文章到 Eloquent Javascript 的部分内容,我阅读了很多关于此的内容。在Aadit M Shah评论中,他提出了一个如下所示的结构:

var svgBuilder = {
  data: [],
  xCoord: 0,  // ?
  create: function( dataset ) {
    var svgBuilder= Object.create(this);
    svgBuilder.data = dataset;
    return svgBuilder;
  },
  startDisplaying: function() {
    // stuff
  }
}

但是,在这一点上,我被卡住了。首先(技术问题),我可以声明变量(数据,xCoord)而不用这种模式初始化它们吗?就像一个this.data;?其次,我应该如何创建继承?我只是手动把相应的功能放在原型里?就像是:

var spiralBuilder = builder.create( dataset );
spiralBuilder.prototype.displayElements = function() {
  // Code to display the elements of a spiral
};
spiralBuilder.displayElements();

如果我是正确的,这意味着在调用构建器的脚本中,我必须在单个构建器实例的原型中添加/修改方法,而不是选择正确的构造函数(它将不再存在)。事情应该怎么做?

或者我应该尝试以完全不同的方式设计我的代码?如果是这样,你能给我一些建议/参考吗?

4

1 回答 1

1

我可以声明变量(数据,xCoord)而不用这种模式初始化它们吗?

var svgBuilder = {
  //removed data here as it's only going to shadowed
  // on creation, defaults on prototype can be done
  // if value is immutable and it's usually not shadowed later
  create: function( dataset, xcoord ) {
    var svgBuilder= Object.create(this);
    svgBuilder.data = dataset;//instance variable
    svgBuilder.xcoord = xcoord;//instance variable
    return svgBuilder;
  },
  startDisplaying: function() {
    // stuff
  },
  constructor : svgBuilder.create
};

我知道我很少在我的示例中这样做,但是在创建实例或调用函数时,通常最好传递参数对象。

在某个时间点,您可能会在这里或那里更改一些内容,并且您不想更改代码中的许多地方。

在前几个示例中,您根本没有使用原型。每个成员都this.something在构造函数中声明,因此是特定于实例的成员。

可以使用构建器,但是当您可以轻松地声明构造函数、原型、混入和静态时,您所需要的只是一个用于继承和混入的辅助函数。

原型介绍可以在这里找到。它还涉及继承、混合、覆盖、调用 super 和this变量。简介副本如下:

构造函数介绍

您可以将函数用作构造函数来创建对象,如果构造函数名为 Person,则使用该构造函数创建的对象是 Person 的实例。

var Person = function(name){
  this.name = name;
};
Person.prototype.walk=function(){
  this.step().step().step();
};
var bob = new Person("Bob");

Person 是构造函数,因为它是一个对象(就像 JavaScript 中的大多数其他东西一样),您可以为其赋予属性,例如:Person.static="something"这对于与 Person 相关的静态成员很有用,例如:

 Person.HOMETOWN=22;
 var ben = new Person("Ben");
 ben.set(Person.HOMETOWN,"NY");
 ben.get(Person.HOMETOWN);//generic get function what do you thing it'll get for ben?
 ben.get(22);//maybe gets the same thing but difficult to guess

当您使用 Person 创建实例时,您必须使用 new 关键字:

var bob = new Person("Bob");console.log(bob.name);//=Bob
var ben = new Person("Ben");console.log(bob.name);//=Ben

属性/成员name是特定于实例的,对于 bob 和 ben 是不同的

所有实例共享成员walkbob 和 ben 是 Person 的实例,因此它们共享 walk 成员 (bob.walk===ben.walk)。

bob.walk();ben.walk();

因为无法在 bob 上直接找到 walk(),所以 JavaScript 将在 Person.prototype 中查找它,因为这是 bob 的构造函数。如果在那里找不到,它会在 Function.prototype 上查找,因为 Person 的构造函数是 Function。函数的构造函数是 Object,所以它最后会看到 Object.prototype。这称为原型链。

即使 bob、ben 和所有其他创建的 Person 实例共享 walk,该函数在每个实例中的行为也会有所不同,因为它在 walk 函数中使用this. 的值this将是调用对象;现在假设它是当前实例,因此bob.walk()“this”将是 bob。(更多关于“this”和稍后的调用对象)。

如果本正在等待红灯,而鲍勃正在等待绿灯;然后你会在 ben 和 bob 上调用 walk() ,显然 ben 和 bob 会发生一些不同的事情。

当我们执行类似的操作时,会发生跟踪成员ben.walk=22,即使 bob 和 benwalk将22分配给 ben.walk 也不会影响 bob.walk。这是因为该语句将walk直接创建一个名为 ben 的成员并为其分配值 22。将有 2 个不同的 walk 成员:ben.walk 和 Person.prototype.walk。

当要求 bob.walk 时,您将获得 Person.prototype.walk 函数,因为walk在 bob 上找不到。然而,询问 ben.walk 会得到值 22,因为成员 walk 是在 ben 上创建的,并且由于 JavaScript 发现 walk on ben,它不会在 Person.prototype 中查找。

因此,分配一个成员会导致 JavaScript 不在原型链中查找它并为其赋值。相反,它会将值分配给对象实例的现有成员或创建它,然后将值分配给它。

下一部分(更多关于原型)将通过示例代码解释这一点,并演示如何继承。

于 2013-11-13T16:38:46.527 回答