JS面試手寫程式碼

草榴社群發表於2018-04-11

Github 程式碼實現案例

JavaScript資料結構與演算法pdf資源

百度網盤 密碼:ll8d

如果不喜歡看pdf可以看下面在網上發現一個部落格講解

資料結構與演算法講解

高階函式實現AOP(面向切面程式設計)

什麼是面向切面程式設計?

    Function.prototype.before = function (beforefn) {
        let _self = this; // 快取原函式的引用
        returnfunction () { // 代理函式
            beforefn.apply(this, arguments); // 執行前置函式
            return _self.apply(this, arguments); // 執行原函式
        }
    }

    Function.prototype.after = function (afterfn) {
        let _self = this;
        returnfunction () {
            letset = _self.apply(this, arguments);
            afterfn.apply(this, arguments);
            returnset;
        }
    }

    let func = () => console.log('func');
    func = func.before(() => {
        console.log('===before===');
    }).after(() => {
        console.log('===after===');
    });

    func();
複製程式碼

輸出結果:

===before===
func
===after===   
複製程式碼

當我們 new 一個類的時候 都發生了什麼

/**
 * new2 new關鍵字的程式碼實現演示
 * @param {function} func 被new的類 (建構函式)
 */
function new2(func) {
    // 建立了一個例項物件 o,並且這個物件__proto__指向func這個類的原型物件 
    let o = Object.create(func.prototype); 
    // (在建構函式中this指向當前例項)讓這個類作為普通函式值行 並且裡面this為例項物件 
    let k = func.call(o);
    // 最後再將例項物件返回 如果你在類中顯示指定返回值k,
    // 注意如果返回的是引用型別則將預設返回的例項物件o替代掉
    return typeof k === 'object' ? k : o;
}

// 實驗
functionM() { // 即將被new的類
    this.name = 'liwenli';
}

let m = new2(M); // 等價於 new M 這裡只是模擬
console.log(m instanceof M); // instanceof 檢測例項
console.log(m instanceof Object);
console.log(m.__proto__.constructor === M);
複製程式碼

Object.create 相容實現

let obj1 = {id: 1};
        Object._create = (o) => {
            let Fn = function() {}; // 臨時的建構函式
            Fn.prototype = o;
            return new Fn;
        }
        
        let obj2 = Object._create(obj1);
        console.log(obj2.__proto__ === obj1); // true
        console.log(obj2.id); // 1

        // 原生的Object.create
        let obj3 = Object.create(obj1);
        console.log(obj3.__proto__ === obj1); // true
        console.log(obj3.id); // 1
複製程式碼

一道面試題

![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="720" height="1280"></svg>)

解法一

function CodingMan(name) { // 主要考察的是 物件導向以及JS執行機制(同步 非同步 任務佇列 事件迴圈)
	function Man(name) {
		setTimeout(() => { // 非同步
			console.log(`Hi! This is ${name}`);
		}, 0);
	}

	Man.prototype.sleep = function(time) {
		let curTime = new Date();
		let delay = time * 1000;
		setTimeout(() => { // 非同步
			while (new Date() - curTime < delay) {} // 阻塞當前主執行緒
			console.log(`Wake up after ${time}`);
		}, 0);
		return this;
	}

	Man.prototype.sleepFirst = function(time) {
		let curTime = new Date();
		let delay = time * 1000;
		while (new Date() - curTime < delay) {} // 阻塞當前主執行緒
		console.log(`Wake up after ${time}`);
		return this;
	}

	Man.prototype.eat = function(food) {
		setTimeout(() => { // 非同步
			console.log(`Eat ${food}~~`);
		}, 0)
		return this;
	}

	return new Man(name);
}

// CodingMan('Peter');
// CodingMan('Peter').sleep(3).eat('dinner');
// CodingMan('Peter').eat('dinner').eat('supper');
// CodingMan('Peter').sleepFirst(5).eat('supper');
複製程式碼

解法二

function CodingMan(name) {
        functionfe() {
            fe.flag = true;
            console.log(`Hi! This is ${name}`);
        }
        fe.flag = false;
        fe.timer = setTimeout(() => {
            clearTimeout(fe.timer);
            if (!fe.flag) fe();
        }, 0);
        return fe;
    }

    Function.prototype.sleep = function (delay) {
        this()
        this.await(delay);
        return this;
    }

    Function.prototype.sleepFirst = function (delay) {
        this.await(delay);
        this();
        return this;
    }

    Function.prototype.eat = function (dinner) {
        setTimeout(() => {
            console.log(`Eat ${dinner}~`);
        });
        return this;
    };

    Function.prototype.await = function (delay) {
        delay = isNaN(delay) ? 0 : delay;
        let startTime = new Date();
        let delayTime = delay * 1000;
        while (new Date() - startTime <= delayTime) {
        }
        console.log(`Wake up after ${delayTime}ms`);
    }
     // CodingMan('peter')
     // CodingMan('peter').sleep(2).eat('hanbao');
     // CodingMan('peter').sleepFirst(2).eat('hanbao');
     CodingMan('peter').eat('haha').eat('hanbao');
複製程式碼

currying 函式柯理化

curry 柯理化的實現(遞迴呼叫 + valueOf)

知識點:valueOf 瀏覽器環境下 當我們以log(fn)這種形式取值時,會隱式呼叫fn自身的valueOf 所以得到的是valueOf的返回值

functionfn() {};
fn.valueOf = () => console.log('valueof');
console.log(fn); // valueof


const mul = x => {
    const result = y => mul(x * y); // 遞迴呼叫mul
    result.valueOf = () => x;
    return result;
}
console.log(mul(2)(3)); // 6

// 在上面mul每執行一次,就會返回一個valueOf被改寫後的新函式result 並且result執行會在裡面呼叫mul(x * y)
// 在result函式的valueOf裡儲存著 由上一次x * y 傳進來的結果x, 也就是上一次x*y 會作為這一次的輸出 依然叫x

// 第一次mul(2) 此時 x為2  return result
result 為 result = y => mul(2 * y); 
// 第二次 mul(2)(3) 等價於 第一個mul返回的result(3), result執行 => mul(2 * 3) 再次呼叫mul 將2*3 = 6 的結果作為mul引數
// 最後mul(6) x = 6 在返回一個新函式result 此時result的valueOf = () => 6

// log(mul(2)(3)) 相當於log的最後返回的result 隱式呼叫valueOf 返回 6
複製程式碼

curry 將多引數函式轉換為接收單一引數的函式

function fe(a, b, c) {
    return a + b + c;
}

function curry(fe) {
    let args = []; // 引數集合
    let len = args.length;
    returnfunctionbar() {
        args = [...args, ...arguments]; // 收集引數
        if (args.length >= fe.length) {
            return fe.apply(this, args);
        }
        return bar;
    }
}

console.log(curry(fe)(1)(2)(3)); // 6
複製程式碼

currying 部分求值

    // currying 函式柯理化
    let currying = function(fn) {
        let args = [];
        returnfunctionfe() {
            if (arguments.length === 0) {
                return fn.apply(this, args);
            }
            [].push.apply(args, arguments);
            return fe;
        }
    }
    let count = currying(function (...rest) {
        return rest.reduce((prev, cur) => prev + cur, 0);
    });

    console.log(count(100)(200)(10)()); // 310
複製程式碼

收集引數 延遲執行 到達指定次數才執行

   // 引數收集 指定次數後執行
        function fn(...rest) {console.log(rest);};
        function after(fn, time = 1) {
            let params = [];
            returnfunction(...rest) {
                params = [...params, ...rest];
                if (--time === 0) {
                    fn.apply(this, params);
                }
            }
        }
        let newFn = after(fn, 3); // 執行3次 內部fn才會執行
        newFn(2);
        newFn(3);
        newFn(4);
複製程式碼

函式節流

throttle 策略的電梯。保證如果電梯第一個人進來後,50毫秒後準時運送一次,不等待。如果沒有人,則待機。

let throttle = (fn, delay = 50) => { // 節流 控制執行間隔時間 防止頻繁觸發 scroll resize mousemove
            let stattime = 0;
            returnfunction (...args) {
                let curTime = new Date();
                if (curTime - stattime >= delay) {
                    fn.apply(this, args);
                    stattime = curTime;
                }
            }
        }
複製程式碼

防抖動

debounce 策略的電梯。如果電梯裡有人進來,等待50毫秒。如果又人進來,50毫秒等待重新計時,直到50毫秒超時,開始運送。

