幾道JS代碼手寫面試題


幾道JS代碼手寫面試題

 

(1) 高階段函數實現AOP(面向切面編程)

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

    Function.prototype.after = function (afterfn) {
        let _self = this;
        return function () {
            let set = _self.apply(this, arguments);
            afterfn.apply(this, arguments);
            return set;
        }
    }

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

    func();

 

輸出結果:

 

 ===before===
func
===after===  

 


斐波那契數列

斐波那契數列從第三項開始,每一項都等於前兩項之和。指的是這樣一個數列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144 …

 

得到數列中第n項指向的數值是多少

 

1.遞歸

 

 

 

 

function fib(n) {
  if (n === 1 || n === 2) return n - 1;
  return fib(n - 1) + fib(n - 2)
}
console.log(fib(10)); // 34

 

時間復雜度為O(2^n)

2.非遞歸

function fib(n) {
  let a = 0;
  let b = 1;
  let c = a + b;
  for (let i = 3; i < n; i++) {
    a = b;
    b = c;
    c = a + b;
  }
  return c;
}
console.log(fib(10)); // 34

 

時間復雜度為O(n)

一道關於Array push的JS題

// 類數組
let obj = {
  '1': 'a',
  '2': 'b',
  length: 2
};

Array.prototype.push.call(obj, 'c');

console.log(obj); // { '1': 'a', '2': 'c', length: 3 }

 

push和pop實現

 

Array的push底層實現是依賴於 數組的length屬性的

 

Array.prototype.push2 = function(){
  this.splice.apply(this, [this.length, 0].concat(Array.prototype.slice.apply(arguments)));
  return this.length;
}

 

對於 pop也是一樣

 

Array.prototype.pop2 = function(){
  return this.splice(this.length - 1, 1)[0];
}

算法題

1.在一個數組中 找出里面其中兩項相加后的和為num,如果存在就返回兩個數的索引位置,否則false

 

function fn(num = 0, ary = []) {
  for (let i = 0; i < ary.length; i++) {
    let diff = num - ary[i];
    let diffIndex = ary.indexOf(diff);
    if (diffIndex !== -1) {
      return [i, diffIndex];
    }
  }
  return false;
}

let num = 3;
let arr = [-1, 4, 6, 2];

console.log(fn(num, arr)); // [0, 1]

 

2. 將兩個有序數組合並為一個排好序的大數組

 

function mergeAry(left = [], right = []) {
  const result = [];
  while (left.length && right.length) {
    result.push(left[0] <= right[0] ? left.shift() : right.shift());
  }
  return result.concat(left, right);
}

console.log(mergeAry([1, 2, 3], [1, 4, 8, 9])); // [ 1, 1, 2, 3, 4, 8, 9 ]

 

字符串repeat實現

 

// 原生repeat
'ni'.repeat(3); // 'ninini'

// 實現一
String.prototype.repeatString1 = function (n) {
  return Array(n + 1).join(this);
}
console.log('ni'.repeatString1(3));

// 實現二
String.prototype.repeatString2 = function (n) {
  return Array(n).fill(this).join('');
}
console.log('ni'.repeatString2(3));

 

當我們 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;
}

