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 URL锚点取值方法
Feb 25 Javascript
js加强的经典分页实例
Mar 15 Javascript
JS随机漂浮广告代码具体实例
Nov 19 Javascript
纯JavaScript 实现flappy bird小游戏实例代码
Sep 27 Javascript
JavaScript中绑定事件的三种方式及去除绑定
Nov 05 Javascript
Extjs gridpanel 中的checkbox(复选框)根据某行的条件不能选中的解决方法
Feb 17 Javascript
vue 实现 tomato timer(蕃茄钟)实例讲解
Jul 24 Javascript
vue自定v-model实现表单数据双向绑定问题
Sep 03 Javascript
Vue刷新修改页面中数据的方法
Sep 16 Javascript
Vue.js上传图片到阿里云OSS存储的方法示例
Dec 13 Javascript
一文看懂如何简单实现节流函数和防抖函数
Sep 05 Javascript
jQuery 常用特效实例小结【显示与隐藏、淡入淡出、滑动、动画等】
May 19 jQuery
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下利用shell后台运行PHP脚本,并获取该脚本的Process ID的代码
2011/09/19 PHP
PHP中获取文件创建日期、修改日期、访问时间的方法
2016/11/05 PHP
Laravel实现定时任务的示例代码
2017/08/10 PHP
Javascript 汉字字节判断
2009/08/01 Javascript
Jquery Change与bind事件代码
2011/09/29 Javascript
面向对象的Javascript之二(接口实现介绍)
2012/01/27 Javascript
实现图片预加载的三大方法及优缺点分析
2014/11/19 Javascript
HTML5之WebSocket入门3 -通信模型socket.io
2015/08/21 Javascript
手机端 HTML5使用photoswipe.js仿微信朋友圈图片放大效果
2016/08/25 Javascript
浅谈Vue.js应用的四种AJAX请求数据模式
2017/08/30 Javascript
Node.JS 循环递归复制文件夹目录及其子文件夹下的所有文件
2017/09/18 Javascript
Vue父子组件双向绑定传值的实现方法
2018/07/31 Javascript
vue使用ajax获取后台数据进行显示的示例
2018/08/09 Javascript
angular6.x中ngTemplateOutlet指令的使用示例
2018/08/09 Javascript
微信小程序实现拖拽功能
2019/09/26 Javascript
vue3.0实现点击切换验证码(组件)及校验
2020/11/18 Vue.js
Python3.4学习笔记之类型判断,异常处理,终止程序操作小结
2019/03/01 Python
详解Python给照片换底色(蓝底换红底)
2019/03/22 Python
Python使用Pandas读写Excel实例解析
2019/11/19 Python
Python图像处理库PIL的ImageDraw模块介绍详解
2020/02/26 Python
详解django使用include无法跳转的解决方法
2020/03/19 Python
浅析Python 多行匹配模式
2020/07/24 Python
Python实现数字的格式化输出
2020/08/01 Python
详解python模块pychartdir安装及导入问题
2020/10/22 Python
html5指南-2.如何操作document metadata
2013/01/07 HTML / CSS
StubHub哥伦比亚:购买和出售您的门票
2016/10/20 全球购物
C语言笔试集
2012/07/24 面试题
C#中的验证控件有几种
2014/03/08 面试题
最新结婚典礼主持词
2014/03/14 职场文书
大三学习计划书范文
2014/05/02 职场文书
商铺消防安全责任书
2014/07/29 职场文书
县长群众路线对照检查材料思想汇报
2014/10/02 职场文书
毕业实习证明(4篇)
2014/10/28 职场文书
出差报告格式模板
2014/11/06 职场文书
征求意见函
2015/06/05 职场文书
SpringRetry重试框架的具体使用
2021/07/25 Java/Android