let debounce = (fn, time = 50) => { // 防抖動 控制空閒時間 使用者輸入頻繁
            let timer;
            returnfunction (...args) {
                let that = this;
                clearTimeout(timer);
                timer = setTimeout(fn.bind(that, ...args), time);
            }
        }
複製程式碼

深度拷貝相容寫法(不包括原型屬性)

function deepCopy(obj) {
    if (typeof obj !== 'object') return obj;
    if (typeof window !== 'undefined' && window.JSON) { // 瀏覽器環境下 並支援window.JSON 則使用 JSON
        return JSON.parse(JSON.stringify(obj));
    } else {
        let newObj = obj.constructor === Array ? [] : {};
        for(let key in obj) {
            newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
        }
        return newObj;
    }
}

let obj = {a: 1, b: [12]};
let newObj = deepCopy(obj);
newObj.b[1] = 100;
console.log(obj);
console.log(newObj);
複製程式碼

Function的bind實現

Function.prototype._bind = function(context) {
    let func = this;
    let params = [].slice.call(arguments, 1);
    returnfunction() {
        params = params.concat([].slice.call(arguments, 0));
        func.apply(context, params);
    }
}

let obj = {id: 24}

function fn1(a) {
    console.log(this, arguments);
}
let foo = fn1._bind(obj, obj.id);
複製程式碼

函式組合串聯compose(koa reduce中介軟體)

// 組合串聯
let fn1 = (a) => a + 1;
let fn2 = (b) => b + 2;
let fn3 = (c) => c + 3;

let funs = [fn1, fn2, fn3];

let compose = (func) => {
    return arg => func.reduceRight((composed, fn) => fn(composed), arg);
}
console.log(compose(funs)(100)); // 相當於fn1(fn2(fn3(100)))
複製程式碼

co函式

function* fn(a) {
  a = yield a;
  let b = yield 2;
  let c = yield 3;
  return a + b + c;
}

function co(fn, ...args) {
  let g = fn(...args);
  return new Promise((resolve, reject) => {
    function next(lastValue) {
        let { value, done } = g.next(lastValue);
        if (done) {
          resolve(value);
        } else {
         if (value instanceof Promise) {
           value.then(next, (val) => reject(val));
         } else {
           next(value)
         }
        }
    }
    next();
  });
}
co(fn, 100).then(value => {
    console.log(value); // 105
});
複製程式碼

如何主動中止Promise呼叫鏈

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => { // 非同步操作
      resolve('start')
  }, 1000);
});

p1.then((result) => {
   console.log('a', result); 
   return Promise.reject('中斷後續呼叫'); // 此時rejected的狀態將直接跳到catch裡,剩下的呼叫不會再繼續
}).then(result => {
   console.log('b', result);
}).then(result => {
   console.log('c', result);
}).catch(err => {
   console.log(err);
});

// a start
// 中斷後續呼叫
複製程式碼

bluebird.promisify實現(將非同步回撥函式api 轉換為promise形式)

// promisify.js
module.exports = {
   promisify(fn){
       returnfunction () {
           var args = Array.from(arguments);
           return new Promise(function (resolve, reject) {
               fn.apply(null, args.concat(function (err) {
                   if (err) {
                       reject(err);
                   } else {
                       resolve(arguments[1])
                   }
               }));
           })
       }
   }
}

// main.js
const fs = require('fs');
const {promisify} = require('./promisify.js');

const readFile = promisify('fs.readFile'); // 轉換非同步讀取

// 非同步檔案 由回撥函式形式變成promise形式
readFile('./1.txt', 'utf8').then(data => { 
   console.log(data);
}).catch(err => {
   console.log(err);
});
複製程式碼

window.requestAnimationFrame相容性處理

window._requestAnimationFrame = (function(){
  return  window.requestAnimationFrame       ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame    ||
          function(callback){
            window.setTimeout(callback, 1000 / 60);
          };
})();
複製程式碼

字串是否符合迴文規則

let str = 'My age is 0, 0 si ega ym.';

方法一
function palindrome(params) {
  params = params.replace(/[\W\s_]/ig, '');
 return params.toLowerCase()  === params.split('').reverse().join('').toLowerCase();
}
console.log(palindrome(str));

方法二
function palindrome(params) {
  params = params.replace(/[\W\s_]/ig, '').toLowerCase();
  for (var i = 0, j = params.length-1; i<j; i++, j--) {
    if (params[i] !== params[j]) {
      returnfalse;
    }
  }
  returntrue;
}
複製程式碼

解構

// 將 destructuringArray([1, [2, 3], 4], "[a, [b], c]") => {a: 1, b: 2, c: 4}
const targetArray = [1, [2, 3], 4];
const formater = "[a, [b], c]";

const destructuringArray = (values, keys) => {
  try {
    const obj = {};
    if (typeof keys === 'string') {
      keys = JSON.parse(keys.replace(/\w+/g, '"$&"'));
    }
    
    const iterate = (values, keys) =>
      keys.forEach((key, i) => {
        if(Array.isArray(key)) iterate(values[i], key)
        else obj[key] = values[i]
      })
      
    iterate(values, keys)
    
    return obj;
  } catch (e) {
    console.error(e.message);
  }
}
複製程式碼

陣列展平

將[[1, 2], 3, [[[4], 5]]] 展平為 [1, 2, 3, 4, 5]

let arr = [[1, 2], 3, [[[4], 5]]]; // 陣列展平
function flatten(arr) {
    return [].concat(
        ...arr.map(x => Array.isArray(x) ? flatten(x) : x)
    )
}
複製程式碼

二分查詢

const arr = [1, 2, 3, 4, 5, 6, 7, 8];
// 二分查詢 遞迴 由中間開始往兩邊查詢 前提是有序的陣列 返回對應的索引位置
function binarySearch1(arr, dest, start = 0, end = data.length) {
	if (start > end) {
		return -1
	}
	let midIndex = Math.floor((start + end) / 2); // 中間位置索引
	let mid = arr[midIndex]; // 中間值

	if (mid == dest) {
		return midIndex;
	}
	if (dest < mid) { // 要找的比中間值小 就從中間往開頭找 一直到0
		return binarySearch1(arr, dest, 0, midIndex - 1);
	}
	if (dest > mid) { // 要找的比中間值大 就從中間往後找 一直到end結束
		return binarySearch1(arr, dest, midIndex + 1, end);
	}
	return -1; // 找不到返回-1
}
console.log(binarySearch1(arr, 7, 3, 6)); // 6

// 非遞迴 arr前提有序陣列 (從小到大)返回對應的索引位置 
function binarySearch2(arr, dest) {
	let max = arr.length - 1;
	let min = 0;
	while (min <= max) {
		let mid = Math.floor((max + min) / 2); // mid中間位置索引
		if (dest < arr[mid]) { // 如果要找的這項比中間項還要小 說明應該在mid中間位置前面 修改最大邊界值max=mid-1 
			max = mid - 1;
		} elseif (dest > arr[mid]) { // 如果要找的這項比中間項還要大 說明應該在mid中間位置的後面 修改最小邊界值min=mid+1
			min = mid + 1;
		} else {
			return mid;
		}
	}
	return -1; // 找不到返回-1
}
console.log(binarySearch2(arr, 3)); // 2
複製程式碼

找出陣列中重複出現過的元素

// 例如:[1,2,4,4,3,3,1,5,3]
// 輸出:[1,3,4]
let arr = [1, 2, 4, 4, 3, 3, 1, 5, 3];

// 方法一
function repeat1(arr){
	var result = [], map = {};
	arr.map(function(num){
	if(map[num] === 1) result.push(num); // 等於1說明之前出現過一次 這次重複出現了
		map[num] = (map[num] || 0) + 1; // 微妙之處 開始第一次出現無值 記為 0 + 1 = 1 下一次從1開始累加
	});
	return result;
}
console.log(repeat1(arr));

// 方法二

function repeat(arr) {
    let result = arr.filter((x, i, self) => {
        return self.indexOf(x) === i && self.lastIndexOf(x) !== i
    }); // 
    return result;
}
console.log(repeat(arr));
複製程式碼

陣列中按照數字重複出現的次數進行排序

// 如果次數相同 則按照值排序 比如  2, 2, 2和 1, 1, 1  應排序為 [1, 1, 1, 2, 2, 2]
// 比如 [1,2,1,2,1,3,4,5,4,5,5,2,2] => [3, 4, 4, 1, 1, 1, 5, 5, 5, 2, 2, 2, 2]

