免费建立个人文章网站,详情页设计软件,网站页面设计模板,网站开发相关外文书籍一、promise方法的案例
Promise对象通过new Promise()语法创建#xff0c;它接受一个函数作为参数#xff0c;该函数接受两个参数#xff1a;resolve和reject。resolve表示异步操作成功#xff0c;reject表示异步操作失败。
案例#xff1a;异步加载图片
const loadIma…
一、promise方法的案例
Promise对象通过new Promise()语法创建它接受一个函数作为参数该函数接受两个参数resolve和reject。resolve表示异步操作成功reject表示异步操作失败。
案例异步加载图片
const loadImage (url) {return new Promise((resolve, reject) {const img new Image();img.onload () {resolve(img);};img.onerror () {reject(new Error(Failed to load image from ${url}));};img.src url;});
};loadImage(https://example.com/image.jpg).then((img) {console.log(Image loaded successfully);document.body.appendChild(img);}).catch((error) {console.error(error.message);});
Promise.prototype.then()
then()方法接受两个可选参数onFulfilled和onRejected分别表示成功时和失败时的回调函数。当Promise对象的状态为fulfilled时会立即执行onFulfilled回调函数并将操作结果作为参数传递给该函数当Promise对象的状态为rejected时会立即执行onRejected回调函数并将拒绝原因错误信息作为参数传递给该函数。
案例
const p new Promise((resolve, reject) {resolve(1);
});p.then((res) {console.log(res); // 输出1return 2; // 返回值会被作为下一个then的输入},(error) {console.log(error);}
)
.then((res) {console.log(res); // 输出2}
);
Promise.prototype.catch()
catch()方法是then(undefined, onRejected)的语法糖用于捕获Promise对象被rejected时的错误信息。
案例
const p new Promise((resolve, reject) {reject(error);
});p.then((res) {console.log(res); // 不会执行
})
.catch((error) {console.log(error); // 输出error
}); 1.1.promise.all
过程
1.创建promise数组首先需要创建一个包含多个promise对象的数组每个promise对象都代表一个异步操作。
2.调用promise.all然后调用promise.all方法并将这个数组作为参数传递给它。promise.all会返回一个新的promise对象
3.处理结果
如果所有的输入promiase对象都成功解析那么promise.all返回的promise对象也会被解析并且它的解析值是一个数组包含了所有输入promise对象的解析值顺序与输入数组一致。
如果任何一个输入promise对象被拒绝那么promise.all返回的promise对象也会被拒绝并且它的拒绝原因与第一个被拒绝的promise对象的拒绝原因相同。
4.使用.then和.catch可以使用.then方法来处理成功的情况使用.catch方法来处理失败的情况
例子同时加载多张图片
const urls [https://example.com/image1.jpg,https://example.com/image2.jpg,https://example.com/image3.jpg
];const loadImage (url) {return new Promise((resolve, reject) {const img new Image();img.onload () {resolve(img);};img.onerror () {reject(new Error(Failed to load image from ${url}));};img.src url;});
};Promise.all(urls.map(loadImage)).then((images) {console.log(All images loaded successfully);images.forEach((img) {document.body.appendChild(img);});}).catch((error) {console.error(error.message);});
1.2.promise.race
过程
1.创建promise数组首先需要创建一个包含多个promise对象的数组每个promise对象都代表一个异步操作。
2.调用promise.race然后调用promise.race方法并将这个数组作为参数传递给它。promise.race会返回一个新的promise对象
3.处理结果
promise.race只关心第一个完成的promise对象无论是成功还是拒绝
如果第一个完成的promise是成功解析的那么promise.race返回的promise对象也会解析并且它的解析值与第一个完成的promise对象的解析值相同
如果过第一个完成的promise是被拒绝的那么promise.race返回的promise对象也会被拒绝并且它的拒绝原因第一个被决绝的promise对象的拒绝原因相同
4.使用.then和.catch同样也可以使用.then方法来处理成功的情况使用.catch方法来处理失败的情况。
例子同时加载多张图片但只显示第一张加载成功的图片
const urls [https://example.com/image1.jpg,https://example.com/image2.jpg,https://example.com/image3.jpg
];const loadImage (url) {return new Promise((resolve, reject) {const img new Image();img.onload () {resolve(img);};img.onerror () {reject(new Error(Failed to load image from ${url}));};img.src url;});
};Promise.race(urls.map(loadImage)).then((img) {console.log(First image loaded successfully);document.body.appendChild(img);}).catch((error) {console.error(error.message);});
// 创建两个异步操作的Promise其中一个会更快完成
const slowFetch () new Promise((resolve) {setTimeout(() resolve(Slow Data), 2000);
});const fastFetch () new Promise((resolve) {setTimeout(() resolve(Fast Data), 500);
});// 使用 Promise.race 来等待第一个异步操作完成
Promise.race([slowFetch(), fastFetch()]).then((data) {// 由于 fastFetch 更快完成所以这里会输出 Fast Dataconsole.log(data); // 输出: Fast Data}).catch((error) {// 如果没有任何Promise被拒绝这里的catch不会被调用console.error(error);});// 另一个例子包含一个会被拒绝的Promise
const fetchWithTimeout () {return Promise.race([fetch(https://jsonplaceholder.typicode.com/posts/1) // 一个真实的fetch请求.then(response response.json()),new Promise((_, reject) {setTimeout(() reject(new Error(Request timed out)), 1000); // 1秒后拒绝})]);
};// 使用 fetchWithTimeout并处理可能的超时
fetchWithTimeout().then(post {console.log(post); // 如果fetch请求在1秒内完成这里会输出post数据}).catch(error {console.error(Error:, error.message); // 如果fetch请求超时这里会输出 Error: Request timed out});
在这个fetchWithTimeout的例子中我们使用了promise.race来设置一个超时机制。如果fetch请求在1秒内没有完成那么promise.race会返回被拒绝的promise并且我们可以在.catch中处理这个超时错误。
promise.resolve()和promise.reject()
promise.resolve()方法返回一个已经成功状态的promise对象并将指定的值作为参数传递给它的then方法。 promise.reject()方法返回一个已经失败状态的promise对象并将指定原因作为参数传递给它的catch方法 案例
// Promise.resolve()
Promise.resolve(10).then((value) {console.log(The value is ${value}); // 输出The value is 10}).catch((error) {console.error(error.message); // 不会执行});// Promise.reject()
Promise.reject(error).then((value) {console.log(value); // 不会执行}).catch((reason) {console.log(reason); // 输出error}); 二、高阶语法与特性
2.1模板字符串的高级用法 本小节讲解模板字符串的嵌套、标签模板等高级用法了解它们如何用于字符串的格式化、插值等操作。 2.1.1.模板字符串的嵌套
模板字符串允许嵌套另一个模板字符串这在处理复杂数据结构时非常有用。例如假设有一个包含地址信息的数组可以使用嵌套模板字符串来生成一个HTML表格
const addrs [{ first: John, last: Doe },{ first: Jane, last: Smith }
];const tmpl addrs table${addrs.map(addr trtd${addr.first}/td/trtrtd${addr.last}/td/tr).join()}/table
;console.log(tmpl(addrs));
上述代码中内部的模板字符串用于生成每一行的表格内容外部的模板字符串则负责将这些行组合成一个完整的表格。
2.1.2.标签模板用于字符串的格式化、插值等操作 标签模板Tagged Template是一种特殊的函数它允许对模板字符串进行自定义处理。标签模板函数的基本语法是在一个函数名后面紧跟一个模板字符串字面量。这个函数会接收两个参数一个由模板字符串中的静态文本部分组成的数组以及由模板字符串中的表达式求值结果组成的剩余参数列表。 1.字符串格式化
可以创建一个标签模板函数来格式化日期、数字或货币等。例如以下是一个简单的标签模板函数用于将数字格式化为货币形式
function formatCurrency(strings, ...values) {let result ;for (let i 0; i strings.length; i) {result strings[i];if (i values.length) {result $ values[i].toFixed(2); // 将数字格式化为两位小数的货币形式}}return result;
}const price 123.456;
console.log(formatCurrencyThe price is ${price}); // 输出 The price is $123.46
2.字符串插值
标签模板函数也可以用于字符串插值但与普通的模板字符串插值不同标签模板函数可以对插入的变量或表达式进行自定义处理。例如以下是一个将字符串转换为大写的标签模板函数
function upperCase(strings, ...values) {return strings.reduce((result, str, i) {const value values[i - 1] || ;return result str value.toUpperCase();}, );
}const greeting upperCaseHello, ${world}!; // 输出 Hello, WORLD!
3. 避免XSS攻击
在处理用户输入时标签模板函数还可以用于避免跨站脚本攻击XSS。例如可以对用户输入进行HTML转义
function escapeHTML(strings, ...values) {let result ;for (let i 0; i strings.length; i) {result strings[i];if (i values.length) {// 简单的HTML转义函数const escape (html) html.replace(/[\/]/g, (s) #${s.charCodeAt(0)};);result escape(values[i]);}}return result;
}const userInput scriptalert(XSS!);/script;
console.log(escapeHTMLSafe input: ${userInput});
// 输出 Safe input: lt;scriptgt;alert(quot;XSS!quot;);lt;/scriptgt; 三、尾调用优化
3.1.尾调用优化Tail Call Optimization的概念和作用
尾调用优化是编译器或解释器对函数调用的一种优化手段。当函数A的最后一步是调用另一个函数B并且A的返回值就是B的调用结果时这个调用就被称为尾调用。如果编译器或解释器支持尾调用优化那么在执行尾调用时可以只保留函数B的调用记录而删除函数A的调用记录因为A的调用结果已经确定并且不再需要A的调用记录。这种优化可以显著减少内存使用避免因为递归调用过深而导致的栈溢出错误。
3.2.递归函数中的尾调用优化
在递归函数中尾调用优化尤为重要。传统的递归调用可能会导致大量的栈空间被占用因为每次递归调用都会创建一个新的栈帧。而尾调用优化可以确保只有一个栈帧被重复使用从而避免了栈空间的无谓消耗。
3.3.举例说明
已计算阶乘的递归函数为例非尾递归和尾递归的实现方式如下
非尾递归实现
function factorial_recursive(n) {if (n 0) {return 1;} else {return n * factorial_recursive(n - 1); // 这里不是尾调用因为乘法操作在调用之后}
}
在这个非尾递归的实现中每次递归调用之后都有一个乘法操作因此这不是一个尾调用。编译器无法对这个递归调用进行优化。
尾递归实现
function factorial_tail_recursive(n, result 1) {if (n 0) {return result;} else {return factorial_tail_recursive(n - 1, n * result); // 这里是尾调用因为调用之后没有其他操作}
}
在这个尾递归的实现中递归调用是函数的最后一个操作并且这个调用的返回值就是整个函数的返回值。因此编译器可以对这个递归调用进行优化通过重用当前栈帧来避免增加额外的栈空间。
3.4.如何应用尾调用优化
要将一个递归函数改写为尾递归函数通常需要引入一个辅助参数来保存中间结果。这个辅助参数在每次递归调用时都会被更新并在递归结束时返回最终结果。
例2计算斐波那契数列
非尾递归实现
function fibonacci_non_tail_recursive(n) {if (n 1) {return n;} else {return fibonacci_non_tail_recursive(n - 1) fibonacci_non_tail_recursive(n - 2);}
}
这个非尾递归的实现导致大量的重复计算和栈空间消耗。
尾递归实现
function fibonacci_tail_recursive(n, a 0, b 1) {if (n 0) {return a;} else if (n 1) {return b;} else {return fibonacci_tail_recursive(n - 1, b, a b);}
}
在这个尾递归的实现中我们引入了两个辅助参数 a 和 b 来保存斐波那契数列的前两个数。每次递归调用时我们都会更新这两个参数并在递归结束时返回最终结果。由于递归调用是函数的最后一个操作并且这个调用的返回值就是整个函数的返回值因此编译器可以对这个递归调用进行优化。 四、模块化进阶
4.1.动态导入
4.1.1.基本语法
动态导入使用import()函数该函数返回一个Promise对象。这意味着你可以使用.then()方法或async/await语法来处理导入的模块。以下是动态导入的常见写法
1.导入整个模块
import(./module.js).then((module) {// 使用 module
});
2.导入模块的特定导出
import(./module.js).then(({ export1, export2 }) {// 使用 export1 和 export2
});
4.1.2.动态导入的特点与优势
按需加载动态导入允许你在需要时才加载模块这有助于减少初始加载时间提高应用性能。
代码分割结合构建工具如Webpack动态导入可以实现代码分割将代码拆分成更小的块以便更高效地加载和执行。
条件加载你可以根据条件动态加载不同的模块这在实现功能切换或按需加载特定功能时非常有用。
4.1.3.动态导入的应用场景
按需加载组件在单页面应用SPA中你可以使用动态导入来按需加载组件从而减少初始加载时间和内存占用。
条件加载模块根据用户的操作或环境变量等条件动态加载不同的模块以实现功能切换。
优化性能通过代码分割和按需加载动态导入可以显著提高应用的加载速度和性能。
4.1.4示例
使用动态导入根据条件加载不同模块
// 定义一个函数根据不同的条件导入不同的模块
function loadModule(condition) {if (condition moduleA) {return import(./moduleA.js);} else {return import(./moduleB.js);}
}// 使用 loadModule() 函数来动态导入模块
loadModule(moduleA).then((module) {// 使用 moduleA 模块
}).catch((error) {// 处理错误
});loadModule(moduleB).then((module) {// 使用 moduleB 模块
}).catch((error) {// 处理错误
});
在这个示例中我们定义了一个loadModule函数它根据传入的条件动态加载不同的模块。然后我们使用loadModule函数来导入模块并在Promise的then方法中使用相应的模块。
4.1.5.注意事项
兼容性虽然大多数现代浏览器都支持动态导入但在一些旧版浏览器中可能无法使用。因此在开发过程中需要注意兼容性问题。
构建工具为了充分利用动态导入的优势通常需要结合构建工具如Webpack进行代码分割和优化。
错误处理在使用动态导入时需要妥善处理可能出现的错误以确保应用的稳定性和用户体验。
4.2.循环依赖 循环依赖指的是两个或多个模块之间互相引用形成一个闭环的依赖关系。这种依赖关系可能会导致一些潜在的问题如模块加载顺序错误、未定义或未初始化的变量等。 4.2.1.循环依赖的定义与示例
循环依赖通常发生在以下情况模块A依赖于模块B的导出同时模块B也依赖于模块A的导出。这种依赖关系形成了一个闭环可能导致模块加载和执行时的错误。 例如有以下连个模块文件
// a.js
import { b } from ./b.js;export const a () {console.log(This is a.js);b();
};// b.js
import { a } from ./a.js;export const b () {console.log(This is b.js);a();
};
在这个例子中a.js和b.js互相引用对方形成了一个循环依赖。
4.2.2.循环依赖的问题与影响
1.加载顺序问题在循环依赖的情况下模块的加载顺序变得不确定。这可能导致某些模块在还未完全初始化的情况下就被其他模块引用从而引发错误。
2.未定义或未初始化的变量由于加载顺序的不确定性可能会出现某些变量在引用时还未被定义或初始化的情况。
3.性能问题循环依赖可能导致模块加载和执行的效率降低因为模块之间的依赖关系变得更加复杂。
4.2.3.解决循环依赖的方法
1.重构代码通过重构代码来消除循环依赖。例如可以将一些公共的功能或数据提取到一个新的模块中并让其他模块都依赖于这个新模块。
2.使用异步加载在ES6中可以使用动态导入import()来异步加载模块。这有助于在需要时才加载模块从而避免循环依赖的问题。但需要注意的是异步加载可能会增加代码的复杂性和加载时间。
3.依赖注入通过依赖注入模式来管理模块之间的依赖关系。这有助于降低模块之间的耦合度并使得依赖关系更加清晰和可控。
4.使用第三方库或工具一些第三方库或工具如Webpack的splitChunks插件可以帮助开发者更好地管理和优化模块之间的依赖关系。
4.2.4.最佳实践与建议
1.避免循环依赖在设计模块时应尽量避免循环依赖的发生。通过合理的模块划分和组织可以降低循环依赖的风险。
2.清晰的模块接口确保每个模块都有清晰的接口和职责。这有助于其他模块更容易地理解和使用它们并降低循环依赖的可能性。
3.使用构建工具利用构建工具如Webpack、Rollup等来优化模块之间的依赖关系。这些工具可以帮助开发者更好地管理模块、减少打包文件的大小并提高加载速度。 亲爱的友友们~~~码这么多字不容易啊 给孩子点点赞叭评论个1也成呐
当然上述内容若有遗漏或不足之处恳请各位大佬不吝赐教指正并帮助美化以期更加完善。