JavaScript Interview Questions Series (4) 10 questions each

1. Understanding of Promise

Promises are a mechanism for handling asynchronous operations in JavaScript. It represents the final completion or failure of an asynchronous operation and can return a value. Promise objects have the following characteristics:

  1. State (State) : Promise has three states, namely pending (in progress), fulfilled (completed) and rejected (failed). The initial state is pending. When the asynchronous operation is completed, the Promise can change to the fulfilled state, indicating that the operation is successful; if the asynchronous operation fails, it will change to the rejected state.

  2. Passing of value : Promise can pass a value when the state changes. When the Promise becomes fulfilled, the final result value will be passed to the subsequent processing function; when the Promise becomes rejected, an error object will be passed to the subsequent processing function.

  3. Chained call : Promise provides a chained call method. You can use .then()the method to register a callback function that handles success, and use .catch()the method to register a callback function that handles failure. This method of chaining calls can efficiently handle the results of asynchronous operations.

Here's a simple example to illustrate the use of Promises:

function fetchData() {
  return new Promise((resolve, reject) => {
    // 异步操作
    setTimeout(() => {
      const data = 'Some data';
      if (data) {
        resolve(data); // 异步操作成功
      } else {
        reject('Error'); // 异步操作失败
      }
    }, 2000);
  });
}

fetchData()
  .then(result => {
    console.log('成功:', result);
  })
  .catch(error => {
    console.log('失败:', error);
  });

In this example, fetchData()the function returns a Promise object. When the asynchronous operation is completed, call resolve(data)to indicate success and pass the final result value; if the asynchronous operation fails, call reject('Error')to indicate failure and pass the error message. Then you can .then()register a successful callback function through the method, and process the successful result value in the callback function; .catch()register a failed callback function through the method, and handle the failure in the callback function.

The advantage of Promise is that it provides a more elegant and readable way to handle asynchronous operations, avoiding the problem of callback hell (callback hell), and making asynchronous code easier to understand and maintain.

2. Understanding of async functions

 Async functions are special functions in JavaScript for handling asynchronous operations. Its definition asyncis identified by preceding the function with the keyword. The Async function is relatively concise and intuitive to use. It is based on the Promise object and awaitwaits for the resolution result of the Promise object through the keyword.

Here is a simple example to illustrate the use of Async functions:

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = 'Some data';
      if (data) {
        resolve(data);
      } else {
        reject('Error');
      }
    }, 2000);
  });
}

async function getData() {
  try {
    const result = await fetchData();
    console.log('成功:', result);
  } catch (error) {
    console.log('失败:', error);
  }
}

getData();

In this example, fetchData()the function returns a Promise object, which simulates an asynchronous operation. getData()The function is an Async function where the result of the function awaitis awaited using the keyword fetchData(). When awaitthe Promise object that the expression is waiting for is resolved (that is, the state becomes fulfilled), it will return the resolved value of the Promise and continue to execute subsequent code. If the Promise is rejected (that is, the status becomes rejected), an error will be thrown, which can be used try...catchto catch the error and handle it.

The characteristics of Async functions are as follows:

  1. Concise syntax : Async functions use the asyncand awaitkeywords to make writing asynchronous code more concise and intuitive. It allows developers to write asynchronous code in a synchronous manner, improving the readability and maintainability of the code.

  2. Based on Promise : Async function is based on Promise object, which internally uses Promise mechanism to handle asynchronous operations. awaitThe keyword can wait for the resolution result of a Promise object and return the resolved value as the return value.

  3. Error handling : Async functions use try...catchto catch errors that may be thrown and perform error handling. tryUse in the block to awaitwait for the resolution result of the Promise object. If an error occurs, it will be catchcaught by the block and processed accordingly.

Async function is a concise and powerful way to handle asynchronous operations. It is widely used in modern JavaScript development to handle network requests, file operations, database queries and other scenarios that require asynchronous operations. Compared with the traditional callback function and Promise chain call, it can express the intent of the code more intuitively, making asynchronous code easier to write and maintain.

