JS原型链通俗讲解

写在开头的话

本文章将从感性的角度理解JS原型链,文章内容将以通俗的语言讲解,力求每个人都能明白JS中原型的含义,但也因为这个原因,本文内容并不严谨,本文重在理解原型概念,而不是明白底层的实际操作!!!

什么是原型链

在理解原型之前,我们先来“跳跃式”地来看一下原型链:原型链实际上就是继承,即子类继承父类的属性和方法

一个proto的例子

假设有两个变量,它们均为对象:

在JS中变量名和函数名可以用汉字,但实际项目中不建议这样做,以下仅作演示

var 中国={};
中国.位置="亚洲";
中国.展示位置=function(){console.log(this.位置)};
var 陕西={};

我现在想让对象陕西继承中国的属性和方法,这时该怎们做呢?

先别急,我们先来看这样一个示例:

var 示例={名称="普通对象"};

这时,如果我们调用示例.名称,我们得到的就是"普通对象",接下来我给你变个魔术

var 示例={};
示例.__proto__={名称="普通对象"};

注:__porto__属性左右各有两个下划线,因为CSDN文章编写问题,以下均写为proto
相比于上一段代码,我们这次给示例添加了一个名为proto的属性,并且proto属性是一个对象,最后,我们把名称放到了proto对象下
这时我们再调用示例.名称,我们得到还是"普通对象"!!!
这时候你可能会问:
"名称"属性应该在示例.proto下才对呀?示例.__proto__.名称这样的写法才正确啊!
*确实,名称属性就是在示例.proto这个对象中的,但proto属性是JS中一个特殊的属性!!!*实际上当我们调用示例.名称时,会发生以下操作:

  1. 示例中寻找属性名称
  2. 示例.名称不存在
  3. 试图在示例.proto中寻找名称
  4. 示例.proto存在属性名称
  5. 返回示例.proto.名称

让我们来看一看到底发生了什么,首先,当我们调用示例.名称时,底层为我们在示例对象中寻找名称属性,可是并未找到,接下来就是最关键的一步!!这时,底层会试图在示例.__proto__中寻找名称属性,最后,它找到了,所以我们调用示例.名称,却返回了示例.__proto__.名称
综上所述,我们可以概括,当我们调用一个对象的属性或方法时,如果这个对象没有该属性或方法,底层就会自动到__proto__属性中去寻找

var 中国={};
中国.位置="亚洲";
中国.展示位置=function(){console.log(this.位置)};
var 陕西={};

回到开头的问题:
让对象陕西继承中国的属性和方法其实很简单,只需要将陕西proto属相指向中国就行了:

var 中国={};
中国.位置="亚洲";
中国.展示位置=function(){console.log(this.位置)};
var 陕西={__proto__:中国};

这时,如果我们调用陕西.展示位置(),将输出亚洲,这时过程图:

  1. 陕西中寻找方法展示位置
  2. 陕西.展示位置不存在
  3. 试图在陕西.proto中寻找展示位置
  4. 陕西.proto也就是中国存在方法展示位置
  5. 返回中国.proto.展示位置
  6. 执行展示位置,打印this.位置
  7. 因为在陕西下调用展示位置所以this.位置替换为陕西.位置,可它不存在
  8. 试图在陕西.proto中寻找位置
  9. 找到陕西.proto.位置
  10. 返回它,也就是中国.位置

proto属性也可以套娃

var 中国={};
中国.位置="亚洲";
中国.展示位置=function(){console.log(this.位置)};
var 陕西={__proto__:中国};
陕西.位置="黄河流域";
var 西安={__proto__:陕西};

仍可以正常调用西安.展示位置()输出"黄河流域",过程与前文一致,只不过是先到陕西中寻找位置,输出黄河流域

以上就是proto属性的用法,proto属性是一个对象(Object类型)你可以把它理解为一个跳板,当调用对象中没有的方法时,底层就回到proto属性中寻找

一个prototype的例子

请看下面的例子:

var 中国={};
中国.位置="亚洲";
中国.展示位置=function(){console.log(this.位置)};
中国.全名="中华人民共和国";
var 陕西={__proto__:中国};

如果我们调用陕西.全名将返回中华人民共和国,因为陕西proto属性指向中国,所以我们可以在陕西下调用中国任何属性和方法,调用陕西.全名显然是很荒谬的!!!为了解决这一问题,我们应将陕西proto属性指向中国可以被继承的属性和方法

var 中国={};
中国.可被继承={};
中国.可被继承.位置="亚洲";
中国.可被继承.展示位置=function(){console.log(this.位置)};
中国.全名="中华人民共和国";

var 陕西={__proto__:中国.可被继承};

这样,我们在调用陕西的属性和方法时,只会在陕西本身和中国.可被继承中寻找,这时陕西.全名就是undefined
当然,在实际项目中,这个可被继承对象,被命名为prototype
即,规范书写为:

var 中国={};
中国.全名="中华人民共和国";
中国.prototype={};
中国.prototype.位置="亚洲";
中国.prototype.展示位置=function(){console.log(this.位置)};

var 陕西={__proto__=中国.prototype};
陕西.简称="秦,陕";
陕西.prototype={__proto__=中国.prototype};
陕西.prototype.位置="黄河流域";

var 西安={__proto__:陕西.prototype};

注:
陕西={__proto__=中国.prototype}是为了实现陕西.展示位置()

陕西.prototype={__proto__=中国.prototype}
是为了实现西安.展示位置()

如果你没看懂上面的例子,没关系,我将会在下一节深入解释它
以上就是prototype属性的用法,prototype属性是一个对象(Object类型)你可以把它理解为一个可被继承列表,里面存放着期望被继承的属性和方法
注:prototype仅仅是期望被继承的!!!毕竟你仍可以var 陕西={__proto__=中国}

原型的概念

在实际项目中,对象的proto属性总是指向另一个对象的prototype属性,如:var 陕西={__proto__=中国.prototype}
即在当前对象中找不到指定属性方法时,去另一个对象可被继承对象中寻找
如上,此时我们称:中国陕西原型!!!

以下是一些关于原型链的规则:
在实际项目中,
任何对象(Object类型)都有proto属性,
只有函数默认拥有prototype属性,
任何函数.prototype.__proto__指向Object.prototypeObject.prototype.__proto__null
所有数据类型的构造函数的__proto__属性一致

小结

以上就是本节的内容,当然像是原型链的规则或是JS的new关键字以及项目中的实际继承等详细信息我会下节讲(constructor也推到之后吧…),希望通过本节的介绍你能对JS原型链有初步的概念。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值