通过
new 函数名来实例化对象的函数叫构造函数。
任何的函数都可以作为构造函数存在。之所以有构造函数与普通函数之分,主要从功能上进行区别的,构造函数的主要 功能为 初始化对象,特点是和new 一起使用。new就是在创建对象,从无到有,构造函数就是在为初始化的对象添加属性和方法。构造函数定义时首字母大写(规范)。
function Person(name) {
this.name = name;
}
let p1 = new Person('张三'); // 实例化
console.log(p1); // Person {name: "张三"}
此时,p1就是一个新对象。
obj {}_proto_指向了构造函数的prototype成员对象apply调用构造器函数,属性和方法被添加到 this 引用的对象中对new理解:new 申请内存, 创建对象,当调用new时,后台会隐式执行new Object()创建对象。所以,通过new创建的字符串、数字是引用类型,而是非值类型。
function _new(func, ...args) {
// 1. 创建空对象
let obj = {};
// 2. 空对象的_proto_指向了构造函数的prototype成员对象
obj.__proto__ = func.prototype; // 一二步合并就相当于 let obj = Object.create(func.prototype)
// 3. 使用apply调用构造器函数,属性和方法被添加到 this 引用的对象中
let result = func.apply(obj, args);
// 4. 确保 new 出来的是个对象
return typeof result === 'object' ? result : obj;
}
测试用例:
function Person(name, age) {
this.name = name;
this.age = age;
}
let obj = _new(Person, 'xia', 20);
console.log(obj); // Person {name: "xia", age: 20}
function Person() {
this.say = function () { // 直接定义方法
console.log('hello');
}
}
let p1 = new Person();
let p2 = new Person();
p1.say(); // hello
p2.say(); // hello
console.log(p1.say === p2.say); // false
很明显,p1 和 p2 指向的不是一个地方。 所以 在构造函数上通过 this 来添加方法的方式来生成实例,每次生成实例,都是新开辟一个内存空间存方法。这样会导致内存的极大浪费,从而影响性能。
构造函数通过原型分配的函数,是所有对象共享的。
function Person(name) {
this.name = name;
}
Person.prototype.say = function () { // 通过原型添加方法
console.log('hello ' + this.name);
}
let p1 = new Person('张三');
let p2 = new Person('李四');
p1.say(); // hello 张三
p2.say(); // hello 李四
console.log(p1.say === p2.say); // true
所以我们经常 将公共属性定义到构造函数里,将公共方法放到原型对象上 。
点击查看“构造函数的五种继承方式”