前端面試官問的手寫代碼不是靠背出來的~

前言


咱們在面試的時候,常常會被面試官問到幾個手寫代碼的問題,手寫一個數組去重,可是在手寫的時候仍是會忘記,本文總結了如何應對手寫代碼的過程javascript

1.起名字與具體功能考慮


起名字

手寫函數的時候,我每次給參數或者函數起名字都要猶豫那麼幾秒,面試官還覺得我不會寫呢!java

  • 代碼規範遵照
  • 函數名稱直接使用功能英文命名(這裏你必須知道你要手寫的函數能實現什麼功能)
  • 至於傳遞的參數,還有函數內部聲明的一些數組,變量等,徹底不要考慮太多,能夠直接使用 arr 和 arr+功能名稱 簡寫。

其實直接可使用 att1,att2來代替,能區分開就行了,別在猶豫這種事浪費時間。node

2.新變量與返回值


看完一道手寫面試題,能夠先看下是否須要聲明新的變量,須要返回的結果和結果類型等。es6

  • 數組拍平,確定須要返回一個數組,能夠考慮在初期聲明一個數組的方式,也能夠考慮使用 map,filter等函數直接返回。
  • 對象深拷貝,確定須要返回一個拷貝後的對象,在初期會聲明一個空對象,最後返回這個處理過的對象。
  • 不少狀況能夠聲明新變量或者不聲明變量,都能解決問題,可是就要考慮到時間複雜度和空間複雜度了,先用一種實現就好,面試官會繼續問你有沒有其餘方案,嘿嘿~~

3.考慮使用循環仍是遞歸?


循環

for 循環

  1. for 有三個表達式:①聲明循環變量;②判斷循環條件;③更新循環變量;
  2. for循環的執行特色:先判斷在執行,與 while 相同
  3. for 循環三個表達式均可以有不少部分組成,第二部分多個判斷條件用&& || 鏈接,第一三部分用逗號分隔:
for (var num =1; num<=10; num++) {
    document.write(num+" <br />"); //1 2 3 4 5 6 7 8 9 10 
}
複製代碼

for in 循環

for-in 循環主要用於遍歷對象:面試

  1. for()中的格式爲for(keys in object){}
  2. keys 表示爲 obj對象的每個鍵值對的鍵,全部的循環中,都須要使用obj[keys]來取到每個值!!

for-in 循環,遍歷時不只能讀取對象自身上面的成員屬性,也能延續原型鏈遍歷出對象的原型屬性,因此,可使用 hasOwnProperty 判斷一個屬性是否是對象自身上的屬性,obj.hasOwnProperty(keys) == true 表示這個屬性是對象的成員屬性,而不是原先屬性。json

//聲明一個Peson類
function Person(){
    this.name = "kaola";
    this.age = 24;
    this.func1 = function(){
        
    }
}
//實例化這個類
var bei = new Person();
//使用for-in遍歷這個對象
for(keys in bei){
    console.log(bei[keys])
}
複製代碼

for of 循環

ES6借鑑C++、Java、C# 和 Python 語言,引入了 for...of 循環,做爲遍歷全部數據結構的統一的方法。數組

一個數據結構只要部署了 Symbol.iterator 屬性,就被視爲具備 iterator 接口,就能夠用 for...of 循環遍歷它的成員,也就是說,for...of循環內部調用的是數據結構的 Symbol.iterator 方法。數據結構

for...of 循環可使用的範圍包括數組、Set 和 Map 結構,某些相似數組的對象(好比arguments對象、DOM 對象)、後文的 Generator 對象,以及字符串。dom

遞歸

四、考慮界限


4.1 類型判斷

如何判斷數據類型?怎麼判斷一個值究竟是數組類型仍是對象?koa

三種方式,分別爲 typeof、instanceof 和 object.prototype.tostring.call()

typeof

經過 typeof 操做符來判斷一個值屬於哪一種基本類型。