let arr = [9, 7, 7, 1, 2, 1, 2, 1, 3, 4, 5, 4, 5, 5, 2, 2];
function sortArray(arr) {
    let obj = {};
    let newArr = [];
    for(let i = 0; i < arr.length; i++) {
      let cur = arr[i];
      if(obj[cur]){
        obj[cur].push(cur);
        continue;
      }
      obj[cur] = [cur];
    }
    for(let k in obj) {
      if(obj.hasOwnProperty(k)) {
        newArr.push(obj[k])
      }
    }
    newArr.sort((a, b) => {
      if(a.length === b.length){
        return a[0] - b[0];
      }
        return a.length - b.length;
    });
    newArr = newArr.reduce((prev, cur) => prev.concat(cur));
    return newArr;
  }
  console.log(sortArray(arr)); // [ 3, 9, 4, 4, 7, 7, 1, 1, 1, 5, 5, 5, 2, 2, 2, 2 ]
複製程式碼

不用迴圈,建立一個長度為 100 的陣列,並且每個元素的值等於它的下標。

// 方法一 遞迴寫法
function createArray(len, arr = []) {

    if (len > 0) {
        arr[--len] = len;
        createArray(len, arr);
    }
    return arr;
}
console.log(createArray(100)); 

// 方法二

// 下面評論中@MaDivH 提供的實現方法 長度為 100 的陣列 
Array(100).fill().map((_,i)=>i+1);

// 方法三
[...Array(100).keys()]
複製程式碼

根據關鍵詞找出 所在物件id

var docs = [
    {
        id: 1,
        words: ['hello', "world"]
    }, {
        id: 2,
        words: ['hello', "hihi"]
    }, {
        id: 3,
        words: ['haha', "hello"]
    }, {
        id: 4,
        words: ['world', "nihao"]
    }
];
findDocList(docs, ['hello']) // 文件id1,文件id2,文件id3
findDocList(docs, ['hello', 'world']) // 文件id1
function findDocList(docs, word = []) {
    if (word.constructor !== Array) return;
    let ids = [];
    for (let i = 0; i < docs.length; i++) {
        let {id, words} = docs[i];
        let flag = word.every((item) => {
            return words.indexOf(item) > -1;
        });
        flag && ids.push(id);
    }
    return ids;
}
findDocList(docs, ['hello', 'world']);
複製程式碼

getElementsByClassName 相容寫法

function getByClass(cName) {
      if ('getElementsByClassName'in this) {
          return this.getElementsByClassName(cName);
      }
      cName = cName.replace(/(^\s+|\s+$)/g, '').split(/\s+/g);
      let eles = this.getElementsByTagName('*');
     for (let i = 0; i < cName.length; i++) {
        let reg = new RegExp(`(^| )${cName[i]}( |$)`);
        let temp = [];
        for (let j = 0; j < eles.length; j++) {
            let cur = eles[j];
            let {className} = cur;
            if (reg.test(className)) {
                temp.push(cur);
            }
        }
        eles = temp;
     }
     return eles;
  }
  console.log(content.getByClass('c1 c2 '));
複製程式碼

插入排序

插入排序 從後往前比較 直到碰到比當前項 還要小的前一項時 將這一項插入到前一項的後面

function insertSort(arr) {
  let len = arr.length;
  let preIndex, current;
  for (let i = 1; i < len; i++) {
    preIndex = i - 1;
    current = arr[i]; // 當前項
    while (preIndex >= 0 && arr[preIndex] > current) {
      arr[preIndex + 1] = arr[preIndex]; // 如果前一項大於當前項 則把前一項往後挪一位
      preIndex-- // 用當前項繼續和前面值進行比較
    }
    arr[preIndex + 1] = current; // 如果前一項小於當前項則 迴圈結束 則將當前項放到 前一項的後面
  }
  return arr;
}
複製程式碼

選擇排序

選擇排序 每次拿當前項與後面其他項進行比較 得到最小值的索引位置 然後把最小值和當前項交換位置

function selectSort(arr) {
  let len = arr.length;
  let temp = null;
  let minIndex = null;
  for (let i = 0; i < len - 1; i++) { // 把當前值的索引作為最小值的索引一次去比較
    minIndex = i; // 假設當前項索引 為最小值索引
    for (let j = i + 1; j < len; j++) { // 當前項後面向一次比小
      if (arr[j] < arr[minIndex]) { // 比假設的值還要小 則保留最小值索引
        minIndex = j; // 找到最小值的索引位置
      }
    }
    // 將當前值和比較出的最小值交換位置
    if (i !== minIndex) {
       temp = arr[i]
       arr[i] = arr[minIndex];
       arr[minIndex] = temp;
    }
  }
  return arr;
}
複製程式碼

