简单了解原型&原型链

一、基本概念

 1.什么是原型

 这里会设计到两个概念:构造函数和原型对象。

 构造函数:在 JavaScript 中,用 new 关键字来调用的函数,称为构造函数。构造函数首字母一般大写。

 原型对象:初始化一个构造函数,它有一个 prototype 属性,该属性值指向的就是构造函数的原型对象

function Person (name, age) {
    this.name = name
    this.age = age
}
​
console.log(Person.prototype)  

比如上文的Person.prototype指向的就是Person的原型

当然了,只有构造函数才有 prototype 这个属性,其他对象是没有的

我们来看看原型的定义:

原型本质是一个对象,原型对象是为了给其他实例对象提供共享属性的

2.访问原型对象

首先通过构造函数初始化一个实例对象

const person1 = new Person('高启强', 38)
console.log(person1);

左边红色框表示调用的哪个,右边是对象实例。

 可以看到 person1 只具有构造函数中初始化的两个默认属性 name 和 age

上面说到构造函数可以通过prototype属性访问到原型对象,那么这个构造函数的实例对象如何访问到这个原型对象呢?

我们可以使用__proto__属性

console.log(person1.__proto__ === Person.prototype) 

看一下控制台打印:

 我们可以看到这两个是完全等价的

__proto__最初是由各个浏览器厂商独立实现的 hack 方法,目前已收入到ES6规范中,可以放心使用

当然,更推荐的还是使用 ES6 规定的获取原型的方法Object.getPrototypeOf()

console.log(Object.getPrototypeOf(person1) === Person.prototype)    // true

反过来,如何通过原型对象访问到构造函数呢?原型对象有个 constructor 属性:

console.log(Person.prototype.constructor === Person)    // true

每个原型对象都有一个 constructor 属性指向关联的构造函数

3.共享属性

如果我们访问一个构造函数没有定义过的属性会怎么样?

console.log(person1.work)  // undefined

当然是 undefined 了,没定义就没有,我们找不到它。

那我们尝试在原型对象上定义它,再次记住原型对象的作用:给实例对象提供共享属性

Person.prototype.work= '杀鱼'

OK,我们尝试在实例对象上访问这个属性

console.log(person1.work)  // 杀鱼

看一下控制台打印:

成功了!

那么 person1 会有什么变化吗?打印下看看

console.log(person1) // Person {name: 'Jason', age: 21}

person1 是没有 work属性,因为它并没有在 person1 或者构造函数中定义过

那 person1.work的值哪来的?答案已经呼之欲出

从 person1 的原型对象上拿到的

person1.work=== person1.__proto__.work

所以我们可以得出第一个结论,当找不到对象的某个属性时,它会继续往这个对象的原型对象上找

我们再用这个构造函数初始化一个对象

const person2 = new Person('高启盛', 22)

看一看这个 person2 长啥样

console.log(person2)    // Person {name: '高启盛', age: 22}

person2 同样也没有 work 属性

console.log(person2.name +"的工作是"+ person2.work)

看一下控制台打印:

可以看到我们声明的第二个人物同样拿到了work这个属性,说明虽然没有这个属性,但是也能访问到原型对象上的共享属性 work,简单说只要是这个对象new出来的都可以使用它。

console.log(person2.__proto__.work=== person2.work)    // true

没错,这俩个就是完全等价的!

我们通过 person2 改变这个绑定在原型上的属性值试试,注意看我这里改变的是person2原型上的work变成了白金瀚的老板,打印出来person1的work也变成白金瀚的老板。也就是原型对象上的work属性是全部实例对象所共享的,一有俱有,一改俱改

这样我们应该就理解了,原型对象是用来提供共享属性这句话了

person2.__proto__.work = '白金瀚老板'
console.log("现在"+person1.name +"的工作是"+ person1.work)

看一下控制台打印:

二、原型链

1.访问对象的某个属性当找不到时?

我们知道了访问对象的某个属性当找不到时,会往它的原型对象上找,如果原型对象上也找不到呢?

首先看看 person1 原型对象目前长啥样

console.log(person1.__proto__) 

看一下控制台打印:

 我们应该可以猜到,原型对象也是对象,在原型对象上没找到,那就继续往上去找!

我们来看看原型的原型是什么

console.log(person1.__proto__.__proto__)    

看一下控制台打印:

 这一眼看过去,好像也不知道是什么,但你是否记得上面提到过的:

每个原型对象都有一个 constructor 属性指向关联的构造函数

我们访问原型的原型的 constructor 属性试试

console.log(person1.__proto__.__proto__.constructor)    // ƒ Object()

没想到吧,实例对象原型的原型就是 Object 的原型对象,其实Object是原型的顶层原型对象

因为是顶层原型对象,它的__proto__指向null。

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

借用冴羽老师的一张图就清楚其中的关系了:

图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线。

简单来说就是,找 person1 对象某个属性没找到,就通过__proto__访问原型一层一层地找,直到找到这个属性或者到 null 位置结束,相互关联的原型就构成了原型链。这样是不是也不难理解。

三.扩展

通过上面的知识我们知道了,构造函数 Person 的prototype也被称为显式原型,实例对象 person 的__proto__也被称为隐式原型

即构造函数的显式原型和实例对象的隐式原型是同一个对象

function Person() {
}
const person = new Person()
​
Person.prototype === person.__proto__  // true

那么构造函数的隐式原型__proto__指向什么呢?

指向Function的原型,即

Person.__proto__ === Function.prototype // true
Object.__proto__ === Function.prototype // true

大家有时间可以看看这篇文章:https://juejin.cn/post/7064188728704499726

里面这位老哥有更详细的解读,本文如果有错误的话也欢迎大家来指正。


总结

  • 原型对象的本质其实就是一个对象
  • 原型的作用是给其他对象提供共享属性,即用来实现基于原型的继承与属性的共享
  • 相互关联的原型组成的链状结构就是原型链
  • 原型链的顶级原型对象是Object,且最终一定指向 null
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

混世大魔王955

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值