1. 原型
- 每个函数都有一个
prototype
(显式原型) - 每个对象都有一个
__proto__
(隐式原型) - 对象的
__proto__
全等于对应构造函数的prototype
// 定义构造函数
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
};
sayMyself() {
console.log(`我是${this.name}, 今年${this.age}岁`);
};
};
// 1. 每个函数都有一个prototype(显式原型)
console.log('构造函数的prototype', Person.prototype);
const personFirst = new Person('温情', 23);
// 2. 每个实例对象都有一个__proto__(隐式原型)
console.log('实例对象的__proto__', personFirst.__proto__);
// 3. 对象的__proto__全等于对应构造函数的prototype
console.log(personFirst.__proto__ === Person.prototype);
2. 原型链
当想访问一个对象的属性或方法时,有则返回,没有就通过
__proto__
去它的原型对象查找,原型对象找到则返回,找不到就继续通过原型对象的__proto__
去查找,一层一层往上查找直到Object.prototype
,有就返回,没有就是undefined
,不会再往上查找了,因为Object
的__proto__
为null
null
的设计是为了避免死循环
// 父构造函数
class SecondLevel {
constructor(name) {
this.name = name;
};
study() {
console.log('学习');
};
}
// 继承 SecondLevel 属性与方法
class FirstLevel extends SecondLevel {
constructor(name, age) {
super(name);
this.age = age;
};
playGame() {
console.log('玩游戏');
};
}
// 实例化对象
const exampleObj = new FirstLevel('温情', 20);
exampleObj.playGame(); // 输出 玩游戏
/* 因为 FirstLevel 通过 extends 继承了 SecondLevel 的属性和方法,通过原型链往上查找到了 SecondLevel 原型对象的 study 方法 */
exampleObj.study(); // 输出 学习
/* 可以看到FirstLevel和SecondLevel都没有定义toString方法,但是调用成功了,
这是因为跟着原型链向上查找,找到了顶层Object的prototype,Object的原型对象内置了toString方法 */
console.log(exampleObj.age.toString()); // '20'
// 再看看整个 exampleObj 的结构 结合 我画的图理解
console.log(exampleObj);