一、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、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是弱类型语言)。
function Employee () {
this.name = "";
this.dept = "general";
}
public class Employee {
public String name = "";
public String dept = "general";
}
二、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 = "";
}
三、使用这些定义,你可以创建这些对象的实例,以获取其属性的默认值。