浅谈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 相关文章推荐
Display SQL Server Version Information
Jun 21 Javascript
让js弹出窗口居前显示的实现方法
Jul 10 Javascript
ff下JQuery无法监听input的keyup事件的解决方法
Dec 12 Javascript
js实现简单随机抽奖的方法
Jan 27 Javascript
深入理解JavaScript系列(45):代码复用模式(避免篇)详解
Mar 04 Javascript
百度地图API之本地搜索与范围搜索
Jul 30 Javascript
浅谈JS原型对象和原型链
Mar 02 Javascript
Boostrap模态窗口的学习小结
Mar 28 Javascript
基于javascript实现图片滑动效果
May 07 Javascript
js实现弹窗暗层效果
Jan 16 Javascript
微信小程序中时间戳和日期的相互转换问题
Jul 09 Javascript
抖音上用记事本编写爱心小程序教程
Apr 17 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
做一个有下拉功能的留言版
2006/10/09 PHP
PHP表单验证的3个函数ISSET()、empty()、is_numeric()的使用方法
2011/08/22 PHP
php fsockopen中多线程问题的解决办法[翻译]
2011/11/09 PHP
php使用GD创建保持宽高比缩略图的方法
2015/04/17 PHP
WordPress中转义HTML与过滤链接的相关PHP函数使用解析
2015/12/22 PHP
Yii列表定义与使用分页方法小结(3种方法)
2016/07/15 PHP
setTimeout 不断吐食CPU的问题分析
2009/04/01 Javascript
JS对象与JSON格式数据相互转换
2012/02/20 Javascript
js实现的标题栏新消息闪烁提示效果
2014/06/06 Javascript
如何在MVC应用程序中使用Jquery
2014/11/17 Javascript
jQuery中parents()方法用法实例
2015/01/07 Javascript
jQuery ui实现动感的圆角渐变网站导航菜单效果代码
2015/08/26 Javascript
Node.js静态文件服务器改进版
2016/01/10 Javascript
js简单实现图片延迟加载的方法
2016/07/19 Javascript
Angular 中 select指令用法详解
2016/09/29 Javascript
js, jQuery实现全选、反选功能
2017/03/08 Javascript
在Vue组件中获取全局的点击事件方法
2018/09/06 Javascript
python中sys.argv参数用法实例分析
2015/05/20 Python
python如何使用unittest测试接口
2018/04/04 Python
TensorFlow 滑动平均的示例代码
2018/06/19 Python
解决pytorch 交叉熵损失输出为负数的问题
2020/07/07 Python
Html5移动端网页端适配(js+rem)
2021/02/03 HTML / CSS
某公司Java工程师面试题笔试题
2016/03/27 面试题
用缩写的指针比较"if(p)" 检查空指针是否可靠?如果空指针的内部表达不是0会怎么样?
2014/01/05 面试题
大学生求职自我评价
2014/01/16 职场文书
报社实习生自荐信
2014/01/24 职场文书
小学生评语集锦
2014/04/18 职场文书
小学一年级学生评语
2014/04/22 职场文书
学校政风行风评议工作总结
2014/10/21 职场文书
幼儿园教师自我评价
2015/03/04 职场文书
小学家长意见怎么写
2015/06/03 职场文书
优秀家长事迹材料(2016推荐版)
2016/02/29 职场文书
施工安全责任协议书
2016/03/23 职场文书
python 利用 PIL 将数组值转成图片的实现
2021/04/12 Python
Python基础之赋值,浅拷贝,深拷贝的区别
2021/04/30 Python
mysql sum(if())和count(if())的用法说明
2022/01/18 MySQL