轻松搞懂js原形和原型链,只需要打开Chrome

!!!想要搞懂原型链,你一定要打开Chrome,跟着笔者一步一步往下点,你即刻可以掌握

切入正题前,笔者先来定义两个名词,可能会与其他地方叫法不同,但笔者觉得这样更容易理解。

  • prototype 原形对象
  • __proto__ 实例对象

prototype和proto的概念

  • prototype
    prototype是函数的一个属性,指向其构造函数的原型,它是一个对象,我们称之为原形对象
    只有函数有这一个对象,不过不止包括Function,包括自定义函数和内置函数。
    所有函数都是Function的实例,Object、Array、Date、Number、String等都是Function构造出来的函数,因此它们都有prototype属性
    在这里插入图片描述
  • __proto__
    __proto__是实例对象,指向其构造函数的原形对象。所以实例化的对象都有这一属性,而js中一切皆对象,可以理解为除了null,都有__proto__属性

在这里插入图片描述
注:__proto__是一个隐形的属性,只能在浏览器内部使用,外部不可见这一属性,但可以通过Object.getPrototypeOf(o)来获取

prototype和proto的详解

秘籍:想要搞懂原型链,打开Chrome调试,一直点点点进去就明白了

  • prototype
function Foo(){}
// 只有函数才有prototype属性
console.log(Foo.prototype)

在这里插入图片描述
空函数的prototype只有一个constructor属性,constructor指向构造函数Foo

Foo.prototype.constructor === Foo // true

在这里插入图片描述
在这里插入图片描述

  • proto
    proto是经过new的实例所拥有的属性,其指向构造函数的原形对象,即Foo.prototype
var foo = new Foo()
console.log(foo.__proto__)  
console.log(foo.__proto__ === Foo.prototype) // true

在这里插入图片描述
在这里插入图片描述

  • 从原型链添加属性
    由于proto是一个指针,因此当prototype的值改变的话,proto也能获取改变后的值
Foo.prototype.name = 'cxm'
console.log(Foo.prototype)
console.log(foo.__proto__)
console.log(foo.name)

在这里插入图片描述
解释:我们并没有给foo赋予对象,但为什么还是能拿到name的值呢?是因为js访问对象的值的时候会首先去访问当前对象有没有name的属性,如果没有,则通过原型链去找,找proto下的值,找到了即停止,直到null
在这里插入图片描述
但实例的foo也添加属性name会怎么样呢?

foo.name = 'mao'
console.log(foo.name)

在这里插入图片描述
可以看到name变成了foo的属性,上面我们说过,js去找对象的属性的时候会默认去寻找自身的属性,找不到才会向原型链上找,并且不会改变原型链上的属性。判断是否是自身是属性可以使用obj.hasOwnProperty()
在这里插入图片描述

  • 从原型链删除属性
    当我们把foo上name的属性删除了会怎么样呢?
foo.name = 'mao' 
console.log(foo.name) // mao,自身添加的属性
console.log(foo.hasOwnProperty('name')) // true
delete foo.name // 删除自身的属性
console.log(foo.name) // 使用了原型链上的属性
console.log(foo.__proto__)

在这里插入图片描述
foo删除自身的属性后,放name为null,则通过proto向原型链找,原型链找到name=‘cxm’,则返回。但这个用obj.hasOwnProperty()去检测的时候就检测到不是自身的属性了
在这里插入图片描述
若在Foo.prototype删除了会怎样呢,毫无疑问,肯定为undefined了
在这里插入图片描述
在这里插入图片描述

  • Foo.__proto__
    Foo为js对象,是由Function实例化的。那么其必然也有__proto__属性,那么它的proto属性是指向谁呢?
    这里有个诀窍来找,即谁构造它,它就指向谁
    那Foo是有Function构造的,其必然指向Function.prototype,下面来验证一下
console.log(Foo.__proto__ === Function.prototype)

