???個(gè)人主頁:前端青山
??系列專欄:JavaScript篇
??人終將被年少不可得之物困其一生
依舊青山,本期給大家?guī)鞪avaScript篇專欄內(nèi)容:JavaScript-場景應(yīng)用
目錄
三、場景應(yīng)用
1. 循環(huán)打印紅黃綠
(1)用 callback 實(shí)現(xiàn)
(2)用 promise 實(shí)現(xiàn)
(3)用 async/await 實(shí)現(xiàn)
2. 實(shí)現(xiàn)每隔一秒打印 1,2,3,4
3. 小孩報(bào)數(shù)問題
4. 用Promise實(shí)現(xiàn)圖片的異步加載
5. 實(shí)現(xiàn)發(fā)布-訂閱模式
6. 查找文章中出現(xiàn)頻率最高的單詞
7. 封裝異步的fetch,使用async await方式來使用
8. 實(shí)現(xiàn)prototype繼承
9. 實(shí)現(xiàn)雙向數(shù)據(jù)綁定
10. 實(shí)現(xiàn)簡單路由
11. 實(shí)現(xiàn)斐波那契數(shù)列
12. 字符串出現(xiàn)的不重復(fù)最長長度
13. 使用 setTimeout 實(shí)現(xiàn) setInterval
15. 判斷對(duì)象是否存在循環(huán)引用
場景應(yīng)用
1. 循環(huán)打印紅黃綠
下面來看一道比較典型的問題,通過這個(gè)問題來對(duì)比幾種異步編程方法:紅燈 3s 亮一次,綠燈 1s 亮一次,黃燈 2s 亮一次;如何讓三個(gè)燈不斷交替重復(fù)亮燈?
三個(gè)亮燈函數(shù):
function red() {
? ?console.log('red');
}
function green() {
? ?console.log('green');
}
function yellow() {
? ?console.log('yellow');
}
復(fù)制代碼
這道題復(fù)雜的地方在于需要“交替重復(fù)”亮燈,而不是“亮完一次”就結(jié)束了。
(1)用 callback 實(shí)現(xiàn)
const task = (timer, light, callback) => {
? ?setTimeout(() => {
? ? ? ?if (light === 'red') {
? ? ? ? ? ?red()
? ? ? }
? ? ? ?else if (light === 'green') {
? ? ? ? ? ?green()
? ? ? }
? ? ? ?else if (light === 'yellow') {
? ? ? ? ? ?yellow()
? ? ? }
? ? ? ?callback()
? }, timer)
}
task(3000, 'red', () => {
? ?task(2000, 'green', () => {
? ? ? ?task(1000, 'yellow', Function.prototype)
? })
})
復(fù)制代碼
這里存在一個(gè) bug:代碼只是完成了一次流程,執(zhí)行后紅黃綠燈分別只亮一次。該如何讓它交替重復(fù)進(jìn)行呢?
上面提到過遞歸,可以遞歸亮燈的一個(gè)周期:
const step = () => {
? ?task(3000, 'red', () => {
? ? ? ?task(2000, 'green', () => {
? ? ? ? ? ?task(1000, 'yellow', step)
? ? ? })
? })
}
step()
復(fù)制代碼
注意看黃燈亮的回調(diào)里又再次調(diào)用了 step 方法 以完成循環(huán)亮燈。
(2)用 promise 實(shí)現(xiàn)
const task = (timer, light) =>
? ?new Promise((resolve, reject) => {
? ? ? ?setTimeout(() => {
? ? ? ? ? ?if (light === 'red') {
? ? ? ? ? ? ? ?red()
? ? ? ? ? }
? ? ? ? ? ?else if (light === 'green') {
? ? ? ? ? ? ? ?green()
? ? ? ? ? }
? ? ? ? ? ?else if (light === 'yellow') {
? ? ? ? ? ? ? ?yellow()
? ? ? ? ? }
? ? ? ? ? ?resolve()
? ? ? }, timer)
? })
const step = () => {
? ?task(3000, 'red')
? ? ? .then(() => task(2000, 'green'))
? ? ? .then(() => task(2100, 'yellow'))
? ? ? .then(step)
}
step()
復(fù)制代碼
這里將回調(diào)移除,在一次亮燈結(jié)束后,resolve 當(dāng)前 promise,并依然使用遞歸進(jìn)行。
(3)用 async/await 實(shí)現(xiàn)
const taskRunner = ?async () => {
? ?await task(3000, 'red')
? ?await task(2000, 'green')
? ?await task(2100, 'yellow')
? ?taskRunner()
}
taskRunner()
復(fù)制代碼
2. 實(shí)現(xiàn)每隔一秒打印 1,2,3,4
// 使用閉包實(shí)現(xiàn)
for (var i = 0; i < 5; i++) {
(function(i) {
? ?setTimeout(function() {
? ? ?console.log(i);
? }, i * 1000);
})(i);
}
// 使用 let 塊級(jí)作用域
for (let i = 0; i < 5; i++) {
?setTimeout(function() {
? ?console.log(i);
}, i * 1000);
}
復(fù)制代碼
3. 小孩報(bào)數(shù)問題
有30個(gè)小孩兒,編號(hào)從1-30,圍成一圈依此報(bào)數(shù),1、2、3 數(shù)到 3 的小孩兒退出這個(gè)圈, 然后下一個(gè)小孩 重新報(bào)數(shù) 1、2、3,問最后剩下的那個(gè)小孩兒的編號(hào)是多少?
function childNum(num, count){
? ?let allplayer = []; ? ?
? ?for(let i = 0; i < num; i++){
? ? ? ?allplayer[i] = i + 1;
? }
? ?
? ?let exitCount = 0; ? ?// 離開人數(shù)
? ?let counter = 0; ? ? ?// 記錄報(bào)數(shù)
? ?let curIndex = 0; ? ? // 當(dāng)前下標(biāo)
? ?
? ?while(exitCount < num - 1){
? ? ? ?if(allplayer[curIndex] !== 0) counter++; ? ?
? ? ? ?
? ? ? ?if(counter == count){
? ? ? ? ? ?allplayer[curIndex] = 0; ? ? ? ? ? ? ? ?
? ? ? ? ? ?counter = 0;
? ? ? ? ? ?exitCount++; ?
? ? ? }
? ? ? ?curIndex++;
? ? ? ?if(curIndex == num){
? ? ? ? ? ?curIndex = 0 ? ? ? ? ? ? ?
? ? ? }; ? ? ? ? ?
? } ? ?
? ?for(i = 0; i < num; i++){
? ? ? ?if(allplayer[i] !== 0){
? ? ? ? ? ?return allplayer[i]
? ? ? } ? ? ?
? }
}
childNum(30, 3)
復(fù)制代碼
4. 用Promise實(shí)現(xiàn)圖片的異步加載
let imageAsync=(url)=>{
? ? ? ? ? ?return new Promise((resolve,reject)=>{
? ? ? ? ? ? ? ?let img = new Image();
? ? ? ? ? ? ? ?img.src = url;
? ? ? ? ? ? ? ?img.οnlοad=()=>{
? ? ? ? ? ? ? ? ? ?console.log(`圖片請(qǐng)求成功,此處進(jìn)行通用操作`);
? ? ? ? ? ? ? ? ? ?resolve(image);
? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?img.οnerrοr=(err)=>{
? ? ? ? ? ? ? ? ? ?console.log(`失敗,此處進(jìn)行失敗的通用操作`);
? ? ? ? ? ? ? ? ? ?reject(err);
? ? ? ? ? ? ? }
? ? ? ? ? })
? ? ? }
? ? ? ?
imageAsync("url").then(()=>{
? ?console.log("加載成功");
}).catch((error)=>{
? ?console.log("加載失敗");
})
復(fù)制代碼
5. 實(shí)現(xiàn)發(fā)布-訂閱模式
class EventCenter{
?// 1. 定義事件容器,用來裝事件數(shù)組
let handlers = {}
?
?// 2. 添加事件方法,參數(shù):事件名 事件方法
?addEventListener(type, handler) {
? ?// 創(chuàng)建新數(shù)組容器
? ?if (!this.handlers[type]) {
? ? ?this.handlers[type] = []
? }
? ?// 存入事件
? ?this.handlers[type].push(handler)
}
?
?// 3. 觸發(fā)事件,參數(shù):事件名 事件參數(shù)
?dispatchEvent(type, params) {
? ?// 若沒有注冊(cè)該事件則拋出錯(cuò)誤
? ?if (!this.handlers[type]) {
? ? ?return new Error('該事件未注冊(cè)')
? }
? ?// 觸發(fā)事件
? ?this.handlers[type].forEach(handler => {
? ? ?handler(...params)
? })
}
? ?// 4. 事件移除,參數(shù):事件名 要?jiǎng)h除事件,若無第二個(gè)參數(shù)則刪除該事件的訂閱和發(fā)布
?removeEventListener(type, handler) {
? ?if (!this.handlers[type]) {
? ? ?return new Error('事件無效')
? }
? ?if (!handler) {
? ? ?// 移除事件
? ? ?delete this.handlers[type]
? } else {
? ? ?const index = this.handlers[type].findIndex(el => el === handler)
? ? ?if (index === -1) {
? ? ? ?return new Error('無該綁定事件')
? ? }
? ? ?// 移除事件
? ? ?this.handlers[type].splice(index, 1)
? ? ?if (this.handlers[type].length === 0) {
? ? ? ?delete this.handlers[type]
? ? }
? }
}
}
復(fù)制代碼
6. 查找文章中出現(xiàn)頻率最高的單詞
function findMostWord(article) {
?// 合法性判斷
?if (!article) return;
?// 參數(shù)處理
?article = article.trim().toLowerCase();
?let wordList = article.match(/[a-z]+/g),
? ?visited = [],
? ?maxNum = 0,
? ?maxWord = "";
?article = " " + wordList.join(" ") + " ";
?// 遍歷判斷單詞出現(xiàn)次數(shù)
?wordList.forEach(function(item) {
? ?if (visited.indexOf(item) < 0) {
? ? ?// 加入 visited
? ? ?visited.push(item);
? ? ?let word = new RegExp(" " + item + " ", "g"),
? ? ? ?num = article.match(word).length;
? ? ?if (num > maxNum) {
? ? ? ?maxNum = num;
? ? ? ?maxWord = item;
? ? }
? }
});
?return maxWord + " " + maxNum;
}
復(fù)制代碼
7. 封裝異步的fetch,使用async await方式來使用
(async () => {
? ?class HttpRequestUtil {
? ? ? ?async get(url) {
? ? ? ? ? ?const res = await fetch(url);
? ? ? ? ? ?const data = await res.json();
? ? ? ? ? ?return data;
? ? ? }
? ? ? ?async post(url, data) {
? ? ? ? ? ?const res = await fetch(url, {
? ? ? ? ? ? ? ?method: 'POST',
? ? ? ? ? ? ? ?headers: {
? ? ? ? ? ? ? ? ? ?'Content-Type': 'application/json'
? ? ? ? ? ? ? },
? ? ? ? ? ? ? ?body: JSON.stringify(data)
? ? ? ? ? });
? ? ? ? ? ?const result = await res.json();
? ? ? ? ? ?return result;
? ? ? }
? ? ? ?async put(url, data) {
? ? ? ? ? ?const res = await fetch(url, {
? ? ? ? ? ? ? ?method: 'PUT',
? ? ? ? ? ? ? ?headers: {
? ? ? ? ? ? ? ? ? ?'Content-Type': 'application/json'
? ? ? ? ? ? ? },
? ? ? ? ? ? ? ?data: JSON.stringify(data)
? ? ? ? ? });
? ? ? ? ? ?const result = await res.json();
? ? ? ? ? ?return result;
? ? ? }
? ? ? ?async delete(url, data) {
? ? ? ? ? ?const res = await fetch(url, {
? ? ? ? ? ? ? ?method: 'DELETE',
? ? ? ? ? ? ? ?headers: {
? ? ? ? ? ? ? ? ? ?'Content-Type': 'application/json'
? ? ? ? ? ? ? },
? ? ? ? ? ? ? ?data: JSON.stringify(data)
? ? ? ? ? });
? ? ? ? ? ?const result = await res.json();
? ? ? ? ? ?return result;
? ? ? }
? }
? ?const httpRequestUtil = new HttpRequestUtil();
? ?const res = await httpRequestUtil.get('http://golderbrother.cn/');
? ?console.log(res);
})();
復(fù)制代碼
8. 實(shí)現(xiàn)prototype繼承
所謂的原型鏈繼承就是讓新實(shí)例的原型等于父類的實(shí)例:
//父方法
function SupperFunction(flag1){
? ?this.flag1 = flag1;
}
?
//子方法
function SubFunction(flag2){
? ?this.flag2 = flag2;
}
?
//父實(shí)例
var superInstance = new SupperFunction(true);
?
//子繼承父
SubFunction.prototype = superInstance;
?
//子實(shí)例
var subInstance = new SubFunction(false);
//子調(diào)用自己和父的屬性
subInstance.flag1; ? // true
subInstance.flag2; ? // false
復(fù)制代碼
9. 實(shí)現(xiàn)雙向數(shù)據(jù)綁定
let obj = {}
let input = document.getElementById('input')
let span = document.getElementById('span')
// 數(shù)據(jù)劫持
Object.defineProperty(obj, 'text', {
?configurable: true,
?enumerable: true,
?get() {
? ?console.log('獲取數(shù)據(jù)了')
},
?set(newVal) {
? ?console.log('數(shù)據(jù)更新了')
? ?input.value = newVal
? ?span.innerHTML = newVal
}
})
// 輸入監(jiān)聽
input.addEventListener('keyup', function(e) {
?obj.text = e.target.value
})
復(fù)制代碼
10. 實(shí)現(xiàn)簡單路由
// hash路由
class Route{
?constructor(){
? ?// 路由存儲(chǔ)對(duì)象
? ?this.routes = {}
? ?// 當(dāng)前hash
? ?this.currentHash = ''
? ?// 綁定this,避免監(jiān)聽時(shí)this指向改變
? ?this.freshRoute = this.freshRoute.bind(this)
? ?// 監(jiān)聽
? ?window.addEventListener('load', this.freshRoute, false)
? ?window.addEventListener('hashchange', this.freshRoute, false)
}
?// 存儲(chǔ)
?storeRoute (path, cb) {
? ?this.routes[path] = cb || function () {}
}
?// 更新
?freshRoute () {
? ?this.currentHash = location.hash.slice(1) || '/'
? ?this.routes[this.currentHash]()
}
}
復(fù)制代碼
11. 實(shí)現(xiàn)斐波那契數(shù)列
// 遞歸
function fn (n){
? ?if(n==0) return 0
? ?if(n==1) return 1
? ?return fn(n-2)+fn(n-1)
}
// 優(yōu)化
function fibonacci2(n) {
? ?const arr = [1, 1, 2];
? ?const arrLen = arr.length;
?
? ?if (n <= arrLen) {
? ? ? ?return arr[n];
? }
?
? ?for (let i = arrLen; i < n; i++) {
? ? ? ?arr.push(arr[i - 1] + arr[ i - 2]);
? }
?
? ?return arr[arr.length - 1];
}
// 非遞歸
function fn(n) {
? ?let pre1 = 1;
? ?let pre2 = 1;
? ?let current = 2;
?
? ?if (n <= 2) {
? ? ? ?return current;
? }
?
? ?for (let i = 2; i < n; i++) {
? ? ? ?pre1 = pre2;
? ? ? ?pre2 = current;
? ? ? ?current = pre1 + pre2;
? }
?
? ?return current;
}
復(fù)制代碼
12. 字符串出現(xiàn)的不重復(fù)最長長度
用一個(gè)滑動(dòng)窗口裝沒有重復(fù)的字符,枚舉字符記錄最大值即可。用 map 維護(hù)字符的索引,遇到相同的字符,把左邊界移動(dòng)過去即可。挪動(dòng)的過程中記錄最大長度:
var lengthOfLongestSubstring = function (s) {
? ?let map = new Map();
? ?let i = -1
? ?let res = 0
? ?let n = s.length
? ?for (let j = 0; j < n; j++) {
? ? ? ?if (map.has(s[j])) {
? ? ? ? ? ?i = Math.max(i, map.get(s[j]))
? ? ? }
? ? ? ?res = Math.max(res, j - i)
? ? ? ?map.set(s[j], j)
? }
? ?return res
};
復(fù)制代碼
13. 使用 setTimeout 實(shí)現(xiàn) setInterval
setInterval 的作用是每隔一段指定時(shí)間執(zhí)行一個(gè)函數(shù),但是這個(gè)執(zhí)行不是真的到了時(shí)間立即執(zhí)行,它真正的作用是每隔一段時(shí)間將事件加入事件隊(duì)列中去,只有當(dāng)當(dāng)前的執(zhí)行棧為空的時(shí)候,才能去從事件隊(duì)列中取出事件執(zhí)行。所以可能會(huì)出現(xiàn)這樣的情況,就是當(dāng)前執(zhí)行棧執(zhí)行的時(shí)間很長,導(dǎo)致事件隊(duì)列里邊積累多個(gè)定時(shí)器加入的事件,當(dāng)執(zhí)行棧結(jié)束的時(shí)候,這些事件會(huì)依次執(zhí)行,因此就不能到間隔一段時(shí)間執(zhí)行的效果。
針對(duì) setInterval 的這個(gè)缺點(diǎn),我們可以使用 setTimeout 遞歸調(diào)用來模擬 setInterval,這樣我們就確保了只有一個(gè)事件結(jié)束了,我們才會(huì)觸發(fā)下一個(gè)定時(shí)器事件,這樣解決了 setInterval 的問題。
實(shí)現(xiàn)思路是使用遞歸函數(shù),不斷地去執(zhí)行 setTimeout 從而達(dá)到 setInterval 的效果
function mySetInterval(fn, timeout) {
?// 控制器,控制定時(shí)器是否繼續(xù)執(zhí)行
?var timer = {
? ?flag: true
};
?// 設(shè)置遞歸函數(shù),模擬定時(shí)器執(zhí)行。
?function interval() {
? ?if (timer.flag) {
? ? ?fn();
? ? ?setTimeout(interval, timeout);
? }
}
?// 啟動(dòng)定時(shí)器
?setTimeout(interval, timeout);
?// 返回控制器
?return timer;
}
15. 判斷對(duì)象是否存在循環(huán)引用
循環(huán)引用對(duì)象本來沒有什么問題,但是序列化的時(shí)候就會(huì)發(fā)生問題,比如調(diào)用JSON.stringify()
對(duì)該類對(duì)象進(jìn)行序列化,就會(huì)報(bào)錯(cuò): Converting circular structure to JSON.
下面方法可以用來判斷一個(gè)對(duì)象中是否已存在循環(huán)引用:文章來源:http://www.zghlxwxcb.cn/news/detail-771536.html
const isCycleObject = (obj,parent) => {
? ?const parentArr = parent || [obj];
? ?for(let i in obj) {
? ? ? ?if(typeof obj[i] === 'object') {
? ? ? ? ? ?let flag = false;
? ? ? ? ? ?parentArr.forEach((pObj) => {
? ? ? ? ? ? ? ?if(pObj === obj[i]){
? ? ? ? ? ? ? ? ? ?flag = true;
? ? ? ? ? ? ? }
? ? ? ? ? })
? ? ? ? ? ?if(flag) return true;
? ? ? ? ? ?flag = isCycleObject(obj[i],[...parentArr,obj[i]]);
? ? ? ? ? ?if(flag) return true;
? ? ? }
? }
? ?return false;
}
?
?
const a = 1;
const b = {a};
const c = ;
const o = {d:{a:3},c}
o.c.b.aa = a;
?
console.log(isCycleObject(o)
查找有序二維數(shù)組的目標(biāo)值:
var findNumberIn2DArray = function(matrix, target) {
? ?if (matrix == null || matrix.length == 0) {
? ? ? ?return false;
? }
? ?let row = 0;
? ?let column = matrix[0].length - 1;
? ?while (row < matrix.length && column >= 0) {
? ? ? ?if (matrix[row][column] == target) {
? ? ? ? ? ?return true;
? ? ? } else if (matrix[row][column] > target) {
? ? ? ? ? ?column--;
? ? ? } else {
? ? ? ? ? ?row++;
? ? ? }
? }
? ?return false;
};
二維數(shù)組斜向打?。?span toymoban-style="hidden">文章來源地址http://www.zghlxwxcb.cn/news/detail-771536.html
function printMatrix(arr){
?let m = arr.length, n = arr[0].length
let res = []
?
?// 左上角,從0 到 n - 1 列進(jìn)行打印
?for (let k = 0; k < n; k++) {
? ?for (let i = 0, j = k; i < m && j >= 0; i++, j--) {
? ? ?res.push(arr[i][j]);
? }
}
?
?// 右下角,從1 到 n - 1 行進(jìn)行打印
?for (let k = 1; k < m; k++) {
? ?for (let i = k, j = n - 1; i < m && j >= 0; i++, j--) {
? ? ?res.push(arr[i][j]);
? }
}
?return res
}
到了這里,關(guān)于從零開始:手寫 JavaScript 代碼應(yīng)用于實(shí)際場景的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!