网站
引入
学了对象和构造函数之后,会发现一个问题:每次用 new 创建对象,如果方法写在构造函数里,每个对象都会有一份相同的方法,很浪费内存。
JS 用原型机制解决这个问题:把方法放到原型对象上,所有实例共享同一份。
正文
定义
定义
原型是 JS 实现对象继承和属性共享的机制。每个函数都有一个
prototype属性,每个对象都有一个__proto__指向它的构造函数的原型。
prototype 属性
每个函数都有一个 prototype 属性,它是一个对象。
function User(name) {
this.name = name;
}
console.log(User.prototype);用构造函数创建的对象,会自动链接到构造函数的 prototype。
__proto__ 属性
每个对象都有一个 __proto__ 属性,指向它的构造函数的原型。
function User(name) {
this.name = name;
}
let user = new User("Tom");
console.log(user.__proto__ === User.prototype);这就是实例和原型之间的链接关系。
在原型上添加方法
把方法放到 prototype 上,所有实例都能共享。
function User(name) {
this.name = name;
}
User.prototype.sayHello = function () {
console.log("你好,我是" + this.name);
};
let user1 = new User("Tom");
let user2 = new User("Jack");
user1.sayHello();
user2.sayHello();两个对象共享同一个 sayHello 方法,不会重复创建。
原型链
当访问对象的属性时,JS 会按照这个顺序查找:
- 先在对象自己身上找
- 找不到就去
__proto__指向的原型上找 - 还找不到就继续沿着原型的
__proto__往上找 - 直到找到
Object.prototype,它的__proto__是null
这条查找链就叫原型链。
function User(name) {
this.name = name;
}
User.prototype.sayHello = function () {
console.log("你好,我是" + this.name);
};
let user = new User("Tom");
console.log(user.name);
user.sayHello();
console.log(user.toString());user.name在user自己身上找到user.sayHello()在User.prototype上找到user.toString()在Object.prototype上找到
原型链的终点
所有对象的原型链最终都会指向 Object.prototype。
Object.prototype.__proto__ 是 null,这就是原型链的终点。
console.log(Object.prototype.__proto__);属性和方法的查找
对象自己的属性和原型上的属性都能访问到,但如果同名,对象自己的属性会优先。
function User(name) {
this.name = name;
}
User.prototype.name = "默认名";
let user = new User("Tom");
console.log(user.name);user 自己有 name 属性,所以不会去原型上找。
instanceof 运算符
instanceof 用来检查对象的原型链中有没有某个构造函数的原型。
function User(name) {
this.name = name;
}
let user = new User("Tom");
console.log(user instanceof User);
console.log(user instanceof Object);user 是 User 的实例,也是 Object 的实例,因为所有对象都继承自 Object。
常见写法
| 写法 | 作用 |
|---|---|
Func.prototype | 访问构造函数的原型对象 |
obj.__proto__ | 访问对象的原型(学习用) |
Object.getPrototypeOf(obj) | 获取对象的原型(推荐) |
obj instanceof Func | 检查原型链中有没有某个构造函数的原型 |
Func.prototype.method = fn | 在原型上添加方法 |
特点
- 每个函数都有
prototype属性 - 每个对象都有
__proto__,指向构造函数的原型 - 属性查找沿着原型链一层层往上
- 原型链终点是
Object.prototype.__proto__,值为null - 对象自己的属性优先级高于原型上的属性
instanceof检查的是原型链,不是构造函数本身
理解
可以把原型链理解为找爸爸。
你自己没有的东西,去问你爸(__proto__)。
你爸也没有的,去问你爷爷(你爸的 __proto__)。
一直往上找,找到最老的祖先(Object.prototype)还没有,那就是真没有(undefined)。
prototype 是每个家庭的家产,家里所有孩子都能用。__proto__ 是每个孩子指回自己家的路。
实际开发中不需要太纠结原型链的底层细节,只要知道:构造函数用 prototype 共享方法,instanceof 检查继承关系就够了。
引出
理解了原型和原型链之后,可以继续学习 JS 自定义对象,看看用不同方式创建对象时原型关系是怎样的。
也可以学习 JS 类,ES6 的 class 语法本质上还是基于原型的,只是写法更清晰。