JS—浅学原型链 | 青训营笔记

106 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的的第3天

上课的时候发现原型链知识点掌握得不好,那就再学一次原型链。

学习文章:这可能是掘金讲「原型链」,讲的最好最通俗易懂的了,附练习题!

原型概念及作用

概念

原型通常指的是prototype(显式原型对象)和__proto__(隐式原型对象)这两个原型对象

作用

  1. 数据共享,节约内存空间
  2. 实现继承

原型链概念及作用

概念

__proto__的路径就叫原型链

作用

查找对象的属性(方法):原型链是实例对象与原型之间的连接,若要查找某个对象上的属性或者方法,过程中会先查找自身,再顺着原型链逐级查找,若没找到则返回undefined

prototype和proto有什么关系

构造函数

在 JavaScript 中,用 new 关键字来调用的函数,称为构造函数,主要功能为初始化对象,按照规范,构造函数首字母一般大写。

function Person(name, age) { // Person就是构造函数
  this.name = name
  this.age = age
}

构造函数的实例

当构造函数以 new 关键字调用时,会创建一个新的内存空间,标记为构造函数的实例

const person1 = new Person('小明', 20) // person1是Person构造函数的实例
const person2 = new Person('小红', 30) // person2也是Person构造函数的实例

关系1:构造函数的prototype和其实例的proto指向同一个地方

函数定义方式

  1. 声明式函数定义
function fn1(name, age) {
  console.log(`我是${name}, 我今年${age}岁`)
}
fn1('Anthony', 20) // 我是Anthony, 我今年20岁
  1. 函数表达式
const fn2 = function(name, age){
  console.log(`我是${name}, 我今年${age}岁`)
}
fn2('Anthony', 20) // 我是Anthony, 我今年20岁
  1. 箭头函数
const arrowFn = (name, age) => {
  console.log(`我是${name}, 我今年${age}岁`)
}
arrowFn('Anthony', 20) // 我是Anthony, 我今年20岁

只考虑函数声明,以上三种方式都可以用new Function 的形式来声明,等同于:

const fn1 = new Function('name', 'age', 'console.log(`我是${name}, 我今年${age}岁`)')
fn1('Anthony', 20) // 我是Anthony, 我今年20岁

const fn2 = new Function('name', 'age', 'console.log(`我是${name}, 我今年${age}岁`)')
fn2('Anthony', 20) // 我是Anthony, 我今年20岁

const arrowFn = new Function('name', 'age', 'console.log(`我是${name}, 我今年${age}岁`)')
arrowFn('Anthony', 20) // 我是Anthony, 我今年20岁

上面说过,在 JavaScript 中,用 new 关键字来调用的函数,称为构造函数,所以Function也是构造函数, fn1 ,fn2 ,arrowFn函数都是它的实例。

关系2:Function构造函数的prototype和其 实例函数的proto指向同一个地方

创建对象方式

  1. 利用对象字面量创建对象
  2. 利用new Object() 创建对象
  3. 利用构造函数创建对象,创建出来的对象都是Function构造函数的实例,和上文一样
  4. Object.create创建对象,创建出来的是一个空原型的对象,不讨论它
// 字面量创建对象
const person2 = {name: 'Anthony', age: 20}
console.log(person2) // { name: 'Anthony', age: 20 }

// new Object创建对象
const person3 = new Object()
person3.name = 'Anthony'
person3.age = 20
console.log(person3) // { name: 'Anthony', age: 20 }

// Object.create创建对象
const person4 = Object.create({})
person4.name = 'Anthony'
person4.age = 20
console.log(person4) // { name: 'Anthony', age: 20 }
字面量创建对象的本质就是new Object创建对象
// new Object创建对象
const person2 = new Object()
person2.name = 'Anthony'
person2.age = 20
console.log(person2) // { name: 'Anthony', age: 20 }

上面说过,在 JavaScript 中,用 new 关键字来调用的函数,称为构造函数,所以Object也是构造函数, person2,person3都是Object构造函数的实例

关系3:Object构造函数的prototype和其 实例对象的proto指向同一个地方

Object构造函数本质上也是函数,因此它是Function构造函数的实例

关系4:Function构造函数的prototype和其 实例Object构造函数的proto指向同一个地方

Function构造函数本质上也是函数,因此它是Function构造函数的实例

关系5:Function构造函数的prototype和其 实例Function构造函数*的proto指向同一个地方

如果a是b的prototype,b即是a的constructor

原型链

Person.prototype 和 Function.prototype

  • Person.prototype,它是构造函数Person的原型对象
  • Function.prototype,他是构造函数Function的原型对象

因为都是对象,因此本质都是通过new Object()来创建的,也就是说Person.prototype 和 Function.prototype都是构造函数Object的实例。也就说明了Person.prototype 和 Function.prototype的__proto__都指向Object.prototype

原型链终点

三条原型链结尾都是Object.prototype,而Object.prototype的__proto__指向null,因此null是原型链的终点

原型继承

原型继承就是实例可以使用构造函数上的prototype中的方法,实例先找自身有没有此方法,没有就沿着原型链找

function Person(name) { // 构造函数
  this.name = name
}
Person.prototype.sayName = function() { // 往原型对象添加方法
  console.log(this.name)
}

const person = new Person('Anthony') // 实例
// 使用构造函数的prototype中的方法
person.sayName() // Anthony

instanceof

作用

判断B的prototype是否在A的原型链上

使用方法

A instanceof B

总结

  1. 构造函数的prototype和其 实例 的proto指向同一个地方
  2. 函数是Function构造函数的实例,即Function构造函数的prototype和其 实例函数 的proto指向同一个地方
  3. 对象是Object构造函数的实例,即Object构造函数的prototype和其 实例对象 的proto指向同一个地方
  4. Function构造函数是Function构造函数的实例,即Function构造函数的prototype和其 实例Function构造函数 的proto指向同一个地方
  5. Object构造函数是Function构造函数的实例,即Function构造函数的prototype和其 实例Object构造函数 的proto指向同一个地方
  6. 构造函数的__proto__指向的地方,它的constructor是此构造函数
  7. Person.prototype 和 Function.prototype都是构造函数Object的实例
  8. 原型链是__proto__的路径
  9. Object.prototype的proto指向null,是原型链终点
  10. 原型继承就是实例可以使用构造函数上的prototype中的方法,实例先找自身有没有此方法,没有就沿着原型链找
  11. A instanceof B的作用是判断B的prototype是否在A的原型链上