原型/继承/构造函数/类
如何生成一个对象?
1. 工厂函数
首先生成一个简单的对象:
const user = {};
user.name = 'zgh';
user.age = 25;
user.say = function () {
return `${this.name} is ${this.age} years old`;
};
如果要生成很多的 user 对象怎么办呢?可以创建一个函数来专门生成 user 对象:
function User(name, age) {
const user = {};
user.name = name;
user.age = age;
user.say = function () {
return `${this.name} is ${this.age} years old`;
};
return user;
}
const user1 = User('zhangsan', 20);
const user2 = User('lisi', 30);
这个函数就是工厂函数
2. Object.create
前面创建对象的方式,每次实例化一个 User 时,都需要重新分配内存去创建一遍 say 方法。可能想到的优化方式是将 User 对象里的方法都提取出去,如下:
const userMethods = {
say() {
return `${this.name} is ${this.age} years old`;
}
};
function User(name, age) {
const user = {};
user.name = name;
user.age = age;
user.say = userMethods.say;
return user;
}
如果继续在 userMethods 里增加方法 song,那么还要手动在 User 函数里增加相应的方法:user.song = userMethods.song
,这样在后续维护的时候就比较麻烦。
如何才能不在 User 函数里添加方法呢?
使用Object.create(proto)
,这个方法生成一个空对象,并将参数设置为自己的原型。
const userMethods = {
say() {
return `${this.name} is ${this.age} years old`;
},
song() {
console.log(this.name);
}
};
function User(name, age) {
const user = Object.create(userMethods);
user.name = name;
user.age = age;
return user;
}
const user1 = User('zhangsan', 20);
user1.song();
可以看到 user 本身是没有 song 方法的,但是也能调用。
假如在一个对象里找某个属性或方法,没找到,那么 js 就会继续往这个对象的原型里找,找不到就继续往这个对象原型的原型里找,直到找到或者返回 undefined,这个就是原型链。
3. 函数的 prototype
前面的 User 函数还需要配合 userMethods 对象使用,能不能省掉这步?
可以使用函数的 prototype 属性。每个函数都有 prototype 属性,prototype 属性指向一个对象,对象的 constructor 指向这个函数。
对象的原型可以通过Object.getPrototypeOf(obj)
或者__proto__
(不推荐)获取。
function User() {}
console.log(User.prototype); // {constructor: ƒ}
const user1 = User();
console.log(user1.prototype); // undefined
console.log(Object.getPrototypeOf(user1)); // {say: ƒ, a: ƒ}
console.log(Object.getPrototypeOf(user1) === userMethods); // true
所以可以将 userMethods 里的方法全部挂在函数的 prototype 上:
function User(name, age) {
const user = Object.create(User.prototype);
user.name = name;
user.age = age;
return user;
}
User.prototype.say = function () {
return `${this.name} is ${this.age} years old`;
};
User.prototype.song = function () {
console.log(this.name);
};
const user1 = User('zhangsan', 20);
4. 构造函数
js 引入了构造函数,将方法挂载到函数的 prototype 属性上是一种优雅的设计选择。
在构造函数前面加上 new
关键字,就可以构造对象了。
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.say = function () {
return `${this.name} is ${this.age} years old`;
};
User.prototype.a = function () {
console.log(this.name);
};
const user = new User('zhangsan', 20);