在这里插入图片描述
在这里插入图片描述

  • Foo.prototype.__proto__
    Foo.prototype指向谁呢,还是上面那句话,谁构造它,它就指向谁。
    Foo.prototype是一个对象,对象的构造函数那就是Object,所以下面就有答案了,它肯定指向Object.prototype

在这里插入图片描述
从上图我们确实可以看到Foo.prototype是有proto属性的

console.log(Foo.prototype.__proto__ === Object.prototype) // true

在这里插入图片描述在这里插入图片描述

那么Object又是谁构造的呢,Object作为顶级的原型链,它已经是老大哥,没有指向了,它最终指向null

console.log(Object.prototype.__proto__) // null

在这里插入图片描述
在这里插入图片描述

至此,先总结一波
  • 除了Object.__proro__指向null外,其他对象的prototype都指向Object.prototype。Why?因为prototype是一个ObjectObject自然是由Object.prototype来构造的
  • 对象的__proto__指向其构造函数,即谁生成它,它就指向谁。即谁构造它,它就指向谁。

下面列举一波,大家也可以多到Chrome去调试,更容易掌握原型链

console.log(Number.prototype.__proto__ === Object.prototype) // true
console.log(Function.prototype.__proto__ === Object.prototype) // true
var num = 1
console.log(num.__proto__.__proto__ === Object.prototype) // true

在这里插入图片描述

  • 给Object.prototype添加属性
    给顶级的原型链Object添加属性会怎么样呢?我们知道js去获取对象的属性时是递归向上遍历的,如果在自身没有找到会向上查找,那么给Object添加一个属性后,即所有js对象都可以访问地到。
Object.prototype.topName = 'cxm'
console.log(Foo.topName) // 'cxm'
console.log(num.topName) // 'cxm'
console.log(foo.topName) // 'cxm'
console.log(String.topName) // 'cxm'
console.log(Date.topName) // 'cxm'

在这里插入图片描述
Object.prototype对象的值
在这里插入图片描述
在这里插入图片描述

  • 给Object.prototype添加方法
    跟属性一样,它也是一个属性,只不过这个属性是一个方法,那么在所有的原型链都可以调用到这个方法。
    下面给Object.prototype添加一个叫whoAmI的方法,用来判断自身是哪一个哪一个对象,非常实用。
Object.prototype.whoAmI = function() {
	// this指向调用者,谁调用它,它就指向谁
	var str = Object.prototype.toString.call(this)
	// [object String],按这个格式截取
	var type = str.substring(8, str.length - 1)
	console.log(`I am ${type}`)
	return type
}

测试

var str = 'cxm'
var num = 123
var bool = true
var obj = {}
var date = new Date()
var promise = new Promise(() => {})

结果如下,所有的类型都可以调用whoAmI函数,
在这里插入图片描述
在这里插入图片描述

总结

!!!想要弄懂原型链,打开Chrome,一直点点点即可

  • prototype 原形对象,可以简化理解为指向本身,比如Foo.prototype的指向就是跟本身有关的(其实是指向构造函数的原形)
  • __proto__ 实例对象,可以简化理解为指向它爹,谁构造它,它就指向谁,永远指向上一级。
  • Object.prototype.__proto__指向null外,其他的prototype.__proto__都指向Object.prototype
  • 原型链上定义的属性都会被其自身和孩子继承,其孩子也可以定义相同的熟悉,只会覆盖并不会替换

完整的原型链图
在这里插入图片描述
终极原型链,最后执行null

console.log(foo.__proto__.__proto__.__proto__) // null

在这里插入图片描述

The End

笔者是阅读了网上的许多材料和根据自己的理解来整理的,其中可能会有一些不正确或描述模糊地地方,请大家见谅,也欢迎大家指出。

有任何问题的,欢迎骚扰

微信QQ公众号
在这里插入图片描述在这里插入图片描述在这里插入图片描述

参考链接:https://www.jianshu.com/p/be7c95714586

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值