文章目录
一、原型
简介
- 每个函数对象都有一个
prototype
属性,默认指向一个空的Object
实例对象(即称为原型对象) - 原型对象中有一个
constructor
属性,它指向当前的函数对象
代码示例:
function Test() {}
Test.prototype.say = () => {
console.log('Say Hello');
}
const t1 = new Test();
const t2 = new Test();
Test.prototype.say = () => {
console.log('Say Hello');
}
console.log("原型对象:", Object.keys(Test.prototype)); // [],说明函数的原型对象是一个空对象
console.log(Test.prototype.constructor === test); // true,说明 原型对象的 constructor 属性,它指向当前的函数对象
显式原型和隐式原型
- 每个函数都有一个
prototype
属性,即显式原型 - 每个实例对象都有一个
__proto__
属性,即隐式原型 - 实例对象的隐式原型的值等于对应函数的显式原型的值
function Test() {}
const t1 = new Test();
console.log(Test.prototype); // 一个空对象,包含 __proto__ 和 constructor 属性(指向 Test)
console.log(t1.__proto__); // 一个空对象,包含 __proto__ 和 constructor 属性(指向 Test)
console.log(Test.prototype === t1.__proto__); // true
显式原型和隐式原型的实现机制
- 函数的
prototype
属性:在定义函数时自动添加的,默认值是一个空的Object
对象 - 对象的
__proto__
属性:创建对象时自动添加的,默认值为构造函数的prototype
属性值 - 开发人员一般都是通过操作函数的
prototype
属性来修改原型
原型补充
- 函数的显式原型指向的对象默认是空
Object
对象(但是Object
除外,Object
的原型不是空对象,且没有__proto__
属性)- 所以
Object
的原型对象是原型链的尽头,Object
的原型对象的原型就是null
了
- 所以
function Test() {}
const o = {};
const t = new Test();
console.log(o.__proto__); // 包含多个方法的 Object 对象,没有 __proto__ 属性
console.log(t.__proto__); // 空对象
console.log(o.__proto__.__proto__); // null
- 每个函数对象既有一个隐式原型,也有一个显式原型
- 所有函数都是
Function
的实例(包括Function
自身) - 每个函数的隐式原型都是同一个实例对象
- 唯一的一个隐式原型等于显式原型的函数对象就是
Function
这个函数对象
- 所有函数都是
function Test() {}
function Hello() {}
console.log(Test.prototype); // 空对象
console.log(Test.__proto__); // ƒ () { [native code] }
console.log(Hello.prototype === Test.prototype) // false
console.log(Hello.__proto__ === Test.__proto__); // true
console.log(Hello.__proto__ === Function.__proto__); // true
console.log(Function.prototype === Function.__proto__); // true
二、原型链(别名:隐式原型链)
概念
-
访问一个对象的属性时
- 先在自身属性中查找,找到返回
- 如果没有,再沿着
__proto__
这条链向上查找,找到返回 - 如果最终没有找到,返回
undefined
-
作用:查询对象属性
对象读写
- 读取对象的属性时,会自动到对象的原型链中查找
function Person() {}
Person.prototype.speak = () => {
console.log('Speak Chinese!');
}
const p1 = new Person();
const p2 = new Person();
// 每个实例对象都拥有函数原型中的属性/方法
p1.speak(); // Speak Chinese!
p2.speak(); // Speak Chinese!
- 设置对象的属性时,不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置值
function Person() {}
Person.prototype.name = '小明';
const p1 = new Person();
const p2 = new Person();
// 读取属性时,会从当前对象以及原型链中进行查找
console.log(p1.name); // 小明
// 设置属性时,直接修改当前对象上的属性,当前对象上没有,则直接添加,不会修改原型链上的属性
p2.name = '小红';
console.log(p1.name); // 小明
console.log(p2.name); // 小红
- 方法一般定义在原型上,属性一般通过构造函数定义在对象本身上
扩展:instanceOf
操作符原理
a instanceOf C
: 如果C
的显式原型在a
的隐式原型链上,返回true
,否则返回false
function A() {};
function B() {};
function C() {}
// 将 C 的原型赋值给 B 的原型,将 B 的原型赋值给 A 的原型,这两行代码位置不能颠倒
B.prototype = C.prototype;
A.prototype = B.prototype;
const a = new A();
console.log(a instanceof C);
三、实例讲解
1、实例一
function A() {}
A.prototype.n = 1;
// 对象的原型在对象初始化时赋值为函数的显示原型,因此 b 的隐式原型为 { n: 1 };
const b = new A();
A.prototype = {
n: 2,
m: 3
}
// 原因同上,因此 c 的隐式原型为 { n: 2, m: 3 };
const c = new A();
console.log(b.n, b.m, c.n, c.m); // 1 undefined 2 3
实例二
function Test() {}
Object.prototype.a = function() {
console.log('a 方法执行');
}
Function.prototype.b = function() {
console.log('b 方法执行');
}
var t = new Test();
/**
* 这里解释一波
* 1、每个函数对象的显式原型都指向一个空对象,而每个实例对象的隐式原型 === 函数对象的显式原型,
* 那么 t.a() 会按照这个顺序查找: t.__proto__ === Test.prototype = {}; {}.__proto__ === Object.prototype;
* 而 Object 的显式原型中有方法 a,因此可以正确执行;
* 2、t.b() 同理,在原型链中没有找到 b 方法,因此会报错:t.b is not a function;
* 3、Test.a() 的查找顺序:Test.__proto__ === Function.prototype = { b: function() { console.log('b 方法执行'); } };
* {b: fucntion() {console.log('b 方法执行');}}.__proto__ === Object.prototype;
* 因此:Test.a() 和 Test.b() 都可以正确执行
*/
t.a(); // a();
t.b(); // t.b is not a function
Test.a(); // a();
Test.b(); // b();
四、总结
-
每个函数对象(
Object
函数对象除外)都默认有一个显式原型对象,指向一个空的Object
对象,空对象中有一个 constructor 属性,指向当前函数对象 -
所有函数对象都有一个隐式原型对象(包括
Function
函数对象),且都默认指向Function
函数对象的显式原型对象 -
函数对象的显式原型等于实例对象的隐式原型
-
访问对象属性时,会先在实例对象本身进行查找,找到则返回,没找到则在隐式原型链中继续查找,找到则返回,没找到返回
undefined
-
额外补充一点:函数对象的隐式原型和显示原型没有关系(
Function
函数对象除外),Function
函数对象的显式原型等于自身的隐式原型(因为所有函数都是Function
的实例(包括Function
自身))
五、推荐链接
之前看到这篇博客里有详细的原型链示意图,有兴趣的朋友可以详细去看一下: