js 100 問

第 1 問 1.toString()

這個問題,先看一個題目,網上不少地方都有javascript

1.toString() // Uncaught SyntaxError: Invalid or unexpected token
1.1.toString() // 1.1
undefined.toString() // VM230:1 Uncaught TypeError: Cannot read property 'toString' of undefined
{}.toString() // Uncaught SyntaxError: Unexpected token 
({}).toString() // [object, object]
null.toString() // VM369:1 Uncaught TypeError: Cannot read property 'toString' of null
[].toString() // ''
[1, 2].toString() // 1,2
(function () {}).toString()// function () {}
'0'.toString() // '0'
複製代碼

主要考點是:html

  • 1.toString() js 整數後面加的第一個點,判斷爲浮點數
  • {}.toString() {} 多是 js 的語句解構,如 if () {}function () {}

第 2 問 async await 優雅處理異步請求異常

const awaitWrap = (promise) => {
	return  promise
	.then(res => [null, res])
	.catch(err => [err, null])
}

const [res, data] = awaitWrap(sendRequest())
if (err) {
	// todo
}
const data = res.body
複製代碼

參考vue

第 3 問 柯里化到底需不須要?

回答這個問題,首先想到js函數柯里化, 用在哪裏?除了面試題出現比較多,真正用的地方比較簡單應用,可是這樣簡單的應用可讓我省去很多代碼?java

首先舉一個例子,計算一個商品打折價格例子:node

// 獲取 1 折商品價格
const getPiceByDiscountOne = function (price) {
    return price * 0.1
}
// 獲取 2 折商品價格
const getPiceByDiscountTwo = function (price) {
    return price * 0.2
}
// 獲取 3 折商品價格
const getPiceByDiscountThree = function (price) {
    return price * 0.3
}
// 獲取 4 折商品價格
const getPiceByDiscountFour = function (price) {
    return price * 0.4
}
// ...
複製代碼

這樣太難看了,用柯里化改進一下jquery

const getDiscountFn = function (discount) {
    return function (price) {
       return price * discount
    }
}
// 獲取 1 折商品價格
const getPiceByDiscountOne = getDiscountFn(0.1)
// 獲取 2 折商品價格
const getPiceByDiscountTwo = getDiscountFn(0.2)
// 獲取 3 折商品價格
const getPiceByDiscountThree = getDiscountFn(0.3)
// 獲取 4 折商品價格
const getPiceByDiscountFour = getDiscountFn(0.4)
// ...
複製代碼

這樣看了,是否是很易讀,你可以爲例子仍是少,還有一個場景,就是表單驗證,這應該是我想到最廣泛的場景。ios

const getValidateFn = function (rgx) {
    return function (str) {
        return rgx.test(str)
    }
}

// 驗證是否數字
valiDateNumber = getValidateFn(/^\d$/)
// 驗證是否字母
validateAz = getValidateFn(/^\w$/)
// 驗證是不是11位數字
valiDateNumbe11r = getValidateFn(/^\d{11}$/)

// todo
複製代碼

彷佛發現一點規律。不妨我先看一下,柯里化定義: 只傳遞給函數一部分參數來調用它,讓它返回一個函數去處理剩下的參數。git

改一下規則:只傳遞給函數相同功能參數來調用它,讓它返回的函數去處理剩下的參數。這就是上述代碼簡化的原則。es6

是否是以爲哪裏有不對呀?咱們看到柯里化都是這樣的—— add(1)(2)(3)...,說句實話,除了面試題和教程,真沒看到代碼又這麼用過。至於函數編程什麼的,那個話題太大,看下面參考。github

大佬,JavaScript 柯里化,瞭解一下?
理解JavaScript的柯里化

第 4 問 談談 instanceof

看到 instanceof,立馬想到兩點;

一、 typeof、instanceof、Object.prototype 的類型檢測 二、instanceof和多全局對象(多個frame或多個window之間的交互)

這兩個算基本常識,不論面試或者項目中,都不會在這個上犯傻。在 instanceof 犯傻,是這類面試題:

Object instanceof Function // true
Object instanceof Object // true
Function instanceof Object // true
Function instanceof Function  // true
Array instanceof Function // true
Array instanceof Array // false
// ...
複製代碼

首先題目主要考的知識點,不是 instanceof ,是 javascript原型鏈。我在 mdn 上查到 instanceof 的定義 :instanceof運算符用於測試構造函數的prototype屬性是否出如今對象(實例)的原型鏈中的任何位

根據定義,能夠得出這定論: instanceof 左邊是對象,右邊是構造函數(一般成爲類)。

