文档章节

javascript中的prototype和__proto__的理解

开元中国2015
 开元中国2015
发布于 05/28 10:34
字数 2006
阅读 9
收藏 0

在工作中有时候会看到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

输出结果如下:

总结:

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

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

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

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

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

上图展示了 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。如下图所示

 

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,最后一步才会找到该方法。

 

您可能感兴趣的文章:

文章同步发布: https://www.geek-share.com/detail/2769998109.html

© 著作权归作者所有

开元中国2015
粉丝 44
博文 66
码字总数 96190
作品 0
大兴
私信 提问
js中的prototype和构造函数

js中没有类的感念,继承也是通过原型链来实现的对象的继承而不是类的继承 Vjeux写的这篇文章应该能帮助我们更好的理解js中原型的工作方式:http://blog.vjeux.com/2011/javascript/how-proto...

leo-H
2013/04/08
619
0
JavaScript中__proto__与prototype的关系

JavaScript中proto与prototype的关系 这里讨论下对象的内部原型(proto)和构造器的原型(prototype)的关系。 一、所有构造器/函数的proto都指向Function.prototype,它是一个空函数(Empty f...

泡不烂的凉粉
2013/05/21
112
0
一个后端眼中的js对象和原型链

js对象 js中的类型有: 所以对象可以这么分类: 函数对象(function) 其他对象 所以函数对象可以大声唱我们不一样,既然不一样肯定得有一点不一样的烟火,函数对象的不一样在什么地方呢?每一...

trayvon
2017/10/19
20
0
关于javascript的原型和原型链,看我就够了(一)

关于js的原型和原型链,有人觉得这是很头疼的一块知识点,其实不然,它很基础,不信,往下看 要了解原型和原型链,我们得先从对象说起 创建对象 创建对象的三种方式: 对象直接量 通过对象直...

陌上寒
2018/11/03
0
0
JS中原型链中的prototype与_proto_的个人理解与详细总结

一直认为原型链太过复杂,尤其看过某图后被绕晕了一整子,今天清理硬盘空间(渣电脑),偶然又看到这图,勾起了点回忆,于是索性复习一下原型链相关的内容,表达能力欠缺逻辑混乱别见怪(为了...

Jack088
2018/12/05
22
0

没有更多内容

加载失败,请刷新页面

加载更多

计算机实现原理专题--二进制减法器(二)

在计算机实现原理专题--二进制减法器(一)中说明了基本原理,现准备说明如何来实现。 首先第一步255-b运算相当于对b进行按位取反,因此可将8个非门组成如下图的形式: 由于每次做减法时,我...

FAT_mt
昨天
6
0
好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
昨天
7
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
昨天
6
0
【技术分享】TestFlight测试的流程文档

上架基本需求资料 1、苹果开发者账号(如还没账号先申请-苹果开发者账号申请教程) 2、开发好的APP 通过本篇教程,可以学习到ios证书申请和打包ipa上传到appstoreconnect.apple.com进行TestF...

qtb999
昨天
10
0
再见 Spring Boot 1.X,Spring Boot 2.X 走向舞台中心

2019年8月6日,Spring 官方在其博客宣布,Spring Boot 1.x 停止维护,Spring Boot 1.x 生命周期正式结束。 其实早在2018年7月30号,Spring 官方就已经在博客进行过预告,Spring Boot 1.X 将维...

Java技术剑
昨天
18
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部