js 常常會被問的 面試題

經常使用函數

深拷貝與淺拷貝

表頭 和原數據是否指向同一對象  第一層數據爲基本數據類型    原數據中包含子對象  
賦值  改變會使原數據一同改變    改變會使原數據一同改變  
淺拷貝  改變不會使原數據一同改變    改變會使原數據一同改變  
深拷貝  改變不會使原數據一同改變   改變不會使原數據一同改變  

淺拷貝實現

function shallowClone(source) {
  if (!source || typeof source !== "object") {
    throw new Error("error arguments");
  }
  // 判斷是數組仍是 對象
  var targetObj = source.constructor === Array ? [] : {};
  for (var keys in source) {
    if (source.hasOwnProperty(keys)) {
      targetObj[keys] = source[keys];
    }
  }
  return targetObj;
}
複製代碼

淺拷貝其餘方法

var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);

initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"
複製代碼

深拷貝實現

若是以爲深拷貝寫的太簡單的,能夠點擊查看lodash 的深拷貝實現,比較詳細javascript

function deepClone(source) {
  if (!source || typeof source !== "object") {
    throw new Error("error arguments");
  }
  var targetObj = source.constructor === Array ? [] : {};
  for (var keys in source) {
    if (source.hasOwnProperty(keys)) {
      if (source[keys] && typeof source[keys] === "object") {
        // 賦予 初始值
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
        
      } else {
        targetObj[keys] = source[keys];
      }
    }
  }
  return targetObj;
}
複製代碼

深拷貝其餘方法

var obj1 = {a:'a'};
var obj2 = JSON.parse(JSON.stringify(obj1));
複製代碼

深拷貝應用場景

當時要把原數據A保存,而後把新數據B遍歷更改指定屬性,最後A和B對比,將B中屬性和A對比判斷提交數據C中push A仍是B。當時數據A嵌套有三層,最後就用了深拷貝解決的。java

防抖節流

防抖(Debounce): 在指定時間A內,連續調用的時間間隔小於A,前面的調用都會被取消,最後一次調用被執行。node

  1. 移動端 上拉刷新
  2. 模糊查詢搜索框的 ajax請求

節流(throttle): 在指定時間A內,該函數只會被調用一次。等待時間A過了函數能夠被再次調用且執行最後一次調用。react

  1. ajax請求的時候,規定時間內設置請求次數,能夠減小ajax請求。
  2. resize或者鼠標移動事件,防止瀏覽器頻繁響應事件,嚴重拉低性能。

防抖(Debounce)

利用延時器就能夠結局git

function debounce(fn, delay) {
  let timer = null;
  return function() {
    let context = this;
    let args = arguments;
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(context, args);
    }, delay);
  };
} 
複製代碼

節流(throttle)

// 時間戳形式表現
function throttle(fn,delay){
  let preDate = new Date();
  return function(){
    const args = arguments;
    const context = this;
    if(preDate + delay <=  new Date()){
      fn.apply(context,args);
      preDate = new Date();
    }
  }
}

// setTimeout表現
function throttle(fn, delay) {
  let timer = null;
  return function() {
    const args = arguments;
    const context = this;
    if (timer) retun;
      timer = setTimeout(() => {
        fn.apply(context, args);
        clearTimeout(timer); 
        timer = null;
      }, delay);
  };
}
複製代碼

模擬實現 call、apply、bind

call: 在使用一個指定的 this 值和若干個指定的參數值的前提下調用某個函數或方法。github

apply: 在使用一個指定的 this 值和一個包含多個參數的數組的前提下調用某個函數或方法。面試

bind: 接收綁定this的對象ajax

call

Function.prototype.call = function(context,...args) {
    var context = context || window;
    context.fn = this;
    const result = context.fn(...args);
    delete context.fn;
    return result
}
複製代碼

apply

Function.prototype.apply = function(context,argsArray) {
    var context = context || window;
    context.fn = this;
    const result = context.fn(...argsArray);
    delete context.fn;
    return result
}
複製代碼

bind

// 這是我本身根據上面改的版本
Function.prototype.bind = function(context, ...args) {
  context.fn = this;
  const bindFn = (...arg) =>{
    const result  = context.fn(...arg);
    delete context.fn;
    return result
  }
  return bindFn;
};

// 這是網上看的版本
Function.prototype.bind2 = function (context) {

    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var context = this;
    var args = Array.prototype.slice.call(arguments, 1);
    var fNOP = function () {};

    var fbound = function () {
        context.apply(this instanceof context ? this : context, args.concat(Array.prototype.slice.call(arguments)));
    }

    fNOP.prototype = this.prototype;
    fbound.prototype = new fNOP();

    return fbound;

}
複製代碼

算法排序

排序常常用在數據處理上,我以爲挺重要的,常常用,都已經耳熟能詳了算法

去重

indexOf

function sort (arr) {
  let result = [];
  arr.forEach((a)=>{
      if(result.indexOf(a) === -1){
        result.push(a)
      }
  })
  return result;
}
複製代碼

