javascript中prototype、constructor以及__proto__之间的三角关系
三者暧昧关系简单整理
在javascript中,prototype、constructor以及__proto__之间有着“著名”的剪不断理还乱的三角关系,楼主就着自己对它们的浅显认识,来粗略地理理以备忘,有不对之处还望斧正。
楼主的一句话理解就是:某个对象的constructor属性返回该对象构造函数,其__proto__属性是个对象,值和其构造函数的prototype属性值一致。
先来说说prototype。prototype的解释是“原型”,js的所有函数都有一个prototype属性,其属性值是个对象,原型对象初始值是空的(其实还有constructor属性和__proto__属性,只是不能被枚举,可以参考最下面留言),代码验证:
- function Person() {
- this.name = 'hanzichi';
- this.age = 10;
- }
- var num = 0;
- for(var i in Person.prototype)
- num++;
- console.log(num);
prorotype的主要作用简单来说就是“便于方法或者属性的重用”,可以利用prototype添加扩展属性和方法,举个简单的例子:
- function Person() {
- this.name = 'hanzichi';
- this.age = 10;
- }
- Person.prototype.show = function() {
- console.log(this.name);
- };
- Person.prototype.sex = 'male';
- var a = new Person();
- console.log(a.sex); // male
- a.show(); // hanzichi
- console.log(a.__proto__ === Person.prototype); // true 对象a的__proto__值取自构造函数Person的prototype值
- console.log(a.constructor === Person); // true 对象a的构造函数是Person
- console.log(Person.prototype.constructor === Person); // true
如上,所有用Person函数构造的对象都可以使用sex值,调用show方法。
那么问题来了,以上代码Person函数的prototype值是什么?我们尝试打印(console.log(Person.prototype)):
我们发现Person.prototype值确实是个对象,本身有两个属性(show和sex),这是自定义的,还有两个属性(constructor和__proto__ 可以通过hasOwnProperty验证),一个是constructor,值为函数本身,另一个就是__proto__(据说ie不支持 楼主没测试),其值为父函数的prototype属性值。因为Person继承自Object,故其__proto__值为Object的prototype属性值,也就是说Person继承了Object本身的所有方法,我们可以展开来细看:
说完prototype,我们再来看看对象实例有哪些属性,我们也将它打印出来(console.log(a)):
我们看到实例对象a除了本身自带属性外,也有个属性__proto__。
我们通过a.show()调用了show方法,但是show方法并不显示在对象本身属性里(可通过hasOwnProperty验证),为何能用?又是__proto__!所有的实例对象都有个__proto__属性,我们看到它的值跟Person.prototype一致,也就是说实例a继承了Person类的属性方法。本身的属性里找不到show方法,自动去__proto__中寻找。
再说说constructor,其值返回该对象的构造函数:
- console.log('string'.constructor); // function String() { [native code] }
- console.log(new String('string').constructor); // function String() { [native code] }
- console.log(/hello/.constructor); // function RegExp() { [native code] }
- console.log([1, 2, 3].constructor); // function Array() { [native code] }
- function A() {};
- var a = new A();
- console.log(a.constructor); // function A() {}
我们依旧看最前面的代码,a.constructor返回的是a的构造函数,也就是Person,其实实例对象a本身并没有constructor属性,但是a中的__proto__拥有constructor属性,没错,a本身没有,就会从它的__proto__属性中寻找constructor方法,如果还没有,就继续从__proto__属性的__proto__属性中寻找... 这样就构成了一个原型链。
我们似乎已经习惯了用new的方式来构造对象,其实new方式的核心实现要分为三个步骤,如下:
- function Person() {
- this.name = 'hanzichi';
- this.age = 10;
- }
- Person.prototype.show = function() {
- console.log(this.name);
- };
- Person.prototype.sex = 'male';
- var a = {}; // 1
- a.__proto__ = Person.prototype; // 2
- Person.call(a); // 3
- console.log(a.sex); // male
- a.show(); // hanzichi
以上代码一目了然。先初始化一个空对象,然后空对象继承构造函数的prototype值,最后call构造函数初始化。
再来看一段稍微复杂一点的代码:
- function Person() {
- this.name = 'hanzichi';
- this.age = 10;
- }
- Person.prototype.show = function() {
- console.log(this.name);
- };
- Person.prototype.sex = 'male';
- function Child() {};
- Child.prototype = new Person();
- var a = new Child();
- console.log(a);
这是一种简单的继承代码,先不管代码对错,我们看看代码执行中发生了什么。
Person函数前面已经分析了,我们又构造了一个Child函数,我们把一个实例化的Person对象(new Person())赋值给了Child的prototype属性,也就是说Child继承了Person的所有方法属性,可以可以尝试打印Child.prototype看看(其值其实也就是上图中的a.__proto__),这样Child的实例就能使用Person的属性方法了。而以上实例对象a如果要调用show函数需经过两个的__proto__原型链传递:
学以致用
试着来做道题看看有没有理解:
- function t1(name) {
- if(name) this.name = name;
- }
- function t2(name) {
- this.name = name;
- }
- function t3(name) {
- this.name = name || "test";
- }
- t1.prototype.name = "hanzichi";
- t2.prototype.name = "hanzichi";
- t2.prototype.name = "hanzichi";
- console.log(new t1().name, new t2().name, new t3().name);
答案:hanzichi undefined test
其实也就是本身有name属性就用,没有就从原型链中寻找。2和3的话都是本身已经拥有,而1是本身没有name属性。ps:没有传入实参而在函数中使用形参的话会被解释成undefined。
恩,再看一道:
- function Person() {
- this.name = 'hanzichi';
- this.age = 10;
- }
- Person.prototype.sex = 'female';
- var a = new Person();
- console.log(a.sex);
- Person.prototype.sex = 'male';
- console.log(a.sex);
- Person.prototype = {
- sex: 'female'
- };
- console.log(a.sex);
答案:female male male
为什么会这样?
一开始Person.prototype指向一个对象,如上图1所示指向对象1,而初始化一个实例后,该实例的__proto__属性同时指向了Person.prototype,即指向了Person.prototype指向的对象1,如上图2,这时a.sex就会返回对象1中sex的值,而因为Person.prototype和a.__proto__引用同一个对象,所以都能改变该对象的值,如下代码也可以同时验证:
- function Person() {
- this.name = 'hanzichi';
- this.age = 10;
- }
- Person.prototype.sex = 'female';
- var a = new Person();
- a.__proto__.sex = 'male';
- console.log(Person.prototype.sex); // male
- console.log(a.sex); // male
而Person.prototype = {...}后Person.prototype引用了一个新的对象,如上图3操作后Person.prototype引用了对象2,但是实例a还是引用在原来的对象1上。
总结
javascript中每个对象除了本身的属性外,还有一个__proto__属性,继承了父对象的方法和属性(形成原型链);而每个函数有个prototype属性,该属性值是个对象,该对象函数自定义的一些属性方法外,还有两个属性,constructor(其值一般为函数本身)和__proto__(其值继承自父对象)。
其实楼主对于以上了解的也很浅显,欢迎指导拍砖~
javascript中prototype、constructor以及__proto__之间的三角关系的更多相关文章
- 在 JavaScript 中 prototype 和 __proto__ 有什么区别
本文主要讲三个 问题 prototype 和 proto function 和 object new 到底发生了什么 prototype 和 proto 首先我们说下在 JS 中,常常让我们感到困惑的 ...
- Javascript中Function,Object,Prototypes,__proto__等概念详解
http://anykoro.sinaapp.com/2012/01/31/javascript%E4%B8%ADfunctionobjectprototypes__proto__%E7%AD%89% ...
- Javascript中prototype属性详解 (存)
Javascript中prototype属性详解 在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不 ...
- (转载)详解Javascript中prototype属性(推荐)
在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不存在类(Class)的概念的,javascript中不 ...
- 深入浅析JavaScript中的constructor
constructor 属性返回对创建此对象的数组函数的引用.本文给大家介绍JavaScript中的constructor ,需要的朋友参考下吧 定义和用法 constructor 属性返回对创建此对 ...
- 【转】JavaScript中的constructor与prototype
最初对js中 object.constructor 的认识: 在学习JS的面向对象过程中,一直对constructor与prototype感到很迷惑,看了一些博客与书籍,觉得自己弄明白了,现在记录如下 ...
- 彻底搞清javascript中this, constructor, prototype
说起这三个属性,肯定有一些同学和我一样,初学js时非常困惑,头大,一脸的迷茫.今天就来给大家彻底解决这些担心受怕的问题. 先看this this定义: this就是函数赖以执行的对象. 分析这句话: ...
- javascript中prototype与__proto__
1.prototype:构造函数独有的属性: __proto__:每个对象都有一个名为__proto__的属性: 注意:每个构造函数(自带与自创)都有一个prototype的属性,构造函数的proto ...
- Javascript中的原型链,__proto__和prototype等问题总结
1.js中除了原始数据类型 都是对象. 包括函数也是对象,可能类似于C++函数对象把 应该是通过解释器 进行()操作符重载或其他操作, 用的时候把它当函数用就行 但是实际上本质是一个对象 原型也是一个 ...
随机推荐
- 每日Scrum--No.9
Yesterday:测试软件 Today:写阶段性的总结 Problem: (1)晚上我们的团队进行了收尾工作:第一阶段的任务基本完成,软件主要实现了校园景点照片以及对应的介绍,查询最短路径,查询涉及 ...
- Java中的显示锁 ReentrantLock 和 ReentrantReadWriteLock
在Java1.5中引入了两种显示锁,分别是可重入锁ReentrantLock和可重入读写锁ReentrantReadWriteLock.它们分别实现接口Lock和ReadWriteLock.(注意:s ...
- 【原创】Java实现手机号码归属地查询
网络上已经有很多的手机号码归属地查询的API接口,但是这些接口总是有一些大大小小的缺陷. 总结一下这些缺陷: 1.要直接将它的搜索框链接形式粘到自己的页面,点击查询的时候还要跳转到他们的网站来展示归属 ...
- 烂泥:ubuntu中使用virt-manager图形化新建虚拟机
本文由秀依林枫提供友情赞助,首发于烂泥行天下. 上一篇文章介绍了,如何在ubuntu下安装KVM的虚拟机管理器virt-manager,这篇文章我们来介绍,如何在图形界面下使用virt-manager ...
- pageX和pageY
pageX() 属性是鼠标指针的位置,相对于文档的左边缘. pageY() 属性是鼠标指针的位置,相对于文档的上边缘. 例1 $(document).mousemove(function(e){ $( ...
- 自定义input[type="file"]的样式
input[type="file"]的样式在各个浏览器中的表现不尽相同: 1. chrome: 2. firefox: 3. opera: 4. ie: 5. edge: 另外,当 ...
- hdu2527哈夫曼编码
/* Safe Or Unsafe Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) T ...
- testng环境设置
1.在eclipse中安装testng插件,地址:http://beust.com/eclipse 2.设置testng环境变量 testng_home D:\Program Files\eclips ...
- 【读书笔记《Android游戏编程之从零开始》】14.游戏开发基础(Bitmap 位图的渲染与操作)
Bitmap 是图形类,Android 系统支持的图片格式有 png.jpg.bmp 等. 对位图操作在游戏中是很重要的知识点,比如游戏中需要两张除了大小之外其他完全相同的图,那么如果会对位图进行缩放 ...
- 八皇后,回溯与递归(Python实现)
八皇后问题是十九世纪著名的数学家高斯1850年提出 .以下为python语句的八皇后代码,摘自<Python基础教程>,代码相对于其他语言,来得短小且一次性可以打印出92种结果.同时可以扩 ...