《原型链重置版》一万多字让你读懂JavaScript原型对象与原型链的继承,探秘属性的查找机制! (2)

简介: 知道什么是原型对象!

logo.png

原型的定义

我们说在js当中一切皆对象,对吧, 那么在js函数对象中都有一个内置的Prototype属性

这个属性指向一个对象,你可以把这个Prototype属性想象成一个指针, 它指向一个对象

而这个对象就成为原函数对象原型,俗称原型对象

我们来看一段简单的代码:

function Test(){
   
   

}

console.log(Test.prototype);

var T1=new Test();
var T2=new Test();
var T3=new Test();

console.log(T1.__proto__);
console.log(T2.__proto__);
console.log(T3.__proto__);
AI 代码解读

结果

1.png

这里还要给大家科普一个小知识,就是普通对象没有原型对象的,也就是说不是函数对象 也可以说成不是通过new Function创建的对象,那么就不会存在原型对象

我们来验证一下

代码如下

//普通对象
var json={
   
   }
console.log(json.prototype);

//元素对象
var oDiv = document.getElementById("connent");
console.log(oDiv.prototype);


//函数对象
function test(){
   
   

}
console.log(test.prototype);
AI 代码解读

结果如下

1-0-1.png

所以说首先只有函数对象才会有原型对象

理解构造函数、实例化对象、原型对象彼此之间的关系

这样就形成了每个函数对象其实都有一个指向另一个对象的指针

这里我们要说明一点的就是,只有函数才有一个prototype属性,这个prototype属性就是我们的原型对象

同时从图中也可以看到原型当中含有一个constructor 属性,而这个属性指向的就是当前原型对象构造函数

我们一般会拿这个构造函数通过new创建出来实例对象,而实例对象是没有prototype属性的!

如果你在一个实例对象上调用prototype必然返回undefined, 而一个实例对象靠的是使用__proto__的隐式属性,进行访问原型对象!

如图

2-2.jpg

如果按照这个逻辑推理的话,你可以使用以下代码进行验证一下,是否正确:

console.log(T1.__proto__==Test.prototype);          //返回true
console.log(Test.prototype.constructor==Test);        //返回true
AI 代码解读

结果证明的确是这样子,实例对象自己会有一个指针属性为__proto__, 用它来指向构造函数原型对象

其实你也可以使用isPrototypeOf方法来判断当前这个实例对象中的__proto__指针到底是不是能够指向到本身构造函数原型对象中!

例如

//构造函数1
function Person(name,age) {
   
   

}

var p1=new Person();


//构造函数2
function Test(){
   
   

}
var test=new Test();

console.log(Person.prototype.isPrototypeOf(p1));   //返回true
console.log(Person.prototype.isPrototypeOf(test)); //返回false
AI 代码解读

以上我们用了isPrototypeOf方法来监测一个实例对象中的__proto__指针是否指向对应的原型对象

js中我们还可以使用一个叫Object.getPrototypeOf()的方法来监测实例对象原型对象之间的关系

例如

console.log(Object.getPrototypeOf(p1)==Person.prototype);  //返回true
console.log(Object.getPrototypeOf(p1)==Test.prototype);    //返回false
AI 代码解读

所以从结果上看

使用这个Object.getPrototypeOf方法返回的就是当前实例对象__proto__属性所指向的原型对象

同时constructor属性也的确指向了本身的构造函数

这样原型对象实例对象之间就通过__proto__连接在一起,形成了一个链条, 而所谓的原型链也就是实例对象原型对象之间的链条关系, __proto__这条线,也就是原型链的关键

并且这条链条,从图中我们也可以看到,还可以往上走到一个叫Object.prototype的地方!

实例对象属性和方法搜索的优先级

上面说了,有了原型对象,那么实例对象可以共享原型对象中的属性方法

那么问题来了,这些实例对象又是如何进行查找属性和方法的呢?

举个栗子