sort排序

const  sort = (arr) => arr.sort((a,b)=>a-b).reduce((target,current)=>{
  if(target[target.length - 1] !== current) {
    target.push(current)
  }
  return target;
},[])
複製代碼

Set

var unique = a => [...new Set(a)];
複製代碼

Map

var unique = arr => {
     const seen = new Map();
     return arr.filter(a => !seen.has(a) && seen.set(a,1))
}
複製代碼

冒泡排序

function bubbleSort(arr) {
  var max = arr.length - 1;
  for (let j = 0; j < max; j++) {
    for (let i = 0; i < max - j; i++) {
      if (arr[i] > arr[i + 1]) {
        var temp = arr[i];
        arr[i] = arr[i + 1];
        arr[i + 1] = temp;
      }
    }
  }
  return arr
}
複製代碼

數據結構

點擊進入在線測試數組

鏈表

react fiber 裏的經典問題

建立單向鏈表

//節點應用類型
function Node(data){
    this.data=data;
    this.next=null;
}

//鏈表引用類型
function List(){
    //哨兵節點
    this.head=new Node();
    this.size=0;
}

List.prototype={
    //在鏈表尾部添加節點
    add: function(data){
        var current=this.head;
        while(current.next!=null){
            current=current.next;
        }
        current.next=new Node(data);

        this.size++;
    },

    //遍歷鏈表,不對鏈表元素操做均可以調用此方法
    forEach: function(callback){
        for(var current=this.head.next;current!=null;current=current.next){
            callback(current.data);
        }
    },

    //打印鏈表中全部元素
    print: function(){
        this.forEach(function(item){
            console.log(item);
        })
    },

    //查找鏈表元素的位置
    indexOf: function(data){
        var pos=0;
        var current=this.head.next;
        while(current!=null){
            if(current.data===data){
                break;
            }
            current=current.next;
            pos++;
        }
        return pos;
    },

   /** * 在位置pos處插入節點值爲data * 若成功則返回插入的值,若失敗則返回null */
    insert: function(pos,data){
        if(pos<0 || pos>this.size-1){
            return null;
        }

        //插入位置的上一個節點
        var last=this.head;
        for(var i=0;i<pos;i++){
            last=last.next;
        }
        //保存下一個節點的引用
        var ready=last.next;
        last.next=new Node(data);
        last.next.next=ready;

        this.size++;
        return data;
    },

    /** * 刪除指定位置的元素 * 若成功則返回刪除的值,若失敗則返回null */
    removeAt: function(index){
        if(index<0 || index>this.size-1){
            return null;
        }

        var current=this.head.next;
        var last=this.head;
        for(var i=0;i<index;i++){
            last=current;
            current=current.next;
        }
        last.next=current.next;

        this.size--;
        return current.data;
    },

    //刪除相應元素
    remove: function(data){
        var current=this.head.next;
        var last=this.head;
        while(current!=null){
            if(current.data===data){
                last.next=current.next;
                //已刪除節點
                this.size--;
                break;
            }
            last=current;
            current=current.next;
        }
    }
};


var list=new List();
list.add(1);
list.add(2);
list.add(3);
list.insert(1,2);
console.log(list.indexOf(2));   //2
list.remove(3);
list.removeAt(1);
console.log(list.size);   //2
list.print();   //1 2
複製代碼

二叉樹

在node處理文件操做,diff算法,動態路由 裏常見

深度優先遍歷

function deepTraversal(node) {
	var nodes = [];
	if (node != null) {  
        	nodes.push(node);  
        	var children = node.children;  
        	for (var i = 0; i < children.length; i++)  
           		deepTraversal(children[i]);  
    	}  
	return nodes;
}

複製代碼

廣度優先遍歷

function wideTraversal(node) {
	var nodes = [];
	var i = 0;
	if (!(node == null)) {
		nodes.push(node);
		wideTraversal(node.nextElementSibling);
		node = nodes[i++];
		wideTraversal(node.firstElementChild);
	}
	return nodes;
}
複製代碼

刪除二叉樹某一個的子節點

//刪除最小值
function delMinNode (root){
    if(!root) {
        return false;
    }
    var current = root;
    if (current.left == null) {
        var rightNode = current.right;
        return rightNode;
    }
    current.left = delMinNode(current.left);
    return current.left;
}
//刪除最大值
function delMaxNode (root) {
    if(!root) {
        return false;
    }
    var current = root;
    if(current.right == null) {
        var leftNode = current.left;
        return leftNode;
    }
    current.right = delMaxNode(current.right)
    return current.right;
}
複製代碼

吐槽

其實搞不懂爲何要面試寫這些代碼,真的很煩 !!!
遇到面試要作筆試題的,一般都溜了 !!!
以爲形式主義很煩人!!!

可是迫於淫威 我仍是寫了 (ಥ_ಥ) , 流下了底層人民的辛酸淚。

歡迎觀衆老爺們給我提評論,我能夠作補充,若是有提議,此博文會一直繼續!!

相關文章
相關標籤/搜索