这是我参与「第四届青训营 」笔记创作活动的的第3天
上课的时候发现原型链知识点掌握得不好,那就再学一次原型链。
学习文章:这可能是掘金讲「原型链」,讲的最好最通俗易懂的了,附练习题!
原型概念及作用
概念
原型通常指的是prototype(显式原型对象)和__proto__(隐式原型对象)这两个原型对象
作用
- 数据共享,节约内存空间
- 实现继承
原型链概念及作用
概念
__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指向同一个地方
函数定义方式
- 声明式函数定义
function fn1(name, age) {
console.log(`我是${name}, 我今年${age}岁`)
}
fn1('Anthony', 20) // 我是Anthony, 我今年20岁
- 函数表达式
const fn2 = function(name, age){
console.log(`我是${name}, 我今年${age}岁`)
}
fn2('Anthony', 20) // 我是Anthony, 我今年20岁
- 箭头函数
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指向同一个地方
创建对象方式
- 利用对象字面量创建对象
- 利用new Object() 创建对象
- 利用构造函数创建对象,创建出来的对象都是Function构造函数的实例,和上文一样
- 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
总结
- 构造函数的prototype和其 实例 的proto指向同一个地方
- 函数是Function构造函数的实例,即Function构造函数的prototype和其 实例函数 的proto指向同一个地方
- 对象是Object构造函数的实例,即Object构造函数的prototype和其 实例对象 的proto指向同一个地方
- Function构造函数是Function构造函数的实例,即Function构造函数的prototype和其 实例Function构造函数 的proto指向同一个地方
- Object构造函数是Function构造函数的实例,即Function构造函数的prototype和其 实例Object构造函数 的proto指向同一个地方
- 构造函数的__proto__指向的地方,它的constructor是此构造函数
- Person.prototype 和 Function.prototype都是构造函数Object的实例
- 原型链是__proto__的路径
- Object.prototype的proto指向null,是原型链终点
- 原型继承就是实例可以使用构造函数上的prototype中的方法,实例先找自身有没有此方法,没有就沿着原型链找
- A instanceof B的作用是判断B的prototype是否在A的原型链上