前端面試遇到的算法題

Q: 解析 URL Params 爲對象

儘量的全面正確的解析一個任意 url 的全部參數爲 Object,注意邊界條件的處理。json

let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* 結果 { user: 'anonymous', id: [ 123, 456 ], // 重複出現的 key 要組裝成數組,能被轉成數字的就轉成數字類型 city: '北京', // 中文需解碼 enabled: true, // 未指定值得 key 約定爲 true } */
複製代碼

答:數組

function parseParam(url) {
  const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 將 ? 後面的字符串取出來
  const paramsArr = paramsStr.split('&'); // 將字符串以 & 分割後存到數組中
  let paramsObj = {};
  // 將 params 存到對象中
  paramsArr.forEach(param => {
    if (/=/.test(param)) { // 處理有 value 的參數
      let [key, val] = param.split('='); // 分割 key 和 value
      val = decodeURIComponent(val); // 解碼
      val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判斷是否轉爲數字

      if (paramsObj.hasOwnProperty(key)) { // 若是對象有 key,則添加一個值
        paramsObj[key] = [].concat(paramsObj[key], val);
      } else { // 若是對象沒有這個 key,建立 key 並設置值
        paramsObj[key] = val;
      }
    } else { // 處理沒有 value 的參數
      paramsObj[param] = true;
    }
  })

  return paramsObj;
}
複製代碼

模板渲染

Q: 實現一個簡單的模板引擎:數據結構

let template = '我是{{name}},年齡{{age}},性別{{sex}}';
let data = {
  name: '姓名',
  age: 18
}
render(template, data); // 我是姓名,年齡18,性別undefined
複製代碼

答案:app

  • 簡單實現dom

    function render(template, data) {
      const reg = /\{\{(\w+)\}\}/; // 模板字符串正則
      if (reg.test(template)) { // 判斷模板裏是否有模板字符串
        const name = reg.exec(template)[1]; // 查找當前模板裏第一個模板字符串的字段
        template = template.replace(reg, data[name]); // 將第一個模板字符串渲染
        return render(template, data); // 遞歸的渲染並返回渲染後的結構
      }
      return template; // 若是模板沒有模板字符串直接返回
    }
    複製代碼
  • 一行代碼實現方式函數

    function render(template, data) {
      return template.replace(new RegExp('{{(.*?)}}', 'g'), (match, key) => data[key.trim()]);
    }
    複製代碼
參考

一行代碼實現一個簡單的模板字符串替換ui

Q: 實現一個簡單的虛擬 DOM 渲染this

let domNode = {
  tagName: 'ul',
  props: { class: 'list' },
  children: [{
    tagName: 'li',
    children: ['item1']
  }, {
    tagName: 'li',
    children: ['item1']
  }]
};

// 構建一個 render 函數,將 domNode 對象渲染爲 如下 dom
<ul class="list">
    <li>item1</li>
    <li>item2</li>
</ul>
複製代碼

答案:url

function render(domNode) {
  if (!domNode) return document.createDocumentFragment();
  let $el
  if (typeof domNode === 'object') {
    $el = document.createElement(domNode.tagName);

    if (domNode.hasOwnProperty('props')) {
      for (let key in domNode.props) {
        $el.setAttribute(key, domNode.props[key]);
      }
    }

    if (domNode.hasOwnProperty('children')) {
      domNode.children.forEach(val => {
        const $childEl = render(val);
        $el.appendChild($childEl);
      })
    }
  } else {
    $el = document.createTextNode(domNode);
  }

  return $el;
}
複製代碼

Q: 字符串查找

請使用最基本的遍從來實現判斷字符串 a 是否被包含在字符串 b 中,並返回第一次出現的位置(找不到返回 -1)。spa

例子:

a='34';b='1234567'; // 返回 2
a='35';b='1234567'; // 返回 -1
a='355';b='12354355'; // 返回 5
isContain(a,b);
複製代碼

答案:

function isContain(a, b) {
  for (let i in b) {
    if (a[0] === b[i]) {
      let tmp = true;
      for (let j in a) {
        if (a[j] !== b[~~i + ~~j]) {
          tmp = false;
        }
      }
      if (tmp) {
        return i;
      }
    }
  }
  return -1;
}
複製代碼

Q:將一個任意長的數字變成逗號分割的格式

例子:

// 保留三位小數
parseToMoney(1234.56); // return '1,234.56'
parseToMoney(123456789); // return '123,456,789'
parseToMoney(1087654.321); // return '1,087,654.321'
複製代碼

答:

function parseToMoney(num) {
  num = parseFloat(num.toFixed(3));
  let [integer, decimal] = String.prototype.split.call(num, '.');
  integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,');
  return integer + '.' + (decimal ? decimal : '');
}
複製代碼

Q: 數據綁定最基本的實現

// 實現一個方法,能夠給 obj 全部的屬性添加動態綁定事件,當屬性值發生變化時會觸發事件
let obj = {
  key_1: 1,
  key_2: 2
}
function func(key) {
  console.log(key + ' 的值發生改變:' + this[key]);
}
bindData(obj, func);
obj.key_1 = 2; // 此時自動輸出 "key_1 的值發生改變:2"
obj.key_2 = 1; // 此時自動輸出 "key_2 的值發生改變:1"
複製代碼

答:

function bindData(obj, fn) {
  for (let key in obj) {
    Object.defineProperty(obj, key, {
      set(newVal) {
        if (this.value !== newVal) {
          this.value = newVal;
          fn.call(obj, key);
        }
      },
      get() {
        return this.value;
      }
    })
  }
}
複製代碼

Q: 數據結構處理

有一個祖先樹狀 json 對象,當一我的有一個兒子的時候,其 child 爲其兒子對象,若是有多個兒子,child 爲兒子對象的數組。

請實現一個函數,找出這個家族中全部有多個兒子的人的名字(name),輸出一個數組。

// 樣例數據
let data = {
  name: 'jack',
  child: [
    { name: 'jack1' },
    {
      name: 'jack2',
      child: [{
        name: 'jack2-1',
        child: { name: 'jack2-1-1' }
      }, {
        name: 'jack2-2'
      }]
    },
    {
      name: 'jack3',
      child: { name: 'jack3-1' }
    }
  ]
}
複製代碼

答案:

  • 用遞歸

    function findMultiChildPerson(data) {
      let nameList = [];
    
      function tmp(data) {
        if (data.hasOwnProperty('child')) {
          if (Array.isArray(data.child)) {
            nameList.push(data.name);
            data.child.forEach(child => tmp(child));
          } else {
            tmp(data.child);
          }
        }
      }
      tmp(data);
      return nameList;
    }
    複製代碼
  • 非遞歸

    function findMultiChildPerson(data) {
      let list = [data];
      let nameList = [];
    
      while (list.length > 0) {
        const obj = list.shift();
        if (obj.hasOwnProperty('child')) {
          if (Array.isArray(obj.child)) {
            nameList.push(obj.name);
            list = list.concat(obj.child);
          } else {
            list.push(obj.child);
          }
        }
      }
      return nameList;
    }
    複製代碼
相關文章
相關標籤/搜索