【前端面試分享】- 寒冬求職下篇

前言

愈來愈多的公司都在面試前加入了筆試環節。javascript

有的甚至會根據你的筆試答題狀況來決定是否進入面試環節。html

固然,進入面試環節,也會時不時的出幾道算法或者其餘類型的相關的題目讓你寫出來。前端

因此不只要會說,還要會寫。java

關於手寫面試題的文章也有好多,實現 call,apply,bind 之類的,也都最好要掌握。python

總結了一些我在面試中遇到的題,這些題不像是基礎部分,會針對特定的公司,也許不必定適合你,因爲面試的級別不高,題目難度屬於中等偏下,因此僅供參考。jquery

若想提升算法思惟,建議多刷刷 leetcodegit

一直在糾結這篇文章該不應分享出來,我的感受此類題目不像是基礎知識,做用不大。可是爲了兌現上篇文章,因此仍是水了這篇。github

筆試篇

1. 替換手機號中間四位

// getPhone('13211223344') => 132****3344
function getPhone(phone) {
    // todo
}
複製代碼

方法有不少,循環遍歷,或者轉換成數組,使用數組的各類 api。面試

固然仍是正則比較簡單。正則表達式

function getPhone(phone) {
    phone = "" + phone;
    const reg = /(\d{3})\d{4}(\d{4})/;
    return phone.replace(reg, "$1****$2")
}
複製代碼

若是學習正則的話,

推薦 老姚 JS正則表達式完整教程

這篇文章很長,需細嚼慢嚥。

2. 拆分數組

/* 將一維數組切分紅二維數組: 按照第二個參數(數字)的值,來決定二維數組長度 */
 let a = ['a', 'b', 'c', 'd'];
 let result = chunk(a, 3);
 console.log(result) => [['a', 'b', 'c'], ['d']];
 let result = chunk(a, 2);
 console.log(result) => [['a', 'b'], ['c','d']];
 let result = chunk(a, 1);
 console.log(result) => [['a'],['b'],['c'],['d']];
複製代碼

提供其中一種解題方法,用數組的 slice 方法。

function chunk(arr, ind) {
    const len = arr.length;
    if(ind > len || ind <= 0) return [];
    const newArr = [];
    for(let i = 0;i < len;i += ind) {
        let j = i;
        newArr.push(arr.slice(j, j += ind));
    }
    return newArr;
}
複製代碼

若是學習數組相關 api 的話,

推薦 OBKoro1 的文章 js 數組詳細操做方法及解析合集

3. 獲取對象中指定 path 的值

// let obj = {foo: {bar: {name:'biz'}}};
// get(obj,'foo.bar.name'); // biz
// obj = {};
// get(obj,'foo.bar.name'); // underfind
// get(obj,'foo.bar.name','biz'); // biz
function get(obj, path, defalutValue) {
    // todo
}
複製代碼

個人解法是利用了遞歸。

function get(obj, path, defalutValue) {
    if (!path || JSON.stringify(obj) === '{}') {
        return defalutValue;
    }
    for(let key in obj) {
        if(!path.split('.').includes(key)) {
            return null
        }
        if(!path.endsWith(key)) {
            return typeof obj[key] === 'object' ? get(obj[key], path, defalutValue) : defalutValue;
        } 
        return obj[key]
    }
}
複製代碼

關於遞歸用法的話,若是不清楚:

推薦 JavaScript算法之遞歸

4. 扁平化指望數組

const data = {
   rows: [
           ["Lisa", 16, "Female", "2000-12-01"], 
           ["Bob", 22, "Male", "1996-01-21"]
        ], 
  metaData: [ 
          { name: "name", note: '' }, 
          { name: "age", note: '' }, 
          { name: "gender", note: '' },
          { name: "birthday", note: '' } 
     ]
}
/* rows 是數據, metaData 是對數據的說明。 現寫⼀個函數 parseData,將上⾯面的對象轉化爲指望的數組 */
outputData = [
 { name: "Lisa", age: 16, gender: "Female", birthday: "2000-12-01" },
 { name: "Bob", age: 22, gender: "Male", birthday: "1996-01-21" }, 
]
複製代碼