typeof 'seymoe'    // 'string'
typeof true        // 'boolean'
typeof 10          // 'number'
typeof Symbol()    // 'symbol'
typeof null        // 'object' 沒法斷定是否爲 null
typeof undefined   // 'undefined'

typeof {}           // 'object'
typeof []           // 'object'
typeof(() => {})    // 'function'
複製代碼

上面代碼的輸出結果能夠看出,

  1. null 的斷定有偏差,獲得的結果,若是使用 typeof null 獲得的結果是 object
  2. 操做符對象類型及其子類型,例如函數(可調用對象)、數組(有序索引對象)等進行斷定,則除了函數都會獲得 object 的結果。

在對象的子類型和 null狀況下,能夠看出 typeof 對於判斷類型還有一些不足。

instanceof

經過 instanceof 操做符也能夠對對象類型進行斷定,其原理就是測試構造函數的 prototype 是否出如今被檢測對象的原型鏈上。

[] instanceof Array            // true
({}) instanceof Object         // true
(()=>{}) instanceof Function   // true
複製代碼

複製代碼注意:instanceof 也不是萬能的。

舉個例子:

let arr = []
let obj = {}
arr instanceof Array    // true
arr instanceof Object   // true
obj instanceof Object   // true
obj instanceof Array   // false
複製代碼

在這個例子中,arr 數組至關於 new Array() 出的一個實例,因此 arr.proto === Array.prototype,又由於 Array 屬於 Object 子類型,即 Array.prototype.proto === Object.prototype,所以 Object 構造函數在 arr 的原型鏈上,因此 instanceof 仍然沒法優雅的判斷一個值到底屬於數組仍是普通對象。

還有一點須要說明下,有些開發者會說 object.prototype.proto === null,豈不是說 arr instanceof null 也應該爲 true,這個語句其實會報錯提示右側參數應該爲對象,這也印證 typeof null 結果 object 真的是 javascript 中的一個 bug。

object.prototype.tostring() (推薦款)

能夠說是斷定 Javascript 中數據類型的終極解決方法了,具體用法請看如下代碼:

Object.prototype.toString.call({})              // '[object Object]'
Object.prototype.toString.call([])              // '[object Array]'
Object.prototype.toString.call(() => {})        // '[object Function]'
Object.prototype.toString.call('seymoe')        // '[object String]'
Object.prototype.toString.call(1)               // '[object Number]'
Object.prototype.toString.call(true)            // '[object Boolean]'
Object.prototype.toString.call(Symbol())        // '[object Symbol]'
Object.prototype.toString.call(null)            // '[object Null]'
Object.prototype.toString.call(undefined)       // '[object Undefined]'

Object.prototype.toString.call(new Date())      // '[object Date]'
Object.prototype.toString.call(Math)            // '[object Math]'
Object.prototype.toString.call(new Set())       // '[object Set]'
Object.prototype.toString.call(new WeakSet())   // '[object WeakSet]'
Object.prototype.toString.call(new Map())       // '[object Map]'
Object.prototype.toString.call(new WeakMap())   // '[object WeakMap]'
複製代碼

咱們能夠發現該方法在傳入任何類型的值都能返回對應準確的對象類型。用法雖簡單明瞭,但其中有幾個點須要理解清除:

  • 該方法本質就是依託 Object.prototype.tostring() 方法獲得對象內部屬性 [[Class]]
  • 傳入原始類型卻可以斷定出結果由於對值進行了包裝
  • null 和 undefined 可以輸出結果是內部實現有作處理

對於類型判斷,咱們能夠經過 Object.prototype.toString() 進行了一個簡單的封裝,這樣咱們再判斷類型的時候,直接使用 type 函數就能夠了。

代碼以下:

var type = function(data) {
      var toString = Object.prototype.toString;
      var dataType = toString
              .call(data)
              .replace(/\[object\s(.+)\]/, "$1")
              .toLowerCase()
      return dataType
};
複製代碼

Array.isArray()

Array.isArray 也能夠判斷傳遞參數是否數組。須要注意的是這是 Array.isArray 是 ES5.1 推出的,不支持 IE6~8,因此在使用的時候也應注意兼容問題。

  • 出現不兼容問題解決方法
