构造函数
约 1419 字大约 5 分钟
2025-05-04
一、简介
构造函数(Constructor Function)是 JavaScript 中用于创建和初始化对象的特殊函数。它作为对象的“模板”,定义了对象的结构(属性)和行为(方法)。
核心作用:批量创建具有相同属性和方法的对象,实现代码复用。
通过 new
关键字调用构造函数时,会自动完成以下操作:
- 创建一个新对象;
- 将新对象的原型(
__proto__
)链接到构造函数的prototype
属性; - 将构造函数内的
this
绑定到新对象; - 执行构造函数内的代码(初始化对象属性);
- 返回新对象(除非显式返回其他对象)。
二、语法
1、定义
定义一个构造函数,通常以大写字母开头(约定),函数体中使用 this
关键字来定义实例成员(属性和方法)。
// 构造函数通常以大写字母开头(约定)
function Person(name, age) {
// 使用 this 定义属性和方法,可被实例化后的对象访问
this.name = name;
this.age = age;
this.sayHello = function () {
console.log(`Hello, my name is ${this.name}`);
};
}
2、调用(实例化)
使用 new
关键字调用构造函数,创建一个新的实例对象。
const alice = new Person("Alice", 30);
alice.sayHello();
输出:
Hello, my name is Alice
3、其他的挂载成员方式
3.1、直接挂载
我们可以直接往构造函数上挂载属性和方法,但是这些属性和方法是无法被实例化后的对象访问的,例如:
Person.sex = "male";
Person.sayAge = function () {
console.log(`I am ${this.age} years old`);
};
const alice = new Person("Alice", 30);
console.log(alice.sex); // undefined
alice.sayAge(); // 报错:TypeError: alice.sayAge is not a function
输出:
> undefined
> Uncaught TypeError: alice.sayAge is not a function
> at <anonymous>:7:7
这种情况下,我们一般是用来封装一些工具函数和一些常量,例如:
// 定义一个常量
Person.ADULT_AGE = 18; // 18岁成年
// 定义一个函数
Person.isAdult = function (age) {
return age > Person.ADULT_AGE;
};
console.log(Person.isAdult(20));
console.log(Person.isAdult(16));
输出:
true
false
3.2、通过原型链挂载
另外一种挂载方式是通过原型链prototype
挂载。
这种方式的好处是可以让所有实例共享这些属性和方法,但是这种方式的坏处是会污染原始对象,例如:
// 通过原型链添加属性和方法
Person.prototype.sex = null;
Person.prototype.saySex = function () {
console.log(`Hello, I'm a ${this.sex}`);
};
let alice = new Person("Alice", 30);
console.log(alice.sex); // 此时获取的是原型链上的值 null
alice.saySex(); // 输出:Hello, I'm a null
// 给alice实例的 sex 属性赋值
alice.sex = "male"; // 此操作并不会改变原型链上 sex 的初始值
alice.saySex(); // 输出:Hello, I'm a male
console.log(Person.prototype.sex); // 输出:null
通过原型挂载的主要使用场景是:
- 兼容老版本浏览器:如
polyfill
,某些方法在低版本浏览器中不存在,我们可以通过原型挂载来兼容。 - 封装工具类:如
Array.prototype
,我们可以通过原型挂载来封装一些常用的工具类方法。
三、特点
1. this
的动态绑定
- 使用
new
调用:this
指向新创建的实例。 - 直接调用:非严格模式下
this
指向全局对象(如window
),严格模式下为undefined
,可能导致错误。
2. 原型链机制
prototype
属性:每个构造函数都有一个prototype
属性,实例的__proto__
会指向它。- 方法共享:原型上的方法被所有实例共享,节省内存。
3. 构造函数的类型标识
instanceof
检测:alice instanceof Person
返回true
,通过原型链判断。constructor
属性:实例的constructor
属性默认指向构造函数(可能被修改)。
4. 必须使用 new
调用
- 未使用
new
的后果:属性会被错误地添加到全局对象或导致运行时错误(严格模式)。 - 安全模式:可在构造函数内检查
this
是否为自身实例,自动纠正调用方式:
function Person(name) {
if (!(this instanceof Person)) {
return new Person(name);
}
this.name = name;
}
5. 与 ES6 类的关系
- 类的本质:ES6 的
class
是构造函数的语法糖,提供更清晰的面向对象语法。
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, ${this.name}`);
}
}
- 区别:类必须通过
new
调用,否则报错;方法默认不可枚举。
四、主要是使用场景
1. 封装对象
集中定义对象的初始状态(属性赋值、方法绑定)。
2. 实现原型继承
通过修改子类构造函数的 prototype
实现继承链。
function Parent() {
this.parentProp = true;
}
function Child() {
Parent.call(this); // 继承父类属性
this.childProp = false;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child; // 修复构造函数引用
3. 创建共享方法、属性
避免为每个实例重复创建相同属性、方法,节省内存。
Person.ADULT_AGE = 18;
Person.prototype.logAge = function () {
console.log(this.age);
};
4. 创建静态方法
直接通过构造函数调用,不可被实例化。
Person.createAnonymous = function () {
return new Person("Anonymous", 0);
};
const anon = Person.createAnonymous();
5. 多态与封装
- 封装数据:通过构造函数隐藏内部状态,暴露必要接口。
- 多态支持:子类可重写父类方法,实现不同行为。
五、注意事项
- 尽量避免修改内置对象的原型(如
Array.prototype
),可能引发不可预见的问题。 - 箭头函数不能作为构造函数,无自身的
this
绑定和prototype
属性。
六、总结
构造函数是 JavaScript 实现面向对象编程的核心机制,通过原型链和 new
关键字实现了对象的创建、继承与方法共享。理解其工作原理是掌握 JavaScript 高级特性(如类、继承、设计模式)的基础。ES6 的 class
语法虽简化了写法,但底层仍依赖于构造函数和原型系统。
更新日志
2025/8/24 08:17
查看所有更新日志
e7112
-1于