继承的概念

2018-07-30  本文已影响0人  麓语

上一篇:原型的基本概念
下一篇:Object 对象

继承的概念

在开发过程中, ⾯向对象是⼀种处理代码的思考⽅式. 在⾯向对象中继承就是其中⼀个⾮常重要的概念. 接下来本节详细讨论继承的概念.

为何需要使⽤继承

在开发中, 经常会发现多个不同类型的对象都有共同的⽅法. 例如代码:

var o1 = new Number(144);
console.log("打印 Number 对象 o1 = " + o1);
var o2 = new Date();
console.log("打印 Date 对象 o2 = " + o2);
var o3 = new Array(1, 4, 4);
console.log("打印 Array 对象 o3 = " + o3);
var o4 = new Error("a test");
console.log("打印 Error 对象 o4 = " + o4);
function MyConstructor() {
}
var o5 = new MyConstructor();
console.log("打印⾃定义对象 MyConstructor o5 = " + o5);

在本例中, 打印对象都有⼀个共同的操作, 即 "和字符串相连接". 该操作在执⾏的时候, 会⾃动的调⽤⼀个⽅法, 即 toSting() ⽅法. 因此, 执⾏代码

console.log("打印⾃定义对象 MyConstructor o5 = " + o5);

等价于执⾏代码:

console.log("打印⾃定义对象 MyConstructor o5 = " + o5.toString());

那么问题来了, 这个类型中没有没有定义 toString() ⽅法呢? 很显然是没有
的. 那么如何实现?

其实很简单, 就是为了 "复⽤".

在开发中常常会有重复的操作, 例如⻚⾯上很多东⻄都可以点击; ⼀个游戏中,常常会有⾓⾊可以⾛动等等. 因此将重复执⾏的代码提取出来, 不⽤再编写代码的时候每次都要将其再写⼀遍. 那么这种拿来主义就是继承.

在继承中, ⼀个对象继承⾃另⼀个对象. 继承的对象中包含被继承对象的所有成员.

例如⼈会说话, 那么将说话的功能提取出来作为⼀个对象. 继承⾃该对象的美国⼈, ⽇本⼈, 或是英国⼈就都具有说话的⽅法了.


继承图1.png

因此⼀句话总结继承, 就是 "为了偷懒, 就拿来主义".

继承的实现⽅式

继承的实现⽅式有很多种, 如今主流的继承有类继承和原型继承. 类继承的语⾔有: C++, Java, C# 等, ⽽原型继承有: Self, Io, JavaScript 等.

C# 中基于类的继承

在基于类继承的编程语⾔中, 有⼀个对象的模板, 称之为类. 需要对象则⾸先设计⼀个类, 由类来创建对象. ⽽继承是指类之间的继承.

例如写⼀个⽗类

class BaseClass {
  public void SayHello() {
    System.Console.WriteLine("你好");
  }
}

然后提供⼀个⼦类

class SubClass : BaseClass {
}

最后直接创建⼦类独享, 即可调⽤⽅法

public static void Main(string[] args) {
  SubClass o = new SubClass();
  o.SayHello(); // => 你好
}

这⾥的继承实际上是利⽤模板来实现的. 在模板 BaseClass中定义了⼀个⽅法 SayHello , 然后设计⼀个⼦类 SubClass 继承⾃ BaseClass , 在⼦类中没有规定任何东⻄. 但是由⼦类创建出来的对象具有 SayHello ⽅法, 并且可以调⽤.

这个就是利⽤类的继承. 类继承了, 那么由该类创建出来的对象就具有被继承的成员.

原型继承

与类继承不同, 在 JavaScript 中, 使⽤原型来实现继承. 在原型中定义的所有成员都会被其构造⽅法创建的对象所继承. 在 JavaScript 中不存在类的概念,因此实现继承的⽅式也不再唯⼀和统⼀.

还是说话的例⼦, 使⽤ JavaScript 来实现

