JS原型链 详解及示例代码


Posted in Javascript onSeptember 06, 2016

前言

在 segmentfault 上看到这样一道题目:

var F = function(){};
Object.prototype.a = function(){};
Function.prototype.b = function(){};
var f = new F();

问:f 能取到a,b吗?原理是什么?

乍一看真的有点懵,仔细研究了一下,发现还是对原型理解不透彻,所以总结一篇,填个洞~

Function和Object

在解题之前,先再说说 原型、原型链,以及 Function 和 Object 的关系,这也是本文的重点。

原型

在创建一个函数的时候,会自动为其创建一个原型对象,可以通过函数的prototype属性访问到。

创建一个构造函数的实例对象,该实例对象内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262 第5版中管这个指针叫[[prototype]]。虽然在脚本中没有标准的方式访问[[prototype]],但Firefox、 Safari、 Chrome在每个对象上都支持一个属性 __proto__,用于访问其构造函数的原型对象。

重要的事情再说一遍:
构造函数通过 prototype 属性访问原型对象。
实例对象通过 [[prototype]] 内部属性访问原型对象,浏览器实现了 _proto_ 属性用于实例对象访问原型对象。

var F = function () {};
var f = new F();
// 假设F的原型对象是 p, 则
// F.prototype === p;
// f.__proto__ === p;

再重复一遍。。prototype说的是构造函数和原型对象之间的关系,__proto__说的是实例对象和原型对象之间的关系。

原型链

类 A继承B,B继承C……其实就是A的原型对象中有指针指向B的原型对象,而B的原型对象中有指针指向C的原型对象……注意是原型对象之间的联系,A B C 这三个构造函数之间并没什么关系,所以才称为“原型链”吧~

假设a是A的实例对象,则 a 的原型链为下图中紫色线条所示,橙色线条连接了构造函数和其原型对象。

JS原型链 详解及示例代码

由图可以看出,原型链的末端是Object.prototype.__proto__即null。当查找a的某个属性或方法时,首先查找a自身有没有,没有则沿着原型链一直查找,直到找到或者最后到null返回undefined。

Function 和 Object

Function 和 Object 之间的关系有点绕:

Object 是构造函数,既然是函数,那么就是Function的实例对象;Function是构造函数,但Function.prototype是对象,既然是对象,那么就是Object的实例对象。

一切对象都是Object的实例,一切函数都是Function的实例。

Object是Function的实例,而Function.prototype是Object的实例。

二者的关系如下图所示。

JS原型链 详解及示例代码

可见,Object作为构造函数,它有 prototype 属性指向 Object.prototype , 作为实例对象, 它有 Object.__proto__ 指向Function.prototype。Function是构造函数,它有prototype属性指向Function.prototype,而Function是函数,从而也是Function的实例,所以它有Function.__proto__指向Function.prototype,从而 Function.__proto__ === Function.prototype 为 true。

可在Chrome控制台下进行验证,如图。

JS原型链 详解及示例代码

原题解析

解决原型链问题最好的办法就是画图了,经过前面的分析,这个图画起来应该不成问题,如下~

JS原型链 详解及示例代码

f 的原型链为蓝色线所画,所以 f 可以访问到 a , 不能访问到 b 。

如果不画图,乍一看,可能会觉得f 可以访问到 b,那是可能跟我一样误认为F.prototype指向Function.prototype,但其实F.prototype是对象而不是函数,所以它的原型对象不会是 Function.prototype。

所以,原型链问题一应要画图啊~

原题扩展

在上题中,f 只能访问 a,不能访问 b 。但 F 既可以访问 a ,又可以访问 b。如果把题修改成下面的样子, F.b()的结果是什么呢?为什么呢?可以想一下哦~

var F = function(){};
Object.prototype.a = function(){};
Function.prototype.b = function(){ console.log('F.__proto__') };
F.prototype.b = function (){console.log('F.prototype');};

总结

读到这里,有没有发现函数一个比较特殊的地方?

一般的对象,只有一个__proto__属性用于访问其构造函数的原型对象,而对于函数来说,它既是函数又是对象。

作为函数,它生来就有prototype属性指向其原型对象函数名.prototype。

作为Function的实例对象,它有__proto__属性指向Function.prototype

通常,这两个属性是指向两个对象的,但Function的这两个属性指向相同,都指向Function.prototype。

对于函数 A( ) 来说,A.prototype 中的方法是供其实例对象调用的,自己并不会用;当A 作为实例运行时,调用的是A.__proto__ 中的方法。也就是说,作为构造函数使用时,走的是A.prototype这条链,方法、属性赋给其实例;作为对象使用时,走的是A.__proto__这条链。在不同的场景下,分清它的身份就不会错了。