氣泡排序

氣泡排序 相鄰兩項進行比較 如果當前值大於後一項 則交換位置

function bubleSort(arr) {
  let length = arr.length;
  let temp = null;
  for (let i = 0; i < length - 1; i++) { // 控制輪數
    let flag = false; // 當前這輪是否交換過標識
    for (let l = 0; l < length - i - 1; l++) { // 控制每輪比較次數
      if (arr[l] > arr[l + 1]) {
        temp = arr[l];
        arr[l] = arr[l + 1];
        arr[l + 1] = temp;
        flag = true; // 如果發生過交換flag則為true
      } 
    }
    if (!flag) { // 優化  如果從頭到尾比較一輪後 flag依然為false說明 已經排好序了 沒必要在繼續下去
      temp = null;
      return arr;
    }
  }
}
複製程式碼

快速排序(遞迴)

function quickSort(arr) {
    if (arr.length <= 1) return arr;
    let midIndex = Math.floor(arr.length / 2);
    let midNum = arr.splice(midIndex, 1)[0];
    let left = [];
    let right = [];
    for(let i = 0; i < arr.length; i++) {
        let cur = arr[i];
        if (cur <= midNum) {
            left.push(cur);
        } else {
            right.push(cur);
        }
    }
    return quickSort(left).concat(midNum, quickSort(right));
}

let arr = [2, 4, 12, 9, 22, 10, 18, 6];
quickSort(arr);
複製程式碼

陣列去重幾種方法

const arr = [1, 2, 1, 2, 3, 4, 2, 1, 3];

// 1 ES6
let newArr = [...new Set(arr)];

// 2
const arr = [1, 2, 1, 2, 3, 4, 'l', 2, 1, 3, 'l'];
const newArr = arr.filter(function(ele, index, array) {
	return index === array.indexOf(ele)
});
console.log(newArr); // [ 1, 2, 3, 4, 'l' ]

// 3
Array.prototype.unique2 = function() {
    let newArr = [];
    let len = this.length;
    for(let i = 0; i < len; i++) {
        let cur = this[i];
        if(newArr.indexOf(cur) === -1) {
            newArr[newArr.length] = cur;
        }
    }
    return newArr;
}
console.log(arr.unique1());

// 4
Array.prototype.unique3 = function() {
    let newArr = this.slice(0);
    let len = this.length;
    let obj = {};
    for(let i = 0; i < len; i++) {
        let cur = newArr[i];
        if(obj[cur]) {
            newArr[i] = newArr[newArr.length - 1];
            newArr.length--;
            i--;
            continue;
        }
        obj[cur] = cur;
    }
    return newArr;
}
console.log(arr.unique3());

// 5
Array.prototype.unique4 = function() {
    let json = {}, newArr = [], len = this.length;
    for(var i = 0; i < len; i++) {
        let cur = this[i];
        if (typeof json[cur] == "undefined") {
            json[cur] = true;
            newArr.push(cur)
        }
    }
    return newArr;
}
console.log(arr.unique4());
複製程式碼

千分符

方法一

// 處理數字
let str1 = 2123456789;
let str2 = 2123456789.12;
console.log(str1.toLocaleString()); // 2,123,456,789
console.log(str2.toLocaleString()); // 2,123,456,789.12
複製程式碼

方法二

    // 處理字串
    let str1 = '2123456789';
    let str2 = '2123456789.12';

    // 利用正向預查 匹配 開頭一個數字\d 後面匹配這個數字後面必須是三個數字為一組為結尾或小數為結尾
    function thousandth(str) { 
        let reg = /\d(?=(?:\d{3})+(?:\.\d+|$))/g; 
        return str.replace(reg, '$&,');
    }
    console.log(thousandth(str1)); // 2,123,456,789
    console.log(thousandth(str2)); // 2,123,456,789.12
複製程式碼

在一個陣列中 如a、b兩項, 要保證a和b兩項的差 與 a和b兩項索引的差 的相加後的結果max 是陣列中其他兩項max 中的最大值 找出符合條件兩項a, b的值 (不可以排序 或改變陣列位置) 如:

let max = (a - b) + (a的索引- b的索引); 求a b

答案:

// 思路:其實也就是找出陣列中當前的每一項與自身索引相加後的和的最大值以及與索引相加後的最小值的和 找出符合條件的兩項即可 如 let result = (maxItem-minItem) + (maxIndex-minIndex) 等價於 (maxItem+maxIndex) - (minItem+minIndex)

// let arr = [1, 2, 3, 4, 5, 6]; // 最簡單的測試陣列 最小項1 最大項6
// 很顯然這個陣列中最大值6與索引相加(6+5)是當中最大值11 1與索引相加(1+0)為當中的最小值1(6 + 5)-(1+0)= 10

// 假設法
let arr = [2, 10, 9, 1, 8, 3, 4];
let minItem = arr[0]; // 假設第一項與自身索引的和是最小值 索引為0因此省略
let maxItem = arr[0]; // 假設第一項與自身索引的和是最大值 索引為0因此省略
let min = minItem; // 最小項
let max = maxItem; // 最大項
let minIndex = 0; // 最小項索引
let maxIndex = 0; // 最大項索引
for(let i = 1; i < arr.length; i++) {
    let cur = arr[i] + i; // 當前項和自身索引的和
    cur < minItem ? (minItem = cur, min = arr[i], minIndex = i) : null;
    cur > maxItem ? (maxItem = cur, max = arr[i], maxIndex = i) : null;
}
console.log(maxItem, minItem); // 最大項與索引的和 最小項與索引的和
console.log(max, min); // 最大項 最小項
console.log(maxIndex, minIndex); // 最大項的索引 最小項的索引
複製程式碼

檢測 字串中括號表示式是否平衡

// 如 balance('[()') = false; balance('[()()()]') = true
// 一
function match(a, b) {
	return (a === '(' && b === ')') || (a === ')' && b === '(') || (a === '[' && b === ']') || (a === ']' && b === '[');
}

function balance(str) {
	if (str.length % 2 === 0) {
		let len = str.length;
		for (let i = 0, j = len - 1; i < len / 2; i++, j--) {
			if (!match(str[i], str[j])) {
				returnfalse;
			}
		}
		returntrue;
	}
	returnfalse;
}
console.log(balance('[()()()]')); // true
console.log(balance('[()')); // false
console.log(balance('[]()')); // false
// 二
function is_balance(str) {
	return [...str].reduce((stack, c) => {
		match(stack[stack.length - 1], c) ?
			stack.pop() : stack.push(c);
		return stack;
	}, []).length === 0;
}
console.log(is_balance('[()()()]')); // true
console.log(is_balance('[()')); // false
console.log(is_balance('[]()')); // false
複製程式碼

求相鄰兩項最大和

// 一
let arr1 = [-1, 3, 1, -5, 2]; // 如 [2, 4, -4, -3] => 4
function sum(arr) {
    let prev = arr[0];
    let sumArr = [];
    let len = arr.length;
    for(let i = 1; i < len; i++) {
        let cur = arr[i];
        sumArr.push(cur + prev);
        prev = cur;
    }   
    return Math.max(...sumArr);
}
console.log(sum(arr1));

// 二
function maxsum(arr) {
    const M = [arr[0]];
    let max = M[0];
    
    for(let i = 1; i < arr.length; i++) {
        M[i] = Math.max(arr[i], M[i - 1] + arr[i]);
        max = Math.max(M[i], max);
    }
    return max;
}
複製程式碼

字串去除相鄰的重複項 如:'aabbccddeexxxxaa' => 'abcdexa'

// 正規表示式
let str = 'aabbccddeexxxxaa';
function uniq1(str) {
    // return str.replace(/([a-z])(\1){1,}/g, '$1');
    return str.replace(/(.)(?=\1)/g, '');
}
console.log(uniq1(str));

// 陣列方式
function uniq2(str) {
    let arr = str.split('');
    let newArr = [arr[0]];
    for(let i = 1; i < arr.length; i++) {
        let cur = arr[i];
        if (cur !== newArr[newArr.length - 1]) {
            newArr.push(cur);
        }
    }
    return newArr.join('');
}
console.log(uniq2(str));
複製程式碼

害怕手寫程式碼 ? 只能硬著頭皮

歡迎大家和我一起來補充 上面很多也都是可以優化的 我還是一個成長中的小白,只是分享和記錄下自己碰到的問題 後續會持續更新...

相關文章