使用了一種比較笨拙的方法,循環遍歷

const parseData = (data) => {
    const newData = [];
    const { metaData, rows } = data;
    const mapArr = metaData.map(item => item.name)
   rows.forEach((item,ind) => {
       const obj = {};
       item.forEach((item,ind) => {
            obj[[mapArr[ind]]]=item
       })
       newData.push(obj)
   });
   return newData;
} 
複製代碼

5. 實現一個紅綠燈

結合生活編碼

利用了 promise 加 遞歸實現。

function delay(timer) {
      return () => (
           new Promise(resolve => {
              setTimeout(resolve, timer);
          })
      )
  }
 const red = delay(10000);
 const green = delay(5000);
 const yellow = delay(2000);
 const traffic = document.getElementById('traffic');
 (function resStart(){
    traffic.innerText = '紅';
     red().then(()=> {
        traffic.innerText = '綠';
        return green();
     })
     .then(()=> {
        traffic.innerText = '黃';
        return yellow();
     })
     .then(()=> {
        return resStart();
     })
 })()
複製代碼

其餘解法能夠參考這篇文章:

推薦 FreePotato 的文章 【javascript】一個能夠配置的紅綠燈

6. 實現能夠鏈式調用的延時

/* 按照調用實例,實現下面的Person方法 */
Person("Li");
// 輸出: Hi! This is Li!
Person("Dan").sleep(10).eat("dinner");
// 輸出:
// Hi! This is Dan!
// 等待10秒
// Wake up after 10
// Eat dinner~
Person("Jerry").eat("dinner").eat("supper");
// 輸出:
// Hi This is Jerry!
// Eat dinner~
// Eat supper~
Person("Smith").sleepFirst(5).eat("supper");
// 輸出:
// 等待5秒
// Wake up after 5
// Hi This is Smith!
// Eat supper
複製代碼

鏈式調用能夠閱讀下 jquery 的源碼,在每一個方法的最後 return this 就行了。

1.連接全部 this;

2.把全部任務放到任務隊列裏面;

3.經過一個方法有序執行隊列裏面的任務;

function Person(name) {
    this.task = [];
    const fn = () => {
        console.log(`Hi! This is ${name}!`);
        this.then();
    }
    this.task.push(fn);
    setTimeout(()=>{
        this.then();
    })
    return this;
}
Person.prototype = {
    sleepFirst(timeout) {
        const fn = () => {
            console.log(`Wake up after ${timeout}`)
            setTimeout(() => {
                this.then();
            }, timeout)
        }
        this.task.unshift(fn);
        return this;
    },
    sleep(timeout) {
        const fn = () => {
            console.log(`Wake up after ${timeout}`)
            setTimeout(() => {
                this.then();
            }, timeout)
        }
        this.task.push(fn);
        return this;
    },
    eat(meal) {
        const fn = () => {
            console.log(`Eat ${meal}`)
            this.then();
        }
        this.task.push(fn);
        return this;
    },
    then() {
        const fn = this.task.shift();
        fn && fn();
    }
}
複製代碼

7. 增長隨機值

/* 下面的數據結構中,不一樣層級的key可能會相同。 實現一個方法,調用時更新數組中的key值, 使所用的key對應的值更新爲新的隨機數, 而且保證更新前相同的key值更新後也相同。 */
let arr = [
    {
        key: 123123,
        child: {
            key: 3213313,
            child: {
                key: 123123,
                child: {
                    key: 3213313
                }
            }
        }
    },
    {
        key: 789789,
        child: {
            key: 312308,
            child: {
                key: 123123,
                child: {
                    key: 3213313,
                    child: {
                        key: 873432
                    }
                }
            }
        }
    }
]

function update(arr) {
    // todo
}
複製代碼

基本思路是:

  1. 每一個key是隨機值,那麼都加上同一個隨機值之後,仍是個隨機值。
  2. 遞歸遍歷。
function update(arr) {
    const num = Math.ceil(Math.random()*1000000)/1000000;
    for(let item of arr) {
        item.key += num;
        (function updateKey(obj){
            if(!obj.hasOwnProperty('child')) {
              return obj.key += num;
            }
            obj.child.key += num;
            if(obj.hasOwnProperty('child')) {
              return  updateKey(obj.child);
            }
        })(item)
    }
    return arr;
}
複製代碼

面試官提供的思路是轉換字符串,而後利用正則匹配。若是有大佬寫出來了,歡迎告知。

數據結構篇

1. 說出你所寫代碼的複雜度

首先須要先了解什麼是代碼的複雜度,

複雜度是怎麼計算的。

能夠參考如下兩篇文章,也可自行搜索,相關文章有不少:

JavaScript 算法之複雜度分析

JavaScript 算法之最好、最壞時間複雜度分析

從網上截取了一張關於排序的複雜度。如若侵權,告知刪除。

2. 用堆實現隊列

在瞭解了基本的堆和隊列之後,

這兩種數據結構相互轉換也是須要了解到的。

能夠參考下面的文章,也可自行搜索,相關文章有不少。

JavaScript 數據結構之隊棧互搏

3. 實現鏈表的添加和查找方法

首先得須要瞭解什麼是鏈表,

鏈表和數組有什麼區別。

能夠參考下面的文章,也可自行搜索,相關文章有不少:

JavaScript數據結構之鏈表--介紹

JavaScript數據結構之鏈表--設計

4. 反轉單鏈表

/* 輸入:1 -> 2 -> 3 -> 4 輸出:4 -> 3 -> 2 -> 1 */
複製代碼

設置兩個指針, cur 爲當前節點, pre 爲當前節點的前一個節點,

利用 pre 讓節點反轉所指方向。

reverseList(){
      if(this.head == null) return null;
      let cur = this.head;
      let prev = null;
      while( cur!==null ){
        let nextNode = cur.next
        cur.next = prev
        prev = cur
        cur = nextNode
      }
     this.head = prev
    }
複製代碼

4. 判斷鏈表是否有環

這題方法也有不少,好比說 硬循環,一直 next 下去,直到爲 null 。設置一個時間段,在這個時間內看是否爲空。雖然說不可取,但也是種方法。

還有一種經常使用的辦法就是龜兔賽跑。利用兩個快慢指針,慢指針每次走一格,快指針每次走兩格,當快慢指針相遇的時候,就能夠說明此單鏈表有環。

hasCircle() {
        let fast = this.head;
        let slow = this.head;
        while (fast !== null && fast.next !== null) {
            fast = fast.next.next
            slow = slow.next
            if (slow === fast) return true
        }
        return false
    }
複製代碼

5. 單鏈表排序

/* 輸入:4 -> 1 -> 3 -> 5 輸出:1 -> 3 -> 4 -> 5 */
複製代碼

這裏使用了是快排

// 建立節點
class Node {
    constructor(value) {
        this.val = value;
        this.next = null;
    }
}
// 建立鏈表
class NodeList {
    constructor(arr) {
        let head = new Node(arr.shift());
        let next = head;
        arr.forEach(item => {
            next.next = new Node(item);
            next = next.next;
        })
        return head;
    }
}
// 交換兩個鏈表的值
function swap(p,q) {
     let val = p.val;
     p.val = q.val;
     q.val = val;
}
// 找到基準值
function partion(begin,end) {
    let val = begin.val;
    let p = begin;
    let q = begin.next;
    while(q !== end) {
        if(q.val < val) {
            p = p.next;
            swap(p, q) // 交換小的值
        }
        q = q.next; // 指針後移
    }
    swap(p, begin);// 交換基準值
    return p;
}
// 利用快排遞歸
function sort(begin,end = null) {
    if(begin !== end) {
        let part = partion(begin,end);
        // 兩路快排
        sort(begin,part);
        sort(part.next,end);
    }
    return begin
}
複製代碼

6. 整合兩個單鏈表

/* 輸入:1 -> 2 -> 3 -> 4, 11 -> 21 -> 31 -> 41 輸出:1 -> 2 -> 3 -> 4 -> 11 -> 21 -> 31 -> 41 */
複製代碼

用遞歸的方法將兩個單鏈表整合在一塊兒

function merge(list1, list2) {
        if (list1 == null) return list2;
        else if (list2 == null) return list1;
        let mergehead = null;
        if (list1.val <= list2.val) {
          mergehead = list1;
          mergehead.next = merge(list1.next, list2);
        } else {
          mergehead = list2;
          mergehead.next = this.merge(list1, list2.next);
        }
        return mergehead;
      }
複製代碼

更多關於鏈表的題目,

推薦 尾尾部落 的文章 [算法總結] 17 題搞定 BAT 面試——鏈表題

算法篇

1. 數組去重

老生常談的問題,習慣了使用 api 去重的童鞋要當心了,

有的面試官可能會刁難你,好比說,不使用 api,不能建立新的變量。

推薦 冴羽的文章 - JavaScript專題之數組去重

2. 查找兩數之和

// 給定 nums = [2, 7, 11, 15], target = 9
// 由於 nums[0] + nums[1] = 2 + 7 = 9
// 因此返回 [0, 1]
/** * @param {number[]} nums * @param {number} target * @return {number[]} */
var twoSum = function(nums, target) {
};
複製代碼

此題解法也有不少種,

好比說 暴力解法,兩層循環,時間複雜度較高。

能夠把代碼在 leetcode 運行一次,看看運行效率,而後尋找更優的解法。

下面的解法只是一種思路,不是最優的。

var twoSum = function(nums, target) {
     const obj = {};
     for(let i=0;i<nums.length;i++) {
         const num = nums[i];
         if(obj[num] === undefined) {
             const mins = target - num;
             obj[mins] = i;
         } else {
             return [obj[num], i]
         }
     }
     return [];
};
複製代碼

3. 最大子序和

// 輸入: [-2,1,-3,4,-1,2,1,-5,4],
// 輸出: 6
// 解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6。
/** * @param {number[]} nums * @return {number} */
var maxSubArray = function(nums) {
  }
複製代碼

提供一種解題思路

var maxSubArray = function(nums) {
    let current =  nums[0];
    let  sum =  nums[0];
    for (let i = 1; i < nums.length; i++) {
      if (current > 0) {
        current += nums[i];
      } else {
        current = nums[i];
      }
      if (current > sum) {
        sum = current;
      }
    }
    return sum;
  }
複製代碼

4. 獲得兩個列表的交集

// 輸入: nums1 = [1,2,2,1], nums2 = [2,2]
// 輸出: [2]

/** * @param {number[]} nums1 * @param {number[]} nums2 * @return {number[]} */

  var intersection = function(nums1, nums2) {
  }

複製代碼

兩個數組中查找重複出現的值,且只能出現一次。

var intersection = function(nums1, nums2) {
  const map = {};
  const res = [];
  nums1.forEach((item) => {
    map[item] = 1;
  });

  nums2.forEach((item) => {
    if (map[item] === 1) {
        res.push(item);
        map[item]++;
    }
  });
  return res;
}
複製代碼

5. 快速排序

這也是常考的一道題。

若是你揮毫潑墨寫出了阮老師版本的快排後就要當心了。

此寫法雖然簡單易理解,可是時間複雜度和空間複雜度仍是比較高的,

不適合大數據排序。

優化版本的三路快排,時間複雜度仍是沒有下降。

從性能來講,仍是推薦 分治法

推薦 百度-舞動乾坤的文章JS快速排序&三路快排

或者 張晨輝Allen 的 java 版本文章 快速排序算法原理及實現

6. 更多解法

推薦 我自建羣裏的一位小哥的 github leet-code-solutions

python,go,js 多語言知足你。

提示

水平有限,若是有不妥的回答,還望指出,謝謝。

【前端面試分享】- 寒冬求職上篇

有你才完美

自認很菜,建立了一個數據結構和算法的交流羣,不限開發語言,前端後端,歡迎各位同窗入駐。

羣以超過掃碼限制人數,能夠加我好友,邀請你入羣。

請備註:掘金加羣

相關文章
相關標籤/搜索