// 定义⼀个对象, 将来作为原型对象
var proto = {
  sayHello : function() {
  console.log("你好!!!");
}};
// 定义⼀个构造函数
function Person() {
}
// 设置 Person 的原型
Person.prototype = proto;
// 创建对象, 具有 sayHello ⽅法
var p = new Person();
p.sayHello();

在本例中没有类的概念, 继承也不是模板之间的继承. ⽽是给构造⽅法设置⼀个原型对象, 由该构造函数创建出来的对象就具有该原型对象中的所有内容.我们称这个对象就继承⾃原型对象.

注意: 前⾯曾经介绍过 __proto__ 的概念, 因此实现继承的⽅法也就不统⼀了, ⽐较随意.

值得说明的是, 所有由该类创建出来的对象, 都具有了原型中定义的属性 (⽅法). 与定义和设置的顺序⽆关. 但是如果重新设置属性就不正确了.

例如, 下⾯的代码可以正常执⾏.

function Person() {
}
var p1 = new Person();
Person.prototype.sayHello = function() {
  console.log("hello, 你好 JavaScript!");
};
var p2 = new Person();
p1.sayHello(); // => hello, 你好 JavaScript!
p2.sayHello(); // => hello, 你好 JavaScript!

上⾯的代码, 给 Person 的原型添加了⼀个 sayHello ⽅法, 因此两个Person 对象都可以调⽤该⽅法.

如果是直接重新设置构造函数 Person 的原型对象, 那么就会报⼀个TypeError 异常.

function Person() {
}
var p1 = new Person();
Person.prototype = { sayHello : function() {
  console.log("hello, 你好 JavaScript!");
}};
var p2 = new Person();
p1.sayHello(); // => 异常
p2.sayHello();

原因很简单, 这个原型赋值修改了构造函数 Person 的原型对象类型.

function Person() { }
var p1 = new Person();
Person.prototype = { sayHello : function() {
  console.log("hello, 你好 JavaScript!");
}};
var p2 = new Person();
console.log(p1.constructor); // => function Person() { }
console.log(p2.constructor); // => function Object() { [native code]

可⻅修改后, 原型不再是 Person 类型的了, ⽽是 Object 类型.

Object.prototype

JavaScript 中, 每⼀个对象都有原型. ⽽且每⼀个原型都直接, 或间接的继承⾃ Object.prototype 对象.

function Person() {}

定义了⼀个构造函数, 那么他就有⼀个 Object 类型的原型对象被设置给了Person.prototype

console.log(Person.prototype);
console.log(Person.prototype instanceof Object); // => true
// note: 可⻅ Person.prototype 是 Object 类型的

如此, 由 Person 创建出来的对象就继承⾃ Object 类型的对象. ⽽Person.prototype 也是⼀个对象. ⾃然也有⼀个原型对象.

在 Chrome 中可以使⽤ __proto__ 引⽤. 因此可以获得 Person.prototype的原型对象

console.log(Person.prototype.__proto__); // => Object {}

那么从逻辑上就有下⾯的继承模型:

继承图2.png
需要注意的是, Object 类型的原型对象的原型对象为 null
console.log(Person.prototype.__proto__.__proto__ === null);
// => true

⼀种继承的实现

有了上⾯的分析, 实现继承就可以分为三个步骤:

简单的实现为:

// 被继承对象
var pareObj = {
  sayHello: function() {
    console.log("Hello, jk");
  }
};
// 创建构造函数
function F() {
}
// 设置原型对象
F.prototype = pareObj;
// 创建继承对象
var obj = new F();

但是这么写太过于繁琐, 因此⼤师 Douglas Crockford 在他的 《JavaScript: The Good Parts》中给出了下⾯的做法:

if (!Object.create) { // ECMAScript 5 中已经⽀持该⽅法
  Object.prototype.create = function (pare) {
  function F() {}
    F.prototype = pare;
    return new F();
  }
}

上一篇:原型的基本概念
下一篇:Object 对象

⼩结

上一篇 下一篇

猜你喜欢

热点阅读