BRUCE_FEBRUCE_FE

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.

影片縮圖

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. 1.
    Check if the object itself has the property
  2. 2.
    If not, look up the prototype pointed to by the object's __proto__
  3. 3.
    If still not found, continue searching up the prototype chain
  4. 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. 1.
    Create a new empty object
  2. 2.
    Set the object's __proto__ to the function's prototype property
  3. 3.
    Bind the function's this to the newly created object
  4. 4.
    Execute the function's code (usually initializing object properties)
  5. 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 to Foo.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 ItemClass SyntaxFunction Syntax
SyntaxMore clear and intuitive, OOP styleMore scattered, need to define prototype methods separately
Hoisting BehaviorNot hoistedFunction declarations are hoisted
Inheritance ImplementationConcise using extends keywordNeed to manually set up prototype chain, more complex
Static MethodsDefined using static keywordDirectly assigned to constructor function
Private PropertiesSupports # syntax for true private propertiesNeed to use closures or other techniques to simulate
CompatibilityES6+, need to consider old browser compatibilityFully 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);
};