know

js相关

高优先级知识点,需要熟练掌握。

# 1. try...catch 可以捕获到异步代码中的错误吗?

不能直接捕获

try {
  setTimeout(() => {
    throw new Error('err')
  }, 200)
} catch (err) {
  console.log(err)
}
// 上面是错误的示范

// 正确的写法
// 方法1: 使用Promise
new Promise((resolve, reject) => {
  setTimeout(() => {
    try {
      throw new Error('err')
      // 如果没有错误,可以调用resolve()
      // resolve('成功结果');
    } catch (error) {
      reject(error)
    }
  }, 200)
})
  .then(result => {
    console.log('成功:', result)
  })
  .catch(err => {
    console.log('捕获到错误:', err)
  })

// 方法2: 使用async/await (更接近同步代码的写法)
async function handleAsyncOperation() {
  try {
    await new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          throw new Error('err')
          // resolve('成功结果');
        } catch (error) {
          reject(error)
        }
      }, 200)
    })
  } catch (err) {
    console.log('捕获到错误:', err)
  }
}

总结:try...catch 不能捕获到异步代码中的错误,对于异步代码,需要结合 Promise 、async/await 或者事件监听器等机制来处理错误。

# 2. Generator 基本语法

Generator 是 ES6 引入的新语法,Generator 是一个可以暂停和继续执行的函数。

function* count() {
  yield 1
  yield 2
  return 3
}
let c = count()
console.log(c.next()) // { value: 1, done: false }
console.log(c.next()) // { value: 2, done: false }
console.log(c.next()) // { value: 3, done: true }
console.log(c.next()) // { value: undefined, done: true }

# 3. Map 和 WeakMap 有什么区别?

  1. Map:键是强引用,支持遍历,所有类型都可做键。
  2. WeakMap:键只能是对象,并且是弱引用;键对象失去外部引用时会被 GC 回收,WeakMap 自动移除对应条目,不可遍历、无 size。

# 4. Map 和 Set 的用法以及区别

  1. 初始化需要的值不一样,Map 需要的是一个二维数组,而 Set 需要的是一维 Array 数组
  2. Map 和 Set 都不允许键重复
  3. Map 的键是不能修改,但是键对应的值是可以修改的;Set 不能通过迭代器来改变 Set 的值,因为 Set 的值就是键。

# 5. 说说对 ES6 中 rest 参数的理解

ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用 arguments 对象了。

rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

// 报错
function f(a, ...b, c) {
  // ...
}

// 正确
function f(a, ...b) {
}

# 6. es5 中的类和 es6 中的 class 有什么区别?

ES5:语法层面没有“类”,全靠函数 + prototype 模拟,繁琐、易错。 ES6:引入真正语法级的 class,语义更清晰、继承更安全,但仍基于原型链实现,只是语法糖而非新模型。

# 7. 箭头函数的 this 指向哪⾥?

箭头函数没有自己的 this,它的 this 是在定义时(词法作用域)就绑定好的外层(最近一层非箭头函数)的 this,并且永远不变。

简单记忆:箭头函数里的 this 跟写代码时所在的位置有关,跟运行时谁调用它无关。

# 8. 箭头函数能当构造函数吗?

  1. 箭头函数不会创建自己的 this
  2. 箭头函数继承而来的 this 指向永远不变
  3. .call()/.apply()/.bind()无法改变箭头函数中 this 的指向
  4. 箭头函数不能作为构造函数使用,会报错
  5. 箭头函数没有自己的 arguments
  6. 箭头函数没有原型 prototype
  7. 箭头函数不能用作 Generator 函数,不能使用 yeild 关键字
let sayHi = () => {
  console.log('Hello World !')
}
console.log(sayHi.prototype) // undefined

# 9. new 操作符的实现步骤如下:

  1. 创建一个空对象,作为将要返回的实例对象。
  2. 为步骤 1 新创建的对象添加属性proto,将该属性链接至构造函数的原型对象 ;
  3. 将步骤 1 新创建的对象作为 this 的上下文 ;
  4. 如果该函数没有返回对象,则返回 this。

# 10. Object.defineProperty 与 Proxy 的区别

# 10.1. 拦截操作的范围

  • Object.defineProperty:只能拦截对象的属性读取(get)和设置(set)操作。
  • Proxy:可以拦截多达 13 种不同的基本操作,包括属性读写、删除、遍历、函数调用、构造函数等。

# 10.2. 监听对象的方式

  • Object.defineProperty:针对对象的单个属性进行监听,需要遍历对象的每个属性。
  • Proxy:针对整个对象进行监听,可以一次性代理整个对象。