if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}
複製代碼

4.2 空值判斷

空值判斷的重要性

if([]) {
    console.log('true');
  } else {
    consoel.log('false');
  }
  // res:true
  
  if({}) {
    console.log('true');
  } else {
    consoel.log('false');
  }
  // res:true
複製代碼

看這段代碼,不知道有沒有小夥伴會認爲輸出 false,以上對象和數組雖然爲空,可是會被轉換爲 true,因此寫一些判斷條件的時候要注意。

空數組判斷

數組爲空很簡單,經過上面的類型判斷是數組類型,而後它的 length>0 便可。

空對象判斷

  1. Object.getOwnPropertyNames()

    使用 object.getOwnPropertyNames()。返回值是對象中屬性名造成的數組

var obj = {}
Object.getOwnPropertyNames(obj).length === 0; // true
複製代碼
  1. json 對象轉換爲字符串

    將 json 對象轉換爲字符串,而後比較該字符串與"{}"是否相等

var obj = {};
  var a = JSON.stringify(obj);
  a === "{}"; // true
// a === {}; // false
複製代碼
  1. for...in... 循環判斷
var obj = {};

function judgeObj(obj) {
  for(let key in obj) {
    return false
  }
  return true
};

judgeObj(obj); // true
複製代碼
  1. object.keys()

    使用object.keys()。ES6的新方法,返回值一樣是屬性名組成的數組

var obj = {}
Object.keys(obj).length === 0; // true
複製代碼
  1. 直接使用對象屬性判斷 前提是要肯定若是 obj 不爲空,必定會包含 name 屬性
var obj = {};

obj && obj.name ? '不爲空' : '爲空'; // 
複製代碼

4.3 等號使用

比較過程:

  • 雙等號 == :

(1) 若是兩個值類型相同,再進行三個等號(===)的比較

(2) 若是兩個值類型不一樣,也有可能相等,須要根據如下規則進行類型轉換在比較:

1) 若是是一個null,一個是undefined,那麼相等

2) 若是一個是字符串,一個是數值,把字符串轉換成數值以後再進行比較
複製代碼
  • 三等號(===):

(1) 若是類型不一樣,就不定相等

(2)若是兩個都是數值,而且是同一個值,那麼相等;若是其中至少一個是NaN,那麼不相等。(判斷一個值是不是NaN,只能使用isNaN()來判斷)

(3) 若是兩個都是字符串,每一個位置的字符串都同樣,那麼相等,不然不相等。

(4) 若是兩個值都是true,或是false,那麼相等

(5) 若是兩個值都引用同一個對象或是函數,那麼相等,不然不相等

(6) 若是兩個值都是null,或是undefined,那麼相等

5. 數組 字符串 對象 經常使用函數掌握


5.1數組部分經常使用函數

splice函數(改變原始數組)

  • 有向數組指定位置添加元素的功能

Array.splice(begin,deleteCount,addIteml,addItem2...)

// a的初始值:[1,2,3,4,5]

var b = arr.splice(1,2)
// a: [1,4,5]
// b: [2,3]

var c = arr.splice(1,2,777,888)
// a: [1,777,888,4,5]
// b: [2,3]
複製代碼

slice 函數(不改變原數組,建立新數組)

slice() 方法將數組中一部分元素淺複製存入新的數組對象,而且返回這個數組對象。

語法:arr.slice([start[,end])

參數 start 指定複製開始位置的索引,end 若是有值則表示複製結束位置的索引(不包括此位置)

若是 start 的值爲負數,假如數組長度爲 length,則表示從 length+start 的位置開始複製,此時參數 end 若是有值,只能是比 start 大的負數,不然將返回空數組。

slice 方法參數爲空時,同 concat 方法同樣,都是淺複製生成一個新數組。

var array = ["one", "two", "three","four", "five"];
console.log(array.slice()); // ["one", "two", "three","four", "five"]
console.log(array.slice(2,3)); // ["three"]
複製代碼

淺複製 是指當對象的被複制時,只是複製了對象的引用,指向的依然是同一個對象。下面來講明slice爲何是淺複製。

var array = [{color:"yellow"}, 2, 3];
var array2 = array.slice(0,1);
console.log(array2); // [{color:"yellow"}]
array[0]["color"] = "blue";
console.log(array2); // [{color:"bule"}]
複製代碼

因爲slice是淺複製,複製到的對象只是一個引用,改變原數組array的值,array2也隨之改變。

同時,稍微利用下 slice 方法第一個參數爲負數時的特性,咱們能夠很是方便的拿到數組的最後一項元素,以下:

console.log([1,2,3].slice(-1)); // [3]

join 函數

join() 方法將數組中的全部元素鏈接成一個字符串。

語法 arr.join('xxx')

var b = arr.join(','); // b: '1,2,3'
var b = arr.join('*'); // b: '1*2*3'
複製代碼

push 函數

數組中添加值

concat 函數

concat() 方法將傳入的數組或者元素與原數組合並,組成一個新的數組並返回。

indexOf 函數

indexOf() 方法用於查找元素在數組中第一次出現時的索引,若是沒有,則返回-1。

語法: arr.indexOf(element,fromIndex = 0)

element 爲須要查找的元素。

fromlndex 爲開始查找的位置,缺省默認爲0。若是超出數組長度,則返回-1,若是爲負值,假設數組長度爲length,則從數組的第 length + fromlndex 項開始往數組末尾查找,若是 length + fromlndex<0 則整個數組都會被查找。

indexOf 使用嚴格相等 (即便用 === 去匹配數組中的元素)

var array = ['abc', 'def', 'ghi','123'];
console.log(array.indexOf('def')); // 1
console.log(array.indexOf('def',-1)); // -1 此時表示從最後一個元素日後查找,所以查找失敗返回-1
console.log(array.indexOf('def',-4)); // 1 因爲4大於數組長度,此時將查找整個數組,所以返回1
console.log(array.indexOf(123)); // -1, 因爲是嚴格匹配,所以並不會匹配到字符串'123'
複製代碼

includes 函數

includes() 方法基於ECMAScript 2016 (ES7 規範),它用來判斷當前數組是否包含某個指定的值,若是是,則返回 true,不然返回 false。

element 爲須要查找的元素。

fromlndex 表示從該索引位置開始查找 element,缺省爲0,它是正向查找,即從索引處往數組末尾查找。

var array = [-0, 1, 2];
console.log(array.includes(+0)); // true
console.log(array.includes(1)); // true
console.log(array.includes(2,-4)); // true
複製代碼

以上,includes 彷佛忽略了 -0 與 +0 的區別,這不是問題,由於Javascript一直以來都是不區分 -0 和 +0 的。

你可能會問,既然有了 indexOf 方法,爲何有造一個 includes 方法,arr.indexOf(x)>-1不就等於arr.includex(x)? 看起來的,幾乎全部的時候它們都等同,惟一的區別就是includes 可以發現 NaN,而indexOf不能。

var array = [NaN];
console.log(array.includes(NaN)); // true
console.log(arra.indexOf(NaN)>-1); // false
複製代碼

5.2 字符串經常使用函數

split 函數

  • 把字符串分割成數組
  • 不改變原始字符串
string.split(separator,limit)
複製代碼

substr 函數

substr() 方法返回字符串指定位置開始的指定數量的字符。

語法: str.substr(start[,length])

start 表示開始截取字符的位置,可取正直或者負值,取正直時表示start位置的索引,去負值時表示 length+start 位置的索引。

length 表示截取的字符長度。

var str = "Yesterday is history. Tomorrow is mystery. But today is a gift.";
console.log(str.substr(47)); // today is a gift.
console.log(str.substr(-16)); // today is a gift.
複製代碼

5.3對象的經常使用函數

Object.prototype.hasOwnProperty(prop)

該方法僅在目標屬性爲對象自身屬性時返回true,而當該屬性是從原型鏈中繼承而來或根本不存在時,返回false。

var o = {prop:1};
o.hasOwnProperty('prop'); // true
o.hasOwnProperty('toString'); // false
o.hasOwnProperty('formString'); // false
複製代碼

object.create(obj,descr)(ES5)

該方法主要用於建立一個新對象,併爲其設置原型,用 (上述) 屬性描述符來定義對象的原型屬性。

var parent = {hi: 'Hello'};
var o = Object.create(parent, {
    prop: {
        value: 1
    }
});
o.hi; // 'Hello'
// 得到它的原型
Object.getPrototypeOf(parent) === Object.prototype; // true 說明parent的原型是Object.prototype
Object.getPrototypeOf(o); // {hi: "Hello"} // 說明o的原型是{hi: "Hello"}
o.hasOwnProperty('hi'); // false 說明hi是原型上的
o.hasOwnProperty('prop'); // true 說明prop是原型上的自身上的屬性。
複製代碼

如今,咱們甚至能夠用它來建立一個徹底空白的對象,這樣的事情在ES3中但是作不到的。

var o = Object.create(null);
typeof o.toString(); // 'undefined'
複製代碼

6.經常使用特殊說明


6.1 arguments 相似數組對象

什麼是相似數組對象,舉個例子:

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};
複製代碼

類數組轉換的幾種方式

  • ES6 擴展運算符進行轉換
var arr1 = [...arrayLike]; // ['a','b','c']
複製代碼
  • ES6 中的 Array.from
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
複製代碼

Array.from() 的另外一個應用是,將字符串轉爲數組,而後返回字符串的長度。

function countSymbols(string) {
  return Array.from(string).length;
}
複製代碼
  • ES5中 Array.prototype.slice.call()
function test(a,b,c,d) { 
      var arg = Array.prototype.slice.call(arguments,1); 
      alert(arg); 
   } 
   test("a","b","c","d"); //b,c,d
複製代碼
  1. 第一個參數是 context(就是上下文的意思),用來替換對象函數中的 this
  2. 第二個參數是傳遞給對象函數的參數

Array.prototype.slice.call(arguments)能將具備 length 屬性的對象轉成數組,除了 IE 下的節點集合(由於ie下的dom對象是以com對象的形式實現的,js對象與com對象不能進行轉換)

7.向面試官證實 ES6 也會


讓你手寫代碼的時候,能夠考慮一下使用 ES6 方式如何簡潔實現。(這裏你們能夠去看一下ES6擴展運算符,set集合,箭頭函數等經常使用的ES6知識點,附上ES6阮一峯老師書籍學習地址:es6.ruanyifeng.com/)

8.練習一下


Node.js 中有一個queryString 模塊,能夠實現將urlStr 主機地址後面的參數轉化爲對象。

let urlStr = 'http://www.inode.club?name=koala&study=js&study=node';
複製代碼

轉換結果以下:

{ name: 'koala', study: [ 'js', 'node' ] }
複製代碼

代碼實現

你能夠自已實現一下,看完本篇想一下流程

let urlStr = 'http://www.inode.club?name=koala&study=js&study=node'
// 參數轉成對象
function queryString(request){
    let params = request.split('?')[1];
    let param = params.split('&');
    let obj = {};
    for (let i = 0;i<param.length;i++){
        let paramsA = param[i].split('=');
        let key = paramsA[0];
        let value = paramsA[1];
        if(obj[key]){
            obj[key] = Array.isArray(obj[key])?obj[key]:[obj[key]];
            obj[key].push(value);
        }else{
            obj[key] = value;
        }
    }
    return obj;
}
console.log(queryString(urlStr));
複製代碼

總結


本文是一個應對手寫代碼的大綱,一下函數並無所有列全,按照大綱複習一下,再去看一些常考的手寫代碼問題,我的感受能好記並且清晰了一些,但願對你們有所幫助。

相關文章
相關標籤/搜索