javascript之Es6八股文
说说var、let、const之间的区别
比较项 | var | let | const |
---|---|---|---|
变量提升 | 存在,声明提升到作用域顶部,值不提升,声明前使用为undefined | 不存在,必须先声明后使用,否则报错 | 不存在,必须先声明后使用,否则报错 |
暂时性死区 | 不存在 | 存在,从块级作用域开始到声明变量的代码行之间,变量不可用,访问报错 | 存在,从块级作用域开始到声明变量的代码行之间,变量不可用,访问报错 |
块级作用域 | 不存在,在函数作用域或全局作用域有效 | 存在,声明的变量仅在所在块级作用域内有效 | 存在,声明的变量仅在所在块级作用域内有效 |
重复声明 | 允许在同一作用域重复声明,后声明覆盖先声明的值 | 在同一作用域不允许重复声明,否则报错 | 在同一作用域不允许重复声明,否则报错 |
修改声明的变量 | 声明的变量值可修改 | 声明的变量值可修改 | 声明的是常量,值不能改变(简单类型);复杂类型指针固定,内部结构可改变 |
使用建议 | 尽量避免使用,可能带来因变量提升和缺乏块级作用域导致的问题 | 大多数情况下使用,尤其是需要块级作用域和变量可修改场景 | 能用const 尽量用,保证数据不可变性,增强代码稳定性和可维护性 |
ES6中数组新增了哪些扩展?
分类 | 新增内容 | 说明 |
---|---|---|
扩展运算符 | 用于函数调用、数据结构转换、数组操作及解构赋值结合 | 将数组转为参数序列;转某些数据结构为数组;实现数组复制、合并;与解构赋值结合生成数组,在数组赋值时只能在最后一位 |
构造函数新增方法 | Array.from() | 将类似数组或可遍历对象转为数组,可对元素处理 |
Array.of() | 将一组值转为数组,无参返回空数组,单参指定长度,多参组成新数组 | |
实例对象新增方法 | copyWithin(target, start, end) | 将指定位置成员复制到其他位置,返回当前数组 |
find(callback)、findIndex(callback) | find 找出首个符合条件成员;findIndex 返回首个符合条件成员位置,不符则返回 -1,均可绑定 this | |
fill(value, start, end) | 用给定值填充数组,可指定范围,填充对象为浅拷贝 | |
entries()、keys()、values() | 分别用于键值对、键名、键值的遍历 | |
includes(value, fromIndex) | 判断数组是否包含给定值,可指定起始位置 | |
flat(depth)、flatMap(callback, thisArg) | flat 扁平化数组,可指定深度;flatMap 先执行函数再扁平化,可绑定 this | |
数组的空位 | ES6 将空位转为 undefined | 涉及多个方法,建议避免空位 |
排序稳定性 | sort () 默认稳定排序算法 | 排序后元素相对顺序与原始顺序一致 |
一、扩展运算符(...)
- 作为函数参数:将数组转为逗号分隔的参数序列,用于函数调用。
function push(array, ...items) {array.push(...items); } function add(x, y) {return x + y; } const numbers = [4, 38]; add(...numbers) // 42
- 数据结构转换:可将某些数据结构转为数组,如
NodeList
、定义了遍历器(Iterator)接口的对象。字符串也可借此转为数组。[...document.querySelectorAll('div')] [...'hello'] // [ "h", "e", "l", "l", "o" ]
- 数组操作:
- 复制数组:实现简单数组复制,但为浅拷贝。
const a1 = [1, 2]; const [...a2] = a1; // [1,2]
- 合并数组:简洁实现数组合并。
const arr1 = ['a', 'b']; const arr2 = ['c']; const arr3 = ['d', 'e']; [...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]
- 解构赋值结合:可与解构赋值结合生成数组,但扩展运算符在数组赋值时只能放在参数最后一位。
const [first, ...rest] = [1, 2, 3, 4, 5]; first // 1 rest // [2, 3, 4, 5]
二、构造函数新增方法
- Array.from():将类似数组的对象和可遍历(iterable)的对象转为真正的数组,可接受第二个参数对每个元素进行处理。
let arrayLike = {'0': 'a','1': 'b','2': 'c',length: 3 }; let arr2 = Array.from(arrayLike); // ['a', 'b', 'c'] Array.from([1, 2, 3], (x) => x * x) // [1, 4, 9]
- Array.of():将一组值转换为数组,无参数时返回空数组,单个参数时指定数组长度,不少于 2 个参数时返回由参数组成的新数组。
Array.of(3, 11, 8) // [3,11,8] Array() // [] Array(3) // [, , ,] Array(3, 11, 8) // [3, 11, 8]
三、实例对象新增方法
- copyWithin():将指定位置成员复制到其他位置(覆盖原有成员),返回当前数组。
- 参数:
target
(必需):替换起始位置,负值表示倒数。start
(可选):读取数据起始位置,默认为 0,负值从末尾计算。end
(可选):读取数据停止位置,默认数组长度,负值从末尾计算。- 示例:
[1, 2, 3, 4, 5].copyWithin(0, 3) // 将从 3 号位直到数组结束的成员(4 和 5),复制到从 0 号位开始的位置,结果覆盖了原来的 1 和 2 // [4, 5, 3, 4, 5]
- find()、findIndex():
- find():找出第一个符合条件的数组成员,参数为回调函数(接受当前值、当前位置、原数组)。
- findIndex():返回第一个符合条件的数组成员位置,都不符合则返回 -1。
- 均可接受第二个参数绑定回调函数的
this
对象。- 示例:
[1, 5, 10, 15].find(function (value, index, arr) {return value > 9; }) // 10 [1, 5, 10, 15].findIndex(function (value, index, arr) {return value > 9; }) // 2 function f(v) {return v > this.age; } let person = { name: 'John', age: 20 }; [10, 12, 26, 15].find(f, person); // 26
- fill():使用给定值填充数组,可接受第二、三个参数指定填充起始和结束位置,填充对象时为浅拷贝。
['a', 'b', 'c'].fill(7) // [7, 7, 7] ['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']
- entries(),keys(),values():
- keys():键名遍历。
- values():键值遍历。
- entries():键值对遍历。
- 示例:(let of)
for (let index of ['a', 'b'].keys()) {console.log(index); } // 0 // 1 for (let elem of ['a', 'b'].values()) {console.log(elem); } // 'a' // 'b' for (let [index, elem] of ['a', 'b'].entries()) {console.log(index, elem); } // 0 "a"
- includes():判断数组是否包含给定值,第二个参数表示搜索起始位置,默认为 0,负数表示倒数位置。
[1, 2, 3].includes(2) // true [1, 2, 3].includes(4) // false [1, 2, NaN].includes(NaN) // true [1, 2, 3].includes(3, 3); // false [1, 2, 3].includes(3, -1); // true
- flat(),flatMap():
- flat():扁平化数组,返回新数组,默认 “拉平” 一层,可传入整数指定拉平层数。
- flatMap():先对原数组每个成员执行函数(类似
map
),再对返回值数组执行flat()
,返回新数组,不改变原数组,可接受第二个参数绑定this
。新旧都有- 示例:
[1, 2, [3, 4]].flat() // [1, 2, 3, 4] [1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5] [2, 3, 4].flatMap((x) => [x, x * 2]) // [2, 4, 3, 6, 4, 8]
四、数组的空位
ES6 将数组空位明确转为
undefined
,涉及方法包括Array.from
、扩展运算符、copyWithin()
、fill()
、entries()
、keys()
、values()
、find()
和findIndex()
,建议书写时避免出现空位。
五、排序稳定性
sort()
默认设置为稳定的排序算法,排序结果中元素相对顺序与原始顺序一致。const arr = ['peach','straw','apple','spork' ]; const stableSorting = (s1, s2) => {if (s1[0] < s2[0]) return -1;return 1; }; arr.sort(stableSorting) // ["apple", "peach", "straw", "spork"]
ES6中对象新增了哪些扩展?
分类 | 内容 | 说明 | 示例 |
---|---|---|---|
属性的简写 | 键值对与方法简写 | 当对象键名与对应值名相等时可简写;对象、方法也能简写 | 键值对简写:
方法简写:
函数返回值简写:
|
属性名表达式 | 定义属性与方法名 | 字面量定义对象时,表达式放括号内作属性名,也可定义方法名 | let lastWord = 'last word'; const a = { 'first word': 'hello', [lastWord]: 'world' }; a['first word']; // "hello" a[lastWord]; // "world" a['last word']; // "world" let obj = { ['h' + 'ello']() { return 'hi'; } }; obj.hello(); // hi |
super 关键字 | 指向原型对象 | super 指向当前对象的原型对象 | const proto = { foo: 'hello' }; const obj = { foo: 'world', find() { return super.foo; } }; Object.setPrototypeOf(obj, proto); obj.find(); // "hello" |
扩展运算符的应用 | 解构赋值与对象合并 | 解构赋值时,未读取的可遍历属性分配到指定对象,必须是最后一个参数且为浅拷贝;对象扩展运算符等同于Object.assign() 方法 | 解构赋值:let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; x; // 1 y; // 2 z; // { a: 3, b: 4 } let obj = { a: { b: 1 } }; let { ...x } = obj; obj.a.b = 2; x.a.b; // 2 |
属性的遍历 | 五种遍历方法及次序规则 |
| Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 }) // ['2', '10', 'b', 'a', Symbol()] |
对象新增的方法
| 严格判断两值相等,与
| |
| 对象合并,将源对象可枚举属性复制到目标对象,浅拷贝,同名属性替换
| |
| 返回指定对象所有自身属性(非继承)的描述对象
| |
| 设置一个对象的原型对象
| |
| 读取一个对象的原型对象
| |
| 返回自身(不含继承)所有可遍历属性键名的数组
| |
| 返回自身(不含继承)所有可遍历属性键对应值的数组
| |
| 返回对象自身(不含继承)所有可遍历属性键值对的数组
| |
| 将键值对数组转为对象
|
ES6中函数新增了哪些扩展?
一、参数
- 默认值:ES6 允许为函数参数设置默认值。
function log(x, y = 'World') {console.log(x, y); }console.log('Hello') // Hello World console.log('Hello', 'China') // Hello China console.log('Hello', '') // Hello
- 声明限制:函数形参默认声明,不能用
let
或const
再次声明。function foo(x = 5) {let x = 1; // errorconst x = 2; // error }
- 与解构赋值结合:参数默认值可与解构赋值默认值联用。注意参数为对象时才能解构,无参数时会报错,可通过设置外层默认值避免。
// 未设置外层默认值 function foo({x, y = 5}) {console.log(x, y); } foo({}); // undefined 5 foo({x: 1}); // 1 5 foo({x: 1, y: 2}); // 1 2 foo(); // TypeError: Cannot read property 'x' of undefined// 设置外层默认值 function foo({x, y = 5} = {}) {console.log(x, y); } foo(); // undefined 5
- 位置限制:参数默认值应是尾参数,非尾部参数设默认值实际不可省略。
function f(x = 1, y) {return [x, y]; } f(); // [1, undefined] f(2); // [2, undefined] f(, 1); // 报错 f(undefined, 1); // [1, 1]
二、属性
- length 属性:返回未指定默认值的参数个数,rest 参数不计入,非尾参数设默认值时,后面参数也不计入。
(function (a) {}).length // 1 (function (a = 5) {}).length // 0 (function (a, b, c = 5) {}).length // 2 (function(...args) {}).length // 0 (function (a = 0, b, c) {}).length // 0 (function (a, b = 1, c) {}).length // 1
- name 属性:返回函数名。具名函数赋值给变量,
name
返回具名函数原本名字;Function
构造函数返回的实例,name
为"anonymous"
;bind
返回的函数,name
值加"bound"
前缀。var f = function () {}; // ES5 f.name // "" // ES6 f.name // "f"const bar = function baz() {}; bar.name // "baz"(new Function).name // "anonymous"function foo() {}; foo.bind({}).name // "bound foo"(function(){}).bind({}).name // "bound "
三、作用域
设置参数默认值时,参数形成单独作用域,初始化结束后作用域消失。不设置参数默认值时无此行为。
let x = 1; function f(y = x) { // 等同于 let y = x let x = 2; console.log(y); } f() // 1
四、严格模式
函数参数使用默认值、解构赋值或扩展运算符时,函数内部不能显式设为严格模式,否则报错。
// 报错 function doSomething(a, b = a) {'use strict';// code }// 报错 const doSomething = function ({a, b}) {'use strict';// code };// 报错 const doSomething = (...a) => {'use strict';// code };const obj = {// 报错doSomething({a, b}) {'use strict';// code} };
五、箭头函数
- 定义方式:使用 “箭头”(
=>
)定义函数。var f = v => v; // 等同于 var f = function (v) {return v; };var f = () => 5; // 等同于 var f = function () { return 5 };var sum = (num1, num2) => num1 + num2; // 等同于 var sum = function(num1, num2) {return num1 + num2; };
- 代码块与返回值:代码块多于一条语句时,用大括号括起来并用
return
返回;返回对象需用括号包裹var sum = (num1, num2) => { return num1 + num2; } let getTempItem = id => ({ id: id, name: "Temp" });
- 注意点:
- 函数体内的
this
对象是定义时所在对象,非使用时所在对象。- 不可当作构造函数,不能用
new
命令,否则报错。- 不可使用
arguments
对象,可用 rest 参数代替。- 不可使用
yield
命令,不能用作 Generator 函数。
你是怎么理解ES6新增Set、Map两种数据结构的?
Set 数据结构
集合
是由一堆无序的、相关联的,且不重复的内存结构【数学中称为元素】组成的组合
- 定义与特性:Set 是一种集合数据结构,类似于数组,但成员值唯一,无重复值。它以
[值, 值]
的形式存储元素。作为构造函数new Set()
可生成 Set 结构。- 增删改查操作:
add(value)
:添加值,若值已存在则不处理,返回 Set 本身,支持链式调用,如s.add(1).add(2).add(2)
。delete(value)
:删除值,返回布尔值表示删除是否成功,例如s.delete(1)
。has(value)
:判断值是否为成员,返回布尔值,如s.has(2)
。clear()
:清除所有成员,无返回值,即s.clear()
。- 遍历方式:
keys()
:返回键名遍历器(Set 中键名与键值相同)。values()
:返回键值遍历器。entries()
:返回键值对遍历器,每个键值对的键和值相同。forEach()
:forEach()
用于对每个成员执行某种操作,没有返回值,键值、键名都相等,同样的forEach
方法有第二个参数,用于绑定处理函数的this。
let set = new Set([1, 4, 9]); set.forEach((value, key) => console.log(key + ' : ' + value)) // 1 : 1 // 4 : 4 // 9 : 9
- 应用场景:
- 去重:结合扩展运算符可实现数组或字符串去重。如
let arr = [3, 5, 2, 2, 5, 5]; let unique = [...new Set(arr)]
实现数组去重;let str = "352255"; let unique = [...new Set(str)].join("")
实现字符串去重。- 集合运算:实现并集、交集和差集运算。例如:
let a = new Set([1, 2, 3]); let b = new Set([4, 3, 2]); // 并集 let union = new Set([...a, ...b]); // 交集 let intersect = new Set([...a].filter(x => b.has(x))); // (a 相对于 b 的)差集 let difference = new Set([...a].filter(x =>!b.has(x)));
Map 数据结构
字典
是一些元素的集合。每个元素有一个称作key 的域,不同元素的key 各不相同
- 定义与特性:Map 是键值对的有序列表,键和值可以是任意类型,以
[键, 值]
的形式存储元素。通过new Map()
构造函数生成。- 增删改查操作:
size
:返回成员总数,如const map = new Map(); map.set('foo', true); map.set('bar', false); map.size // 2
。set(key, value)
:设置键值对,若键已存在则更新值,返回 Map 本身,支持链式操作,例如m.set(1, 'a').set(2, 'b').set(3, 'c')
。get(key)
:读取键对应的值,若键不存在返回undefined
,如m.get(hello)
。has(key)
:判断键是否存在,返回布尔值,例如m.has('edition')
。delete(key)
:删除键,返回布尔值表示删除是否成功,如m.delete(undefined)
。clear()
:清除所有成员,无返回值,即map.clear()
。- 遍历方式:
keys()
:返回键名遍历器。values()
:返回键值遍历器。entries()
:返回所有成员的遍历器。forEach(callback[, thisArg])
:遍历 Map 所有成员,通过回调函数处理每个成员,可绑定this
。- 应用场景:当需要存储键值对且键可以为任意类型时适用,比如在复杂数据关联场景中,键为对象、函数等类型的情况。
WeakSet 和 WeakMap
- WeakSet:
- 特性:成员只能是引用类型,不支持遍历操作 API 且没有
size
属性。WeakSet 对成员的引用为弱引用,当外部对成员的引用消失,WeakSet 内的引用自动消失。- 创建与使用:可接受具有
Iterable
接口的对象作为参数,如const a = [[1, 2], [3, 4]]; const ws = new WeakSet(a)
。- WeakMap:
- 特性:键名只能是对象(
null
除外),不支持遍历操作 API 且没有clear
方法。键名对对象的引用为弱引用,当键名所指向对象不再需要,相关键值对自动消失,但键值是正常引用。- 创建与使用:例如
const wm1 = new WeakMap(); const key = {foo: 1}; wm1.set(key, 2); wm1.get(key) // 2
;也可接受数组作为构造函数参数const k1 = [1, 2, 3]; const k2 = [4, 5, 6]; const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]); wm2.get(k2) // "bar"
。常用于在 DOM 元素上添加数据,当 DOM 元素被清除,WeakMap 记录自动移除。
总之,Set 和 Map 及其变体 WeakSet 和 WeakMap 为 JavaScript 开发者提供了更灵活、高效处理数据集合与键值对存储的能力,在不同场景下能优化代码逻辑与性能。
你是怎么理解ES6中 Promise的?使用场景?
一、Promise 基本概念
- 定义与优势
- Promise 是 ES6 为异步编程提供的解决方案,它比传统的回调函数方式更合理、强大。传统的多层异步操作使用回调函数容易形成回调地狱,代码可读性和维护性差。例如:
// 回调地狱示例 doSomething(function(result) {doSomethingElse(result, function(newResult) {doThirdThing(newResult, function(finalResult) {console.log('得到最终结果:'+ finalResult);}, failureCallback);}, failureCallback); }, failureCallback);
- 而使用 Promise 改写后,通过链式调用降低了编码难度,增强了代码可读性:
// Promise 链式调用示例 doSomething().then(function(result) {return doSomethingElse(result); }) .then(function(newResult) {return doThirdThing(newResult); }) .then(function(finalResult) {console.log('得到最终结果:'+ finalResult); }) .catch(failureCallback);
状态
- Promise 对象有三种状态:
- pending(进行中):初始状态,异步操作正在执行。
- fulfilled(已成功):异步操作成功完成。
- rejected(已失败):异步操作失败。
- Promise 对象状态具有以下特点:
- 状态不受外界影响,仅由异步操作的结果决定。
- 状态一旦改变(从 pending 变为 fulfilled 或从 pending 变为 rejected),就不会再变,任何时候都能获取到这个结果。
二、Promise 的用法
- 构造函数
- Promise 是一个构造函数,用于生成 Promise 实例:
const promise = new Promise(function(resolve, reject) {});
- 构造函数接受一个函数作为参数,该函数包含两个参数
resolve
和reject
:
resolve
函数:将 Promise 对象的状态从 “未完成(pending)” 变为 “成功(fulfilled)”。reject
函数:将 Promise 对象的状态从 “未完成(pending)” 变为 “失败(rejected)”。
- 实例方法
- then():
- 用于指定状态改变时的回调函数,第一个参数是
resolved
状态的回调函数,第二个参数是rejected
状态的回调函数。then
方法返回一个新的 Promise 实例,这是 Promise 能链式书写的原因。例如:getJSON("/posts.json").then(function(json) {return json.post; }).then(function(post) {//... });
- catch():
catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。- Promise 对象的错误具有 “冒泡” 性质,会一直向后传递,直到被捕获为止。一般使用
catch
方法代替then()
的第二个参数来处理错误。例如:getJSON('/posts.json').then(function(posts) {//... }).catch(function(error) {// 处理 getJSON 和前一个回调函数运行时发生的错误console.log('发生错误!', error); });
- finally():
finally()
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。例如:promise .then(result => {···}) .catch(error => {···}) .finally(() => {···});
- 构造函数方法
all():
Promise.all()
方法将多个 Promise 实例包装成一个新的 Promise 实例,接受一个数组(迭代对象)作为参数,数组成员都应为 Promise 实例。const p = Promise.all([p1, p2, p3]);
- 新实例
p
的状态由传入的实例决定:
- 只有当所有传入的 Promise 实例(如
p1
、p2
、p3
)的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时这些实例的返回值组成一个数组,传递给p
的回调函数。- 只要有一个实例被
rejected
,p
的状态就变成rejected
,此时第一个被rejected
的实例的返回值,会传递给p
的回调函数。- 注意,如果作为参数的 Promise 实例自己定义了
catch
方法,它被rejected
时,不会触发Promise.all()
的catch
方法。
race():
Promise.race()
方法同样将多个 Promise 实例包装成一个新的 Promise 实例。- 只要其中有一个实例率先改变状态,新 Promise 的状态就跟着改变,且率先改变状态的 Promise 实例的返回值传递给新 Promise 的回调函数。例如:
const p = Promise.race([fetch('/resource - that - may - take - a - while'),new Promise(function(resolve, reject) {setTimeout(() => reject(new Error('request timeout')), 5000)}) ]); p .then(console.log) .catch(console.error);
allSettled():
Promise.allSettled()
方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。- 只有等到所有参数实例都返回结果(无论是
fulfilled
还是rejected
),包装实例才会结束。例如:const promises = [fetch('/api - 1'),fetch('/api - 2'),fetch('/api - 3') ]; await Promise.allSettled(promises); removeLoadingIndicator();
resolve():
- 将现有对象转为 Promise 对象。参数情况如下:
- 参数是一个 Promise 实例,
Promise.resolve
将不做任何修改、原封不动地返回这个实例。- 参数是一个
thenable
对象(具有then
方法的对象),Promise.resolve
会将这个对象转为 Promise 对象,然后立即执行thenable
对象的then()
方法。- 参数不是具有
then()
方法的对象,或根本就不是对象,Promise.resolve()
会返回一个新的 Promise 对象,状态为resolved
。- 没有参数时,直接返回一个
resolved
状态的 Promise 对象。reject():
Promise.reject(reason)
方法返回一个新的 Promise 实例,该实例的状态为rejected
。例如:const p = Promise.reject('出错了'); // 等同于 const p = new Promise((resolve, reject) => reject('出错了')); p.then(null, function(s) {console.log(s); }); // 出错了
Promise.reject()
方法的参数,会原封不动地变成后续方法的参数。
三、Promise 的使用场景
- 图片加载:可以将图片加载操作封装成 Promise,当图片加载完成(成功或失败)时,Promise 状态相应改变,便于后续处理。例如:
const preloadImage = function(path) {return new Promise(function(resolve, reject) {const image = new Image();image.onload = resolve;image.onerror = reject;image.src = path;}); };
- 链式异步操作:通过链式操作,将多个渲染数据分别交给不同的
then
处理,各司其职;当下个异步请求依赖上个请求结果时,也能通过链式操作友好解决。例如:// 各司其职示例 getInfo().then(res => {let { bannerList } = res;// 渲染轮播图console.log(bannerList);return res; }).then(res => {let { storeList } = res;// 渲染店铺列表console.log(storeList);return res; }).then(res => {let { categoryList } = res;console.log(categoryList);// 渲染分类列表return res; });
- 多个请求合并:使用
Promise.all()
实现多个请求合并,汇总所有请求结果,只需设置一个加载指示器(如loading
)。例如:function initLoad() {// loading.show(); // 加载loadingPromise.all([getBannerList(), getStoreList(), getCategoryList()]).then(res => {console.log(res);loading.hide(); // 关闭loading}).catch(err => {console.log(err);loading.hide();// 关闭loading}); } // 数据初始化 initLoad();
- 设置请求超时:利用
Promise.race()
可以设置图片请求等操作的超时。例如:// 请求某个图片资源 function requestImg() {var p = new Promise(function(resolve, reject) {var img = new Image();img.onload = function() {resolve(img);};// img.src = "https://b - gold - cdn.xitu.io/v3/static/img/logo.a7995ad.svg"; 正确的img.src = "https://b - gold - cdn.xitu.io/v3/static/img/logo.a7995ad.svg1";});return p; }// 延时函数,用于给请求计时 function timeout() {var p = new Promise(function(resolve, reject) {setTimeout(function() {reject('图片请求超时');}, 5000);});return p; }Promise .race([requestImg(), timeout()]) .then(function(results) {console.log(results); }) .catch(function(reason) {console.log(reason); });