JavaScript 构造函数与 new
前面几篇创建对象一直用的是对象字面量,一个两个没问题,数量多了就繁琐了。假如一个网站有上千个用户,每个用户都有 name、age、email 这些属性,难道要手写一千遍 { name: '...', age: ... } 吗?构造函数就是用来解决这种批量创建对象的问题。
为什么需要构造函数
用字面量创建几个结构相同的对象,可以看到问题所在。
const user1 = {
name: '张三',
age: 18,
email: 'zhangsan@example.com'
};
const user2 = {
name: '李四',
age: 20,
email: 'lisi@example.com'
};
问题很明显,每写一个用户就要把属性名完整重复一遍。如果以后要加一个 phone 属性,得回头把所有对象都改一遍,很容易漏改或写错。我们想要的是一份”模板”,传入不同的数据,就能批量生成结构相同的对象。
构造函数是什么
构造函数本质上还是一个普通函数,只是有两条约定俗成的规矩。
- 函数名首字母大写,用来和普通函数区分。
- 函数内部用 this 给新对象添加属性。
function User(name, age) {
this.name = name;
this.age = age;
}
注意这个函数没有 return 任何东西,看起来只是往 this 上挂属性。单独调用 User('张三', 18) 没有意义,因为 this 不知道指向谁,它必须配合 new 使用。关于参数传递的细节可以回顾 JavaScript 函数参数。
new 关键字做了什么
执行 new User('张三', 18) 时,JavaScript 在内部悄悄完成了三步。
- 创建一个新的空对象。
- 把这个新对象赋值给函数内部的 this,于是函数里的
this.name = name都作用在这个新对象上。 - 把这个新对象作为结果返回出来。
也就是说,new 帮我们完成了”创建对象、绑定 this、返回对象”这三件事,构造函数里只需要专心往 this 上挂属性即可。this 的完整规则可以回顾 JavaScript this 指向基础。
function User(name, age) {
this.name = name;
this.age = age;
}
const user1 = new User('张三', 18);
console.log(user1.name); // 张三
console.log(user1.age); // 18
const user2 = new User('李四', 20);
console.log(user2.name); // 李四
同一个构造函数可以 new 出任意多个结构相同的对象,批量创建的问题就解决了。传不同参数,得到不同数据、相同结构的实例。
实例是什么
用 new 出来的对象,称为这个构造函数的实例。user1 和 user2 都是 User 的实例,实例之间互不影响,改 user1 的 name 不会波及 user2。
判断一个对象是不是某个构造函数的实例,可以用 instanceof 运算符,结果为布尔值。
const user1 = new User('张三', 18);
console.log(user1 instanceof User); // true
console.log(user1 instanceof Array); // false
给实例添加方法
构造函数里也可以挂方法,先看一种写法(下一篇会讲更推荐的写法)。
function User(name, age) {
this.name = name;
this.age = age;
this.sayHi = function () {
console.log('你好,我是 ' + this.name);
};
}
const user1 = new User('张三', 18);
user1.sayHi(); // 你好,我是 张三
每个实例都带上了自己的 sayHi,调用时 this 指向调用它的实例。
小结
- 构造函数是首字母大写的普通函数,内部用 this 给新对象添加属性。
- new 负责三件事,创建空对象、把 this 指向新对象、返回新对象。
- new 出来的对象叫实例,instanceof 可以判断实例属于哪个构造函数。
- 构造函数解决批量创建结构相同对象的问题。
- 构造函数的 prototype 属性和原型机制有关,下一篇专门讲。