JS
JS 基础知识
JS 是前端开发的核心能力,面试重点考察,无论工作经验长短�?
::: tip
如有疑问,可免费 加群 讨论咨询,也可参�?1v1 面试咨询服务�?专业、系统、高效、全流程 准备前端面试
:::
了解哪些最新的 ES 新特性?
参考答�?
::: details
特�?1: ES2024 �?JSON 模块
支持直接通过 import 语法加载 JSON 文件,避免额外的文件读取逻辑�?
1 | import config from './config.json' assert { type: 'json' } |
*特�?3: ES2022 的类字段与私有方�?
支持类中的私有字�?�?field) 和私有方法,增强了封装性�?
1 | class Counter { |
特�?5: ES2020 的可选链和空值合并操作符
简化深层嵌套对象属性的访问,并安全处理空值�?
1 | const user = { |
特�?6: ES2019 的数�?flat �?flatMap 方法
flat 展开多层嵌套数组,flatMap 结合映射与扁平化操作�?
1 | const nestedArray = [1, [2, [3, 4]], 5] |
:::
参考文�?
::: details
:::
typeof 能判断哪些类�?
参考答�?
::: details
| 类型 | *返回�? | 备注 |
|---|---|---|
| Undefined | "undefined" |
当变量未被定义或未赋值时,返回此值�? |
| Null | "object" |
历史遗留问题,null 被错误地识别为对象�? |
| Boolean | "boolean" |
适用�?true �?false 值�? |
| Number | "number" |
适用于整数和浮点数(包括特殊�?NaN �?Infinity)�? |
| String | "string" |
适用于字符串(例�?"hello")�? |
| BigInt | "bigint" |
适用于任意大的整数(例如 10n)�? |
| Symbol | "symbol" |
适用�?Symbol 类型�? |
| *Function(classes�? | "function" |
适用于可调用的对象(如函数和类定义)�? |
| 其他对象 | "object" |
包括数组、普通对象、日期对象、正则表达式等非函数对象�? |
*注意�?
typeof null === "object"
�?JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签�?0。由�?null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返�?”object”实际使用
对于更复杂的类型检测,可以使用工具函数,如Object.prototype.toString.call()或第三方库(�?lodash)�?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45// 数�?typeof 37 === 'number'
typeof 3.14 === 'number'
typeof 42 === 'number'
typeof Math.LN2 === 'number'
typeof Infinity === 'number'
typeof NaN === 'number' // 尽管它是 "Not-A-Number" (非数�? 的缩�?typeof Number(1) === 'number' // Number 会尝试把参数解析成数�?typeof Number('shoe') === 'number' // 包括不能将类型强制转换为数字的�?
typeof 42n === 'bigint'
// 字符�?typeof '' === 'string'
typeof 'bla' === 'string'
typeof `template literal` === 'string'
typeof '1' === 'string' // 注意内容为数字的字符串仍是字符串
typeof typeof 1 === 'string' // typeof 总是返回一个字符串
typeof String(1) === 'string' // String 将任意值转换为字符串,�?toString 更安�?
// 布尔�?typeof true === 'boolean'
typeof false === 'boolean'
typeof Boolean(1) === 'boolean' // Boolean() 会基于参数是真值还是虚值进行转�?typeof !!1 === 'boolean' // 两次调用 !(逻辑非)运算符相当于 Boolean()
// Symbols
typeof Symbol() === 'symbol'
typeof Symbol('foo') === 'symbol'
typeof Symbol.iterator === 'symbol'
// Undefined
typeof undefined === 'undefined'
typeof declaredButUndefinedVariable === 'undefined'
typeof undeclaredVariable === 'undefined'
// 对象
typeof { a: 1 } === 'object'
// 使用 Array.isArray 或�?Object.prototype.toString.call
// 区分数组和普通对�?typeof [1, 2, 4] === 'object'
typeof new Date() === 'object'
typeof /regex/ === 'object'
// 下面的例子令人迷惑,非常危险,没有用处。避免使用它们�?typeof new Boolean(true) === 'object'
typeof new Number(1) === 'object'
typeof new String('abc') === 'object'
// 函数
typeof function () {} === 'function'
typeof class C {} === 'function'
typeof Math.sin === 'function'
:::
== �?=== 有什么区别?
参考答�?
::: details
==(宽松相等):会在比较两个操作数时执�?类型转换,尝试将两者转换为相同类型后再比较�?-===(严格相等):不会执行类型转换,仅在类型和值完全相同的情况下返�?true�?- **推荐使用===**:因为它更严格、更符合预期,能避免潜在的错误。尤其是在需要精确判断值和类型时�?- 实际工作中,使用 if (a == null) 可判�?a 是否�?null 或�?undefined�?
常见比较结果
| x | y | == | === |
|---|---|---|---|
undefined |
undefined |
�? | �? |
null |
null |
�? | �? |
true |
true |
�? | �? |
false |
false |
�? | �? |
'foo' |
'foo' |
�? | �? |
0 |
0 |
�? | �? |
+0 |
-0 |
�? | �? |
+0 |
0 |
�? | �? |
-0 |
0 |
�? | �? |
0n |
-0n |
�? | �? |
0 |
false |
�? | �? |
"" |
false |
�? | �? |
"" |
0 |
�? | �? |
'0' |
0 |
�? | �? |
'17' |
17 |
�? | �? |
[1, 2] |
'1,2' |
�? | �? |
new String('foo') |
'foo' |
�? | �? |
null |
undefined |
�? | �? |
null |
false |
�? | �? |
undefined |
false |
�? | �? |
{ foo: 'bar' } |
{ foo: 'bar' } |
�? | �? |
new String('foo') |
new String('foo') |
�? | �? |
0 |
null |
�? | �? |
0 |
NaN |
�? | �? |
'foo' |
NaN |
�? | �? |
NaN |
NaN |
�? | �? |
说明�?
- �?表示比较结果�?
true - �?表示比较结果�?
false
:::
你熟悉哪些数�?API �?
参考答�?
::: details
- 创建数组
Array(),Array.of(),Array.from()
1 | Array.of(1, 2, 3) // [1, 2, 3] |
添加/删除元素
push(): 在末尾添�? -pop(): 删除末尾unshift(): 在开头添�? -shift(): 删除开�?1
2
3
4
5let arr = [1, 2]
arr.push(3) // [1, 2, 3]
arr.pop() // [1, 2]
arr.unshift(4) // [4, 1, 2]
arr.shift() // [1, 2]
组合/拆分数组
concat(): 合并数组,不影响原数组,浅拷�? -join(): 将数组连接为字符�? -slice(): 截取部分数组(不修改原数组)
1 | ;[1, 2].concat([3, 4]) // [1, 2, 3, 4] |
- 替换/重组
splice(): 添加、删除或替换元素
1 | let arr = [1, 2, 3] |
- 查找单个元素
indexOf(): 查找首次出现的索�? -lastIndexOf(): 查找最后出现的索引find(): 找到第一个满足条件的元素findIndex(): 找到第一个满足条件的索引
1 | ;[1, 2, 3].indexOf(2) // 1 |
判断
includes(): 判断是否包含某元�? -some(): 判断是否至少有一个元素满足条�? -every(): 判断是否所有元素满足条�?1
2
3;[1, 2, 3].includes(2) // true
;[1, 2, 3].some((x) => x > 2) // true
;[1, 2, 3].every((x) => x > 0) // true
迭代
forEach(): 遍历元素,无�?break,可以用 try/catch �?throw new Error 来停�?1
;[1, 2, 3].forEach((item, index) => console.log(item, index))
映射/变换
map(): 对每个元素进行操作并生成新数�?1
;[1, 2, 3].map((x) => x * 2) // [2, 4, 6]
过滤
filter(): 筛选出满足条件的元�?1
;[1, 2, 3].filter((x) => x > 1) // [2, 3]
规约
reduce(): 将数组缩减为单一�?-reduceRight(): 从右向左缩减
1 | ;[1, 2, 3].reduce((acc, val) => acc + val, 0) // 6 |
- 排序
sort(): 对数组进行排�?-reverse(): 反转数组顺序
1 | ;[3, 1, 2].sort((a, b) => a - b) // [1, 2, 3] |
- 填充
fill(): 用指定值填充数�?1
new Array(3).fill(0) // [0, 0, 0]
- *扁平�?
flat(): 将多维数组展平成一�?-flatMap(): 映射并展�?1
2;[1, [2, [3]]].flat(2) // [1, 2, 3]
;[1, 2].flatMap((x) => [x, x * 2]) // [1, 2, 2, 4]
- 复制/填充
copyWithin(): 将数组的部分内容复制到其他位�?1
;[1, 2, 3, 4].copyWithin(1, 2) // [1, 3, 4, 4]
- 生成键值对
keys(),values(),entries()
1 | const arr = ['a', 'b', 'c'] |
- *判断是否是数�?
Array.isArray()
1 | Array.isArray([1, 2, 3]) // true |
:::
值类型和引用类型的区�?
1 | // 值类�?let a = 100 |
1 | // 引用类型 |
参考答�?
::: details
| 特�? | 值类�? | 引用类型 |
|---|---|---|
| 存储内容 | 数据值本�? | 数据的引用(地址�? |
| 存储位置 | 栈内�? | 栈存引用,堆存实际数�? |
| *赋值方�? | 拷贝�? | 拷贝引用(地址�? |
| *变量之间独立�? | 互相独立,互不影�? | 指向同一数据,互相影�? |
| 常见数据类型 | 基本数据类型(如 number,string,boolean,undefined,null,symbol�? |
复杂数据类型(如 Object,Array,Function�? |
- 为什么有值类型和引用类型�?
- **值类�?*适合存储简单、占用内存较小的数据,操作快速�?- 引用类型适合存储复杂、占用内存较大的数据,支持动态扩展�?
- 如何避免引用类型的共享问题?
- 如果需要创建引用类型的副本,使用深拷贝,而非浅拷贝�?
深拷贝例子:
1 | const obj1 = { name: 'Alice' } |
浅拷贝例子:
1 | const obj1 = { name: 'Alice' } |
:::
箭头函数和普通函数的区别
参考答�?
::: details
| 特�? | 箭头函数 | 普通函�? |
|---|---|---|
| 语法 | 简洁,使用 => 定义 |
使用 function 定义 |
this 绑定 |
词法绑定,继承外�?this |
动态绑定,调用时决�? |
arguments 对象 |
没有,需要使�?...args |
有自己的 arguments 对象 |
| 是否能作为构造函�? | 不能 | 可以 |
是否�?prototype 属�? |
没有 | �? |
是否支持 bind/call/apply |
不支�? | 支持 |
| 适用场景 | 用于回调函数、闭包、需要继承外�?this 的场�? |
需要动态绑�?this,或用作构造函数时 |
1 | // 箭头函数 this |
:::
什么时候不能使用箭头函�?
参考答�?
::: details
- 需要动态绑�?
this的场景�?2. 作为构造函数�?3. 需�?arguments对象的场景�?4. 需要显式修�?this的场景(使用bind/call/apply等)�?5. 类的实例方法(特别是getter �?setter)。—�?无法动态绑�?this
:::
for…in �?for…of 的区�?
参考答�?
::: details
| 特�? | for...in |
for...of |
|---|---|---|
| *用�? | 遍历对象�?*可枚举属�? | 遍历 **可迭代对�?*(如数组、字符串等) |
| *返回�? | 返回 **�?*(属性名�? | 返回 **�?*(元素值) |
| 适用范围 | 对象、数组(不推荐用于数组) | 数组、字符串、Set、Map等可迭代对象 |
| *是否遍历原型�? | 会遍历原型链上的可枚举属�? | 不会遍历原型�? |
1 | // for...in 遍历对象 |
:::
JS 原型和原型链
参考答�?
::: details

*1. 原型(Prototype�?
每个 函数(构造函数)都有一�?
prototype属性,指向�?原型对象�?- 每个 对象 都有一�?__proto__指向其构造函数的prototype,形成继承关系�?
*2. 原型链(Prototype Chain�?访问对象属性时,先查找自身属性,找不到则�?
__proto__逐级向上查找,直�?null终止�?-Object.prototype.__proto__ === null,原型链的顶端是Object.prototype�?1
2
3
4
5
6
7
8
9
10
11function Person(name) {
this.name = name
}
Person.prototype.sayHello = function () {
console.log('Hello!')
}
const p = new Person('Rain')
console.log(p.__proto__ === Person.prototype) // true
console.log(Person.prototype.__proto__ === Object.prototype) // true
console.log(Object.prototype.__proto__ === null) // true
:::
JS 继承有几种方式?
参考答�?
::: details
*1. 原型链继�?
*核心思路�? 让子类的 prototype 指向父类实例�?
1 | function Parent() { |
�?*优点�? 父类方法可复�?�?*缺点�? 1. 共享引用类型属性(�?arr = [] 会被多个实例共享),2. 无法向父类构造函数传�?
*2. 借用构造函数继�?
*核心思路�? 在子类构造函数中使用 call 继承父类属性�?
1 | function Parent(name) { |
�?*优点�? 1. 解决原型链继承共享问题,2. 可传�?�?*缺点�? 无法继承父类原型上的方法
*3. 组合继承(原型链 + 构造函数继承,最常用�?
核心思路�? 结合前两种方式,*继承属性用构造函数,继承方法用原型链**�?
1 | function Parent(name) { |
�?*优点�? 解决了前两种方法的缺�?�?*缺点�? 调用两次 Parent 构造函数(一�?call,一�?Object.create()�?
*4. Object.create() 继承(原型式继承�?
*核心思路�? 直接�?Object.create() 创建一个新对象,继承已有对象�?
1 | const parent = { |
�?*优点�? 适合创建对象而非类的继承
�?*缺点�? 不能传参,只适用于简单继�?
5. 寄生组合继承(优化版,推荐)
*核心思路�? **组合继承的优化版�?*,避免了 Parent 被调用两次的问题�?
1 | function Parent(name) { |
�?*优点�? 1. 继承属性和方法�?. 只调用一�?Parent
�?*缺点�? 代码略微复杂
*6. ES6 class 继承(最现代化的方式�?
*核心思路�? class 语法糖,实际仍然基于原型继承�?
1 | class Parent { |
�?*优点�? 语法更清晰,易读易用
�?*缺点�? 本质仍是 prototype 继承
:::
JS 作用域和作用域链
参考答�?
::: details
- **作用�?*:变量的可访问范围,分为 全局作用域、函数作用域、块级作用域�?- 作用域链:变量查找机制,从当前作用域 逐级向上查找,直到全局作用域或
ReferenceError�?- **ES6 关键�?*�? -let/const**具有块级作用�?*,避�?var变量提升带来的问题�? - 闭包 利用作用域链,保留外部作用域的变量�?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var a = 'global'
function outer() {
var b = 'outer'
function inner() {
var c = 'inner'
console.log(a, b, c) // �?global outer inner
}
inner()
}
outer()
console.log(b) // �?ReferenceError: b is not defined
:::
JS 自由变量,如何理�?
参考答�?
::: details
自由变量 指的�?在当前作用域中未声明,但在上层作用域中找到的变量�?
�?JavaScript 中,当代码执行时,如果遇到一个变量:
- *当前作用�? 找不到该变量,就会沿着 作用域链 向上查找,直到找到该变量或报
ReferenceError�?- *这个在外层作用域中找到的变量,就是自由变量�?
1 | var a = 10 // 全局变量(自由变量) |
:::
JS 闭包,如何理�?
参考答�?
::: details
闭包的核心特性:
访问外部函数作用域的变量
即使外部函数执行结束,变量依然被保留
不会被垃圾回收,直到闭包不再被引�?
闭包的应用场景:私有变量(模拟封装)
1 | function createCounter() { |
- 回调 & 事件监听
1 | function addEventLogger(eventName) { |
- 定时�?& 异步操作
1 | function delayedGreeting(name) { |
闭包的缺点:
- 可能导致内存泄漏
- 闭包会持有外部变量的引用,导致变量无法被垃圾回收
- 解决方案:手动将变量置为 null 或谨慎管理作用域
- 滥用闭包可能影响性能
- 每次调用都会创建新的作用域,影响垃圾回收机制
- 适度使用,避免不必要的闭�?
:::
同步和异步有什么区别?异步的意义是什么?
参考答�?
::: details
同步:任务按顺序执行,当前任务未完成时,后续代码必须等待,代码是阻塞的�?异步:任务可�?*不按顺序执行,不会阻塞代码,后续代码可以继续执行,代码是非阻�?*的�?
| 特�? | 同步 | 异步 |
|---|---|---|
| 执行方式 | 顺序执行,阻塞后续任�? | 非阻塞,任务可以并行执行 |
| 代码特点 | 阻塞,必须等待上一个任务完�? | **非阻�?*,任务可以同时进�? |
| 适用场景 | 计算密集型、简单逻辑处理 | 网络请求、I/O 操作、高并发 |
1 | // 同步 |
1 | // 异步 |
*为什么要用异步?(异步的意义�?
- 避免阻塞,提升用户体�?
- 异步任务(如网络请求、文件读写)可以在后台执行,避免阻塞 UI,保证页面流畅�?
- 提升系统性能,支持高并发
- 服务器可以同时处理多个请求,提高吞吐量(�?Node.js 处理高并发)�?
- 更适合现代 Web 开�?
Promise/async-await让异步代码更可读,配�?fetch进行网络请求,提升开发效率�?
:::
JS Promise 有几种状态?如何变化
参考答�?
::: details
1. Promise 有几种状态?
| 状�? | 说明 | 是否可变�? |
|---|---|---|
| *Pending(进行中�? | 初始状态,异步操作未完�? | �?可以变更 |
| *Fulfilled(已完成�? | 操作成功,返�?resolve 结果 |
�?变更结束 |
| *Rejected(已拒绝�? | 操作失败,返�?reject 错误 |
�?变更结束 |
2. Promise 状态如何变化?
Promise 的状�?*只会�?Pending �?Fulfilled �?Pending �?Rejected,且一旦变化就不会再改�?*(不可逆)�?
1 | const promise = new Promise((resolve, reject) => { |
:::
JS Promise 使用
参考答�?
::: details
*1. 什么是 Promise�?
**Promise �?JavaScript 处理异步操作的一种方�?*,用于解决回调地狱(Callback Hell)问题�?> 它表示一个未来才会完成(或失败)的异步操作,并提�?
.then()、.catch()、.finally()方法进行处理�?
*2. Promise 的基本用�?
创建一�?Promise
1 | const myPromise = new Promise((resolve, reject) => { |
使用 then、catch 处理结果
1 | myPromise |
4. Promise 并行执行
*多个异步任务同时执行,全部完成后再处�?
1 | const p1 = new Promise((resolve) => setTimeout(() => resolve('任务 1'), 1000)) |
如果只要最快完成的结果
1 | Promise.race([p1, p2]) |
5. 面试回答总结
*Promise 解决异步回调问题,提�?
.then()、.catch()、.finally()处理状态变化。支�?Promise.all()并行执行,Promise.race()竞争执行。用async/await可以让异步代码更清晰�?
:::
async/await 使用
参考答�?
::: details
async/await �?ES2017(ES8)引入的 基于 Promise 的语法糖,用于更清晰地编写异步代码,使其看起来像同步代码,提高可读性�?
- async 关键字:用于声明一个异步函数,返回值始终是 Promise�?- await 关键字:只能�?async 函数中使用,等待 Promise 解析(resolve)并返回结果,而不会阻塞线程�?
1
2
3
4
5
6
7
8
9
10async function fetchData() {
try {
let response = await fetch('https://api.example.com/data')
let data = await response.json()
console.log(data)
} catch (error) {
console.error('Error:', error)
}
}
fetchData()
:::
JS 异步执行顺序
执行以下代码,会输出什么?
1 | async function async1() { |
答案
::: details
1 | script start |
:::
宏任务和微任务的区别
参考答�?
::: details
�?JavaScript �?事件循环(Event Loop�?机制中,任务分为 *宏任务(Macro Task�? �?**微任务(Micro Task�?*�?
- **微任务优�?*:微任务队列会在每次 宏任务执行完�?后立即执行,保证微任务先执行完再进入下一个宏任务�?- **宏任�?*:常见的宏任务包�?
setTimeout、setInterval、setImmediate(Node.js)、I/O、UI 渲染�?- **微任�?*:常见的微任务包�?Promise.then、MutationObserver、queueMicrotask、process.nextTick(Node.js)�?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21console.log('start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
Promise.resolve()
.then(() => {
console.log('promise1')
})
.then(() => {
console.log('promise2')
})
console.log('end')
// 输出�?// start
// end
// promise1
// promise2
// setTimeout
:::
描述 Event Loop 运行机制
参考答�?
::: details

Event Loop(事件循环)�?JavaScript 处理 异步操作 的核心机制。它允许 JavaScript �?*非阻�? 的方式执行代码,即使遇到 I/O 操作(如网络请求、定时器),也不会影响主线程继续执行其他任务�?
执行流程(核心步骤)
- 执行同步任务
- 所有同步任务在 调用栈(Call Stack�?中依次执行,直到调用栈清空�?
- *处理微任�?
- 检�?微任务队列(MicroTask Queue�?是否有任务(�?Promise.then()、queueMicrotask())�?- 依次执行所有微任务,直到微任务队列清空�?
- *执行宏任�?
- �?宏任务队列(MacroTask Queue�?取出 一�?任务(如 setTimeout 回调、I/O 任务),放入调用栈执行�?
- *重复步骤 2(处理新的微任务�?
- 宏任务执行完毕后,再次检查微任务队列,如果有新产生的微任务,立即执行所有微任务�?
- *重复步骤 3(执行下一个宏任务�?
- 继续取出下一�?宏任务,重复整个过程,形成循环(Event Loop�?
:::
Set �?Array 有什么区�?
参考答�?
::: details
| 特�? | Array | Set |
|---|---|---|
| *是否允许重复�? | �?允许重复元素 | �?只能存储唯一值,自动去重 |
| 索引访问 | �?可通过索引 (arr[0]) 访问 |
�?不支持索引访�? |
| 查找性能 | 🔴 O(n),需要遍历整个数�? |
🟢 O(1),基于哈希表查找更快 |
| 删除性能 | 🔴 O(n),需要遍历查找删�? |
🟢 O(1),删除性能更优 |
| 遍历方式 | �?forEach / map / filter |
�?forEach / for...of |
| *适合的场�? | 存储有序数据,支持索引访�? | 需要唯一值集合,去重、快速查�? |
| 转换方式 | Array.from(set) (Set �?Array) |
new Set(array) (Array �?Set) |
1 | // Array 允许重复�?const arr = [1, 2, 2, 3, 4, 4] |
:::
Map �?Object 有什么区�?
参考答�?
::: details
| 特�? | Object | Map |
|---|---|---|
| 键的类型 | 只能�?string �?symbol |
可以是任何类型(对象、函数等�? |
| *键值对的存储顺�? | 无序(属性顺序可能变化) | 有序(插入顺序保持不变) |
| 查找性能 | 相对较慢(基于哈希表�? | 更快(专门优化的键值存储结构) |
| 迭代方式 | for...in,Object.keys() �? |
forEach(),for...of(支持迭代器�? |
| 获取键的方式 | Object.keys(obj) 只能获取 string �? |
map.keys() 可获取所有类型的�? |
| 获取大小 | 需手动计算 Object.keys(obj).length |
map.size 直接获取大小 |
| 是否能轻松转换为 JSON | �?可以 JSON.stringify() |
�?不能直接 JSON.stringify() |
| 适用场景 | 适用于存储结构化数据,如对象属�? | 适用�?高效键值存储和查找 |
1 | // Object 只能用字符串作为�?const obj = {} |
:::
setTimeout、requestAnimationFrame �?requestIdleCallback 有什么区�?
参考答�?
::: details
| 特�? | setTimeout |
requestAnimationFrame |
requestIdleCallback |
|---|---|---|---|
| 执行时机 | 设定时间后执行(不保证准时) | 下一帧渲染前�?6.6ms 以内�? | 浏览器空闲时(可能延迟执行) |
| *主要用�? | 延迟执行代码 | *动画和流畅渲�? | 低优先级任务(如日志、分析) |
| 帧率控制 | **�?*,可能丢�? | **跟随屏幕刷新�?*(一�?60FPS�? | 不受限制,完全取决于浏览�? |
| 影响页面性能 | *可能影响页面流畅�? | 保证流畅动画 | *不会阻塞主线�? |
| *是否适用于动�? | �?可能卡顿 | �?适合 | �?不适合 |
| 是否�?CPU 影响 | �?受影�? | �?受影�? | �?受影�? |
| 适用场景 | *定时任务、轮�? | 动画、过渡、流�?UI 渲染 | 后台任务、低优先级执行(如数据同步、日志收集) |
setTimeout - 定时执行
1 | setTimeout(() => { |
**requestAnimationFrame - 适用于动�?*
1 | function animate() { |
**requestIdleCallback - 空闲时执�?*
1 | requestIdleCallback((deadline) => { |
:::
写一个验�?email 的正则表达式
参考答�?
::: details
1 | const reg = /\w+((-\w+)|(\.\w+))*@[a-zA-Z0-9]+((\.|-)[a-zA-Z0-9]+)*\.[a-zA-Z0-9]+$/ |
:::
JS 模块化规范有哪些�?
参考答�?
::: details
CommonJS
- 概述:这�?Node.js 中使用的模块化规范。它通过
module.exports�?require()来导出和引入模块�? - 特点:同步加载,主要用于服务器端(Node.js)�? - 使用场景:服务器端开发,尤其是在 Node.js 中�?1
2
3
4
5
6
7
8// 导出模块
module.exports = function () {
console.log('Hello, CommonJS!')
}
// 导入模块
const hello = require('./hello')
hello()
- 概述:这�?Node.js 中使用的模块化规范。它通过
*AMD(Asynchronous Module Definition�?
- 概述:AMD 是一种异步加载模块的规范,常用于浏览器端�? - 特点:支持异步加载,模块和依赖是按需加载的,通常使用
define()�?require()�? - 使用场景:浏览器端的模块化,尤其是当需要异步加载模块时�?1
2
3
4
5define(['dependency'], function (dep) {
return function () {
console.log('Hello, AMD!')
}
})
- 概述:AMD 是一种异步加载模块的规范,常用于浏览器端�? - 特点:支持异步加载,模块和依赖是按需加载的,通常使用
*UMD(Universal Module Definition�?
- 概述:UMD 是一个兼容多种模块化规范(CommonJS、AMD 和全局变量)的模块化方案�? - 特点:确保模块在不同的环境中都能使用�? - 使用场景:需要在多种环境下(�?Node.js、浏览器)使用的库或框架�?
1
2
3
4
5
6
7
8
9
10
11
12
13;(function (root, factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = factory()
} else if (typeof define === 'function' && define.amd) {
define(factory)
} else {
root.myModule = factory()
}
})(this, function () {
return function () {
console.log('Hello, UMD!')
}
})
- 概述:UMD 是一个兼容多种模块化规范(CommonJS、AMD 和全局变量)的模块化方案�? - 特点:确保模块在不同的环境中都能使用�? - 使用场景:需要在多种环境下(�?Node.js、浏览器)使用的库或框架�?
*ES6 Modules(ESM�?
- 概述:ES6 模块化是 JavaScript 原生的模块化标准,使�?
import�?export语法�? - 特点:支持静态分析,加载时可以进行优化,现代 JavaScript 标准�? - 使用场景:现代前端开发(浏览器和 Node.js)�?1
2
3
4
5
6
7
8// 导出模块
export function greet() {
console.log('Hello, ESM!')
}
// 导入模块
import { greet } from './greet.js'
greet()
- 概述:ES6 模块化是 JavaScript 原生的模块化标准,使�?
SystemJS
- 概述:SystemJS 是一个支持多种模块规范(CommonJS、AMD �?ESM)的模块加载器�? - 特点:支持多种模块格式,动态加载模块�? - 使用场景:需要跨模块加载器兼容的复杂应用�?
1
2
3
4
5
6
7
8System.config({
map: {
greet: './greet.js',
},
})
System.import('greet').then((greet) => {
greet()
})
- 概述:SystemJS 是一个支持多种模块规范(CommonJS、AMD �?ESM)的模块加载器�? - 特点:支持多种模块格式,动态加载模块�? - 使用场景:需要跨模块加载器兼容的复杂应用�?
:::
JS 如何捕获异常?有几种方式�?
参考答�?
::: details
- try…catch 语句
1 | try { |
- *Promise 中的错误捕获(catch�?
1 | someAsyncFunction() |
- *window.onerror(全局错误处理�?
1 | window.onerror = function (message, source, lineno, colno, error) { |
:::
0.1 + 0.2 === 0.3 表达式返回什么?
参考答�?
::: details0.1 + 0.2 === 0.3 �?JavaScript 中会返回 **false**�?
*原因�?
JavaScript 中的浮点数运算存在精度问题。由于计算机在内部表示浮点数时不能精确表示某些小数,导致 0.1 + 0.2 的结果并不是精确�?0.3,而是一个接近于 0.3 的小数�?
具体来说,0.1 + 0.2 的计算结果是 0.30000000000000004,而不�?0.3。因此,当你�?===(严格相等)进行比较时,0.30000000000000004 �?0.3 不相等,结果�?false�?
*解决方法�?
四舍五入�?
1
console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON) // true
**自定义精度比�?*�? 将浮动值限制到一定的小数位,进行比较�? ```javascript
console.log(Math.round((0.1 + 0.2) * 100) / 100 === 0.3) // true1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
:::
## 如何理解 JS 单线程?
参考答�?
::: details
**什么是 JavaScript 单线程?**
JavaScript �?**单线�?* 的意思是它只有一个线程来执行代码,这意味着它一次只能执行一个任务。所有的 JavaScript 代码,默认情况下,都会按照顺序在同一个线程中依次执行。单线程的特性使�?JavaScript 相比多线程语言在处理并发时有一些限制,但它也有一套机制来处理异步操作,避免阻塞主线程�?
**为什么是单线程?**
JavaScript 的设计目的是为了简化开发,尤其是在浏览器环境中。单线程可以避免多线程带来的复杂性,比如线程同步、资源竞争等问题。为了不让长时间的任务阻�?UI 渲染,JavaScript 提供了异步编程的机制�?
**如何处理并发任务�?*
虽然 JavaScript 是单线程的,但它通过以下机制来实现并发任务的处理�?
1. **事件循环(Event Loop�?*:JavaScript 使用事件循环来管理异步任务。通过事件循环,JavaScript 可以在任务执行时不中断主线程的执行。异步任务(比如 `setTimeout`、`Promise`、`XHR` 等)会先进入 **消息队列(Event Queue�?*,当主线程空闲时,再从队列中取出任务执行�?
2. **Web APIs**:浏览器提供�?**Web APIs**(如 `setTimeout`、`fetch`、`DOM` 等)来处理一些异步操作。这些操作会被交给浏览器�?API 处理,处理完后通过事件循环机制将回调函数推送到消息队列,等待主线程执行�?
3. **异步编程**:通过 **`setTimeout`**�?*`Promise`**�?*`async/await`** 等方式,JavaScript 可以非阻塞地处理 I/O 操作,避免卡住整个程序的执行�?
:::
## 什么是 WebWorker 如何理解它?
参考答�?
::: details
**Web Worker** 是一种浏览器提供�?API,允许你在一个独立的线程中执�?JavaScript 代码�?*与主线程(UI 线程)分�?*。Web Worker 可以处理计算密集型任务,如数据处理、文件解析等,这些任务通常会阻塞主线程,导�?UI 卡顿。通过 Web Worker,你可以将这些耗时操作移到后台线程,确保主线程始终保持响应状态�?
**工作原理�?*
1. **独立线程**:Web Worker 在一个与主线程(UI 线程)分离的线程中运行,主线程和 Worker 线程之间通过消息传递(postMessage)进行通信�?2. **主线程与 Worker 通信**:主线程可以通过 `postMessage()` 方法�?Worker 发送数据,Worker 完成计算后,通过 `postMessage()` 将结果返回给主线程�?
3. **异步操作**:由�?Worker 在后台线程中运行,因此它的执行不会阻塞主线程,所有的计算任务都是异步执行的�?
4. **线程间通信**:Worker 无法直接访问主线程的 DOM、`window` 或�?`document` 等对象,它只能通过 `postMessage()` 与主线程进行数据交换。返回的数据是通过事件机制传递的,使�?`onmessage` 监听数据的返回�?
**Web Worker 的优势:**
- **性能提升**:Web Worker 可以让长时间的计算任务在后台线程中执行,避免 UI 阻塞,提升用户体验�?- **非阻塞�?*:主线程可以继续处理用户交互和渲染,而不被复杂计算所阻塞�?- **多线程处�?*:对�?CPU 密集型任务,Web Worker 可以将工作分配给多个 Worker,实现并行计算,提高性能�?
**Web Worker 的应用场景:**
- **大数据处�?*:例如,处理大量的数组计算、排序、数据筛选等任务�?- **图像处理**:例如,进行图像的处理和转换,而不影响 UI 渲染�?- **音视频处�?*:例如,音视频的编码、解码等计算密集型操作�?- **异步任务**:一些需要后台执行的异步任务,可以通过 Worker 来处理�?
**Web Worker 的局限性:**
- **无法操作 DOM**:Web Worker 在独立线程中运行,不能直接访�?DOM �?`window`,只能通过消息传递来与主线程交换数据�?- **数据传�?*:数据通过 `postMessage()` 传递时会发生深拷贝,因此传递大数据时可能会有性能开销�?- **浏览器支�?*:大多数现代浏览器支�?Web Worker,但在旧版浏览器中可能不被支持�?
1. **创建一�?Web Worker�?*
```javascript
// main.js (主线�?
const worker = new Worker('worker.js') // 创建 Worker 实例
worker.postMessage('Hello, Worker!') // �?Worker 发送消�?
worker.onmessage = function (event) {
console.log('Worker says: ', event.data) // 接收 Worker 的响�? }Worker 文件(worker.js):
1
2
3
4// worker.js (Worker 线程)
onmessage = function (event) {
console.log('Main thread says: ', event.data)
postMessage('Hello, Main Thread!') // 发送响应到主线�? }
:::
JS 如何进行内存管理和垃圾回收?
参考答�?
::: details
JavaScript 的内存管理是自动的,主要通过 *垃圾回收(GC�? 来实现�?
*内存管理�?
JavaScript 使用 自动内存管理,开发者不需要手动分配和释放内存�?2. 内存通过 堆(用于存储对象和数组等动态分配的内存)和 栈(用于存储函数调用和局部变量)进行管理�?
*常用的垃圾回收机制有�?**标记-清除(Mark-and-Sweep�?*:标记活动对象,清除未标记对象,释放内存�?2. 引用计数:计算对象的引用次数,引用为 0 时回收。但会有循环引用的问题�?3. **生成式垃圾回�?*:通过将内存分为年轻代和老年代,优化垃圾回收频率,减少内存碎片�?
:::
如何检�?JS 内存泄漏?内存泄漏的场景有哪些?
参考答�?
::: details
**使用浏览器开发者工�?*�?
- Chrome DevTools 中的 Memory 面板可以用来检测内存泄漏。可以查�?Heap Snapshot �?Allocation instrumentation on timeline,分析对象分配、释放情况�? - Heap Snapshot:查看对象的分配情况,并通过比较不同时间点的快照来发现泄漏�? - Timeline:在页面交互过程中,查看内存的使用情况,发现持续增长的内存占用�?
通过
performance.memoryAPI�?- 在支持的浏览器中,可以通过
performance.memoryAPI 获取当前的内存使用情况(�?JS 堆内存大小),来跟踪内存的变化�?1
console.log(window.performance.memory)
- 在支持的浏览器中,可以通过
**手动检�?*�?
- 通过创建和销毁对象,使用
setInterval�?setTimeout来检测是否有对象未被回收�? - 观察垃圾回收器是否清理不再使用的对象,如果内存不断增长,可能就是内存泄漏�?
- 通过创建和销毁对象,使用
**第三方工�?*�? - Valgrind�?Memory.js* 等工具可以帮助检测内存泄漏�?
内存泄漏的常见场景:全局变量�?
- 意外的全局变量会导致对象无法被回收�? ```javascript
function test() {
leakedVar = ‘This is a global variable’ // 未声明的变量成为全局变量
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2. **未移除的事件监听�?*�?
- 如果事件监听器被绑定�?DOM 元素上,但没有在元素移除后正确移除,可能导致内存泄漏�? ```javascript
const button = document.getElementById('myButton')
button.addEventListener('click', function () {
/* some logic */
})
// 如果没有 button.removeEventListener,按钮被移除后内存仍未释�? ```
3. **闭包(Closures�?*�?
- 闭包会保持对外部函数变量的引用,如果闭包生命周期过长,会导致外部函数的变量无法释放�? ```javascript
function createClosure() {
let largeObject = new Array(1000).fill('Some data')
return function () {
console.log(largeObject) // largeObject 被闭包引用,无法�?GC 回收
}
}
let closure = createClosure()
- 意外的全局变量会导致对象无法被回收�? ```javascript
DOM 引用�?
- 保留对已删除 DOM 元素的引用,导致内存泄漏�?
1
2
3
4
5
6
7
8
9
10
11
12let div = document.createElement('div')
document.body.appendChild(div)
// 删除 DOM
document.body.removeChild(div)
// �?如果还保留引用,GC 无法回收
let cache = div
// ✔️ 手动断开引用,GC 才能回收
div = null
cache = null
- 保留对已删除 DOM 元素的引用,导致内存泄漏�?
定时器(setInterval/setTimeout)未清除�?
- 如果定时器没有清除,仍然会占用内存�? ```javascript
let interval = setInterval(function () {
console.log(‘Running’)
}, 1000)
// 如果没有 clearInterval(interval),定时器将一直运行,导致内存泄漏1
2
3
4
6. **Web Workers 和后台线�?*�? - 如果 Web Worker 或后台线程没有正确终止,可能会导致内存泄漏�? ```javascript
const worker = new Worker('worker.js')
// 如果没有 worker.terminate(),worker 可能导致内存泄漏
- 如果定时器没有清除,仍然会占用内存�? ```javascript
:::
如何理解 WebAssembly�?
参考答�?
::: details
*WebAssembly(Wasm�? 是一种新�?Web 技术,它允许开发者将其他编程语言(如 C、C++、Rust 等)编译成高效的二进制代码,并在浏览器中运行。WebAssembly 旨在提供接近原生性能�?Web 体验,特别适用于高性能计算任务�?
关键点:
- **高效�?*:WebAssembly 是一种二进制格式,比 JavaScript 的文本格式更紧凑,加载速度更快,执行速度更快,适用�?CPU 密集型任务,如图像处理、游戏开发和科学计算�?
- �?JavaScript 协作:WebAssembly �?JavaScript 可以协同工作,JavaScript 用于 UI 操作和事件处理,WebAssembly 负责计算密集型任务。它们通过 共享内存 �?*消息传�? 进行通信�?
- **跨平�?*:WebAssembly 是跨平台的,可以在所有支�?WebAssembly 的现代浏览器中运行,并且不需要针对不同操作系统和硬件做额外的修改�?
- **安全�?*:WebAssembly 运行在沙盒环境中,不能直接访问操作系统资源,保证�?Web 应用的安全性�?
应用场景�?
- **游戏开�?*:通过高效的计算,WebAssembly 可以�?Web 上的游戏运行得更流畅�?- 图像/视频处理:利�?WebAssembly 进行高效的图像处理和视频编解码�?- 科学计算:WebAssembly 能大大提�?JavaScript 在处理大数据和复杂计算时的性能�?
:::
JS V8 Nodejs Deno Bun 这几个,他们是什么关系?
参考答�?
::: details
1. V8
V8 是一个开源的 JavaScript 引擎,由 Google 开发,主要用于 Chrome 浏览器和 Node.js�?- V8 �?JavaScript 代码编译成机器代码并执行,从而提�?JavaScript 的执行效率�?- 作用:V8 �?JavaScript 执行的“心脏”,负责解析和执�?JavaScript 代码�?- 关系:V8 �?Node.js �?Deno 的底层引擎。它本身不提供完整的 JavaScript 环境或库,只负责执行 JavaScript�?
2. Node.jsNode.js 是一个基�?V8 引擎�?**JavaScript 运行时环�?*,使�?JavaScript 不仅可以在浏览器中运行,还可以在服务器端运行�?- 它为 JavaScript 提供�?I/O 操作、文件系统访问、网络请求等功能,这些功能通常由操作系统提供�?- 作用:Node.js 使得 JavaScript 可以用于构建服务器端应用,支持事件驱动、非阻塞�?I/O 机制�?- 关系:Node.js 使用 V8 作为�?JavaScript 引擎,除此之外,它还包含一些额外的 API(如
fs、http、path等)来提供对文件系统、网络等资源的访问�?
3. DenoDeno 是一个由 Node.js 的原始开发�?Ryan Dahl 创建的新�?JavaScript/TypeScript 运行时环境�?- 它同样使�?V8 引擎,但是与 Node.js 不同的是,Deno 内置了对 TypeScript 的支持,且具有现代化的安全特性(如权限控制)�?- 作用:Deno 旨在修复 Node.js 中存在的一些设计问题,提供更简洁和安全的运行时环境�?- 关系:Deno 使用 V8 作为 JavaScript 引擎,但它不�?Node.js 的直接继承者,而是对现�?JavaScript 运行时环境的一次重构,加入了许多新的功能和改进�?
4. BunBun 是一个新兴的 **JavaScript/TypeScript 运行�?*,其目标是提供更高效的性能,特别是在构建工具和服务器端应用中�?- Bun 是基�?JavaScriptCore(Safari 浏览器的 JavaScript 引擎)构建的,而不�?V8�?- 作用:Bun 具有非常快速的执行速度,提供类似于 Node.js �?API,同时它也是一个现代的构建工具(例如,能够快速打包、转译和运行 JavaScript/TypeScript 代码)�?- 关系:Bun 不是基于 V8 引擎,它使用的是 JavaScriptCore 引擎,但它与 Node.js �?Deno 类似,作为一�?JavaScript 运行时环境提供底层支持�?
:::
有了解过WeakMap吗?WeakMap与Map的区别是什么?
参考答�?
::: details
1. 什么是WeakMap
WeakMap �?JavaScript 中的一种集合类型,它存储键值对,且键必须是对象,并且键是弱引用的。这意味着,如果键对象没有其他引用,它会被垃圾回收器回收,对应的键值对也会被自动删除�?
*2. 与Map的区�?
键的类型
**
Map**:键可以是任意类型,包括基本数据类型(像字符串、数字等)和引用类型(如对象、函数)�?- **WeakMap**:键只能是对象,不能使用基本数据类型作为键�?
垃圾回收机制**
Map**:对键所引用的对象是强引用。只�?Map存在,键引用的对象就不会被垃圾回收,即便其他地方无该对象的引用�?- **WeakMap**:对键所引用的对象是弱引用。若对象没有其他强引用,垃圾回收时对象会被回收,WeakMap里对应的键值对也会自动移除�?
*可遍历�?**
Map**:是可迭代的,能使用for...of循环、forEach方法等遍历其键值对�?- **WeakMap**:不可迭代,没有keys()、values()、entries()这些迭代方法,也不能�?for...of�?forEach遍历�?
*方法和属�?**
Map**:有size属性来获取键值对数量,还�?set()、get()、has()、delete()、clear()等方法�?- **WeakMap**:只�?set()、get()、has()、delete()方法,没�?size属性和clear()方法�?
使用场景**
Map**:适用于需存储任意类型键值对,且要对这些键值对进行遍历和操作的场景,如缓存数据�?- **WeakMap**:常用于避免内存泄漏的场景,例如给对象添加私有数据,当对象被销毁时,WeakMap里相关数据也会被清理�?
:::
如何�?var [a, b] = {a: 1, b: 2} 解构赋值成功?
参考答�?
::: details
迭代协议�?题目问怎么能让var [a,b] = {a:1,b:2} 成立,那么我们首先要运行一下,看看它是怎么个不成立法。�?
1 | const obj = {�? a:'1',�? b:'2',�?}�?�?const [a,b] = obj�?``` |
打印iterator对象后发现在它的原型上有一个next()方法,调用next()方法,会得到一个对象value就是当前迭代的值,done则代表当前迭代器是否已经迭代完成�?
数组 解构 的本�?
1 | const array = [1,2,3]�?var [a,b,c] = array�?// 本质上是�?const iterator = array[Symbol.iterator]()�?var a = iterator.next().value�?var b = iterator.next().value�?var c = iterator.next().value |
解决方法�?到此为止我们可知,要想满足迭代协议需要对象身上有一个名为[Symbol.iterator]的方法。再使用for..of或者解构赋值的时候会隐式的调用这个方法,得到一个迭代对象,通过迭代对象的next方法判断当前是否完成迭代和具体迭代的值。�?也就是说我们要在obj上添加[Symbol.iterator]方法并且完成next方法的逻辑�?
最终代码如下:
1 | const obj = {�? a: '1',�? b: '2',�? [Symbol.iterator]() { |
当然,我们也可以用for…of去循环遍历这个对象,我看谁再说for…of不能遍历对象(doge)
1 | for(let i of obj){�? console.log(i)�?}�?// 1�?// 2 |
:::
postMessage 有哪些使用场景?
参考答�?
::: details
window.postMessage 定义
window.postMessage()方法可以安全地实现跨源通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全
*用�?
可用于两个不同的Ifrom(不同源�?之间的通讯�?
语法
1 | otherWindow.postMessage(message, targetOrigin, [transfer]); |
参数说明
- data�?- 从其�?window 中传递过来的对象。�?- origin�?- 调用
postMessage时消息发送方窗口�?origin . 这个字符串由 协议、�?//“、域名、�?: 端口号”拼接而成。例�?�?https://example.org* (隐含端口 443)”、�?http://example.net* (隐含端口 80)”、�?*<http://example.com:8080**”。请注意,这个origin不能保证是该窗口的当前或未来origin,因为postMessage被调用后可能被导航到不同的位置。�? - source�?- 对发送消息的窗口对象的引�? 您可以使用此来在具有不同origin的两个窗口之间建立双向通信�?
例子
子框架传递信�?
1 | <script>�?�?// 子框架向父框架发送信息�?�?function goParentIfromPostMessage(msg,parentUrl){�?�? var parentUrl = window.parent.location.origin;�?�? window.onload=function(){�?�? window.parent.postMessage(msg,parentUrl);�?�? }�? }�? }�? �? goParentIfromPostMessage('msgStr',parentIfromUrl)�?�?</script> |
父框架接收端
1 | <script>�?�? window.addEventListener('message',function(e){�?�? console.log(e.origin,e.data);�?�? console.log(e.data);�?�? })�?�?</script> |
这样即可以实现简单的框架跨域通信,但是会有一些安全问题�?
安全问题
如果您不希望从其他网站接收message,请不要为message事件添加任何事件侦听器�?这是一个完全万无一失的方式来避免安全问题。�?如果您确实希望从其他网站接收message,请始终使用origin和source属性验证发件人的身份�?任何窗口(包括例�?<http://evil.example.com)都可以向任何其他窗口发送消息,并且您不能保证未知发件人不会发送恶意消息�? 但是,验证身份后,您仍然应该始终验证接收到的消息的语法�?否则,您信任只发送受信任邮件的网站中的安全漏洞可能会在您的网站中打开跨网站脚本漏洞。�?
- 当您使用postMessage将数据发送到其他窗口时,始终指定精确的目标origin,而不是。恶意网站可以在您不知情的情况下更改窗口的位置,因此它可以拦截使用postMessage发送的数据�?
示例
1 | /*�? * A窗口的域名是<http://example.com:8080>,以下是A窗口的script标签下的代码:�? */�?�?var popup = window.open(...popup details...);�?�?// 如果弹出框没有被阻止且加载完成�?�?// 这行语句没有发送信息出去,即使假设当前页面没有改变location(因为targetOrigin设置不对)�?popup.postMessage("The user is 'bob' and the password is 'secret'",�? "https://secure.example.net");�?�?// 假设当前页面没有改变location,这条语句会成功添加message到发送队列中去(targetOrigin设置对了)�?popup.postMessage("hello there!", "http://example.org");�?�?function receiveMessage(event)�?{�? // 我们能相信信息的发送者吗? (也许这个发送者和我们最初打开的不是同一个页�?.�? if (event.origin !== "http://example.org")�? return;�?�? // event.source 是我们通过window.open打开的弹出页�?popup�? // event.data �?popup发送给当前页面的消�?"hi there yourself! the secret response is: rheeeeet!"�?}�?window.addEventListener("message", receiveMessage, false); |
1 | /*�? * 弹出�?popup 域名�?http://example.org>,以下是script标签中的代码:�? */�?�?//当A页面postMessage被调用后,这个function被addEventListener调用�?function receiveMessage(event)�?{�? // 我们能信任信息来源吗?�? if (event.origin !== "http://example.com:8080")�? return;�?�? // event.source 就当前弹出页的来源页面�? // event.data �?"hello there!"�?�? // 假设你已经验证了所受到信息的origin (任何时候你都应该这样做), 一个很方便的方式就是把event.source�? // 作为回信的对象,并且把event.origin作为targetOrigin�? event.source.postMessage("hi there yourself! the secret response " +�? "is: rheeeeet!",�? event.origin);�?}�?�?window.addEventListener("message", receiveMessage, false) |
:::