浅谈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 相关文章推荐
一个加密JavaScript的开源工具PACKER2.0.2
Nov 04 Javascript
XP折叠菜单&仿QQ2006菜单
Dec 16 Javascript
用一段js程序来实现动画功能
Mar 06 Javascript
JSON序列化与解析原生JS方法且IE6和chrome测试通过
Sep 05 Javascript
可插入图片的TEXT文本框
Dec 27 Javascript
jquery实现不同大小浏览器使用不同的css样式表的方法
Apr 02 Javascript
javascript实现PC网页里的拖拽效果
Mar 14 Javascript
react-router实现按需加载
May 09 Javascript
十大 Node.js 的 Web 框架(快速提升工作效率)
Jun 30 Javascript
使用jQuery给Table动态增加行、清空table的方法
Sep 05 jQuery
如何根据业务封装自己的功能组件
Apr 19 Javascript
jQuery实现文本显示一段时间后隐藏的方法分析
Jun 20 jQuery
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
一拳超人中怪人协会钦定! S级别最强四人!
2020/03/02 日漫
第四节--构造函数和析构函数
2006/11/16 PHP
PHP获取指定函数定义在哪个文件中以及其所在的行号实例
2014/05/08 PHP
用php+ajax新建流程(请假、进货、出货等)
2017/06/11 PHP
使用自定义setTimeout和setInterval使之可以传递参数和对象参数
2009/04/24 Javascript
NodeJS Express框架中处理404页面一个方式
2014/05/28 NodeJs
Javascript限制网页只能在微信内置浏览器中访问
2014/11/09 Javascript
jquery对象和DOM对象的相互转换详解
2016/10/18 Javascript
JS封装的三级联动菜单(使用时只需要一行js代码)
2016/10/24 Javascript
bootstrap选项卡使用方法解析
2017/01/11 Javascript
jQuery插件HighCharts实现的2D对数饼图效果示例【附demo源码下载】
2017/03/09 Javascript
vue2组件之select2调用的示例代码
2017/10/12 Javascript
原生JS实现ajax与ajax的跨域请求实例
2017/12/01 Javascript
Vue脚手架的简单使用实例
2018/07/10 Javascript
Typescript的三种运行方式(小结)
2019/09/18 Javascript
[01:04:05]Mineski vs TNC 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/16 DOTA
Python中使用中文的方法
2011/02/19 Python
使用Python3编写抓取网页和只抓网页图片的脚本
2015/08/20 Python
Fiddler如何抓取手机APP数据包
2016/01/22 Python
Python面向对象编程之继承与多态详解
2018/01/16 Python
Selenium元素的常用操作方法分析
2018/08/10 Python
Python中的CSV文件使用"with"语句的方式详解
2018/10/16 Python
OpenCV3.0+Python3.6实现特定颜色的物体追踪
2019/07/23 Python
opencv+python实现鼠标点击图像,输出该点的RGB和HSV值
2020/06/02 Python
浅谈keras使用中val_acc和acc值不同步的思考
2020/06/18 Python
全球最大的跑步用品商店:Road Runner Sports
2016/09/11 全球购物
Original Penguin英国官方网站:美国著名休闲时装品牌
2016/10/30 全球购物
意大利领先的线上奢侈品销售电商:Eleonora Bonucci
2017/10/17 全球购物
复古服装:RetroStage
2019/05/10 全球购物
阿拉伯书店:Jamalon
2019/07/24 全球购物
英国高街奥特莱斯:Highstreet Outlet
2019/11/21 全球购物
一份Java笔试题
2012/02/21 面试题
求职信模板怎么做
2014/01/26 职场文书
医药公司开票员岗位职责
2015/04/15 职场文书
高三语文教学反思
2016/02/16 职场文书
2019年个人工作总结范文
2019/03/25 职场文书