// 實驗
function M() { // 即將被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);

 

this/bind

 

let obj = { a: 1};
  function fn() {
    this.b = 100;
    return this.a;
  }
  let fe = fn.bind(obj);
  console.log(fe()); // 1  里面this是obj
  console.log(obj); // { a: 1, b: 100 }
  console.log(new fe()); // 里面this是當前創建實例對象 { b: 100 }

 

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

 

一道面試題

 

 

解法一:

 

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) {
        function fe() {
            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 函數柯理化

bind 柯理化

 

function add(a, b, c) {
    return a + b + c;
  }
  let f1 = add.bind(undefined, 100);
  console.log(f1(2, 3)); // 105 = 100 + 2 + 3
  let f2 = f1.bind(undefined, 200);
  console.log(f2(3)); // 303 = 100 + 200 + 3

 

curry 柯理化的實現(遞歸調用 + valueOf)

 

知識點:對象的valueOf瀏覽器環境下 隱式轉化為基本數據類型(一元操作符+object/字符串拼接 ”+object)時,會調用自身的valueOf方法取值,如果自身也存在toString方法 先調用valueOf 如果valueOf返回值不是基本數據類型 則調用toString, toString的返回值也不是基本數據類型就報錯。

 

valueOf調用場景

 

let obj = { x: 1, y: 2 };

obj.toString = function() {
  return this.x + this.y;
}

obj.valueOf = function() {
  return this.x + this.y + 100;
}

// 以下情況下自身valueOf被調用
console.log('' + obj); // 103
console.log(+obj); // 103

 

function fn() {};
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;
    return function bar() {
        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 = [];
        return function fe() {
            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 = [];
            return function(...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;
            return function (...args) {
                let curTime = new Date();
                if (curTime - stattime >= delay) {
                    fn.apply(this, args);
                    stattime = curTime;
                }
            }
        }

函數節流安全版

/**
 * @desc 函數防抖
 * @param func 函數
 * @param wait 延遲執行毫秒數
 * @param immediate true 表立即執行,false 表非立即執行
 */

function debounce(func,wait,immediate) {
    var timeout;

    return function () {
        var context = this;
        var args = arguments;

        if (timeout) clearTimeout(timeout);
        if (immediate) {
            var callNow = !timeout;
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
            if (callNow) func.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
    }
}

防抖動

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

 

let debounce = (fn, time = 50) => { // 防抖動 控制空閑時間 用戶輸入頻繁
            let timer;
            return function (...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 cloneDeep(obj) {
  let type = isType(obj)
  if (type === 'Array' || type === 'Object') {
    return cloneObj(obj)
  } else if (type === 'Date') {
    return obj.constructor(obj)
  } else {
    return obj
  }
}

function cloneObj(obj) {
  let newObj = obj instanceof Array ? [] : {};
  for (let key in obj) {
    newObj[key] = typeof obj[key] === 'object' ? cloneObj(obj[key]) : obj[key]
  }
  return newObj;
}

function isType(o) {
  return /\[object\s(.*?)\]/.exec(Object.prototype.toString.call(o))[1]
}

let fn = function () {
  return 123
}
var a = [[1, 2, 3], [4, 5, 6, 7, fn]];
// let c = new Date();
// var b = cloneDeep(c);
var b = cloneDeep(a);
console.log(b[0], a[0]);
console.log(b[0] === a[0]);

 

原生數據類型檢測簡易封裝

 

Object.prototype.isType = function (type) {
  return function (params) {
    return Object.prototype.toString.call(params) === `[object ${type}]`
  }
}

let isString = Object.isType('String')
let isArray = Object.isType('Array')

console.log(isString(1)) // false
console.log(isString('hello')) // true

console.log(isArray(2)) // false
console.log(isArray(['hello'])) // true

 

Array的reduce實現

 

Array.prototype._reduce = function (callback, initVal) {
  let i = 0
  let result = initVal
  if (typeof initVal === 'undefined') {
    result = this[0]
    i++
  }

  for (i; i < this.length; i++) {
    result = callback(result, this[i])
  }
  return result
}

const arr = [1, 2, 3]
let result = arr._reduce((a, b) => {
  return a + b
}, 0)
console.log(result) // 6

 

Function的bind實現

 

1.es5
    Function.prototype._bind = function(context) {
      let func = this;
      let params = [].slice.call(arguments, 1);
      let Fnop = function() {};
      let fbound = function() {
        params = params.concat([].slice.call(arguments, 0));
        return func.apply(this instanceof Fnop ?
          this : context, params);
      }
      Fnop.prototype = this.prototype;
      fbound.prototype = new Fnop();
      return fbound;
    }

    function foo() {
      this.b = 100;
      return this.a;
    }
    let fe = foo._bind({ a: 1 });
    console.log(fe()); // 1
    console.log(new fe()); // 實例 {b: 100}

2.es6
  Function.prototype.mybind = function(context, ...rest) {
    return (...params) => this.call(context, ...rest, ...params);
  }

 

函數組合串聯compose(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)))

 

redux 源碼中compose實現(https://github.com/reduxjs/redux/blob/master/src/compose.js)

 

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

 

koa-compose 實現

 

function compose(middleware) {
  return function(ctx, next) {
    let index = -1;
    return dispatch(0)
    function dispatch(i) {
      if(i <= index) return Promise.reject(new Error('next() called multiple times'));
      index = i;
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)))
      } catch(e) {
        return Promise.reject(e)
      }
    }
  }
}

let mid1 = (ctx, next) => {
  console.log('ctx1', ctx);
  next()
}

let mid2 = (ctx, next) => {
  console.log('ctx2', ctx);
  next()
}

let mid3 = (ctx, next) => {
  console.log('ctx3', ctx);
}

let co = compose([mid1, mid2, mid3])
co('ctx')

 

co函數

 

function* fn(a = 0) {
  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
});

 

es6簡版 promise

 

class Promise { 
constructor(fn) { 
this.status = ‘Pending’ 
setTimeout(() => { 
fn((data) => { 
this.resolve(data) 
}, (error) => { 
this.reject(error) 
}) 
}) 
}

resolve(data) { 
if (this.status === ‘Pending’) { 
this.success(data) 
this.status = ‘Fulfilled’ 

}

reject(error) { 
if (this.status === ‘Pending’) { 
this.error(error) 
this.status = ‘Rejected’ 

}

then(success, error) { 
this.success = success 
this.error = error 

}

let p1 = new Promise((resolve, reject) => { 
// reject(‘hello error’); 
setTimeout(() => { 
resolve(‘hello promise’) 
}, 1000) 
})

p1.then((data) => console.log(data), (err) => console.log(err))

 

如何主動中止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){ 
return function () { 
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); 
}; 
})();

字符串是否符合回文規則

方法一

 

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]) { 
return false; 


return true; 
}