function createPerson(name,age) {
   
   
    this.name=name;
    this.age=age;

    this.say=function (){
   
   
        console.log('2.构造函数中定义的say方法!');
    }
}

createPerson.prototype.say=function () {
   
   
    console.log('我的名字叫:'+this.name);
}

var a=new createPerson('张三','33');
var b=new createPerson('李四','55');
var c=new createPerson('王武','66');


c.say=function(){
   
   
    console.log('1.实例对象c 定义的say方法!');
}

a.say();
b.say();
c.say();
AI 代码解读

结果如下:

4.png

代码分析

按照这个查找逻辑上来看的话,调用的查找方式如下:

先在实例对象上查找定义的属性方法,优先级最高,如果找不到的情况下,然后再是构造函数中进行查找我们定义的属性方法, 最后如果也找不到的情况下,就到原型对象中去寻找!

注意:这并不是把原型对象中的属性方法覆盖了,只是优先调用的顺序而已!

例如

function Person(){
   
   

}

Person.prototype.username='张三';
Person.prototype.age=30;
Person.prototype.job='设计师';
Person.prototype.say=function (){
   
   
    console.log('我是'+this.username);
}

var p1=new Person();
var p2=new Person();

p1.username='李四';

console.log(p1.username);
console.log(p2.username);
AI 代码解读

代码分析

首先这里构造函数中我们什么都没有定义的情况下,这里就是先搜索实例对象本身,如果在实例对象中找到了具有给定的属性或者方法则进行返回!

如果没有找到,则会根据一个叫__proto__指针原型对象中去寻找,如果找到就返回! 如果最终都没有找到则返回undefined

那么这个案例中,则执行了两次搜索!先询问了实例对象本身是否具有, 然后顺着指针到原型对象中去询问

所以说我们在实例对象上调用属性方法的时候,都会出现以上相同的搜索过程!

而有了这个搜索模式的帮助下,多个实例对象则可以共享原型对象所定义的属性方法就是这个原理!

如图

4-1.jpg

这里我再次提一嘴,前面不是使用到了constructor属性吗, 这个属性也默认是共享的,也就是所有实例对象默认情况下,都可以通过访问这个属性来确定构造函数是谁!

所以大家也应该注意一下,就是如果你在实例对象上定义了一个属性或者方法,而且原型对象中也定义了同名的属性或者方法,依照查找的顺序会依次搜索实例对象--->构造函数--->原型对象, 即便是同名也是优先调用最先找到的位置!

也就是说当你在实例对象上定义一个与原型对象中同名属性方法的时候,会自动屏蔽原型对象中的同名属性和方法,注意这里也仅仅是屏蔽,而不是覆盖! 当然换句话说也可以理解为你在实例对象上添加同名属性和方法的时候,会阻止访问原型对象中的同名属性和方法,明白这个意思吧!

即便是你在实例对象上把某个属性和方法的值定义为null , 那么访问的时候也只会停留在实例对象这个层面,而不会恢复其指向构造函数原型对象的链接!

但是如果你使用delete操作符是可以完全删除实例属性同时也删除构造函数中的同名属性,从而让我们能够访问原型对象中的同名属性!

例如

function Person(){
   
   
    this.username='李四';
}

Person.prototype.username='王五';
Person.prototype.age=30;
Person.prototype.job='设计师';
Person.prototype.say=function (){
   
   
    console.log('我是'+this.username);
}

var p1=new Person();

p1.username=null;

delete p1.username;

console.log(p1.username);  //这里输出的结果来自于 原型对象
AI 代码解读

其实我们就可以按照这个查找逻辑,来修改原型对象从而实现父子继承的关系 这个我们后面再说!

__proto__的真正含义!

那么实例对象到底底层是如何查到原型对象中去的呢?

这其实就要说到刚刚我们提及到的__proto__这个东西了! 嘿嘿

我们来看一张图:

如图

5.png

每个实例对象都会有一个 __ proto__ 属性,这个属性是自动生成的, __ proto__ 属性指向自己的原型对象

而且实例对象也就是通过这一条__ proto__线路,找到原型对象中的属性方法

这就是我马上要提到的原型链

特别注意

这里我提醒一下,可能你以前看到的也的确是叫__proto__这个

但是目前Chrome打印出来之后效果提示的是[[Prototype]]

如图

6.png

这里只是显示变了而已,代码层面上,实例对象还是可以继续使用__proto__这个属性的

然而__proto__的真正意义也就在于两个字:查找 也就是接下来要说的原型链

因为原型链就是通过__proto__属性形成的,任何对象普通对象函数对象都有__proto__属性

prototype与__proto__的区别

其实我们在上面的图中也能看出来彼此的一个很明显的区别:

__proto__实例对象指向原型对象的指针,我们俗称隐式原型,并且是每个实例对象都会有的一个属性!

prototype构造函数/函数才有的原型对象,我们俗称为显式原型

这里我特别提一下,其实prototype就是一个用来设置原型,而另外一个__proto__则用来查找数据,如果你还不明白,那么就看下面的原型链解释就清楚了!

所以说大家不要再把__proto__与函数的 func.prototype 属性混淆了!

目录
打赏
0
0
0
0
199
分享
相关文章
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
如何确保 Math 对象的方法在不同的 JavaScript 环境中具有一致的精度?
【10月更文挑战第29天】通过遵循标准和最佳实践、采用固定精度计算、进行全面的测试与验证、避免隐式类型转换以及持续关注和更新等方法,可以在很大程度上确保Math对象的方法在不同的JavaScript环境中具有一致的精度,从而提高代码的可靠性和可移植性。
JavaScript 原型链的实现原理是什么?
JavaScript 原型链的实现原理是通过构造函数的`prototype`属性、对象的`__proto__`属性以及属性查找机制等相互配合,构建了一个从对象到`Object.prototype`的链式结构,实现了对象之间的继承、属性共享和动态扩展等功能,为 JavaScript 的面向对象编程提供了强大的支持。
JavaScript中对象的数据拷贝
本文介绍了JavaScript中对象数据拷贝的问题及解决方案。作者首先解释了对象赋值时地址共享导致的值同步变化现象,随后提供了五种解决方法:手动复制、`Object.assign`、扩展运算符、`JSON.stringify`与`JSON.parse`组合以及自定义深拷贝函数。每种方法都有其适用场景和局限性,文章最后鼓励读者关注作者以获取更多前端知识分享。
41 1
JavaScript中对象的数据拷贝
深入浅出Node.js中间件机制
【10月更文挑战第36天】在探索Node.js的奥秘之旅中,中间件的概念如同魔法一般,它让复杂的请求处理变得优雅而高效。本文将带你领略这一机制的魅力,从概念到实践,一步步揭示如何利用中间件简化和增强你的应用。
原型链在 JavaScript 中的作用是什么?
原型链是 JavaScript 中实现面向对象编程的重要机制之一,它为代码的组织、复用、扩展和多态性提供了强大的支持,使得 JavaScript 能够以简洁而灵活的方式构建复杂的应用程序。深入理解和熟练运用原型链,对于提升 JavaScript 编程能力和开发高质量的应用具有重要意义。
如何使用原型链继承实现 JavaScript 继承?
【10月更文挑战第22天】使用原型链继承可以实现JavaScript中的继承关系,但需要注意其共享性、查找效率以及参数传递等问题,根据具体的应用场景合理地选择和使用继承方式,以满足代码的复用性和可维护性要求。
JavaScript中的原型和原型链
JavaScript中的原型和原型链
115 0
【面试题】最详尽的 JS 原型与原型链终极详解(一)
【面试题】最详尽的 JS 原型与原型链终极详解(一)
174 0

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等