在JS中,原型链有时候让人觉得很胡里花哨,又是prototype__proto__又是各种指向什么的,让人觉得很头疼。如果你也有这种感觉,或许这篇文章可以帮助到你

一、认识原型

1、先来一串代码

var Person = function(msg){this.msg = msg;
}
var person1 = new Person("wanger")person1.constructor===Person;    //true
Person === Person.prototype.constructor; //true
person1.__proto__ === Person.prototype; //true
person1.__proto__.constructor === person1.constructor //true

看晕了吧?是不是很胡里花哨?不用担心,其实一张图就能了明白这其中的关系:

Alt text

  • 蓝色的是构造函数
  • 绿色的是构造函数实例出来的对象
  • 橙色的是构造函数的prototype,也是构造函数实例出来的对象的原型(它其实也是一个对象)

2、这里特别要注意的是prototype__proto__的区别,prototype是函数才有的属性,而__proto__是每个对象都有的属性。(__proto__不是一个规范属性,只是部分浏览器实现了此属性,对应的标准属性是[[Prototype]])。

二、认识原型链

1、我们刚刚了解了原型,那原型链在哪儿呢?不要着急,再上一张图:

Alt text

通过这张图我们可以了解到,person1的原型链是:

person1 ----> Person.prototype ----> Object.prototype ----> null

2、事实上,函数也是一个对象,所以,Person的原型链是:

Person ----> Function.prototype ----> Object.prototype ----> null

由于Function.prototype定义了apply()等方法,因此,Person就可以调用apply()方法。

3、如果把原型链的关系都显示清楚,那会复杂一些,如下图:

Alt text

这里需要特别注意的是:所有函数的原型都是Function.prototype,包括Function构造函数和Object构造函数(如图中的标红部分)

三、原型链的继承

1、假设我们要基于Person扩展出Student,Student的构造如下:

function Student(props) {// 调用Person构造函数,绑定this变量:Person.call(this, props);this.grade = props.grade || 1;
}

但是,调用了Person构造函数不等于继承了PersonStudent创建的对象的原型是:

new Student() ----> Student.prototype ----> Object.prototype ----> null

示意图如下所示:

Alt text

必须想办法把原型链修改为:

new Student() ----> Student.prototype ----> Person.prototype ----> Object.prototype ----> null

示意图如下所示:

Alt text

那我们应该怎么修改呢?仔细观察两张图的差异,我们会发现,如果我们将Studentprototype改成person1对象不就大功告成了?于是有了下面的代码:

Student.prototype = person1 ;

但是这时候有个问题:

Student.prototype.constructor === Student; //false

原来Student.prototype(即person1)的constructor指向的还是Person,这时候还需要我们再改一下代码:

Student.prototype.constructor = Student;

这样就能把Student的原型链顺利的修改为: new Student() ----> Student.prototype ----> Person.prototype ----> Object.prototype ----> null 了;

完整的代码显示如下:

var Person = function(msg){this.msg = msg;
}
var Student = function(props) {// 调用Person构造函数,绑定this变量:Person.call(this, props);this.grade = props.grade || 1;
}
var person1 = new Person("wanger")
Student.prototype = person1 ;
Student.prototype.constructor = Student;

三、用以上原型链继承带来的问题

1、如果在控制台执行一遍上述的代码,我们会发现一些问题,如图所示:

Alt text

Student.prototype上含有之前person1带有的属性,那么,这样的继承的方法就显得不那么完美了

2、这个时候,我们可以借助一个中间对象来实现正确的原型链,这个中间对象的原型要指向Person.prototype。为了实现这一点,参考道爷(就是发明JSON的那个道格拉斯)的代码,中间对象可以用一个空函数F来实现:

var Person = function(msg){this.msg = msg;
}
var Student = function(props) {// 调用Person构造函数,绑定this变量:Person.call(this, props);this.grade = props.grade || 1;
}// 空函数F:
function F() {
}// 把F的原型指向Person.prototype:
F.prototype = Person.prototype;// 把Student的原型指向一个新的F对象,F对象的原型正好指向Person.prototype:
Student.prototype = new F();// 把Student原型的构造函数修复为Student:
Student.prototype.constructor = Student;// 继续在Student原型(就是new F()对象)上定义方法:
Student.prototype.getGrade = function () {return this.grade;
};// 创建wanger:
var wanger = new Student({name: '王二',grade: 9
});
wanger.msg; // '王二'
wanger.grade; // 9// 验证原型:
wanger.__proto__ === Student.prototype; // true
wanger.__proto__.__proto__ === Person.prototype; // true// 验证继承关系:
wanger instanceof Student; // true
wanger instanceof Person; // true

这其中主要用到了一个空函数F作为过桥函数。为什么道爷会用过桥函数?用过桥函数F(){}主要是为了清空构造的属性。如果有些原Person的构造用不到,那么过桥函数将是一个好的解决方案

这样写的话,Student.prototype上就没有任何自带的私有属性,这是理想的继承的方法

3、如果把继承这个动作用一个inherits()函数封装起来,还可以隐藏F的定义,并简化代码:

function inherits(Child, Parent) {var F = function () {};F.prototype = Parent.prototype;Child.prototype = new F();Child.prototype.constructor = Child;
}

封装后,写起来就像这样:

var Person = function(msg){this.msg = msg;
}
var Student = function(props) {// 调用Person构造函数,绑定this变量:Person.call(this, props);this.grade = props.grade || 1;
}
inherits(Student,Person) ;

