JavaScript中__proto__与prototype的关系深入理解


Posted in Javascript onDecember 04, 2012

这里讨论下对象的内部原型(__proto__)和构造器的原型(prototype)的关系。
一、所有构造器/函数的__proto__都指向Function.prototype,它是一个空函数(Empty function)

Number.__proto__ === Function.prototype // true 
Boolean.__proto__ === Function.prototype // true 
String.__proto__ === Function.prototype // true 
Object.__proto__ === Function.prototype // true 
Function.__proto__ === Function.prototype // true 
Array.__proto__ === Function.prototype // true 
RegExp.__proto__ === Function.prototype // true 
Error.__proto__ === Function.prototype // true 
Date.__proto__ === Function.prototype // true

JavaScript中有内置(build-in)构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎创建,Math,JSON是以对象形式存在的,无需new。它们的__proto__是Object.prototype。如下
Math.__proto__ === Object.prototype // true 
JSON.__proto__ === Object.prototype // true

上面说的“所有构造器/函数”当然包括自定义的。如下
// 函数声明 
function Person() {} 
// 函数表达式 
var Man = function() {} 
console.log(Person.__proto__ === Function.prototype) // true 
console.log(Man.__proto__ === Function.prototype) // true

这说明什么呢?
所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了Function.prototype的属性及方法。如length、call、apply、bind(ES5)。
Function.prototype也是唯一一个typeof XXX.prototype为 “function”的prototype。其它的构造器的prototype都是一个对象。如下
console.log(typeof Function.prototype) // function 
console.log(typeof Object.prototype) // object 
console.log(typeof Number.prototype) // object 
console.log(typeof Boolean.prototype) // object 
console.log(typeof String.prototype) // object 
console.log(typeof Array.prototype) // object 
console.log(typeof RegExp.prototype) // object 
console.log(typeof Error.prototype) // object 
console.log(typeof Date.prototype) // object 
console.log(typeof Object.prototype) // object

噢,上面还提到它是一个空的函数,alert(Function.prototype) 下看看。
知道了所有构造器(含内置及自定义)的__proto__都是Function.prototype,那Function.prototype的__proto__是谁呢?
相信都听说过JavaScript中函数也是一等公民,那从哪能体现呢?如下
console.log(Function.prototype.__proto__ === Object.prototype) // true

这说明所有的构造器也都是一个普通JS对象,可以给构造器添加/删除属性等。同时它也继承了Object.prototype上的所有方法:toString、valueOf、hasOwnProperty等。
最后Object.prototype的__proto__是谁?
Object.prototype.__proto__ === null // true

已经到顶了,为null。
二、所有对象的__proto__都指向其构造器的prototype
上面测试了所有内置构造器及自定义构造器的__proto__,下面再看看所有这些构造器的实例对象的__proto__指向谁?
先看看JavaScript引擎内置构造器
var obj = {name: 'jack'} 
var arr = [1,2,3] 
var reg = /hello/g 
var date = new Date 
var err = new Error('exception') 
console.log(obj.__proto__ === Object.prototype) // true 
console.log(arr.__proto__ === Array.prototype) // true 
console.log(reg.__proto__ === RegExp.prototype) // true 
console.log(date.__proto__ === Date.prototype) // true 
console.log(err.__proto__ === Error.prototype) // true

再看看自定义的构造器,这里定义了一个Person
function Person(name) { 
this.name = name 
} 
var p = new Person('jack') 
console.log(p.__proto__ === Person.prototype) // true

p是Person的实例对象,p的内部原型总是指向其构造器Person的prototype。
每个对象都有一个constructor属性,可以获取它的构造器,因此以下打印结果也是恒等的
function Person(name) { 
this.name = name 
} 
var p = new Person('jack') 
console.log(p.__proto__ === p.constructor.prototype) // true

上面的Person没有给其原型添加属性或方法,这里给其原型添加一个getName方法
function Person(name) { 
this.name = name 
} 
// 修改原型 
Person.prototype.getName = function() {} 
var p = new Person('jack') 
console.log(p.__proto__ === Person.prototype) // true 
console.log(p.__proto__ === p.constructor.prototype) // true

可以看到p.__proto__与Person.prototype,p.constructor.prototype都是恒等的,即都指向同一个对象。
如果换一种方式设置原型,结果就有些不同了
function Person(name) { 
this.name = name 
} 
// 重写原型 
Person.prototype = { 
getName: function() {} 
} 
var p = new Person('jack') 
console.log(p.__proto__ === Person.prototype) // true 
console.log(p.__proto__ === p.constructor.prototype) // false

这里直接重写了Person.prototype(注意:上一个示例是修改原型)。输出结果可以看出p.__proto__仍然指向的是Person.prototype,而不是p.constructor.prototype。
这也很好理解,给Person.prototype赋值的是一个对象直接量{getName: function(){}},使用对象直接量方式定义的对象其构造器(constructor)指向的是根构造器Object,Object.prototype是一个空对象{},{}自然与{getName: function(){}}不等。如下
var p = {} 
console.log(Object.prototype) // 为一个空的对象{} 
console.log(p.constructor === Object) // 对象直接量方式定义的对象其constructor为Object 
console.log(p.constructor.prototype === Object.prototype) // 为true,不解释