整篇下来,感觉自己说的也比较絮叨……不足之处,还请各位指正~ 至于题目,真的不知道该叫什么好。。

愿本文能带给坚持看完的你一些收获~ ^_^

谢谢大家对本站的支持,后续继续更新相关资料,帮助大家学习了解这部分知识!

Javascript 相关文章推荐
JQuery 国际象棋棋盘 实现代码
Jun 26 Javascript
用表格输出1-1000之间的数字实现代码(附特效)
Apr 21 Javascript
js 剪切板应用clipboardData详细解析
Dec 17 Javascript
javascript回车完美实现tab切换功能
Mar 13 Javascript
js实现div弹出层的方法
Nov 20 Javascript
jQuery中:not选择器用法实例
Dec 30 Javascript
jQuery中scrollLeft()方法用法实例
Jan 16 Javascript
详解JavaScript中的every()方法
Jun 08 Javascript
jQuery实现的左右移动焦点图效果
Jan 14 Javascript
vue.js学习笔记之绑定style样式和class列表
Oct 31 Javascript
Vue 实现手动刷新组件的方法
Feb 19 Javascript
详解微信小程序开发之formId使用(模板消息)
Aug 27 Javascript
JavaScript中push(),join() 函数 实例详解
Sep 06 #Javascript
jquery实现全选、不选、反选的两种方法
Sep 06 #Javascript
fullpage.js全屏滚动插件使用实例
Sep 06 #Javascript
AngularJS  $on、$emit和$broadcast的使用
Sep 05 #Javascript
JavaScript中最容易混淆的作用域、提升、闭包知识详解(推荐)
Sep 05 #Javascript
Vuejs第六篇之Vuejs与form元素实例解析
Sep 05 #Javascript
Vue表单实例代码
Sep 05 #Javascript
You might like
支持oicq头像的留言簿(二)
2006/10/09 PHP
php递归遍历多维数组的方法
2015/04/18 PHP
PHP设计模式之简单投诉页面实例
2016/02/24 PHP
PHP常用的三种设计模式汇总
2016/08/28 PHP
jQuery的一些特性和用法整理小结
2010/01/13 Javascript
Asp.net下利用Jquery Ajax实现用户注册检测(验证用户名是否存)
2010/09/12 Javascript
js一般方法改写成面向对象方法的无限级折叠菜单示例代码
2013/07/04 Javascript
浅谈javascript中的call、apply、bind
2016/03/06 Javascript
jQuery通过写入cookie实现更换网页背景的方法
2016/04/15 Javascript
不同js异步函数同步的实现方法
2016/05/28 Javascript
实现图片首尾平滑轮播(JS原生方法—节流)
2017/10/17 Javascript
ES6下子组件调用父组件的方法(推荐)
2018/02/23 Javascript
微信小程序实现弹出菜单功能
2018/06/12 Javascript
JavaScript使用math.js进行精确计算操作示例
2018/06/19 Javascript
vue+iview 实现可编辑表格的示例代码
2018/10/31 Javascript
详解vue-cli项目在IE浏览器打开报错解决方法
2020/12/10 Vue.js
python的正则表达式re模块的常用方法
2013/03/09 Python
使用Python装饰器在Django框架下去除冗余代码的教程
2015/04/16 Python
对numpy中的transpose和swapaxes函数详解
2018/08/02 Python
Python数据可视化之画图
2019/01/15 Python
浅谈Python中的可迭代对象、迭代器、For循环工作机制、生成器
2019/03/11 Python
浅谈python编译pyc工程--导包问题解决
2019/03/20 Python
Python 测试框架unittest和pytest的优劣
2020/09/26 Python
自然健康的概念:Natural Healthy Concepts
2020/01/26 全球购物
美国基督教约会网站:ChristianCafe.com
2020/02/04 全球购物
什么是岗位职责
2013/11/12 职场文书
给分销商的致歉信
2014/01/14 职场文书
网络技术专业求职信
2014/02/18 职场文书
校本教研活动总结
2014/07/01 职场文书
三问三解心得体会
2014/09/05 职场文书
个人优缺点总结
2015/02/28 职场文书
小学音乐课歌曲《堆雪人》教学反思
2016/02/18 职场文书
Django使用channels + websocket打造在线聊天室
2021/05/20 Python
Java Shutdown Hook场景使用及源码分析
2021/06/15 Java/Android
全面盘点MySQL中的那些重要日志文件
2021/11/27 MySQL
Python闭包的定义和使用方法
2022/04/11 Python