浅谈javascript中的prototype和__proto__的理解


Posted in Javascript onApril 07, 2019

在工作中有时候会看到prototype和__proto__这两个属性,对这两个属性我一直比较蒙圈,但是我通过查阅相关资料,决定做一下总结加深自己的理解,写得不对的地方还请各位大神指出。

  1. 跟__proto__属性相关的两个方法
  2. 判断属性是存在实例对象中,还是存在原型对象中的方法
  3. 获取或遍历对象中属性的几种方法

1、prototype

每个函数都有一个prototype属性,该属性是一个指针,指向一个对象。 而这个对象的用途是包含由特定类型的所有实例共享的属性和方法。使用这个对象的好处就是可以让所有实例对象共享它所拥有的属性和方法

2、 __proto__

每个实例对象都有一个__proto__属性,用于指向构造函数的原型对象。__proto__属性是在调用构造函数创建实例对象时产生的。

function Person(name, age, job){ 
 this.name = name;
 this.age = age;
 this.job = job;
 this.sayName = function(){
 console.log(this.name);
 }; // 与声明函数在逻辑上是等价的
}
var person1=new Person("Nicholas",29,"Software Engineer");
console.log(person1);
console.log(Person);
console.log(person1.prototype);//undefined
console.log(person1.__proto__);
console.log(Person.prototype);
console.log(person1.__proto__===Person.prototype);//true

输出结果如下:

浅谈javascript中的prototype和__proto__的理解

总结:

1、调用构造函数创建的实例对象的prototype属性为"undefined",构造函数的prototype是一个对象。

2、__proto__属性是在调用构造函数创建实例对象时产生的。

3、调用构造函数创建的实例对象的__proto__属性指向构造函数的prototype。

4、在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。

下图展示了使用Person构造函数创建实例后各个对象之间的关系

浅谈javascript中的prototype和__proto__的理解

上图展示了 Person 构造函数、 Person 的原型属性以及 Person现有的两个实例之间的关系。

3、 跟__proto__属性相关的两个方法

isPrototypeOf():虽然在所有实现中都无法访问到__proto__,但可以通过 isPrototypeOf()方法来确定对象之间是否存在这种关系。

alert(Person.prototype.isPrototypeOf(person1)); //true
 alert(Person.prototype.isPrototypeOf(person2)); //true

Object.getPrototypeOf():在所有支持的实现中,这个方法返回__proto__的值。例如:

alert(Object.getPrototypeOf(person1) == Person.prototype); //true
 alert(Object.getPrototypeOf(person1).name); //"Nicholas"

注意:虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。请看下面的例子:

function Person(){
 }
 Person.prototype.name = "Nicholas";
 Person.prototype.age = 29;
 Person.prototype.job = "Software Engineer";
 Person.prototype.sayName = function(){
 alert(this.name);
 };
 var person1 = new Person();
 var person2 = new Person();
 person1.name = "Greg";
 alert(person1.name); //"Greg"—— 来自实例
 alert(person2.name); //"Nicholas"—— 来自原型

4、 判断属性是存在实例对象中,还是存在原型对象中,有以下方法

hasOwnProperty():可以检测一个属性是存在于实例中,还是存在于原型中。返回值为true表示该属性存在实例对象中,其他情况都为false。

in 操作符:无论该属性存在于实例中还是原型中。只要存在对象中,都会返回true。但是可以同时使用 hasOwnProperty()方法和 in 操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中。

var person1 = new Person();
 var person2 = new Person();
 alert(person1.hasOwnProperty("name")); //false
 alert("name" in person1); //true
 person1.name = "Greg";
 alert(person1.name); //"Greg" —— 来自实例
 alert(person1.hasOwnProperty("name")); //true
 alert("name" in person1); //true
 alert(person2.name); //"Nicholas" —— 来自原型
 alert(person2.hasOwnProperty("name")); //false
 alert("name" in person2); //true
 delete person1.name;
 alert(person1.name); //"Nicholas" —— 来自原型
 alert(person1.hasOwnProperty("name")); //false
 alert("name" in person1); //true

5、 获取或遍历对象中属性的几种方法

for-in:通过for-in循环的返回的是能够被访问的、可枚举的属性,不管该属性是在实例中,还是存在原型中。