上面代码中用到的__proto__目前在IE6/7/8/9中都不支持。IE9中可以使用Object.getPrototypeOf(ES5)获取对象的内部原型。
var p = {} 
var __proto__ = Object.getPrototypeOf(p) 
console.log(__proto__ === Object.prototype) // true
Javascript 相关文章推荐
javascript 自动填写表单的实现方法
Apr 09 Javascript
javascript:history.go()和History.back()的区别及应用
Nov 25 Javascript
js使用心得分享
Jan 13 Javascript
jQuery实现自定义右键菜单的树状菜单效果
Sep 02 Javascript
jquery+php实现滚动的数字特效
Nov 29 Javascript
javascript基本数据类型及类型检测常用方法小结
Dec 14 Javascript
Javascript中引用类型传递的知识点小结
Mar 06 Javascript
javascript  数组排序与对象排序的实例
Jul 17 Javascript
Angular中支持SCSS的方法
Nov 18 Javascript
JS实现同一DOM元素上onClick事件与onDblClick事件并存的解决方法
Jun 07 Javascript
jQuery实现菜单的显示和隐藏功能示例
Jul 24 jQuery
vue中watch和computed为什么能监听到数据的改变以及不同之处
Dec 27 Javascript
js 限制数字 js限制输入实现代码
Dec 04 #Javascript
JSON语法五大要素图文介绍
Dec 04 #Javascript
js关闭子窗体刷新父窗体实现方法
Dec 04 #Javascript
cument.execCommand()用法深入理解
Dec 04 #Javascript
页面只能打开一次Cooike如何实现
Dec 04 #Javascript
解析John Resig Simple JavaScript Inheritance代码
Dec 03 #Javascript
cookie在javascript中的使用技巧以及隐私在服务器端的设置
Dec 03 #Javascript
You might like
提升PHP执行速度全攻略(下)
2006/10/09 PHP
Yii2 rbac权限控制之菜单menu实例教程
2016/04/28 PHP
DWZ+ThinkPHP开发时遇到的问题分析
2016/12/12 PHP
php实现文件上传及头像预览功能
2017/01/15 PHP
Javascript中的常见排序算法
2007/03/27 Javascript
Javascript继承机制的设计思想分享
2011/08/28 Javascript
比较新旧两个数组值得增加和删除的JS代码
2013/10/30 Javascript
jquery实现点击变换导航样式的方法
2015/08/31 Javascript
理解javascript函数式编程中的闭包(closure)
2016/03/08 Javascript
JS+CSS实现的漂亮渐变背景特效代码(6个渐变效果)
2016/03/25 Javascript
javascript中call,apply,bind函数用法示例
2016/12/19 Javascript
es7学习教程之fetch解决异步嵌套问题的方法示例
2017/07/21 Javascript
浅谈vue-router2路由参数注意的问题
2017/11/08 Javascript
webpack+vue中使用别名路径引用静态图片地址
2017/11/20 Javascript
babel的使用及安装配置教程
2018/02/22 Javascript
详解javascript对数组和json数组的操作
2019/04/15 Javascript
详解vuejs2.0 select 动态绑定下拉框支持多选
2019/04/25 Javascript
elementUI table表格动态合并的示例代码
2019/05/15 Javascript
layer设置maxWidth及maxHeight解决方案
2019/07/26 Javascript
vue draggable resizable gorkys与v-chart使用与总结
2019/09/05 Javascript
[00:12]DAC2018 no[o]ne亮相SOLO赛 他是否如他的id一样无人可挡?
2018/04/06 DOTA
[01:14:31]Secret vs VG 2018国际邀请赛淘汰赛BO3 第一场 8.23
2018/08/24 DOTA
Python制作爬虫采集小说
2015/10/25 Python
Python常用库推荐
2016/12/04 Python
python实战之实现excel读取、统计、写入的示例讲解
2018/05/02 Python
HTML5+WebSocket实现多文件同时上传的实例
2016/12/29 HTML / CSS
法国在线宠物店:zooplus.fr
2018/02/23 全球购物
工程造价管理专业大专生求职信
2013/10/06 职场文书
小学运动会班级口号
2014/06/09 职场文书
社区反邪教工作方案
2014/06/16 职场文书
2015年班组建设工作总结
2015/05/13 职场文书
八年级作文之我的母亲
2019/12/10 职场文书
用Python简陋模拟n阶魔方
2021/04/17 Python
浅谈pytorch中的dropout的概率p
2021/05/27 Python
springboot layui hutool Excel导入的实现
2022/03/31 Java/Android
PyTorch中permute的使用方法
2022/04/26 Python