因为上篇文章Prototypal Inheritance没有对一些基本概念作出阐述,所以加入这篇文章作为补充。
许多人有着传统的面向对象语言(如C++,Java)的背景,当他们使用JavaScript时,JavaScript中的一些区域往往会使他们感到困惑。因为JavaScript的工作方式不同于传统的面向对象语言。
在这篇文章中,我想阐述JavaScript的面向对象的实现,特别是JavaScript是如何实现继承的。
这篇文章假设读者都是熟悉JavaScript的。虽然这里阐述的概念普遍适用于所有的‘原型语言(如js)’,但是本文的例子只使用JavaScript来解释。
大多数读者都熟悉传统的继承(如C++,Java,C#中的继承)。但是当他们尝试理解JavaScript中的继承(原型继承)时,传统的继承可能会造成一定程度的困扰。JavaScript不是唯一的使用原型继承的语言。其它的诸如Self
, Lua
, NewtonScript
也都是原型继承。
在传统的面向对象语言中,你可以依据抽象概念创建各种类
。我们可以给这些类
添加属性和行为。这些类
在我们的代码中被初始化并执行各种各样的动作和任务。例如,你可以定义一个Order
类,并给Order
类添加一个行为。在你的应用中,当需要的时候,你会创建这些Order
类的实例,并调用这些实例的行为。
Objects
在JavaScript中是个重要的原始类型。JavaScript也是基于Objects
构建起来的。但是传统的面向对象语言是基于classes
构建的。因此,在JavaScript中 你可以像下面那样创建一个object
的实例。
myObject = new Object();
你也可以使用下面的语法,创建同样的对象
var myObject = {};
该语法使用了object literal
语法。在JavaScript中,一个object基本上是键值对的集合。像下面的例子一样:
myObject = {"Firstname": "Fred", "LastName": "Smith" };
从这个object literal
概念,可以看出:这是JSON格式的来源。JSON是JavaScript中的不携带方法的对象。
因为JavaScript也是一个函数语言(函数是一等公民), 那么你也可以像下面的例子那样,使用函数创建对象:
function myObject(){}
var myObject = new myObject();
在JavaScript中你可以使用这些方法创建对象,让我们接着讨论继承
。
到目前为止,一切都相当地直接。但是,这并不是JavaScript的工作方式。JavaScript是没有类
的,面向对象的语言。在JavaScript中没有类
的概念。虽然某些文本可能揭示了,JavaScript有能力实现类继承,但是事实上并不是这样。这不过是语法甜点,给了传统的面向对象开发者‘在JavaScript中他们能实现类继承
的错觉’。最重要的是“在JavaScript中的所有的继承 都是使用原型继承
实现的”。这是因为:
类
的(所有传统的面向对象语言都依赖类
这个基本概念)。在JavaScript中只是模拟了传统的继承。
在JavaScript中,所有的对象都包含一个内部的object property
,被称之为prototype
。
Object.prototype
current object
是依据prototype object
构建的(current object
的原型是prototype object
)。该prototype object
也包含一个prototype object
, 并且前者是在后者的基础上构建的。沿该链条(也叫原型链)一直向上走的话,最终我们可以找到root Object
。此时,我们不能继续向前了,因为我们到达了原型链的顶端。
让我们以对象o
开始。
var o;
我们的对象o
包含一个叫做prototype(即prototype对象)
的属性。对象o
就是基于该prototype对象
创建的。
var o = new Object()
console.log("typeof Object: " + typeof o); //outputs object
var p = o.prototype;
console.log("typeof o.prototype: " + typeof p); //outputs undefined
我们可以反复做这个操作,直到我们到达原型链的顶端。
JavaScript中 继承的实现 是靠2个重要的概念:
property/method
,那么它会查找对象的原型
。如果在原型
中找不到,它会查找原型的原型
。它会沿着原型链查找每个对象的原型,直到找到指定的property/method
,或者 到达原型链的顶端。prototype object
可以被不同的对象共享。不同的对象可以共享同一个prototype object
,因此这些不同的对象 共享 同样的方法和属性。既然我们了解了‘在JavaScript中继承是如何被实现的’这个潜在的概念,让我们看一个简单的例子。这里我们定义了一个对象Vehicle
。接着我们又给对象Vehicle
添加了一个属性(叫name)和一个方法(该方法返回name属性)。
//使用函数, 定义和实例化了一个对象`Vehicle`
function Vehicle() {}
var vehicle = new Vehicle();
//为对象`Vehicle`添加属性。我们既可以把它们作为实例的methods/properties添加进来,也可以把它们添加到Vehicle的prototype,以便所有的Vehicle都能继承到这些属性和方法
//添加一个`instance property`
function Vehicle(name) {
// 在`类`的每个实例上,`Instance properties`都可以被设置。
this.name = name;
}
//为原型添加属性,确保了所有的实例都共享该属性
Vehicle.prototype.Name = function() {
console.log("My name is " + this.name);
};
var vehicle = new Vehicle('Vehicle');
vehicle.Name(); // My name is Vehicle
到目前为止,一切顺利。让我们通过创建一个Car class
来扩展Vehicle object
function Car(name){
Vehicle.call(this, name);
}
Car.prototype = new Vehicle();
var car = new Car("Car");
car.Name();
我们所做的是 把Vehicle object
赋值给Car的prototype
。这允许Car object
从Vehicle继承所有的方法和属性。我们使用JavaScript中的方法call()
来实现这个目的。Vehicle.call(this, name)
所做的是 “允许我们在调用函数时,指定该函数的执行上下文”。因此,这就是我们为什么传递this
的原因。我们知道“Car已经从Vehicle继承了所有的属性和方法”,因为 我们调用了 只在Vehicle object
上定义的Name()方法。
这种继承方式 对于习惯了传统面向对象的开发者来说:可能是更难理解的。我也是花了一段时间才完全‘掌握究竟发生了什么’。如果你使用JavaScript的话,那么 了解JavaScript是如何实现继承的 就很重要了。
本文翻译自https://www.codeproject.com/Articles/1007871/Prototypal-Inheritance-with-Javascript
转载请注明出处