一、构造函数介绍:

JavaScript中,构造函数是用于创建和初始化一个由new关键字生成的对象的特殊函数。构造函数的名字通常以大写字母开头,但这并不是JavaScript语法的一部分,而是一种约定俗成的命名规范,有助于区分构造函数和普通函数。

  • 示例:
function Person(name, age) {  
    this.name = name;  
    this.age = age;  
  
    // 还可以添加其他方法和属性  
    this.say= function() {  
        console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);  
    };  
}  
  
// 使用new关键字和构造函数创建一个新的Person对象  
var obj = new Person('xiaoming', 30);  
  
// 访问对象的属性和方法  
console.log(obj.name); // 输出: xiaoming
console.log(obj.age);  // 输出: 30  
obj.say();            // 输出: Hello, my name is xiaoming and I'm 30 years old.

在上面的例子中,Person是一个构造函数,它接受两个参数nameage,并使用this关键字来设置新创建对象的属性。this在构造函数中引用的是新创建的对象。

构造函数的一个常见错误是忘记使用new关键字来调用它们。如果忘记使用new,那么this将不会引用新创建的对象,而是引用全局对象(在浏览器中是window对象)。这通常会导致意外的全局变量和难以调试的错误。 image.png

二、构造函数特点

  1. 命名约定: 构造函数的命名通常以大写字母开头,以区分它们与其他函数(尽管这不是强制性的,但遵循这个约定可以提高代码的可读性)。
  2. 使用new关键字: 当你使用new关键字和一个构造函数时,JavaScript会创建一个新的空对象,将这个新对象的[[Prototype]](内部链接)链接到构造函数的prototype对象上,然后将this关键字绑定到这个新对象上,并执行构造函数中的代码。
  3. this关键字: 在构造函数内部,this关键字引用的是新创建的对象实例。你可以使用this来设置新对象的属性或方法。
  4. prototype属性: 每个构造函数都有一个prototype属性,它是一个对象,用于存储所有实例共享的属性和方法。当你创建一个新对象时,这个新对象会继承构造函数的prototype对象上的属性和方法。
  5. 返回值: 通常,构造函数不返回任何值(或返回this,这是隐式的)。然而,如果构造函数显式地返回了一个非原始值(如对象或数组),那么这个值将替代新创建的对象作为new表达式的返回值。但是,如果返回的是原始值(如numberstringbooleannullundefined),那么新创建的对象仍然会被返回。
  6. 继承: JavaScript中的继承通常通过修改原型链或使用ES6中的class语法(它是基于原型链的语法糖)来实现。构造函数可以通过其prototype属性参与原型链,从而允许子类继承父类的属性和方法。

三、简易实现

示例1:

// 假如一个普通函数Person
function Person(name, age) {  
    const obj = {};
    obj.name = name;  
    obj.age = age; 
    obj.__proto__ = Person.prototype
    return obj
}  

const a = Person('a', 20)

image.png

示例2:

// 定义构造函数Person
function Person(name, age) {  
    this.name = name;  
    this.age = age; 
}  

const a = new Person('a', 20)

image.png

四、与普通函数区别

构造函数(Constructor Functions)和普通函数(Regular Functions)主要差异:

  1. 使用 new 关键字:
    • 构造函数通常与 new 关键字一起使用来创建和初始化对象。当使用 new 关键字调用一个函数时,这个函数就被当作构造函数来使用。
    • 普通函数则不需要使用 new 关键字来调用,它们通常用于执行某些操作或返回某个值。
  2. this 的行为:
    • 在构造函数中,this 关键字引用的是通过 new 创建的新对象。构造函数内部通常使用 this 来为新对象添加属性和方法。
    • 在普通函数中,this 的值取决于函数如何被调用。如果函数作为普通函数调用,this 通常指向全局对象(在浏览器中是 window)。但如果函数作为对象的方法被调用,this 将指向调用该方法的对象。此外,还可以使用 Function.prototype.callFunction.prototype.apply 或箭头函数来显式设置 this 的值。
  3. 返回值:
    • 如果构造函数没有显式地返回一个对象,那么 new 表达式将返回新创建的对象。如果构造函数显式地返回了一个非原始值(如对象或数组),那么这个值将替代新创建的对象作为 new 表达式的返回值(但这种情况很少见,通常不建议这样做)。
    • 普通函数可以返回任何类型的值,包括原始值和对象。返回值由函数体内的 return 语句确定。
  4. 命名约定:
    • 虽然JavaScript并没有强制要求构造函数和普通函数有不同的命名方式,但按照惯例,构造函数通常使用大驼峰命名法(PascalCase),即首字母大写的命名方式(例如 PersonCar 等)。
    • 普通函数则可以使用任何有效的命名方式,但通常使用小驼峰命名法(camelCase),即首字母小写的命名方式(例如 calculateSumgreetUser 等)。
  5. 目的:
    • 构造函数的主要目的是创建和初始化对象,为新对象设置属性和方法。
    • 普通函数则用于执行各种任务,如计算、数据处理、逻辑判断等。
  6. 原型链:
    • 构造函数与原型链密切相关。每个构造函数都有一个 prototype 属性,这个属性是一个对象,包含可以由构造函数创建的所有实例共享的属性和方法。通过原型链,我们可以实现继承和其他面向对象的编程模式。
    • 普通函数也可以有原型,但它们的原型通常不用于创建实例共享的属性和方法。
  7. instanceof 操作符:
    • 由于构造函数创建的实例与构造函数之间存在一种特殊的联系,因此我们可以使用 instanceof 操作符来检测一个对象是否由某个构造函数创建
    • 普通函数与它们调用的结果之间不存在这种联系,因此不能使用 instanceof 操作符来检测。