function Person(name, age, job) {
		this.name = name;
		this.age = age;
		this.job = job;	
	}
	Person.prototype={
		sayName:function(){
			return this.name;
		}
	}
	var p=new Person("李明",30,"诗人");
	for(var prop in p){
		console.log(prop);//name、age、job、sayName
	}
 console.log(Object.keys(p));//["name", "age", "job"]
 console.log(Object.keys(Person.prototype));//["sayName"]
 console.log(Object.getOwnPropertyNames(Person.prototype))
 // ["constructor", "sayName"]

Object.keys():取得实例对象上所有可枚举的属性。 Object.getOwnPropertyNames(): 获取实例对象所有属性,无论它是否可枚举。

注意:使用对象字面量来重写整个原型对象时,本质上完全重写了默认的 prototype 对象,因此 constructor 属性也就变成了新对象的 constructor 属性(指向 Object 构造函数),不再指向 Person。但是可以通过在重写原型对象时指定constructor属性,使之还是指向原来的constructor。此时,尽管 instanceof 操作符还能返回正确的结果,但通过 constructor 已经无法确定对象的类型了。

object instanceof constructor:检测 constructor.prototype 是否存在于参数 object 的原型链上。

function Person() {}
 var friend2 = new Person();
 Person.prototype = {
 	//constructor : Person,
 	name: "Nicholas",
 	age: 29,
 	job: "Software Engineer",
 	sayName: function() {
 		alert(this.name);
 	}
 };
 var friend = new Person();
 console.log(friend2 instanceof Object); //true
 console.log(friend2 instanceof Person); //false,
 console.log(friend2.constructor == Person); //true
 console.log(friend2.constructor == Object); //false
 
 console.log(friend instanceof Object); //true
 console.log(friend instanceof Person); //true
 console.log(friend.constructor == Person); //false
 console.log(friend.constructor == Object); //true

由于原型的动态性,调用构造函数时会为实例添加一个指向最初原型的Prototype指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。看下面的例子

function Person(){
 }
 var friend = new Person();
 Person.prototype = {
 constructor: Person,
 name : "Nicholas",
 age : 29,
 job : "Software Engineer",
 sayName : function () {
  alert(this.name);
 }
 };
 var friend2=new Person();
 friend.sayName(); //Uncaught TypeError: friend.sayName is not a function 
 friend2.sayName();//Nicholas
 console.log(friend instanceof Person);//false
 console.log(friend instanceof Object);//true
 console.log(friend2 instanceof Person);//true

结果分析:这是因为friend1的prototype指向的是没重写Person.prototype之前的Person.prototype,也就是构造函数最初的原型对象。而friend2的prototype指向的是重写Person.prototype后的Person.prototype。如下图所示

浅谈javascript中的prototype和__proto__的理解 

6、 原型链

基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。最直观的表现就是让原型对象等于另一个类型的实例。

function SuperType(){
 this.property = true;
 }
 SuperType.prototype.getSuperValue = function(){
 return this.property;
 };
 function SubType(){
 this.subproperty = false;
 }
 //继承了 SuperType
 SubType.prototype = new SuperType();
 SubType.prototype.getSubValue = function (){
 return this.subproperty;
 };
 var instance = new SubType();
 alert(instance.getSuperValue()); //true

SubType.prototype = new SuperType();这句代码使得原来存在于 SuperType 的实例中的所有属性和方法,现在也存在于 SubType.prototype 中。使得instance的constructor指向了SuperType。

console.log(instance.constructor===SuperType);//true

总结: 访问一个实例属性时,首先会在实例中搜索该属性。如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。在找不到属性或方法的情况下,搜索过程总是要一环一环地前行到原型链末端才会停下来。

就拿上面的例子来说,调用 instance.getSuperValue()会经历4个搜索步骤:

  1. 搜索instance实例;
  2. 搜索 SubType.prototype;
  3. 搜索SuperType的实例;
  4. 搜索 SuperType.prototype,最后一步才会找到该方法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery复选框CHECKBOX全选、反选
Aug 30 Javascript
基于Css3和JQuery实现打字机效果
Aug 11 Javascript
JS+JSP通过img标签调用实现静态页面访问次数统计的方法
Dec 14 Javascript
JavaScript资源预加载组件和滑屏组件的使用推荐
Mar 10 Javascript
JS中innerHTML和pasteHTML的区别实例分析
Jun 22 Javascript
jquery实现自定义图片裁剪功能【推荐】
Mar 08 Javascript
微信小程序 wx.login解密出现乱码的问题解决办法
Mar 10 Javascript
JavaScript Drum Kit 指南(纯 JS 模拟敲鼓效果)
Jul 23 Javascript
jQuery图片缩放插件smartZoom使用实例详解
Aug 25 jQuery
详解Vuex管理登录状态
Nov 13 Javascript
JS数组去重常用方法实例小结【4种方法】
May 28 Javascript
vue组件化中slot的基本使用方法
May 01 Javascript
javascrit中undefined和null的区别详解
Apr 07 #Javascript
详解服务端预渲染之Nuxt(介绍篇)
Apr 07 #Javascript
vue设计一个倒计时秒杀的组件详解
Apr 06 #Javascript
js字符串处理之绝妙的代码
Apr 05 #Javascript
微信小程序自定义导航栏实例代码
Apr 05 #Javascript
Node.js事件的正确使用方法
Apr 05 #Javascript
利用Node.js如何实现文件循环覆写
Apr 05 #Javascript
You might like
一个SQL管理员的web接口
2006/10/09 PHP
php版微信公众平台接口参数调试实现判断用户行为的方法
2016/09/23 PHP
php生成二维码图片方法汇总
2016/12/17 PHP
javascript之通用简单的table选项卡实现(二)
2010/05/09 Javascript
通过继承IHttpHandle实现JS插件的组织与管理
2010/07/13 Javascript
js实现连续英文字符自动换行兼容ie6 ie7和firefox
2013/09/06 Javascript
用jQuery实现的智能隐藏、滑动效果的返回顶部代码
2014/03/18 Javascript
JavaScript字符串对象toLowerCase方法入门实例(用于把字母转换为小写)
2014/10/17 Javascript
jquery使用Cookie和JSON记录用户最近浏览历史
2016/04/19 Javascript
Vue.js常用指令汇总(v-if、v-for等)
2016/11/03 Javascript
浅谈javascript alert和confirm的美化
2016/12/15 Javascript
js中编码函数:escape,encodeURI与encodeURIComponent详解
2017/03/21 Javascript
AngularJS ui-router刷新子页面路由的方法
2018/07/23 Javascript
深入理解与使用keep-alive(配合router-view缓存整个路由页面)
2018/09/25 Javascript
[01:01:22]VGJ.S vs OG 2018国际邀请赛淘汰赛BO3 第一场 8.22
2018/08/23 DOTA
Python脚本实现下载合并SAE日志
2015/02/10 Python
基于使用paramiko执行远程linux主机命令(详解)
2017/10/16 Python
python tensorflow学习之识别单张图片的实现的示例
2018/02/09 Python
python中partial()基础用法说明
2018/12/30 Python
python实现雪花飘落效果实例讲解
2019/06/18 Python
Python3 tkinter 实现文件读取及保存功能
2019/09/12 Python
PyCharm中配置PySide2的图文教程
2020/06/18 Python
CSS3 transforms应用于背景图像的解决方法
2019/04/16 HTML / CSS
MediaMarkt比利时:欧洲最大电器连锁店
2020/12/21 全球购物
路由表示做什么用的?在linux环境中怎么来配置一条默认路由?
2013/06/07 面试题
Python使用openpyxl复制整张sheet
2021/03/24 Python
办公室前台岗位职责
2014/01/04 职场文书
五年后的职业生涯规划
2014/03/04 职场文书
2015年公司新年寄语
2014/12/08 职场文书
幼师辞职信范文
2015/02/27 职场文书
红白喜事主持词
2015/07/06 职场文书
pytorch锁死在dataloader(训练时卡死)
2021/05/28 Python
基于Redis的List实现特价商品列表功能
2021/08/30 Redis
Java练习之潜艇小游戏的实现
2022/03/16 Java/Android
vue elementUI表格控制对应列
2022/04/13 Vue.js
windows11选中自动复制怎么开启? Win11自动复制所选内容的方法
2022/07/23 数码科技