3. What problem does Promise solve?

Promises solve some of the problems with handling asynchronous operations in JavaScript, including the following:

  1. Callback Hell (Callback Hell) : In the traditional callback function method, when there are multiple asynchronous operations that need to be executed sequentially, the code often has multiple nested callback functions, resulting in poor code readability, difficulty in understanding and maintenance. This nested structure is known as callback hell. Promise uses chain calls .then()to connect callback functions through the method, which avoids the problem of callback function nesting and makes the code structure clearer.

  2. Processing of asynchronous operation results : In traditional callback functions, the results of processing asynchronous operations need to be processed in the callback function. This results in logic scattered across multiple callback functions, making the code difficult to read and maintain. Promise provides a unified way to handle the results of asynchronous operations. .then()The callback function for successful processing is registered through the method, and .catch()the callback function for failed processing is registered through the method, so that the logic is concentrated in one place, which is easier to manage and maintain.

  3. Error handling : In traditional callback functions, error handling usually passes error information through the parameters of the callback function, and errors need to be checked manually in each callback function. This is prone to omissions or confusion. Promise provides .catch()methods to catch errors of asynchronous operations, making error handling more uniform and simple.

  4. Control of concurrent asynchronous operations : In some scenarios, it is necessary to control the execution sequence or the number of concurrent operations of multiple asynchronous operations. The state and control logic of these asynchronous operations need to be manually managed in the traditional callback function approach. Promise provides some methods (for example Promise.all(), Promise.race()) to conveniently control the execution order and result processing of concurrent asynchronous operations.

In short, Promise provides a more elegant, understandable and maintainable way to handle asynchronous operations, avoids the callback hell problem, unifies the processing of asynchronous operations, and provides better error handling and concurrency control mechanisms. It plays an important role in asynchronous programming and has become one of the commonly used tools in modern JavaScript development.

4. What are the methods of object creation?

In JavaScript, there are several ways to create objects. The following are common object creation methods:

1. Object literals{} : Create objects directly using object literal notation (braces ).

const obj = { 
  key1: value1,
  key2: value2,
  // ...
};

2. Constructor : Create an object by defining a constructor, and use newthe keyword to instantiate the object.

function Person(name, age) {
  this.name = name;
  this.age = age;
}

const person = new Person('John', 25);

3.Object.create() : Use Object.create()the method to create a new object and point its prototype to another object.

const obj = Object.create(proto);

4. Classes (ES6+): Create objects using the concepts of classes and constructors, define classes with the class keyword, and instantiate objects with new.

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

const person = new Person('John', 25);

 5. Factory function : By defining a function, the function internally creates and returns an object.

function createPerson(name, age) {
  return {
    name: name,
    age: age,
  };
}

const person = createPerson('John', 25);

 5. What are the methods of object inheritance? 

In JavaScript, there are several ways to implement inheritance between objects. The following are common object inheritance methods:

1. Prototype chain inheritance : By letting the prototype of one object point to another object, the inheritance relationship is realized. This way, child objects can access the properties and methods of the parent object.

function Parent() {
  this.name = 'Parent';
}
Parent.prototype.sayHello = function() {
  console.log('Hello');
};
function Child() {
  this.name = 'Child';
}
Child.prototype = new Parent();
const child = new Child();
child.sayHello(); // 输出: Hello

2. Constructor inheritance : By calling the constructor of the parent class in the constructor of the subclass, the inheritance of attributes is realized. This way, each instance will have its own copy of the properties.

function Parent(name) {
  this.name = name;
}
function Child(name) {
  Parent.call(this, name);
}
const child = new Child('Child');
console.log(child.name); // 输出: Child

3. Combination inheritance : Combining the methods of prototype chain inheritance and constructor inheritance to achieve simultaneous inheritance of properties and methods.

function Parent(name) {
  this.name = name;
}

Parent.prototype.sayHello = function() {
  console.log('Hello');
};

function Child(name) {
  Parent.call(this, name);
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

const child = new Child('Child');
child.sayHello(); // 输出: Hello
console.log(child.name); // 输出: Child

4. ES6 class inheritance: use the class keyword and extends keyword to realize class inheritance, and the subclass inherits the properties and methods of the parent class. 

class Parent {
  constructor(name) {
    this.name = name;
  }

    sayHello() {
    console.log('Hello');
  }
}
class Child extends Parent {
  constructor(name) {
    super(name);
  }
}
const child = new Child('Child');
child.sayHello(); // 输出: Hello
console.log(child.name); // 输出: Child

6. What situations will lead to memory leaks 

A memory leak refers to the situation where the memory allocated in the application cannot be released by the garbage collection mechanism, resulting in a continuous increase in memory usage and eventually exhausting the available memory. Here are some common situations that lead to memory leaks:

  1. Unlimited references : Circular references are formed between objects, making it impossible for the garbage collector to determine whether the objects are recyclable. For example, two objects refer to each other, and no other references point outside of them.

  2. Timers and event listeners that are not cleaned up in time : Timers, intervalrs, or event listeners that are not cleaned up in a timely manner can cause objects to persist in memory. If the timer or event listener is not properly canceled or removed, the object will continue to be alive and occupy memory.

  3. A large amount of cached data : cached data is usually stored in memory, if the cached data is not properly managed and cleaned up, they will continue to occupy memory space, leading to memory leaks.

  4. Unclosed database connections or file handles : When using database connections or file handles, if you forget to close them, these resources will always occupy memory, resulting in memory leaks.

  5. Closures used incorrectly : Closures are a powerful feature in JavaScript, but can lead to memory leaks if used incorrectly. External variables referenced in closures are not garbage collected unless the closure is explicitly freed.

  6. DOM references : Keeping references to DOM elements that are no longer needed can lead to memory leaks. When removing or replacing DOM elements, ensure that references are properly cleaned up.

  7. Creation and destruction of large objects : If large objects are created and destroyed frequently, memory leaks may result. This is because the garbage collector may not be able to reclaim the memory occupied by large objects in a timely manner.

The above are just some common situations, and there are many other reasons for memory leaks. To avoid memory leaks, developers should pay attention to properly manage memory resources, including releasing objects that are no longer used, cleaning up timers and event listeners in a timely manner, closing connections and handles that are no longer needed, etc. Using developer tools and memory analysis tools can help identify and resolve potential memory leaks.

7. The understanding and advantages and disadvantages of closure

A closure is the combination of a function and the lexical environment in which it is declared. In simple terms, a closure is a function that has access to variables in its own lexical scope as well as the enclosing scope.

To understand the concept of closures, you need to understand the following key points:

  1. Lexical scoping : Scope in JavaScript is determined by where a function is defined. Variables in the outer function or global scope can be accessed inside the function, but variables inside the function cannot be accessed by the outer function or the global scope. This nested scope chain forms the basis of closures.

  2. Functions as values : In JavaScript, functions can be passed and assigned as values. We can pass a function as an argument to another function, and we can assign functions to variables.

  3. Functions inside functions : JavaScript allows defining a function inside another function. The inner function can access the variables of the outer function, even if the outer function has been executed, this access still exists.

Advantages of closures include:

  1. Encapsulation : Closures allow the creation of private variables and functions. Through the scope inside the closure, some data and implementation details can be hidden, and only a limited interface is exposed. This helps modularize development and prevents naming conflicts.

  2. Data Persistence : Closures allow a function to access variables outside its lexical scope, and these variables can still be accessed even after the execution of the outer function is complete. This enables the data created outside the function to still exist after the function is executed, enabling persistent operations on the data.

  3. Implement callbacks and asynchronous operations : Closures are very useful when dealing with callbacks and asynchronous operations. You can pass functions as parameters to other functions and access variables of outer functions in inner functions. This allows the inner function to still have access to the context data it needs in an asynchronous environment.

However, closures also have some disadvantages:

  1. Memory consumption : Since closures keep references to outer scopes, memory usage can be high, especially if a large number of function instances are created where closures are used. If the closure is not released properly, it may cause a memory leak.

  2. Performance impact : Since closures involve accessing variables in the outer scope, additional scope chain lookups are required, which may result in certain performance overhead.

  3. Potential problems with misuse : Closures can be misused, and excessive use of closures can lead to increased code complexity and reduced readability and maintainability. Closures need to be used judiciously to ensure that the advantages they bring outweigh the potential disadvantages.

Therefore, when using closures, you need to carefully weigh their advantages and disadvantages, and ensure that they are used reasonably to avoid potential problems.

8. Talk about the understanding of the prototype chain in JS: what is it, what is it for, how to use it, advantages and disadvantages 

The prototype chain is the mechanism for implementing object inheritance in JavaScript. It is based on the prototype relationship between objects, allowing objects to access and share properties and methods through inheritance. To understand the prototype chain, you need to understand the following concepts:

  1. Prototype object : Every JavaScript object has a prototype object ( prototype). A prototype object is an ordinary object that contains properties and methods shared by object instances.

  2. [[Prototype]] chain : Every object has a link ( ) internally to its prototype object [[Prototype]]. When we access a property or method of an object, if the object itself does not have this property or method, the JavaScript engine will look up the prototype chain until it finds or reaches the top of the prototype chain ( ) null.

The uses of the prototype chain include:

  1. Inheritance of properties and methods : Through the prototype chain, an object can inherit the properties and methods of its prototype object. When accessing a property or method of an object, if the object itself does not have one, it will be searched in its prototype object until the corresponding property or method is found.

  2. Realize shared properties and methods of objects : By defining properties and methods on prototype objects, multiple object instances can share the same properties and methods. This reduces memory consumption and improves code efficiency and maintainability.

  3. Addition and modification of dynamic properties and methods : By modifying the prototype object, the properties and methods of the object can be dynamically added and modified without modifying the definition of each object instance.

When using the prototype chain, it can be defined and used in the following ways:

  1. Constructor and prototype object : Create an object through a constructor and use newthe keyword to instantiate an object. Inside the constructor, properties of the object instance can be defined. By defining properties and methods on the prototype object of the constructor, the inheritance and sharing of properties and methods can be realized.

  2. Access to the properties and methods of the prototype object : Through __proto__the properties or Object.getPrototypeOf()methods of the object instance, you can access the prototype object of the object. Properties and methods in the prototype object can be accessed using dot syntax or square bracket syntax.

The advantages of the prototype chain include:

  1. Object inheritance is realized : the prototype chain allows objects to share properties and methods through inheritance, reducing the writing of repeated code.

  2. Support dynamic addition and modification of properties and methods : By modifying the prototype object, the properties and methods of the object can be dynamically added and modified, and changes can also be reflected for existing object instances.

  3. Improved code efficiency and maintainability : the prototype chain allows multiple object instances to share the same properties and methods, reducing memory consumption and code redundancy, and improving code efficiency and maintainability.

Disadvantages of the prototype chain include:

  1. Shared features of inheritance : through prototype chain inheritance, properties and methods are shared. If the properties and methods of the prototype object are modified, all object instances inherited from the prototype object will be affected.

  2. Hierarchical nesting of the prototype chain : When the hierarchical nesting of the prototype chain is too deep, the efficiency of finding properties and methods may be reduced because it needs to be looked up layer by layer.

  3. Implicit dependencies : When using prototype chain inheritance, the properties and methods of objects may have implicit dependencies. When the structure of the prototype chain is complex or improperly modified, unexpected problems may occur.

It should be noted that in modern JavaScript, there are other ways to achieve object inheritance, such as using classes and extendskeywords to achieve inheritance. The prototype chain is a prototype-based inheritance mechanism, which may not be the best choice in some scenarios. Developers should choose an appropriate inheritance method according to actual needs and situations.

9. Talk about the understanding of JS inheritance (including ES6) -- or people ask that there are two classes A and B, how does B inherit A? 

In JavaScript, inheritance can be implemented in different ways, including prototypal chain inheritance, constructor inheritance, composition inheritance, and class inheritance in ES6. Below I will introduce the concept and usage of class inheritance in ES6 in detail.

In ES6, you can use classthe keyword and extendskeyword to implement class inheritance. Class inheritance is a prototype-based inheritance method, which allows subclasses to inherit the properties and methods of the parent class, and can add their own properties and methods.

The following is an example that demonstrates how to use ES6 class inheritance to implement the process of class B inheriting class A:

class A {
  constructor(propA) {
    this.propA = propA;
  }

  methodA() {
    console.log('Method A');
  }
}

class B extends A {
  constructor(propA, propB) {
    super(propA);
    this.propB = propB;
  }

  methodB() {
    console.log('Method B');
  }
}

const instanceB = new B('Property A', 'Property B');
instanceB.methodA(); // 输出: Method A
instanceB.methodB(); // 输出: Method B
console.log(instanceB.propA); // 输出: Property A
console.log(instanceB.propB); // 输出: Property B

 In the above example, we defined two classes A and B. Class B extendsinherits class A using the keyword. In the constructor of class B, we use superthe keyword to call the constructor of parent class A and pass the required parameters. In this way, when class B is instantiated, the constructor of class A will be called to initialize the properties of the parent class.

Through class inheritance, class B inherits the properties and methods of class A, and can add its own properties and methods in class B. In the example, instanceBan instance of class B can call methodA()methods inherited from class A and methods of class B itself methodB(). At the same time, instanceBit also has the attribute inherited from class A propAand the attribute of class B itself propB.

The advantages of ES6 class inheritance include:

  1. Concise syntax : Using classthe and extendskeywords, you can define classes and implement inheritance relationships in a clearer and more intuitive way.

  2. Constructor support : use superthe keyword to call the constructor of the parent class in the constructor of the subclass to facilitate the initialization of properties.

  3. Support instance and static methods : class inheritance can inherit the instance methods and static methods of the parent class, making the code more organized and structured.

  4. Easy to understand and maintain : Class inheritance provides a syntax closer to traditional object-oriented programming, making the code easier to understand, debug and maintain.

It should be noted that although class inheritance in ES6 provides convenience and syntactic sugar, it is essentially inheritance based on the prototype chain. In some cases, prototypal inheritance may be better suited to specific needs. Developers should choose the appropriate inheritance method according to the specific situation and needs.

10. Talk about how to bind JS native events

In JavaScript, there are several ways to bind native events. Here are a few common methods:

addEventListener() method : addEventListener() is a method of a DOM element to add an event listener. It accepts two parameters: event type and event handler function. Multiple event listeners can be bound by calling addEventListener() multiple times.

const element = document.getElementById('myElement');

function handleClick(event) {
  console.log('点击事件触发');
}

element.addEventListener('click', handleClick);

on event attribute : DOM elements also provide a series of on event attributes, such as onclick, onkeydown, onmouseoverand so on. You can directly assign the event handler function to the corresponding on event attribute.

const element = document.getElementById('myElement');

function handleClick(event) {
  console.log('点击事件触发');
}

element.onclick = handleClick;

Note that when using the on event attribute to bind an event handler, only one handler can be bound at the same time. If multiple assignments are made to the same on event property, subsequent assignments will override previous assignments.

HTML attributes : Inline event attributes can be used directly on HTML elements to bind event handlers. onBy adding prefixed attributes to element tags , such as onclick, onkeydown, onmouseoveretc.

<button onclick="handleClick(event)">点击按钮</button>

<script>
  function handleClick(event) {
    console.log('点击事件触发');
  }
</script>

It should be noted that when using HTML attributes to bind event handlers, the function name needs to be globally accessible, so you need to ensure that the function has been defined in the global scope.

These are common ways of binding native events. Using the addEventListener() method is the recommended way, which provides more flexible event binding and unbinding capabilities, and can bind multiple event listeners at the same time without overwriting previous bindings. However, according to project requirements and personal preferences, you can choose an appropriate way to bind native events.

Guess you like

Origin blog.csdn.net/weixin_52003205/article/details/131744337