解構

// 將 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; 
else if (dest > arr[mid]) { // 如果要找的這項比中間項還要大 說明應該在mid中間位置的后面 修改最小邊界值min=mid+1 
min = mid + 1; 
else { 
return mid; 


return -1; // 找不到返回-1 

console.log(binarySearch2(arr, 3)); // 2

 

二分查找題

在一個二維數組中,每一行都按照從左到右遞增,每一列都從上到下遞增的順序排序,完成一個函數,輸入這個二維數組和一個整數,判斷數組中是否含有該整數

 

思路是一樣的,只不過從一維變成了二維,我們遍歷思路可以這樣子:

 

選取第一行的最后一個進行判斷(這個是第一行中最大的)

如果目標大於該值,行數加1,遍歷第二行(因為每列都是遞增的)

如果目標小於該值,則在這一行中進行查找

 

循環以上步驟

 

function findTarget(arr,target) { 
let i = 0; // i代表行 
let j = arr[i].length -1; // j每行中每項索引位置 從最后項開始比較 
while(i < arr.length && j>=0) { 
if(target < arr[i][j]) { 
j–; 
else if (target > arr[i][j]) { 
i++; 
else { 
return 找到了,位置在${i},${j} 


return (${i},${j}) 
}

let arr = [ 
[1,2,3,4], 
[5,9,10,11], 
[13,20,21,23] 
//測試

 

找出數組中重復出現過的元素

 

// 例如:[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 insert(arr, i, x) { 
let prev = i - 1; 
while(prev >= 0 && arr[prev] > x) { 
arr[prev + 1] = arr[prev]; 
prev–; 

arr[prev + 1] = x; 
}

function insert_sort(arr) { 
for (let i = 1; i < arr.length; i++) { 
insert(arr, i, arr[i]); 

return arr; 
}

console.log(insert_sort([1, 10, 3, 0]));

 

選擇排序

 

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

 

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; 
}

 

冒泡排序

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

 

冒泡1

 

function swap(arr, i, j) { 
[arr[i], arr[j]] = [arr[j], arr[i]] 
}

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; 
swap(arr, l, l + 1); 
flag = true; // 如果發生過交換flag則為true 


if (!flag) { // 優化 如果從頭到尾比較一輪后 flag依然為false說明 已經排好序了 沒必要在繼續下去 
temp = null; 
return arr; 


return arr; 
}

 

冒泡2

 

function swap(arr, i, j) { 
[arr[i], arr[j]] = [arr[j], arr[i]] 
}

function bubble_sort(arr) { 
for (let i = arr.length - 1; i >= 1; i–) { 
for (let j = 1; j <= i; j++) { 
arr[j - 1] > arr[j] && swap(arr, j - 1, j) 


return arr; 
}

console.log(bubble_sort([1, 10, 3, 0]));

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM