每日一篇——23秋招JS面经(4)

96 阅读5分钟

每日一篇——23秋招JS面经(4)

⭐说说你常用的ES6新特性

  • 块级作用域(Block Scope): 使用 letconst 关键字声明的变量具有块级作用域,即只在当前代码块中有效。相比于 var 声明的变量具有函数作用域而言,这种方式更加灵活和可控。

  • 箭头函数(Arrow Function): 箭头函数通过 => 符号定义,可以简化函数的书写。它具有以下特点:

    • 自动绑定上下文:箭头函数不会创建自己的 this,而是继承外层作用域的 this
    • 省略 return 关键字:当函数体只有一条返回语句时,可以省略 return 关键字。
    • 更简洁的语法:当只有一个参数时,可以省略括号;当函数体为空时,可以使用 () => {} 表示。
  • 解构赋值(Destructuring Assignment): 解构赋值允许从数组或对象中提取值并赋给变量。例如,可以通过以下方式提取数组元素:

 Codeconst [a, b] = [1, 2];

​ 也可以通过以下方式提取对象属性:

 Codeconst { name, age } = { name: 'Alice', age: 18 };
  • 模板字符串(Template Strings): 模板字符串使用反引号 `` 包裹,并支持在字符串中插入变量或表达式。例如:
 Codeconst name = 'Alice';
console.log(`Hello, ${name}!`);
  • 默认参数(Default Parameters): 定义函数时可以为参数提供默认值,当调用函数时未传递对应参数时,将使用默认值。例如:
 Codefunction greet(name = 'Alice') {
  console.log(`Hello, ${name}!`);
}
greet(); // 输出:Hello, Alice!
  • 扩展运算符(Spread Operator): 扩展运算符用于展开可迭代对象(如数组和字符串),将其元素逐个展开,例如:
 Codeconst arr = [1, 2, 3];
console.log(...arr); // 输出:1 2 3
  • 类(Class): ES6 引入了类语法,可以使用 class 关键字定义一个类。类可以包含构造函数、实例方法、静态方法等。例如:
 Codeclass Person {
  constructor(name) {
    this.name = name;
  }

  sayHello() {
    console.log(`Hello, my name is ${this.name}!`);
  }

  static getInfo() {
    console.log('This is a person class.');
  }
}

const person = new Person('Alice');
person.sayHello();     // 输出:Hello, my name is Alice!
Person.getInfo();      // 输出:This is a person class.
  • 简化对象字面量(Object Literal Shorthand): 当定义对象字面量时,如果属性名和变量名相同,可以直接省略写属性值。例如:
 Codeconst name = 'Alice';
const age = 18;

const person = { name, age };
console.log(person);   // 输出:{ name: 'Alice', age: 18 }
  • 模块化(Module): ES6 引入了模块化概念,可以将代码拆分为多个模块,每个模块有自己的作用域,并通过 importexport 关键字进行模块之间的导入和导出。例如:
// moduleA.js
export function greet(name) {
  console.log(`Hello, ${name}!`);
}

// moduleB.js
import { greet } from './moduleA.js';
greet('Alice');       // 输出:Hello, Alice!
  • Promise: Promise 是用于处理异步操作的对象,避免了传统的回调地狱。它表示一个异步操作的最终完成或失败,并可以链式调用 thencatchfinally 方法处理结果。例如:
function fetchData() {
  return new Promise((resolve, reject) => {
    // 异步操作...
    if (/* 异步操作成功 */) {
      resolve('Data fetched successfully!');
    } else {
      reject('Failed to fetch data!');
    }
  });
}

fetchData()
  .then(data => console.log(data))        // 成功时执行
  .catch(error => console.error(error))   // 失败时执行
  .finally(() => console.log('Done'));    // 不论成功与否都执行
  • 数组方法(Array Methods): ES6 提供了许多方便的数组方法来处理和遍历数组,如 forEachmapfilterreduce 等。这些方法提供了更简洁、可读性更高的方式来操作数组。例如:
javascript Codeconst numbers = [1, 2, 3, 4];

numbers.forEach(num => console.log(num));                 // 遍历输出每个元素
const doubledNumbers = numbers.map(num => num * 2);       // 对每个元素进行操作并返回新数组
const evenNumbers = numbers.filter(num => num % 2 === 0); // 过滤出符合条件的元素
const sum = numbers.reduce((acc, num) => acc + num, 0);    // 对数组进行累加

⭐说说WeakMap

感觉这个人讲的比较清楚:WeakMap详解

🌙应用

弱引用:WeakMap的键是弱引用,意味着在没有其他引用指向键时,键会被自动回收,从而释放内存。这对于处理大量临时数据或需要临时缓存的场景非常有用。例如,可以使用WeakMap来存储临时的中间计算结果,当不再需要时自动清除。

隐藏数据:由于WeakMap的键是弱引用的,无法直接遍历获取所有键,因此可以使用WeakMap来隐藏对象的私有数据。通过将对象作为键存储数据,可以确保只有拥有该对象的变量才能访问对应的数据。这样可以有效防止数据被外部修改或访问,提高数据的安全性。

内存管理:WeakMap可以用于处理一些需要手动管理内存的场景。当我们需要在对象之间建立关联关系,并且希望在某个对象被垃圾回收时自动解除关联,就可以使用WeakMap。通过将关联的对象存储为键,可以确保在关联对象被回收时,相关的数据也会自动释放。

需要注意的是,WeakMap的键只能是对象,而不能是原始数据类型(如字符串、数字等)。同时,由于弱引用的特性,WeakMap没有提供像Map那样的迭代方法(如forEach、keys、values等),也没有size属性。所以在使用WeakMap时,需要根据具体场景合理选择,并且注意其特殊的限制和行为。

⭐说一说apply、call、bind

applycallbind 都是 JavaScript 中用于改变函数执行上下文(this)的方法,它们的区别如下:

  1. apply:
    • 语法:function.apply(thisArg, [argsArray])
    • 参数:需要指定函数执行时的上下文对象 thisArg 和可选的参数数组 argsArray
    • 功能:立即调用函数,并改变函数内部的 this 指向 thisArg,同时可以传递一个数组或类数组对象作为参数(通过 arguments 对象传递参数也可以)。
    • 示例:fn.apply(obj, [arg1, arg2])
  2. call:
    • 语法:function.call(thisArg, arg1, arg2, ...)
    • 参数:需要指定函数执行时的上下文对象 thisArg 和可选的多个参数 arg1, arg2, ...
    • 功能:立即调用函数,并改变函数内部的 this 指向 thisArg,同时可以传递多个参数(通过逗号分隔)。
    • 示例:fn.call(obj, arg1, arg2)
  3. bind:
    • 语法:function.bind(thisArg, arg1, arg2, ...)
    • 参数:需要指定函数执行时的上下文对象 thisArg 和可选的多个参数 arg1, arg2, ...
    • 功能:创建一个新的函数,函数内部的 this 指向 thisArg,并且绑定了指定的参数。不会立即执行函数,而是返回一个新函数,之后可以再次调用。
    • 示例:var boundFn = fn.bind(obj, arg1, arg2)

总结:

  • applycall 可以立即调用函数并改变函数内部的 this 指向,区别在于参数传递方式不同,apply 接受参数数组,而 call 接受多个参数。
  • bind 不会立即执行函数,而是返回一个新函数,函数内部的 this 指向绑定的对象,并且可以预先绑定一些参数。
  • 这三种方法都可以实现改变函数执行上下文的目的,根据具体需求选择合适的方法。