# 10.3. 对数组的支持

  • Object.defineProperty:无法直接监听数组的变化,需要通过重写数组方法(如 push、pop、shift 等)来实现。
  • Proxy:可以直接监听数组的变化,包括通过索引访问、修改数组元素,以及数组方法的调用。

# 10.4. 对新增属性的处理

  • Object.defineProperty:无法监听对象属性的新增或删除,只能监听已经定义的属性。
  • Proxy:可以监听到对象属性的新增和删除操作。

# 10.5. 嵌套对象的处理

  • Object.defineProperty:需要递归遍历对象的所有嵌套属性,对每个属性单独进行监听。
  • Proxy:可以实现对嵌套对象的深层拦截,当访问嵌套属性时可以自动为嵌套对象创建新的代理。

# 10.6. 性能方面

  • Object.defineProperty:需要对对象的每个属性进行遍历,当属性较多时可能会影响性能。
  • Proxy:拦截操作是惰性执行的,只有在需要拦截时才会触发相应的操作,对于大型对象可能有更好的性能表现。

# 10.7. 错误处理

  • Object.defineProperty:错误处理能力有限,通常只能通过抛出异常来处理错误。
  • Proxy:提供了更灵活的错误捕获和处理机制。

# 10.8. 兼容性考虑

  • Object.defineProperty:IE9 及以上版本支持,兼容性较好。
  • Proxy:IE 不支持,需要使用 polyfill 或者降级处理,在现代浏览器中支持良好。

# 10.9. 总结

Proxy 相比 Object.defineProperty 提供了更全面的拦截功能,支持更多的操作类型,对数组和新增属性有更好的支持,并且在性能和错误处理方面也有优势。这也是为什么 Vue 3.0 选择使用 Proxy 替代 Object.defineProperty 来实现其响应式系统的原因。但是,Proxy 的浏览器兼容性不如 Object.defineProperty,在需要支持旧版浏览器的项目中需要谨慎使用。

用户想了解 ES6 中的 Reflect 对象的用途。我需要搜索相关信息,然后整理成一个全面的回答。

toolName: web_search

status: success

query: ES6 Reflect 对象 用途 特点 方法

我需要继续搜索更多关于 Reflect 对象的具体方法和用例,以便提供更全面的回答。

toolName: web_search

status: success

query: ES6 Reflect 对象 方法 实例 应用场景

# 11. ES6 中的 Reflect 对象有什么用?

Reflect 是 ES6 引入的一个新的全局对象,它提供了一系列用于操作对象的静态方法。Reflect 对象的设计目的主要有以下几点:

