原型和原型链
原型
shell
# 回答
每个对象都有一个 __proto__ 隐藏指针指向其构造函数的原型对象 prototype,这个对象就是原型。 这是整个原型链的基石,所有逻辑都源于此。
# 原型分类
prototype(显式原型):只有函数才有(函数专属),是函数的一个属性,指向一个对象。
__proto__(隐式原型):所有对象都有(对象专属),是对象的一个隐藏指针,指向创建该对象的构造函数的 prototype(二者指向同一对象)。
constructor:原型对象里的属性,指向构造函数本身。
即:
函数用 prototype 指向原型
对象用 __proto__ 指向原型
原型用 constructor 指回函数
即:
对象的 __proto__ === 创造它的构造函数的 prototype原型链
shell
# 原型链
每个对象都有 __proto__ 属性,指向其构造函数的原型对象。
当访问对象属性时,
先从对象自身实例找,
若 对象本身 没有该属性,则通过它的 __proto__ 找自身原型对象,
若 自身原型 没有该属性,则通过自身原型对象的 __proto__ 找其父级的原型对象,
若 父级原型 没有该属性,则通过父级原型对象的 __proto__ 找Object的原型对象,
若 Object原型 没有该属性,则到头了,返回 null
这条沿着原型依次向上查找的链式结构,就叫做原型链。拓展
shell
# 所有对象都有 __proto__ 吗?
是,除了 Object.create(null)。
# 所有函数都有 prototype 吗?
是。
# 原型链的终点是什么?
null。
# JS 继承靠什么实现?
原型链。
# 为什么 对象.__proto__ 能子→父→爷爷→Object→null一层层往上找?
是语法规则 + 查找机制 + 对象结构三者共同决定的说说 JS 原型和原型链?
原型
shell
# 原型 or 原型对象 or prototype
原型是构造函数的独有属性 prototype,prototype的值是一个对象,且构造函数实例化的每一个对象都继承原型对象上的所有属性和方法
# 原型的作用
共享(JS 通过原型来共享属性和方法,因此 JS 是一门基于原型的语言)原型链
shell
# 原型链
原型链是 JavaScript 对象通过 __proto__ 相互关联形成的链状结构,用于实现继承和属性查找。
# 原理
每个对象都有 __proto__ 属性,指向其原型对象。当访问对象属性时,若对象本身没有该属性,就会沿 __proto__ 链向上查找,直到找到属性或到达原型链顶端(null)。函数对象的 prototype 属性会成为由该函数创建的实例对象的原型。
# 详细
当访问一个对象的属性时,首先找当前对象自身,如果没有,则
通过当前对象的对象原型 __proto__ 访问当前对象的构造函数的原型对象 prototype 上的属性和方法,如果还没有,则
通过当前对象的构造函数的原型对象 prototype 的对象原型 __proto__ 访问父类构造函数的原型对象 prototype 上的属性和方法,如果还没有则以此类推
最终访问到 Object.prototype,而 Object.prototype 的 __proto__ 指向 null
即:child实例 -→ Child.prototype -→ Parent.prototype -→ Object.prototype -→ null,中间的剪头过程通过 __proto__ 自动访问
# 注意
每个 构造函数 都有一个 prototype 原型对象,prototype 指向构造函数所有实例都共享的对象
每个 对象 都有一个 __proto__ 对象原型,__proto__ 指向创建这个对象的构造函数的 prototype 原型对象(__proto__是真实存在的属性,ES6后确认)
# 作用
实现继承,即对象方法和属性的复用
# 示例
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('Hello');
};
const p = new Person('张三');
## 关系链
p.__proto__ === Person.prototype (实例的原型指向构造函数的原型)
Person.prototype.constructor === Person (原型对象的构造函数指向自己)
Person.prototype.__proto__ === Object.prototype (所有对象最终都继承自 Object)
Object.prototype.__proto__ === null (原型链的终点)
## 关系图谱
p (实例对象)
│
├── 自身属性: name = "张三"
│
└── __proto__ ──→ Person.prototype (原型对象)
│
├── 公共方法: sayHello
├── constructor ──→ Person (构造函数)
│
└── __proto__ ──→ Object.prototype
│
├── 公共方法: toString, hasOwnProperty...
│
└── __proto__ ──→ null拓展
shell
# JS 中通过实例对象怎么访问实例对象的原型对象呢?
- 方式一:通过 obj.__proto__ ,但是旧的浏览器不支持
- 方式二:获取原型:Object.getPrototypeOf(obj),设置原型:Object.setPrototypeOf(obj, proto)
# 总结
1. 原型链的顶端是 null,其次是 Object.prototype
2. 原型和原型对象是一个东西即prototype,但是原型对象和对象原型不是一个东西,原型对象 prototype,对象原型 __proto__
3. 这就可以解释为什么浏览器 console 的实例对象上会有很多对象可用的方法呢?因为是从 Object.prototype 上继承而来
4. 原型链向上都只是共享原型对象 prototype 上的方法和属性,如果想要访问父类构造函数的属性和方法则还需要通过构造函数继承来实现