说说JavaScript的原型链
在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
构造函数不等于继承了Person
,Student
创建的对象的原型是:
new Student() ----> Student.prototype ----> Object.prototype ----> null
示意图如下所示:
Alt text
必须想办法把原型链修改为:
new Student() ----> Student.prototype ----> Person.prototype ----> Object.prototype ----> null
示意图如下所示:
Alt text
那我们应该怎么修改呢?仔细观察两张图的差异,我们会发现,如果我们将Student
的prototype
改成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的原型链相关推荐
- Javascript的原型链图
90%的前端或者js程序员或者老师们对Javascript懂得不比这个多 给手机看的 但是这个图里的所有褐色单向箭头链就是Javascript的原型链(颜色标注对理解js原型链很关键) 这图中的各个_ ...
- Javascript的原型链、instanceof与typeof
为什么80%的码农都做不了架构师?>>> 在Javascript里,一切对象(Object和Function)都有内部的属性_proto_,但只Object.prototype ...
- Javascript的原型链
Javascript基于 '__proto__' 的原型链 图片说明 1.总共三类对象(蓝色大框) 2.实例对象(通过new XX() 所得到的实例),跟原型链相关的只有 __proto__ 属性,指 ...
- JavaScript:原型链、继承
1.理解原型对象 我们先使用构造函数创建一个对象: function Person() { } var person = new Person(); person.name = 'Kevin'; co ...
- JavaScript完整原型链图解
了解完整原型链对我们有什么帮助? 没什么帮助,装逼意义大于实用(doge) 好的吧,可能是我还是新手,没碰上这方面的需求 完整原型链图示 上图里,我们常用的部分是右下至右上的链,即从构造函数到构造函数 ...
- 前端学习(1698):前端系列javascript之原型链和instance
- JavaScript进阶学习(二)—— 基于原型链继承的js工具库的实现方法
文章来源:小青年原创 发布时间:2016-07-03 关键词:JavaScript,原型链,jQuery类库 转载需标注本文原始地址: http://zhaomenghuan.github.io... ...
- JavaScript 原型对象和原型链
开篇 之前对js中的原型链和原型对象有所了解,每当别人问我什么是原型链和原型对象时,我总是用很官方(其实自己不懂)的解释去描述.有一句话说的好:如果你不能把一个很复杂的东西用最简单的话语描述出来,那就 ...
- JavaScript 原型 原型链
JavaScript 原型 本文讲介绍JavaScript原型 以及JavaScript的原型链 JS中的原型和原型链 所有的引用类型(数组.函数.对象)可以自由扩展属性(除null以外) 所有的函数 ...
最新文章
- jsp中九大内置对象
- 价值为王,市场需要降温
- 利用css布局效果图
- JS字符串转换为JSON的四种方法笔记
- 仅用语音,AI 就能“脑补”你的脸! | 技术头条
- 2021青海省普通高考成绩查询,青海省教育考试网:2021年青海高考成绩查询入口、查分系统...
- AWT_Swing_单选框(Java)
- python登录界面代码_超酷 Python 程序包 ,一行代码实现 GUI 界面
- 毕业论文 一级标题段前段后问题
- iterm2 + oh my zsh 实现 macOS X 下炫酷终端
- “由于这台计算机没有远程桌面客户端访问许可证,远程会话被中断“的解决方案
- cocos2d-x小游戏——飞机大战
- BUUCTF 每天10道Misc Day4
- 人工智能发展如何,未来有哪些就业方向?
- Liferay开发学习(1)
- SpringBoot+Vue+ElementUI实现后台管理系统
- 扫盲贴:手游圈行业术语汇总
- 201671030125 曾佳 + 实验三作业互评与改进报告
- java注解和反射——狂神说java学习笔记三
- 关于深度学习在生物学领域的应用分析Applications of Deep Learning in Biomedicine
热门文章
- 接口压力测试:Siege压测安装、使用和说明
- java计算机毕业设计在线毕设选题系统源码+系统+mysql数据库+lw文档
- Linux-USB学习 -- USB枚举过程
- linux 怎么查看系统用户,Linux系统下查看用户的常用方法
- 印章与印鉴的区别_篆刻与印章到底有什么区别?
- aecc2019能装saber吗_【2018年10月重磅】After Effects CC2019 分享新功能介绍
- ArcGIS如何获取地理要素的几何属性
- MPB:青岛大学苏晓泉组分享基于分类学和系统发育的宏基因组比较DMS算法
- 更新xcode至12.3,编译报错Building for iOS, but the linked and embedded framework ‘xxx.framework’ was buil...
- python 离群值 q1 q3_设第一分位数是Q1,第二分位数是Q2,第三分位数是Q3,那么四分位差(又称内距)则用 _________表示。_学小易找答案...