一、构造函数
在JavaScript中,构造函数是一种用于创建和初始化对象的特殊函数。构造函数的名字通常以大写字母开头,以区分于普通函数。通过new关键字调用构造函数,可以创建一个新的实例对象,并自动执行构造函数内部的代码来初始化这个对象。
1、构造函数的定义
javascript
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
}
上述示例,是一个似曾相识的示例。因为在"函数、对象和类(一)"这一章节中简单讲解this关键字时的一个示例。在这个例子中,Person是一个构造函数,它接受两个参数name和age,并将它们赋值给新对象的属性。同时,它还定义了一个方法sayHello。
2、使用new关键字创建实例
javascript
const person1 = new Person('Alice', 30);
const person2 = new Person('Bob', 25);
person1.sayHello(); // 输出: Hello, my name is Alice and I am 30 years old.
person2.sayHello(); // 输出: Hello, my name is Bob and I am 25 years old.
使用new关键字调用Person构造函数,会创建两个新的Person对象person1和person2,它们分别有自己的name和age属性,以及sayHello方法。
3、只能使用new关键字进行实例化
javascript
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
}
function Person1(name, age) {
if (!(this instanceof Person1)) throw new Error("只能通过new实例化");
this.name = name;
this.age = age;
this.sayHello = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
}
let p = Person(); //不报错
let p1 = Person1(); //抛出异常
效果图如下所示:
4、构造函数执行过程
创建一个新的空对象。
将这个新对象的__
proto__
属性指向构造函数的prototype对象。
在新对象的上下文中执行构造函数的代码(即this指向新对象)。
如果构造函数没有显式返回对象,则默认返回新对象。
4.1、prototype属性
每个构造函数都有一个prototype属性,这是一个对象,包含应该由构造函数的所有实例共享的属性和方法。
javascript
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
console.log(`Hi, I'm ${this.name}.`);
};
const person1 = new Person('Alice', 30);
const person2 = new Person('Bob', 25);
person1.greet(); // 输出: Hi, I'm Alice.
person2.greet(); // 输出: Hi, I'm Bob.
在构造函数外部,可以通过prototype属性在构造函数的原型链添加新的方法,如上述示例中,使用Person.prototype.greet = function(){} 的形式为Person对象添加了一个新的方法greet。
4.2、this关键字的用途
this是引用当前对象的属性或方法的关键字,在ES6类中,指向的是类的实例。上述每一个示例中都使用到了this关键字。
5、优点和缺点
优点:
- 构造函数提供了一种方便的方式来创建和初始化对象。
- 通过prototype可以实现方法和属性的共享,节省内存。
缺点:
- 每个实例上的方法(如果不在prototype上定义)不会共享,会浪费内存。
- 使用构造函数创建对象相对于使用对象字面量或类(ES6引入)来说,语法上稍显繁琐。
二、工厂函数
在JavaScript中,工厂函数是一种创建对象的模式,它不同于使用构造函数或类的方式。工厂函数是一个普通的函数或者说是一个高阶函数,它返回一个新对象,而不是使用new关键字来实例化对象。这种模式在需要创建多个相似对象时非常有用,尤其是在不需要使用原型链继承或类的复杂结构时。
1、特点
简单性和灵活性:
工厂函数是普通的函数,因此它们比使用class或构造函数更简单和灵活。你可以轻松地返回不同类型的对象,或者根据需要添加额外的逻辑来创建对象。
不依赖new 关键字:使用工厂函数时,不需要使用new关键字来创建对象,这减少了错误的可能性(例如忘记使用new)。
无原型链继承的复杂性:工厂函数创建的对象不会自动共享方法或属性通过原型链,这有时可以简化代码,尤其是当不需要这种共享时。
易于理解和使用:工厂函数的代码通常更易于理解和维护,特别是对于不熟悉JavaScript原型链和构造函数的人来说。
2、示例
javascript
function createPerson(name, age) {
return {
name: name,
age: age,
greet: function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
};
}
const alice = createPerson('Alice', 30);
const bob = createPerson('Bob', 25);
alice.greet(); // 输出: Hello, my name is Alice and I am 30 years old.
bob.greet(); // 输出: Hello, my name is Bob and I am 25 years old.
3、工厂函数的优势
- 语法简洁性 :工厂函数通常比构造函数更简洁,因为它们不需要使用
new
关键字。 - 原型链继承:构造函数允许通过原型链共享方法和属性,这在需要节省内存或实现继承时很有用。工厂函数创建的对象不会自动共享这些方法或属性,但你可以通过其他方式(如使用Object.create或手动复制方法)来实现共享。
- 实例识别 :使用构造函数创建的对象可以通过
instanceof
操作符识别为特定构造函数的实例。工厂函数创建的对象没有这种内置的实例识别机制。
三、工厂函数与构造函数的比较
工厂函数 | 构造函数 | |
---|---|---|
定义 | 普通的函数,用于创建并返回一个对象 | 特殊的函数,用于创建对象,需要使用new 关键字 |
灵活性 | 高,可以创建不同类型或结构的对象 | 较低,通常用于创建具有相同结构和行为的对象 |
代码简洁性 | 简洁,不需要使用new 关键字 |
复杂,需要使用new 关键字,并注意this 的指向 |
实例识别 | 无法通过instanceof 识别对象类型 |
可以通过instanceof 识别对象类型 |
原型链继承 | 无法自动通过原型链共享方法和属性 | 可以通过原型链共享方法和属性 |
使用场景 | 适用于需要创建不同类型或结构对象的场景 | 适用于需要创建具有相同结构和行为对象的场景 |
四、总结
工厂函数和构造函数都是JavaScript中用于创建对象的常见模式。它们各有特点,适用于不同的场景。选择哪种模式主要取决于项目的具体需求和代码的复杂性。在实际开发中,可以根据需要灵活选择使用哪种模式来创建对象。