EN/CH Mode
BRUCE_FE JS Interview Notes - JS new, Prototype Chain and Class Implementation Analysis
In-depth explanation of JavaScript's new keyword, prototype chain mechanism, Class syntax, and implementation principles and best practices for private variables.
EN/CH Mode
Lazy to read articles? Then watch videos!
Function Constructor vs Class Syntax
JavaScript can create objects through function constructors or ES6 Class syntax. Both have the same underlying mechanism but differ in syntax and usage.
Function Constructor Approach
// Function Constructor
function Person(name, age) {
this.name = name;
this.age = age;
}
// Prototype method - shared by all instances
Person.prototype.introduce = function() {
return `I'm ${this.name}, ${this.age} years old`;
};
// Static method simulation
Person.createAnonymous = function () {
return new Person('Anonymous', 0);
};
// Create instance
const john = new Person('John', 30);
console.log(john.introduce()); // "I'm John, 30 years old"
Class Syntax (ES6+)
// Class Syntax (ES6+)
class Person {
// Constructor
constructor(name, age) {
this.name = name;
this.age = age;
}
// Prototype methods (shared)
introduce() {
return `I'm ${this.name}, ${this.age} years old`;
}
// Static method - attached to class, can be called without instantiation
static createAnonymous() {
return new Person('Anonymous', 0);
}
}
// Create instance directly with new
const jane = new Person('Jane', 25);
// Call static method
const anonymous = Person.createAnonymous();
/*
Differences between direct new vs static method instantiation:
1. Direct new:
- Pros: Intuitive and clear, full control over parameters
- Cons: Cannot encapsulate repeated logic, need to provide full parameters each time
2. Static method:
- Pros: Encapsulates complex construction logic, provides default values, implements factory pattern
- Pros: Can implement singleton pattern or object pool design patterns
- Cons: Indirectness may reduce code readability
Practical applications:
- Static methods are more suitable when object creation logic is complex or needs to reuse specific configurations
- Common in API designs like React.createElement(), Array.from()
*/
Prototype Chain Analysis
__proto__
and prototype
JavaScript's object system is prototype-based, with two key concepts:
- •
prototype
: is a property of functions that contains methods and properties to be shared with instances - •
__proto__
: is a property of objects that points to the prototype of the constructor function that created the object
function Dog(name) {
this.name = name;
}
Dog.prototype.bark = function() {
return `${this.name} says woof!`;
};
const fluffy = new Dog('Fluffy');
console.log(fluffy.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.constructor === Dog); // true
console.log(fluffy.bark()); // "Fluffy says woof!"
function Dog(name) {
this.name = name;
}
Dog.prototype.bark = function() {
return `${this.name} says woof!`;
};
const fluffy = new Dog('Fluffy');
console.log(fluffy.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.constructor === Dog); // true
console.log(fluffy.bark()); // "Fluffy says woof!"
Prototype Chain Lookup Mechanism
When accessing object properties, the JavaScript engine will:
- 1.Check if the object itself has the property
- 2.If not, look up the prototype pointed to by the object's
__proto__
- 3.If still not found, continue searching up the prototype chain
- 4.Until the property is found or reaching the end of the prototype chain (
Object.prototype.__proto__ === null
)
// Prototype chain example
function Animal() {}
Animal.prototype.eat = function() { return 'Eating...'; };
function Dog() {}
// Set up prototype inheritance
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // Fix constructor
Dog.prototype.bark = function() { return 'Woof!'; };
const doggy = new Dog();
console.log(doggy.bark()); // "Woof!" - found in Dog.prototype
console.log(doggy.eat()); // "Eating..." - found in Animal.prototype
Alternatives to __proto__
Since __proto__
is deprecated, it's recommended to use the following methods:
// Get prototype
Object.getPrototypeOf(obj); // Alternative to obj.__proto__
// Set prototype
Object.setPrototypeOf(obj, prototype); // Alternative to obj.__proto__ = prototype
// Create new object with specified prototype
Object.create(prototype); // More direct than new
Private Variables Implementation
1. Closure-based Private Variables
function Counter() {
// Private variable
let count = 0;
// Public methods
this.increment = function() {
count++;
return count;
};
this.decrement = function() {
count--;
return count;
};
this.getValue = function() {
return count;
};
}
const counter = new Counter();
console.log(counter.getValue()); // 0
counter.increment();
console.log(counter.getValue()); // 1
console.log(counter.count); // undefined (cannot access directly)
2. ES2022 Private Fields (#)
class BankAccount {
#balance = 0; // Private field
constructor(initialBalance) {
if (initialBalance > 0) {
this.#balance = initialBalance;
}
}
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
return true;
}
return false;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount(100);
console.log(account.getBalance()); // 100
// console.log(account.#balance); // SyntaxError: Private field
🔥 Common Interview Questions
(1)What does the new keyword do? Please describe its execution process.
Answer:When using the new operator to call a function, the following steps are executed:
- 1.Create a new empty object
- 2.Set the object's
__proto__
to the function's prototype property - 3.Bind the function's this to the newly created object
- 4.Execute the function's code (usually initializing object properties)
- 5.If the function doesn't return an object, return the newly created object
function myNew(Constructor, ...args) {
// 1. Create a new object and set its prototype to the constructor's prototype
const obj = Object.create(Constructor.prototype);
// 2. Execute the constructor with the new object as 'this'
const result = Constructor.apply(obj, args);
// 3. Return the result if it's an object, otherwise return the new object
return result instanceof Object ? result : obj;
}
// Usage example
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
const person = myNew(Person, 'John');
person.sayHello(); // Output: Hello, I'm John
(2)What's the difference between prototype and __proto__?
Answer:
- •
prototype
: is a property unique to functions, pointing to an object that contains properties and methods to be inherited by instances - •
__proto__
: is a property of all objects, pointing to the prototype of the constructor function that created the object - •When creating an object with
new Foo()
, the object's__proto__
is set toFoo.prototype
- •Therefore
instance.__proto__ === Constructor.prototype
always holds true
(3)What are the differences between ES6 Class and traditional function constructors?
Answer:
ES6 Class is essentially syntactic sugar for prototype inheritance, with no change in the underlying mechanism.
Comparison Item | Class Syntax | Function Syntax |
---|---|---|
Syntax | More clear and intuitive, OOP style | More scattered, need to define prototype methods separately |
Hoisting Behavior | Not hoisted | Function declarations are hoisted |
Inheritance Implementation | Concise using extends keyword | Need to manually set up prototype chain, more complex |
Static Methods | Defined using static keyword | Directly assigned to constructor function |
Private Properties | Supports # syntax for true private properties | Need to use closures or other techniques to simulate |
Compatibility | ES6+, need to consider old browser compatibility | Fully supported, good compatibility |
Code Comparison:
Class Syntax
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
hello() {
return `你好,我是 ${this.name}`;
}
static create(name, age) {
return new Person(name, age);
}
#privateField = '私有';
getPrivate() {
return this.#privateField;
}
}
Function Syntax
function Person(name, age) {
this.name = name;
this.age = age;
// Private variable using closure
var privateField = 'private';
this.getPrivate = function() {
return privateField;
};
}
// Prototype method
Person.prototype.hello = function() {
return `Hello, my name is ${this.name}`;
};
// Static method
Person.create = function(name, age) {
return new Person(name, age);
};