# 11.1. Reflect 的主要用途

  1. 将对象的内部方法集中化管理

    • 将 Object 对象中一些明显属于语言内部的方法(如 Object.defineProperty)移植到 Reflect 对象上
    • 保持 JavaScript 代码的整洁和干净,避免这些方法散布在全局或通过原型调用
  2. 提供函数式的对象操作方法

    • 将命令式操作(如 deletein 等)转变为函数式操作(如 Reflect.deletePropertyReflect.has
    • 使代码更易于维护和向下兼容,避免出现更多的保留字
  3. 与 Proxy 配合使用

    • Reflect 对象的方法与 Proxy 对象的处理器(handler)方法一一对应
    • 在 Proxy 中可以方便地调用对应的 Reflect 方法来获取默认行为
  4. 改进错误处理方式

    • 传统的 Object 方法在操作失败时会抛出异常,需要使用 try-catch 捕获
    • Reflect 方法在操作失败时会返回 false,使错误处理更加简洁

# 11.2. Reflect 的特点

  1. 不是构造函数

    • 与大多数全局对象不同,Reflect 不能通过 new 运算符调用
    • 不能将 Reflect 作为函数调用
  2. 所有方法都是静态的

    • 类似于 Math 对象,所有方法都直接通过 Reflect 对象调用

# 11.3. Reflect 的主要方法

Reflect 对象提供了 13 个静态方法,这些方法与 Proxy 的处理器方法一一对应:

  1. Reflect.apply(target, thisArgument, argumentsList)

    • 对一个函数进行调用操作,类似于 Function.prototype.apply()
    • 例如:Reflect.apply(Math.max, undefined, [1, 2, 3]) 等同于 Math.max.apply(undefined, [1, 2, 3])
  2. Reflect.construct(target, argumentsList[, newTarget])

    • 对构造函数进行 new 操作,相当于执行 new target(...args)
    • 可以指定原型对象,实现更灵活的对象创建
  3. Reflect.defineProperty(target, propertyKey, attributes)

    • 类似于 Object.defineProperty(),但返回布尔值表示操作是否成功
  4. Reflect.deleteProperty(target, propertyKey)

    • 作为函数的 delete 操作符,相当于执行 delete target[name]
  5. Reflect.get(target, propertyKey[, receiver])

    • 获取对象属性的值,类似于 target[name]
    • 可以指定 receiver 参数作为 getter 中的 this 值
  6. Reflect.getOwnPropertyDescriptor(target, propertyKey)

    • 类似于 Object.getOwnPropertyDescriptor()
  7. Reflect.getPrototypeOf(target)

    • 类似于 Object.getPrototypeOf()
  8. Reflect.has(target, propertyKey)

    • 判断一个对象是否存在某个属性,功能与 in 运算符相同
  9. Reflect.isExtensible(target)

    • 类似于 Object.isExtensible()
  10. Reflect.ownKeys(target)

    • 返回一个包含所有自身属性(不包含继承属性)的数组
    • 不受 enumerable 影响,类似于 Object.keys()
  11. Reflect.preventExtensions(target)

    • 类似于 Object.preventExtensions()
  12. Reflect.set(target, propertyKey, value[, receiver])

    • 设置对象的属性值,返回布尔值表示是否设置成功
    • 可以指定 receiver 参数作为 setter 中的 this 值
  13. Reflect.setPrototypeOf(target, prototype)

    • 设置对象的原型,返回布尔值表示是否设置成功

# 12. 使用示例

# 12.1. 检测对象是否存在特定属性

const obj = { name: 'John', age: 30 }

// 使用 Reflect.has
Reflect.has(obj, 'name') // true
Reflect.has(obj, 'gender') // false

// 等同于使用 in 运算符
'name' in obj // true
'gender' in obj // false

# 12.2. 获取和设置属性值

const obj = { name: 'John' }

// 获取属性值
Reflect.get(obj, 'name') // 'John'

// 设置属性值
Reflect.set(obj, 'age', 30) // true
console.log(obj) // { name: 'John', age: 30 }

# 12.3. 与 Proxy 配合使用

const target = { name: 'John' }
const handler = {
  get(target, prop, receiver) {
    console.log(`Getting ${prop}`)
    return Reflect.get(target, prop, receiver)
  },
  set(target, prop, value, receiver) {
    console.log(`Setting ${prop} to ${value}`)
    return Reflect.set(target, prop, value, receiver)
  }
}

const proxy = new Proxy(target, handler)
proxy.name // 输出: "Getting name",返回: "John"
proxy.age = 30 // 输出: "Setting age to 30",返回: true

# 12.4. 函数调用

// 查找数组中的最大值
const arr = [1, 3, 5, 7]

// 使用 Reflect.apply
const max = Reflect.apply(Math.max, undefined, arr) // 7

// 等同于
const max2 = Math.max.apply(undefined, arr) // 7

总结来说,Reflect 对象提供了一种更加函数式、更加统一的方式来操作对象,特别是在与 Proxy 配合使用时,能够更方便地实现对象的元编程。

# 13. 什么是 let 的临时性死区?

在 let/const 声明语句 之前 的代码区域里,该变量已存在但不可访问;一旦访问就会抛出 ReferenceError

console.log(a) // ❌ ReferenceError: Cannot access 'a' before initialization
let a = 1

对比 var

console.log(b) // undefined(不会报错)
var b = 2

# 14. symbol 有什么用处?

ES5 的对象属性名都是字符串,这容易造成属性名的冲突

Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

# 15. async/await 和 Promise 有什么关系?

es2017 的新语法,async/await 就是 generator + promise 的语法糖 async/await 就是将异步操作以同步的流程表达出来,避免了回调地狱,而且语义更清晰。

# 16. Generator 使用场景有哪些?

Generator 让函数拥有了「时间切片」能力,下面是高频使用场景

场景 说明 极简示例
1. 自定义可迭代对象 yield* 轻松写出 for…of 可用的数据结构 function* fib(){ let a=0,b=1; while(true){ yield a; [a,b]=[b,a+b]; } }
2. 惰性序列/无限流 按需计算,节省内存 同上斐波那契,用到第 n 个才计算第 n 个
3. 异步流程管理 async/await 出现前,用库(co、redux-saga)把 Generator 变成「同步写法异步执行」 co(function*(){ const u = yield fetch('/user'); console.log(u); });
4. 状态机 yield 做状态节点,代码比回调/标志位清晰很多 见下方小例子
5. 任务分片 & 可取消 长任务拆成多次 yield,浏览器有时间渲染;可中途 return 取消 页面大列表分批渲染
上次更新: