一、JavaScript是一种基于原型而不是基于类的基于对象(object-based)语言。

基于类VS基于原型的语言

一、基于类的面向对象语言,比如java和c++,是构建在两个不同实体上的:类和实例。
1、一个类(class)定义了某一对象集合所具有的特征性属性(可以将java中的方法和域以及c++中的成员都视作属性)。类是抽象的,而不是其所描述的对象集合中的任何特定的个体。

【示例】Employee类可以用来表示所有雇员的集合。

2、一个实例(instance)是一个类的实例化。实例具有和其父类完全一致的属性,不多也不少。

【示例】Victoria可以是Employee类的一个实例,表示一个特定的雇员个体。

二、基于原型的语言(如JavaScript)并不存在这种区别,它只有对象。
1、基于原型的语言具有所谓原型对象(prototypical object)的概念。
2、原型对象可以作为一个模板,新对象可以从中获得原始的属性。
3、任何对象都可以指定其自身的属性,既可以是创建时,也可以在运行时创建。
4、任何对象都可以作为另一个对象的原型(prototype),从而允许后者共享前者的属性。

差异总结

一、基于类(Java)和基于原型(JavaScript)的对象系统的比较

基于类的(Java) 基于原型的(JavaScript) 示例


定义类
类和实例是不同的事物。 所有对象均为实例。
一、通过类定义(class definition)
来定义类
1、通过构造器(constructor)方法来实例化类。
一、通过构造器函数来定义和创建一组对象。
1、任何JavaScript函数都可以被用作构造函数。
二、ES6中引入了类定义,但实际上是已有的原型继承方式的语法糖而已
通过 new 操作符创建单个对象。 通过 new 操作符创建单个对象。
子类 通过类定义来定义现存类的子类,从而构建对象的层级结构。 指定一个对象作为原型并且与构造函数一起构建对象的层级结构
继承 遵循类链继承属性。
子类将继承父类的全部属性,并可以添加新的属性或者修改继承的属性。
假设Employee类只有name和dept属性,而Manager是Employee的子类并添加了reports属性。这时,Manager类的实例将具有所有三个属性:name、dept和reports。 遵循原型链继承属性。
1、JavaScript通过将构造函数与原型对象相关联的方式来实现继承
定义Employee构造函数,在该构造函数内定义name、dept属性;接下来,定义Manager构造函数,在该构造函数内调用Employee构造函数,并定义reports属性。最后,将一个获得了Employee.prototype(Employee构造函数原型)的新对象赋予manager构造函数,以作为Manager构造函数的原型。之后当你创建新的Manager对象实例时,该实例会从Employee对象继承name、dept属性。
添加和移除属性 类定义指定类的所有实例的所有属性(
在编译时创建类,然后在编译时或者运行时对类的实例进行实例化
)。无法在运行时动态添加属性。
构造器函数或原型指定实例的初始属性集。允许动态地向单个的对象或者整个对象集中添加或移除属性。
1、如果为一个对象中添加了属性,而这个对象又作为其他对象的原型,则以该对象作为原型的所有其他对象也将获得该属性。

示例

一、Employee示例
基于原型的面向对象编程 - 图1
1、Employee 具有 name 属性(默认值为空的字符串)和 dept 属性(默认值为 “general”)。
2、Manager 是 Employee的子类。它添加了 reports 属性(默认值为空的数组,以 Employee 对象数组作为它的值)。
3、WorkerBee 是 Employee的子类。它添加了 projects 属性(默认值为空的数组,以字符串数组作为它的值)。
4、SalesPerson 是 WorkerBee的子类。它添加了 quota 属性(其值默认为 100)。它还重载了 dept 属性值为 “sales”,表明所有的销售人员都属于同一部门。
5、Engineer 基于 WorkerBee。它添加了 machine 属性(其值默认为空字符串)同时重载了 dept 属性值为 “engineering”。

创建层级结构:使用简单的定义,使得继承得以实现

一、Employee的定义
1、java中需要制定每个属性的类型,而在JavaScript中则不需要(因为Java是强类型语言,而JavaScript是弱类型语言)。

  1. function Employee () {
  2. this.name = "";
  3. this.dept = "general";
  4. }
  1. public class Employee {
  2. public String name = "";
  3. public String dept = "general";
  4. }

二、Manager和WorkerBee的定义表示在如何制定继承链中上一层对象时,两者存在不同点。
1、在JavaScript中,会添加一个原型实例作为构造器函数prototype属性的值,然后将该构造函数原型的构造器重载为其自身。这一动作可以在构造函数定义后的任意时刻执行。
2、在Java中,需要在类定义中指定父类,且不能在类定义之外改变父类。

function Manager() {
  Employee.call(this);
  this.reports = [];
}
Manager.prototype = Object.create(Employee.prototype);

function WorkerBee() {
  Employee.call(this);
  this.projects = [];
}
WorkerBee.prototype = Object.create(Employee.prototype);
public class Manager extends Employee {
   public Employee[] reports = new Employee[0];
}



public class WorkerBee extends Employee {
   public String[] projects = new String[0];
}

三、在对Engineer和SalesPerson定义时,创建了集成自WorkerBee的对象,该对象会进而继承自Employee。这些对象会具有在这个链之上的所有对象的属性。另外,它们在定义时,又重载了集成的dept属性值,赋予新的属性值。

function SalesPerson() {
   WorkerBee.call(this);
   this.dept = 'sales';
   this.quota = 100;
}
SalesPerson.prototype = Object.create(WorkerBee.prototype);

function Engineer() {
   WorkerBee.call(this);
   this.dept = 'engineering';
   this.machine = '';
}
Engineer.prototype = Object.create(WorkerBee.prototype);
public class SalesPerson extends WorkerBee {
   public String dept = "sales";
   public double quota = 100.0;
}


public class Engineer extends WorkerBee {
   public String dept = "engineering";
   public String machine = "";
}

三、使用这些定义,你可以创建这些对象的实例,以获取其属性的默认值。