这样再一封装的话,代码就很完美了。

事实上,我们也可以在inherits中使用Object.create()来进行操作,代码如下:

function inherits(Child, Parent) {Child.prototype = Object.create(Parent.prototype);Child.prototype.constructor = Child;
}

如果有兴趣了解Object.create()的其他用法,可以参考我的这篇博客JS中Object.create的使用方法;

四、ES6的新关键字class

在ES6中,新的关键字class,extends被正式被引入,它采用的类似java的继承写法,写起来就像这样:

class Student extends Person {constructor(name, grade) {super(msg); // 记得用super调用父类的构造方法!this.grade = grade || 1;}myGrade() {alert('I am at grade ' + this.grade);}
}

这样写的话会更通俗易懂,继承也相当方便。读者可以进入廖雪峰的官方网站详细了解class的用法

参考文献:廖雪峰的官方网站
原文地址:王玉略的个人网站

说说JavaScript的原型链相关推荐

  1. Javascript的原型链图

    90%的前端或者js程序员或者老师们对Javascript懂得不比这个多 给手机看的 但是这个图里的所有褐色单向箭头链就是Javascript的原型链(颜色标注对理解js原型链很关键) 这图中的各个_ ...

  2. Javascript的原型链、instanceof与typeof

    为什么80%的码农都做不了架构师?>>>    在Javascript里,一切对象(Object和Function)都有内部的属性_proto_,但只Object.prototype ...

  3. Javascript的原型链

    Javascript基于 '__proto__' 的原型链 图片说明 1.总共三类对象(蓝色大框) 2.实例对象(通过new XX() 所得到的实例),跟原型链相关的只有 __proto__ 属性,指 ...

  4. JavaScript:原型链、继承

    1.理解原型对象 我们先使用构造函数创建一个对象: function Person() { } var person = new Person(); person.name = 'Kevin'; co ...

  5. JavaScript完整原型链图解

    了解完整原型链对我们有什么帮助? 没什么帮助,装逼意义大于实用(doge) 好的吧,可能是我还是新手,没碰上这方面的需求 完整原型链图示 上图里,我们常用的部分是右下至右上的链,即从构造函数到构造函数 ...

  6. 前端学习(1698):前端系列javascript之原型链和instance

  7. JavaScript进阶学习(二)—— 基于原型链继承的js工具库的实现方法

    文章来源:小青年原创 发布时间:2016-07-03 关键词:JavaScript,原型链,jQuery类库 转载需标注本文原始地址: http://zhaomenghuan.github.io... ...

  8. JavaScript 原型对象和原型链

    开篇 之前对js中的原型链和原型对象有所了解,每当别人问我什么是原型链和原型对象时,我总是用很官方(其实自己不懂)的解释去描述.有一句话说的好:如果你不能把一个很复杂的东西用最简单的话语描述出来,那就 ...

  9. JavaScript 原型 原型链

    JavaScript 原型 本文讲介绍JavaScript原型 以及JavaScript的原型链 JS中的原型和原型链 所有的引用类型(数组.函数.对象)可以自由扩展属性(除null以外) 所有的函数 ...

最新文章

  1. jsp中九大内置对象
  2. 价值为王,市场需要降温
  3. 利用css布局效果图
  4. JS字符串转换为JSON的四种方法笔记
  5. 仅用语音,AI 就能“脑补”你的脸! | 技术头条
  6. 2021青海省普通高考成绩查询,青海省教育考试网:2021年青海高考成绩查询入口、查分系统...
  7. AWT_Swing_单选框(Java)
  8. python登录界面代码_超酷 Python 程序包 ,一行代码实现 GUI 界面
  9. 毕业论文 一级标题段前段后问题
  10. iterm2 + oh my zsh 实现 macOS X 下炫酷终端
  11. “由于这台计算机没有远程桌面客户端访问许可证,远程会话被中断“的解决方案
  12. cocos2d-x小游戏——飞机大战
  13. BUUCTF 每天10道Misc Day4
  14. 人工智能发展如何,未来有哪些就业方向?
  15. Liferay开发学习(1)
  16. SpringBoot+Vue+ElementUI实现后台管理系统
  17. 扫盲贴:手游圈行业术语汇总
  18. 201671030125 曾佳 + 实验三作业互评与改进报告
  19. java注解和反射——狂神说java学习笔记三
  20. 关于深度学习在生物学领域的应用分析Applications of Deep Learning in Biomedicine

热门文章

  1. 接口压力测试:Siege压测安装、使用和说明
  2. java计算机毕业设计在线毕设选题系统源码+系统+mysql数据库+lw文档
  3. Linux-USB学习 -- USB枚举过程
  4. linux 怎么查看系统用户,Linux系统下查看用户的常用方法
  5. 印章与印鉴的区别_篆刻与印章到底有什么区别?
  6. aecc2019能装saber吗_【2018年10月重磅】After Effects CC2019 分享新功能介绍
  7. ArcGIS如何获取地理要素的几何属性
  8. MPB:青岛大学苏晓泉组分享基于分类学和系统发育的宏基因组比较DMS算法
  9. 更新xcode至12.3,编译报错Building for iOS, but the linked and embedded framework ‘xxx.framework’ was buil...
  10. python 离群值 q1 q3_设第一分位数是Q1,第二分位数是Q2,第三分位数是Q3,那么四分位差(又称内距)则用 _________表示。_学小易找答案...