原型 和 原型链
+ 原型: 每个函数天生自带的 prototype 对象数据类型
=> 作用: 由构造函数向 原型上 添加方法, 提供给该构造函数的所有实例使用
=> 为了解决构造函数将方法书写在构造函数体内时造成的资源浪费
+ 原型链:
=> 概念: 用 __proto__ 串联起来的对象链状结构
=> 作用: 为了对象访问机制服务(当你访问一个对象成员的时候, 为你提供一些服务)
=> 注意: 只是 __proto__ 串联起来的对象链状结构, 千万不要往 prototype 上靠
概念:
1. 每一个函数天生自带一个属性叫做 prototype, 是一个对象数据类型
2. 每一个对象天生自带一个属性叫做 __proto__, 指向所属构造函数的 prototype
3. 任何一个对象, 如果没有准确的构造函数, 那么看做是 Object 的实例
=> 只要是一个单纯的对象数据类型, 都是内置构造函数 Object 的实例
万物皆对象
+ 在 JS 内, 任何一个数据类型其实都是对象
+ 函数也是一个对象, 数组也是一个对象, 正则也是一个对象, ...
=> 是对象, 就可以存储 键值对
+ 以函数为例
=> 当你书写完毕一个函数的时候
=> 此时 函数数据类型 出现了, 同时该函数名也是一个对象数据类型
问题1: p1 身上的 __proto__ 是谁 ?
=> 因为 p1 是 Person 的时候
=> 根据 概念1 得到, p1.__proto__ 指向所属构造函数的 prototype
问题2: Person 的 __proto__ 是谁 ?
=> Person 是一个构造函数, 同时也是一个函数, 同时也是一个对象
=> 只要是对象就会有 __proto__ 属性
=> JS 内有一个内置构造函数叫做 Function, 只要是函数, 就看做 Function 的实例
=> 任何一个函数数据类型所属的构造函数都是 Function
=> Person 看做是 Function 的实例
=> Person 所属的构造函数就是 Function
=> Person.__proto__ 指向 Function.prototype
问题3: Person.prototype 的 __proto__ 是谁 ?
=> Person.prototype 是函数天生自带的一个对象数据类型
=> 只要是对象就会有 __proto__ 属性
=> JS 内有一个内置构造函数叫做 Object, 只要是单纯的对象, 都是 Object 的实例
=> Person.prototype 是一个天生的对象数据类型, 并且是一个单纯的对象数据类型
=> 把 Person.prototype 看做是 Object 的实例
=> Person.prototype 的 __proto__ 就是 Object.prototype
问题4: Function 的 __proto__ 是谁 ?
=> Function 是一个构造函数, 同时也是一个函数, 同时也是一个对象
=> 只要是对象就会有 __proto__ 属性
=> JS 内有一个内置构造函数叫做 Function, 只要是函数就是 Function 的实例
=> Function 自己本身是一个内置构造函数, 本身也是一个函数
=> Function 自己是自己的实例, 自己是自己的构造函数
=> 在 JS 内管 Function 叫做顶级函数
=> Function.__proto__ 就是 Function.prototype
问题5: Function.prototype 的 __proto__ 是谁 ?
=> Function.prototype 是函数天生自带的一个对象数据类型
=> 只要是对象就会有 __proto__ 属性
=> Function.prototype 是一个天生的对象数据类型, 并且是一个单纯的对象数据类型
=> 把 Function.prototype 看做是 Object 的实例
=> Function.prototype 的 __proto__ 就是 Object.prototype
问题6: Object 的 __proto__ 是谁 ?
=> Object 是一个构造函数, 同时也是一个函数, 同时也是一个对象
=> 只要是对象就会有 __proto__ 属性
=> Object 也是一个函数, 只要是函数就是 Function 的实例
=> Object 这个内置函数所属的构造函数依旧是 Function
=> Object.__proto__ 就是 Function.prototype
问题7: Object.prototype 的 __proto__ 是谁 ?
=> Object.prototype 势函数天生自带的一个对象数据类型
=> 只要是对象就会有 __proto__ 属性
=> 在 JS 内, Object 是顶级对象, Object.prototype 是顶级原型
=> Object.prototype 是唯一一个没有 __proto__ 的对象数据类型
=> Object.prototype 的 __proto__ 是 null
function Person(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
Person.prototype.sayHi = function () { console.log('hello world') }
const p1 = new Person('Jack', 18, '男')
console.log(p1)
// 问题1证明
console.log(p1.__proto__ === Person.prototype)
// 问题2证明
console.log(Person.__proto__ === Function.prototype)
// 问题3证明
console.log(Person.prototype.__proto__ === Object.prototype)
// 问题4证明
console.log(Function.__proto__ === Function.prototype)
// 问题5证明
console.log(Function.prototype.__proto__ === Object.prototype)
// 问题6证明
console.log(Object.__proto__ === Function.prototype)
// 问题7证明
console.log(Object.prototype.__proto__)
prototype 和 __proto__ 有什么区别
/*
prototype 和 __proto__ 有什么区别
1. 名字不一样
2. 有可能指向同一个对象空间
=> prototype 前面主语一定是函数
=> __proto__ 前面的主语一定是对象
*/
// 万物皆对象
// 当函数创建完毕以后, 可以把 fn 当做一个对象名来使用
function fn() {
console.log('你好 世界')
}
// // 我把 fn 当做对象名来使用
fn.a = 100
// // 此时是把函数内 函数体 空间的代码执行掉
// // 和函数对象空间内的数据没有任何关系
fn()
console.log(fn)
console.dir(fn)
// 数组同时也是一个对象空间
var arr = [100, 200, 300, 400]
// 完全可以把 arr 当做一个对象名来使用
arr.age = 18
// 当你遍历数组的时候, 只是遍历数组中的数组空间
arr.forEach(item => {
console.log(item)
})
console.log(arr)
最后总结