继承的概念

上一篇:原型的基本概念
下一篇: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

⼀种继承的实现

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

    1. 获得构造函数 F
    1. 设置 F 的原型为被继承的对象
    1. F 创建继承对象

简单的实现为:

// 被继承对象
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 对象

⼩结

  • 所谓继承就是拿来主义, 将重复的东⻄进⾏复⽤
  • JavaScript 中继承就是给构造函数的 prototype 设置对象
  • 每⼀个对象都有⼀个原型对象
  • ⼀个经典的继承⽅法
    此系列笔记均参考自 蒋坤 笔记
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和...
    Java小辰阅读 1,291评论 1 1
  • 继承的概念 继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。 继承就是子类继承父类的特征和...
    耳东陈_f1e5阅读 973评论 0 4
  • 很多天没有记录,没有看书没有学习没有早睡早起,日子过得浑浑噩噩的,唯独坚持的就是每天给孩子做早餐以及陪孩子一起读书...
    青鸟_01阅读 142评论 0 0
  • 打着爱的旗号,入侵对方的领域,这样的事情时时刻刻都在生活中发生。 比如"孩子是我生的,她的日记必须给我看。" 比如...
    彩虹色的猪阅读 619评论 0 5
  • 对于感情,我们总会有太多太对的困惑、问题,也总是坚信各种流传已久的爱情理念,希望借此能摆脱目前的困境。但很多时候,...
    一世爱情阅读 591评论 1 1