浅谈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实现密码验证
Nov 10 Javascript
jQuery Easyui加载表格出错时在表格中间显示自定义的提示内容
Dec 08 Javascript
从零学习node.js之搭建http服务器(二)
Feb 21 Javascript
JavaScript无阻塞加载和defer、async详解
Feb 26 Javascript
js实现省市级联效果分享
Aug 10 Javascript
微信小程序支付之c#后台实现方法
Oct 19 Javascript
原生javascript AJAX 三级联动的实现代码
May 04 Javascript
解决vue-cli项目webpack打包后iconfont文件路径的问题
Sep 01 Javascript
vue+axios+element ui 实现全局loading加载示例
Sep 11 Javascript
基于iview的router常用控制方式
May 30 Javascript
echarts饼图各个板块之间的空隙如何实现
Dec 01 Javascript
小程序实现筛子抽奖
May 26 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
php图片加水印原理(超简单的实例代码)
2013/01/18 PHP
PHP 使用redis简单示例分享
2015/03/05 PHP
php简单实现无限分类树形列表的方法
2015/03/27 PHP
php抽象类用法实例分析
2015/07/07 PHP
解决laravel中日志权限莫名变成了root的问题
2019/10/17 PHP
页面中js执行顺序
2009/11/09 Javascript
JS实现图片预加载无需等待
2012/12/21 Javascript
JS的encodeURI和java的URLDecoder.decode使用介绍
2014/05/08 Javascript
在JavaScript中重写jQuery对象的方法实例教程
2014/08/25 Javascript
javascript实现简单的html5视频播放器
2015/05/06 Javascript
创建基于Bootstrap的下拉菜单的DropDownList的JQuery插件
2016/06/02 Javascript
使用JS中的exec()方法构造正则表达式验证
2016/08/01 Javascript
jQuery快速实现商品数量加减的方法
2017/02/06 Javascript
angular实现IM聊天图片发送实例
2017/05/08 Javascript
Angularjs实现多图片上传预览功能
2018/07/18 Javascript
vue写h5页面的方法总结
2019/02/12 Javascript
Vue自定义全局Toast和Loading的实例详解
2019/04/18 Javascript
javascript实现左右缓动动画函数
2020/11/25 Javascript
[04:51]TI10典藏宝瓶Ⅱ外观视频展示
2020/08/15 DOTA
Python中的闭包详细介绍和实例
2014/11/21 Python
Python实现的各种常见分布算法示例
2018/12/13 Python
python 实现识别图片上的数字
2019/07/30 Python
Django中文件上传和文件访问微项目的方法
2020/04/27 Python
Pycharm及python安装详细教程(图解)
2020/07/31 Python
泰国网上购物:Shopee泰国
2018/09/14 全球购物
英国美发和美容产品商城:HQhair
2019/02/08 全球购物
英国川宁茶官方网站:Twinings茶
2019/05/21 全球购物
马智宇婚礼主持词
2014/03/22 职场文书
《水上飞机》教学反思
2014/04/10 职场文书
cf战队收人口号
2014/06/21 职场文书
软件研发工程师岗位职责
2014/09/30 职场文书
2015重阳节座谈会主持词
2015/07/30 职场文书
初中运动会闭幕词范本3篇
2019/12/09 职场文书
windows11怎么查看wifi密码? win11查看wifi密码的技巧
2021/11/21 数码科技
Apache Hudi集成Spark SQL操作hide表
2022/03/31 Servers
MySQL索引 高效获取数据的数据结构
2022/05/02 MySQL