稍加理解一下,不就很清楚知道Array instanceof Array 爲何是 false?,左邊的Array是對象(實例),這時,實例的原型鏈 Array.__proto__ => Function.prototype =>Function.prototype.__proto__=>Object.prototype,Array 構造函數的 prototype 屬性就是Array.prototype,一看 Array.prototype 並無出現左邊 Array 的原型鏈中。

參考地址

總結一下:瞭解這一點,對本身業務開發,實際沒有特別做用,徹底解答本身內心的疑問。

第 5 問 數組去重

首先, 數組去重用 es6 最優的解以下:

Array.from(new Set(ary))
複製代碼

[...new Set(ary)]
複製代碼

上面只是應對面試,必須知道 es五、es6 的數組的方法去實現去重。尤爲是迭代方法、splice方法、indexOf方法,迭代方法包含filter、some、every、map、match、find、findIndex等。

splice 方法主要功能,刪除數組某個元素,固然它有不少用法。 indexOf 方法主要檢測數組存在某個元素,爲何通用它了,不用find?主要應爲性能快,能夠從兩個緯度出發,一、語言設計上,find 是一種迭代器,迭代器在遍歷查詢至少找了 n 次,indexOf 查找即返回;二、直接性能測試,比什麼都來的快一些。

迭代方法均可以用來去重,去重只不過有兩種思路:

  • 從數組挑選不一樣元素,也就是建立一個新的數組,添加不一樣數組
  • 將數組相同元素給刪除,保留原數組

說了這麼多,其實,我想說的這種題目很基礎,主要的意義在於理解數組增刪改查,僅此而已,若是要算算法什麼的,那都是後話。

第 6 問 獲取 url 參數,並將轉化爲對象

首先說明,url 參數一般來講,尤爲在協做,建議使用 location.search。不知道對方面是的模塊使用什麼框架,並不能依靠框架的提供api,如 vue-router 的query。

傳統的實現

function getParamsObj (url) {
    ulr = decodeURIComponent(url) // 考慮 decodeURIComponent
    let search = url.split('?')[1] // 考慮 ?
    search = search && search.split('#')[0] // 考慮hash
    const obj = {}
    return search.split('&')
        .map(item => {
            const keyValue = item.split('=') 
            const key = keyValue[0]
            const value = keyValue[1]
            obj[key] = value
        })
}
複製代碼

用 UrlSearchParams 實現

function getParamsObj(url) {
  url = decodeURIComponent(url) // 考慮 decodeURIComponent
  let search = url.split('?')[1] // 考慮 ?
  search = search && search.split('#')[0] // 考慮hash
  search = new URLSearchParams(url)
  const urlObj = {}
  for (let [key, value] of search.entries()) {
    urlObj[key] = value
  }
}
複製代碼

第 7 問 尾調用和尾遞歸

尾調用優化:只保留內層函數的調用幀

尾調用

定義:某個函數的最後一步是調用另外一個函數 形式:function f(x) { return g(x); }

尾遞歸

定義:函數尾調用自身 做用:只要使用尾遞歸就不會發生棧溢出,相對節省內存 實現:把全部用到的內部變量改寫成函數的參數並使用參數默認值

爲何說這?最近在學算法,尾遞歸可實現比較複雜的邏輯,例如求最大公約數:

function getCd (a, b) {
    if (b === 0) {
        return a
    } else {
        return getCd(b, a % b)
    }
}
複製代碼

第 8 問 正則的模式匹配

mdn 正則

leetcode有這麼個題目,驗證字符是否由重複子串組成?

答案很簡單:

const hasRepeatStrCom = (str) => {
  return /^(\w+)\1+$/i.test(str)
}

hasRepeatStrCom('abab') // true
hasRepeatStrCom('ababc') // false
// 隨便說一句,像不少題目考察,最後變成一個純函數,能夠寫單測
複製代碼

這種小的知識點,只要實踐一次,有了印象,最後就變成積累,因此要多實踐。

第 9 問 js 運算符

~~1.1 // 1,取整數最快的方法
^1 // 0 取反
複製代碼

參考地址

第 10 問 ajax 封裝

ajax 的封裝分爲三個步驟

  1. 建立 xhr 的實例,不考慮瀏覽器問題,基本使用 XMLHttpRequest 就能夠了
  2. xhr.onreadystatechange 事件觸發,監聽回調
  3. 執行 xhr 請求步驟
    • xhr.open(), 在此以前,參數編程queryString的格式,參數要進行 encodeURIComponent 編碼
    • xhr.setRequestHeader() 設置請求頭,通常設置 post 請求的 Content-Type: application/x-www-form-urlencoded
    • xhr.send()
(function () {
  // 參數的編碼問題
  const _stringfyParams = function (data) {
    var ary = [];
    for (var i in data) {
      var item = encodeURIComponent(i) + '=' + encodeURIComponent(data[i])
      ary.push(item)
    }
    return ary.join('&')
  }
  // xhr 瀏覽器版本問題
  function _createXHR() {
    if (typeof XMLHttpRequest !== 'undefined') {
      return new XMLHttpRequest();
    } else if (typeof ActiveXObject != 'undefined') {
      var versions = [
        'MSXML2.XMLHttp.6.0',
        'MSXML2.XMLHttp.3.0',
        'MSXML2.XMLHttp'
      ];
      for (var i = 0; i < versions.length; i++) {
        try {
          return newActiveXObject(version[i]);
        } catch (e) {
          //跳過
        }
      }
    } else {
      thrownewError('您的瀏覽器不支持 XHR對象!')
    }
  }

  function $ajax(obj) {
    // 第 1 步 建立一個 XMLHttpRequest 實例
    var xhr = new _createXHR();
    obj.url = obj.url + '?rand=' + Math.random()
    obj.data = _stringfyParams(obj.data)
    if (!('async' in obj)) obj.async = true
    if (obj.method === 'get')  obj.url += obj.url.indexOf('?') === -1 ? '?' + obj.data : '&' + obj.data
    // 第 2 步聲明事件監聽回調
    if (obj.async === true) {
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) _callback()
      }
    }
    // 第 3 步 執行 xhr 發生請求步驟 1. open,2. 設置post的請求頭 3. send
    xhr.open(obj.method, obj.url, obj.async);
    if (obj.method === 'post') {
      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
      xhr.send(obj.data);
    } else {
      xhr.send(null);
    }
    if (obj.async === false) {
      _callback();
    }

    // 回調函數
    function _callback() {
      if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
        obj.success(xhr.responseText)
      } else {
        alert('數據返回失敗!狀態代碼:' + xhr.status + ',狀態信息:' + xhr.statusText)
      }
    }
  }
  window.$ajax = $ajax
})()
複製代碼

測試

document.addEventListener('click', function () {
  $ajax({
    method: 'get',
    url: './get.json',
    data: {
      'name': '無樣'
    },
    success: function (text) {
      console.log(text)
    },
    async: true
  })
}, false)
複製代碼

測試源碼地址

總結:

如今對 ajax 的使用,基本上使用 jquery 的 $.ajax()、axios、fetch的api。

第 11 問 require 和 import 的區別

  1. CommonJS 仍是 ES6 Module 輸出均可以當作是一個具有多個屬性或者方法的對象;
  2. default 是 ES6 Module 所獨有的關鍵字,export default fs 輸出默認的接口對象,import fs from 'fs' 可直接導入這個對象;
  3. ES6 Module 中導入模塊的屬性或者方法是強綁定的,包括基礎類型;而 CommonJS 則是普通的值傳遞或者引用傳遞。

分別運行 commonjs.js 和 es6.js:

// counter.js
exports.count = 0
setTimeout(function () {
  console.log('increase count to', ++exports.count, 'in counter.js after 500ms')
}, 500)

// commonjs.js
const {count} = require('./counter')
setTimeout(function () {
  console.log('read count after 1000ms in commonjs is', count)
}, 1000)

//es6.js
import {count} from './counter'
setTimeout(function () {
  console.log('read count after 1000ms in es6 is', count)
}, 1000)
複製代碼
test node commonjs.js
increase count to 1 in counter.js after 500ms
read count after 1000ms in commonjs is 0
➜  test babel-node es6.js
increase count to 1 in counter.js after 500ms
read count after 1000ms in es6 is 1

複製代碼

參考地址

第 12 問 類型檢測

1. typeof

undefine、string、function、boolean、number 均能被檢測,其餘一概返回 'object'

2. Object.prototype.constructor

  • 不能檢測基本類型(包含null),不然報錯
  • 用於檢測引用類型

3. instanceof

不太靠譜,驗證是 Array 仍是 Object,還要多驗證一次

4. Object.prototype.toString.call(varible)

基本上可以驗證全部類型

5. Array.isArray

只能驗證Array

應用——實現一個深拷貝

function deepCopy(obj){
    if(typeof obj === "object" && obj !== null){
        var result = obj.constructor == Array ? [] : {};
        for(let i in obj){
            result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
        }
    } else {
        var result = obj;
    }
    return result;
}

複製代碼

待續。。。

相關文章
相關標籤/搜索