在JavaScript中,所有的东西都是对象,但是JavaScript中的面向对象并不是面向类,而是面向原型的,这是与C++、Java等面向对象语言的区别,比较容易混淆,因此把我自己学习的过程记录下来。

首先说,原型链有什么用?在Java中,继承都是基于类的,在JavaScript中继承都是基于原型链的。也就是说在JavaScript中,原型链是实现继承的基础,想要掌握好JavaScript中的面向对象编程,必须对原型链有一定的了解。

要理解原型链,必须先了解两个对象,一个是 prototype ,另一个是 __proto__ 。当前只需要记住名字,下面会仔细说明。

首先是 prototype : prototype ,或者叫原型对象,是函数特有的一个属性,其类型是 Object ,因此也常常被称作函数的原型对象。虽然每个函数都拥有自己的原型对象,但只有用作构造函数时,这个属性才会发挥作用,关于构造函数的知识这里不说。原型对象其实很简单,他就是一个普通的 Object ,当其作为构造函数时默认有一个 constructor 。

 // 举个简单的例子
function fn() { } console.log(fn.prototype); // {}

我们可以给原型对象添加一些方法或属性,就可以被其子类继承:

 // 后面会讲怎么继承
function fn() { } // 添加一个方法
fn.prototype.sayHello = function() {
alert('hello');
}; // 添加一个属性
fn.prototype.name = 'my_fn';

上面就完成了对原型对象的介绍,接下来是 __proto__ ,这是一个所有对象都拥有的属性。其实 __proto__ 与原型对象密不可分,因为一个对象的 __proto__ 就是指向其构造函数的原型对象。需要注意的是 __proto__ 并不是JavaScript的规范,只是大多数浏览器都实现了,从ECMAScript 6开始,应该用Object.getPrototypeOf()和Object.setPrototypeOf()来访问这个属性。看一个例子:

 function fn() { }

 var f = new fn();
console.log(f.__proto__ === fn.prototype); // true

从上面的代码,应该就可以明白这二者的关系了,如果能理解这一点,接下来就可以开始分析继承的实现原理了。

开头说过,继承是基于原型链实现的,那么什么是原型链呢?首先我们看几个例子:

 function fn() { }

 // 首先记住,一个对象的__proto__指向它的构造函数的原型对象
var f = new fn();
console.log(f instanceof Object); // true,说明此时f是一个Object
console.log(f.__proto__ === fn.prototype); // true,没毛病,因为f的构造函数就是fn
var obj = fn.prototype; // 我们看看fn的原型对象是什么类型?肯定是对象!
console.log(obj.__proto__ === Object.prototype); // true,那对象就是Object,它的构造函数就是Object
obj = Object.prototype; // 那Object的原型对象应该也是个对象吧
console.log(obj.__proto__ === null); // true,为什么是null?这是JavaScript设计的,因为如果不是null,就会无限循环

以上的例子说明了f的构造函数的原型,f的构造函数的原型的原型,f的构造函数的原型的原型的原型,用图形表示就是:

f.__proto__ ---> f.__proto__.__proto__ ---> f.__proto__.__proto__.__proto__

那么上面这条“链”就是我们所说的原型链了!这个过程理解了可以继续往下。

那么原型链是如何实现继承的?在JavaScript中,你对一个对象调用一个方法或者获取一个属性,它就会自动的在原型链上面寻找,一直到找到或者原型对象为null。

 // 再举个例子
function fn() {} var f = new fn(); // 此时fn并没有方法toString
console.log(f.toString()); // 输出[object Object]
// 为什么?
// 按照刚刚分析的原型链,它会现在fn.prototype中寻找
console.log(fn.prototype); // fn {},没有
// 再在fn.prototype.__proto__(Object.prototype)中寻找
console.log(fn.prototype.__proto__);

上面这个例子说明,f的 toString 方法其实是从 Object.prototype 继承而来的。

如果上面这些都能明白,那我们就可以自己实现继承了。

 // 回到前面的例子
function SuperClass() {
this.time = new Date().toLocaleString();
} // // 添加一个方法
SuperClass.prototype.sayHello = function() {
console.log('hello');
}; // // 添加一个属性
SuperClass.prototype.name = 'super'; // 定义一个子类继承SuperClass
function SubClass() {
// 在子类的构造函数中调用父类的构造函数
SuperClass.call(this);
} SubClass.prototype = Object.create(SuperClass.prototype); // 继承父类的属性和方法
// SubClass.prototype = SuperClass.prototype; // 不能直接赋值!因为JavaScript中的对象赋值都是浅复制,有副作用,也就是说修改子类的原型也会修改父类
// SubClass.prototype = new SuperClass(); // 也不要这样做,因为这样会实例化一个SuperClass,假如SuperClass的构造函数中定义了一个很大的对象,就会造成内存浪费!
SubClass.constructor = SubClass; // 上一行代码会把子类的构造函数给覆盖了,这里把它恢复了。注意:constructor会影响instanceof运算符的结果 SubClass.prototype.subfn = function() {
console.log('this is a sub func');
} var sc = new SubClass();
console.log(sc.time); // 2018-12-18 22:02:09
sc.sayHello(); // hello
sc.subfn(); // this is a sub func

上面就是一个简单的继承,要实现多重继承也是类似的:

 // 在上面的代码修改
// 另一个父类
function SuperClassB() {}
SuperClassB.prototype.anotherfn = function() {
console.log('another super');
} // ... // 定义一个子类继承SuperClass
function SubClass() {
// 在子类的构造函数中调用父类的构造函数
SuperClass.call(this);
SuperClassB.call(this); // 添加
} var prototype = Object.create(SuperClass.prototype); // 继承父类的属性和方法
prototype = Object.assign(prototype, SuperClassB.prototype);  // 合并两个父类的原型对象
SubClass.prototype = prototype // 继承父类的属性和方法 // ... sc.anotherfn(); // another super

JavaScript原型链及继承的更多相关文章

  1. JavaScript原型链与继承

    最近学习了<Javascript高级程序设计>面向对象部分,结合书中的例子总结一下原型链和继承部分的内容. 创建对象 在Js当中没有类这个概念,当我们想要创建具有相同属性的对象的时候,有如 ...

  2. JavaScript原型链和继承

    1.概念 JavaScript并不提供一个class的实现,在ES6中提供class关键字,但是这个只是一个语法糖,JavaScript仍然是基于原型的.JavaScript只有一种结构:对象.每个对 ...

  3. [转]深入javascript——原型链和继承

    在上一篇post中,介绍了原型的概念,了解到在javascript中构造函数.原型对象.实例三个好基友之间的关系:每一个构造函数都有一个“守护神”——原型对象,原型对象心里面也存着一个构造函数的“位置 ...

  4. 白话JavaScript原型链和继承

    原型基础 每个函数都有一个prototype属性,指向函数的原型对象 每个对象都一个私有属性 __proto__, 默认指向其构造函数的prototype 在JS中所有函数都是Function构造出来 ...

  5. javascript原型链继承

    一.关于javascript原型的基本概念: prototype属性:每个函数都一个prototype属性,这个属性指向函数的原型对象.原型对象主要用于共享实例中所包含的的属性和方法. constru ...

  6. 《JAVASCRIPT高级程序设计》根植于原型链的继承

    继承是面向对象的语言中,一个最为津津乐道并乐此不疲的话题之一.JAVASCRIPT中的继承,主要是依靠原型链来实现的.上一篇文章介绍过,JAVASCRIPT中,每一个对象都有一个prototype属性 ...

  7. 对Javascript 类、原型链、继承的理解

    一.序言   和其他面向对象的语言(如Java)不同,Javascript语言对类的实现和继承的实现没有标准的定义,而是将这些交给了程序员,让程序员更加灵活地(当然刚开始也更加头疼)去定义类,实现继承 ...

  8. JavaScript中的原型链和继承

    理解原型链 在 JavaScript 的世界中,函数是一等公民. 上面这句话在很多地方都看到过.用我自己的话来理解就是:函数既当爹又当妈."当爹"是因为我们用函数去处理各种&quo ...

  9. 三张图搞懂JavaScript的原型对象与原型链 / js继承,各种继承的优缺点(原型链继承,组合继承,寄生组合继承)

    摘自:https://www.cnblogs.com/shuiyi/p/5305435.html 对于新人来说,JavaScript的原型是一个很让人头疼的事情,一来prototype容易与__pro ...

随机推荐

  1. react 的基础

    首先下载React 的安装包,可以到官网下载.也可以使用React Demos 已经自带 React 源码,不用另外安装,只需把这个库拷贝到硬盘中使用. (可参考http://www.ruanyife ...

  2. Excel中如何截取字符串中指定字符后的部分字符

    1.如何给某列属性为时间整体加一个时间值:      场景一:假如我有一个excel中的某一列如下图所示,如何将该列的时间(用B代替整列)整体加一分钟呢?方法很简单,在空白单元格填写时间格式图中A所示 ...

  3. Linux系统启动过程(通俗易懂)

    前言: Linux是一种自由和开放源代码的类UNIX操作系统.该操作系统的内核由林纳斯·托瓦兹在1991年10月5日首次发布.在加上用户空间的应用程序之后,成为Linux操作系统.Linux是自由软件 ...

  4. EVE-NG简单入门介绍

    此篇文章简单的介绍下模拟器EVE-NG的使用,具体包括Dynamips设备导入与运行,IOL设备的导入与运行,QEMU设备的导入与运行,客户端软件的安装,物理网络与虚拟网络的结合等. 一.导入镜像 D ...

  5. css制作倒三角

    布局div,并命名为id="dropdown",在style使用border属性对div进行控制 #dropdown{ width:0px; height:0px; border- ...

  6. [tour]2019HUST onsite签到

    先定一个小目标,从签到题开始讲清楚 虽然因为我喜欢签到题的气球导致签到题并没有行使责任.. F.Mesh 和某CF题(我找不到了)完 全 一 致,由于某些玄学原因没有get到(orz谢罪) 给出一个6 ...

  7. C# Global.asax.cs 定时任务

    定时执行更新Redis缓存操作 protected void Application_Start(object sender, EventArgs e) { Timer timer = new Tim ...

  8. (一)java异常处理的几个问题

    1.java中两种异常? 答:java中存在两种异常:受检查(checked)异常和不受检查(unchecked)异常.不受检查的异常不需要在方法或者构造函数上声明,就算是方法或是构造函数会发生这样的 ...

  9. code-Behind 技术

    就是代码隐藏,在ASP.NET 中通过ASPX 页面指向CS 文件的方法实现显示逻辑和处理逻辑的分离,这样有助于web 应用程序的创建.比如分工,美工和编程的可以个干各的,不用再像以前asp 那样都代 ...

  10. python-之-深浅拷贝一

    深浅拷贝 一.数据为不可变类型 (str.int.bool) import copy v1 = "abc" v2 = copy.copy(v1) v3 = copy.deepcop ...