Sự khác biệt giữa function Person() {}, var person = Person(), và var person = new Person() là gì?

Trong JavaScript, có ba cách sử dụng thường gặp liên quan đến hàm và đối tượng: function declaration, function invocation, và constructor invocation. Chúng ta sẽ phân tích kỹ từng cách sử dụng này và sự khác biệt giữa chúng.

1. function Person() {} (Function Declaration)

Đây là một function declaration (khai báo hàm) đơn giản. Hàm này có thể được sử dụng như một hàm thông thường, hoặc như một constructor khi kết hợp với từ khóa new.

Ví dụ:

function Person() {
    console.log('This is a function declaration');
}
  • Chức năng: Person là một hàm, và bạn có thể gọi nó ở bất cứ đâu trong phạm vi của nó. Trong trường hợp này, Person có thể được sử dụng để thực thi một đoạn mã cụ thể khi gọi hàm.
  • Hoisting: Hàm này được hoisted (kéo lên đầu phạm vi), vì vậy bạn có thể sử dụng Person trước cả khi nó được khai báo trong mã của bạn.

2. var person = Person() (Function Invocation)

Ở đây, bạn đang gọi hàm Person như một function invocation (gọi hàm thông thường) mà không sử dụng từ khóa new.

Ví dụ:

function Person() {
    console.log('This is a function invocation');
    return { name: 'John' };
}

var person = Person();
console.log(person); // { name: 'John' }
  • Chức năng: Khi bạn gọi Person() mà không có từ khóa new, hàm sẽ thực thi giống như bất kỳ hàm nào khác. Nó có thể trả về một giá trị cụ thể (trong ví dụ trên, nó trả về một đối tượng { name: 'John' }).
  • Không có đối tượng mới được tạo ra: Khi bạn gọi hàm này, không có đối tượng mới nào được tạo ra, và từ khóa this trong hàm sẽ tham chiếu đến phạm vi toàn cục hoặc đối tượng hiện tại.
  • Kết quả: Biến person sẽ chứa giá trị trả về từ hàm Person(). Nếu hàm không có giá trị trả về (trả về undefined), person sẽ là undefined.

3. var person = new Person() (Constructor Invocation)

Khi bạn sử dụng từ khóa new, hàm Person được coi là một constructor function (hàm khởi tạo) và sẽ tạo ra một đối tượng mới.

Ví dụ:

function Person() {
    this.name = 'John';
    console.log('This is a constructor invocation');
}

var person = new Person();
console.log(person.name); // 'John'
  • Chức năng: Khi bạn gọi hàm Person() với từ khóa new, một đối tượng mới được tạo ra và this bên trong hàm sẽ tham chiếu đến đối tượng đó. Hàm sẽ gán các thuộc tính cho đối tượng mới này.
  • Constructor Invocation: Sử dụng từ khóa new sẽ thực hiện ba điều chính:
    1. Tạo một đối tượng trống mới.
    2. Gán đối tượng mới này cho từ khóa this trong hàm.
    3. Nếu hàm không trả về một đối tượng cụ thể, nó sẽ ngầm trả về đối tượng mới được tạo ra.
  • Kết quả: Biến person sẽ chứa đối tượng mới được tạo bởi hàm Person, với thuộc tính name'John'.

Sự khác biệt chính

  1. function Person() {} (Function Declaration):
    • Đây là cách khai báo một hàm thông thường. Hàm này có thể được sử dụng để gọi như một hàm thông thường hoặc như một hàm khởi tạo (constructor).
  2. var person = Person() (Function Invocation):
    • Đây là cách gọi hàm Person như một hàm thông thường. Không có đối tượng mới nào được tạo ra và từ khóa this sẽ tham chiếu đến phạm vi toàn cục hoặc đối tượng hiện tại.
    • Nếu hàm Person trả về một giá trị, biến person sẽ lưu giá trị đó.
  3. var person = new Person() (Constructor Invocation):
    • Khi sử dụng từ khóa new, một đối tượng mới được tạo ra. Hàm Person trở thành một constructor và gán các thuộc tính cho đối tượng mới.
    • Biến person sẽ lưu đối tượng mới được tạo ra bởi hàm Person.

Ví dụ minh họa chi tiết

// Khai báo hàm
function Person() {
    this.name = 'John';
    console.log('Person function is called');
    return { age: 30 };
}

// Function Invocation
var person1 = Person(); // Gọi hàm như một hàm thông thường
console.log(person1);   // { age: 30 }

// Constructor Invocation
var person2 = new Person(); // Gọi hàm như một constructor
console.log(person2);       // { age: 30 }
console.log(person2.name);  // undefined (vì đối tượng trả về từ hàm không chứa thuộc tính name)

Trong ví dụ này:

  • person1 chứa đối tượng { age: 30 } do hàm Person trả về khi gọi hàm thông thường.
  • person2 cũng chứa đối tượng { age: 30 }, nhưng nó không chứa thuộc tính name vì hàm đã trả về đối tượng thay vì this.