前端演算法類面試總結(持續更新...)

小圖子發表於2019-02-27

1.字串反轉,即"I am Tony" 變成"Tony am I"

這道題本來是很簡單的,str.split("").reverse().join() 即可,但是題目說明單詞間有可能有多個空格,比如

var s = "I     am Tony";
//這時需要給split函式傳遞正則

s.split(/\s+/)
  .reverse()
  .join(" ");
//"Tony am I"
複製程式碼

2.寫一個 js 指令碼整理檔案

整理前:

111.232.213 ascqwdwd
111.232.213 qwdqwdqw
122.31.34.1 wdojqwodjqwp
232.34.13.3 adhwdhwqhd
複製程式碼

整理後:

'111.232.213': [ 'ascqwdwd', 'qwdqwdqw' ],
'122.31.34.1': [ 'wdojqwodjqwp' ],
'232.34.13.3': [ 'adhwdhwqhd' ]
複製程式碼

解析:讀取檔案,遍歷每行,去掉頭尾空白以後以空格風格,以 js 的 hashmap 儲存

var fs = require("fs");
function processFile(filepath) {
  fs.readFile(filepath, "utf-8", (err, data) => {
    if (err) throw err;
    var arr = data.split("\n");
    var res = {};
    arr.forEach(function(item) {
      var temp = item.trim().split(" ");
      //如果沒有該ip,則初始化該ip陣列
      if (!res[temp[0]]) {
        res[temp[0]] = [];
      }
      res[temp[0]].push(temp[1]);
    });
    console.log(res);
  });
}
processFile("./test.txt");
複製程式碼

3. Javascript 字串模板

需要將字串中所有使用花括號括起來的關鍵詞,同義替換為物件字面量中對應的鍵值; eg: 字串:{text} 物件字面量:{ href:'www.zyy1217.com' , text:'媛媛小窩'}}

解析: 實現一個 render(template, context) 方法,將 template 中的佔位符用 context 填充;要求: 不需要有控制流成分(如 迴圈、條件 等等),只要有變數替換功能即可 級聯的變數也可以展開 被轉義的的分隔符 { 和 } 不應該被渲染,分隔符與變數之間允許有空白字元 程式碼

function render(template, context) {
  //匹配{}和前面的轉義字元
  var reg = /^<(.*?) \{(\w+)\}<\/(\1)>$/;
  var reg = /(\\)?\{([^\{\}])(\\)?\}/;
  template.replace(reg, function(word, slash1, token, slash2) {
    if (slash1 || slash2) {
      return word.replace("\\", "");
    }
    var current = context;
    var variables = token.replace(/\s/g, "").split(".");
    for (var i = 0; i < variables.length; i++) {
      current = current[variables[i]];
      if (!current) return "";
    }
    return current;
  });
}
var template = '<a href="{href}">{text}</a>';
var context = {
  href: "www.zyy1217.com",
  text: "媛媛小窩"
};
render(template, context);
複製程式碼

4. 寫一個求和的函式 sum,達到下面的效果

// Should equal 15
sum(1, 2, 3, 4, 5);
// Should equal 0
sum(5, null, -5);
// Should equal 10
sum(
  "1.0",false,1,true,1,"A",1,"B",1,"C",1,"D",1,"E",1,"F",1,"G",1
);
// Should equal 0.3, not 0.30000000000000004
sum(0.1, 0.2);
複製程式碼

對於小數和小整數的簡單運算可用如下方式,但是,可能會有溢位情況:

預設值

function numAdd(num1 /*:String*/, num2 /*:String*/) {
  var baseNum, baseNum1, baseNum2;
  try {
    baseNum1 = num1.split(".")[1].length;
  } catch (e) {
    baseNum1 = 0;
  }
  try {
    baseNum2 = num2.split(".")[1].length;
  } catch (e) {
    baseNum2 = 0;
  }
  baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
  return (num1 * baseNum + num2 * baseNum) / baseNum;
}
複製程式碼

完整程式碼如下:

function sum() {
  var arr = [].slice.call(arguments);
  return arr.reduce(function(prev, next) {
    if (isNaN(next)) return prev;
    basenum1 = prev.toString().split(".")[1]
      ? prev.toString().split(".")[1].length
      : 0;
    basenum2 = next.toString().split(".")[1]
      ? next.toString().split(".")[1].length
      : 0;

    basenum = Math.pow(10, Math.max(basenum1, basenum2));
    return (prev * basenum + next * basenum) / basenum;
  });
}
複製程式碼

5.js 判斷物件是否為空

假如是 a={},這時候如果用 if(!a) isEmptyObject 的實現,jQuery 中有一個很有想法的方式

function isEmptyObject(obj) {
  for (var key in obj) {
    return false;
  }
  return true;
}
複製程式碼

6. 求 add(1)(2)(3),其中呼叫次數未限定

看到這個就想到了閉包,通過閉包儲存上次相加的結果,以下程式碼實現:

function add(n) {
  var fn = function(x) {
    return add(n + x);
  };
  return fn;
}
複製程式碼

理論上這樣就解決問題了,但在除錯中我們會發現,當函式呼叫完畢後,輸出的結果並不是 sum,而是 add 函式,因為我們每次呼叫後都 return 函式自身;那現在需要解決的問題就是如何讓函式輸出 sum 值;

首先要知道 JavaScript 中,列印和相加計算,會分別呼叫 toString 或 valueOf 函式,所以我們重寫 tmp 的 toString 或者 valueOf 方法,返回 sum 的值;

function add(n) {
  var fn = function(x) {
    return add(n + x);
  };
  fn.valueOf = function() {
    return n;
  };
  return fn;
}
複製程式碼

ok,現在我們通過遞迴返回閉包的方法解決了問題;可現在,如果 add 引數個數不固定,比如要實現 add(2,3)(5), 上面的方法就不適用了; 解決方法如下:

function add() {
  var sum = 0;
  var arr = [].slice.call(arguments);
  sum = arr.reduce(function(prev, next) {
    return prev + next;
  });
  // for ( var index in arguments){
  // 	sum = sum+ arguments[index];
  // }
  var fn = function() {
    // for (var val in arguments){
    // 	sum += arguments[val];
    // }
    var arr = [].slice.call(arguments);
    sum += arr.reduce(function(prev, next) {
      return prev + next;
    });
    return fn;
  };
  fn.toString = function() {
    return sum;
  };
  return fn;
}
複製程式碼

注意:這裡的 arguments 是引數物件,不是陣列;因此如果想要獲得引數有兩種方法

呼叫 Array.slice 將 arguments 轉化為陣列 var arr = [].slice.call(arguments) 使用 for in 遍歷 argumens 物件;這裡要注意,for in 遍歷例項和原型的可列舉屬性;

for (var val in arguments) {
  sum += arguments[val];
}
複製程式碼

7. 求 1000 以內的質數

function prime(n) {
  var arr = [];
  for (var i = 2; i < n; i++) {
    for (var j = 2; j < Math.sqrt(i); j++) {
      if (i % j === 0) {
        break;
      }
    }
    if (j >= Math.sqrt(i)) {
      arr.push(i);
    }
  }
  return arr;
}
複製程式碼

8. 下面的程式碼會輸出什麼?為什麼?

    console.log(1 + "2" + "2");
    console.log(1 + +"2" + "2");
    console.log(1 + -"1" + "2");
    console.log(+"1" + "1" + "2");
    console.log( "A" - "B" + "2");
    console.log( "A" - "B" + 2);
    //輸出什麼,自己去執行吧,需要注意三個點:

    //多個數字和數字字串混合運算時,跟運算元的位置有關
    console.log(2 + 1 + '3'); / /'33'
    console.log('3' + 2 + 1); //'321'

    //數字字串之前存在數字中的正負號(+/-)時,會被轉換成數字
    console.log(typeof '3'); // string
    console.log(typeof +'3'); //number

    //同樣,可以在數字前新增 '',將數字轉為字串
    console.log(typeof 3); // number
    console.log(typeof (''+3)); //string

    //對於運算結果不能轉換成數字的,將返回 NaN
    console.log('a' * 'sd'); //NaN
    console.log('A' - 'B'); // NaN
複製程式碼

9. 給基本型別資料新增屬性,不報錯,但取值時是 undefined

var a = 10;
a.pro = 10;
console.log(a.pro + a);
var s = "hello";
s.pro = "world";
console.log(s.pro + s);
//其中a.pro和s.pro都為undefined

//答案:NaN undefinedhello
複製程式碼

給基本型別資料加屬性不報錯,但是引用的話返回 undefined,10+undefined 返回 NaN,而 undefined 和 string 相加時轉變成了字串;

10. 看下列程式碼,輸出什麼?解釋原因;

var a = null;
alert(typeof a); //object
//解釋:null是一個只有一個值的資料型別,這個值就是null;
//表示一個空指標物件,所以用typeof檢測會返回”object”;
複製程式碼

11. 原生 JS 的 window.onload 與 Jquery 的$(document).ready(function(){})有什麼不同?如何用原生 JS 實現 Jq 的 ready 方法?

window.onload()方法是必須等到頁面內包括圖片的所有元素載入完畢後才能執行; $(document).ready()是 DOM 結構繪製完畢後就執行,不必等到載入完畢;

function ready(fn) {
  if (document.addEventListener) {
    //標準瀏覽器
    document.addEventListener(
      "DOMContentLoaded",
      function() {
        //登出事件, 避免反覆觸發
        document.removeEventListener(
          "DOMContentLoaded",
          arguments.callee,
          false
        );
        fn(); //執行函式
      },
      false
    );
  } else if (document.attachEvent) {
    //IE
    document.attachEvent("onreadystatechange", function() {
      if (document.readyState == "complete") {
        document.detachEvent("onreadystatechange", arguments.callee);
        fn(); //函式執行
      }
    });
  }
}
複製程式碼

12. 判斷一個單詞是否是迴文?

function checkPalindrom(str) {
  return (
    str ==
    str
      .split("")
      .reverse()
      .join("")
  );
}
複製程式碼

13. 不借助臨時變數,進行兩個整數的交換

function swap(a, b) {
  b = b - a;
  a = a + b;
  b = a - b;
  return [a, b];
}

module.exports = swap;
複製程式碼

14. 找出下列陣列的最大差值:

輸入 [10,5,11,7,8,9]; 輸出 6

function maxdiff(arr) {
  var minvalue = arr[0],
    maxprofit = 0;
  for (var i = 0; i < arr.length; i++) {
    minvalue = Math.min(minvalue, arr[i]);
    maxprofit = Math.max(arr[i] - minvalue, maxprofit);
  }
  return maxprofit;
}
複製程式碼

15. 實現類似 getElementsByClassName 的功能

自己實現一個函式,查詢某個 DOM 節點下面的包含某個 class 的所有 DOM 節點?不允許使用原生提供的 getElementsByClassName querySelectorAll 等原生提供 DOM 查詢函式;

function getElementsByClass(oParent, target) {
  var aEle = oParent.getElementsByTagName("*");
  var aResult = [];
  var i = 0;
  for (i = 0; i < aEle.length; i++) {
    if (aEle[i].className.splite(' ').indexOf(target) > -1) {
      aResult.push(aEle[i]);
    }
  }
  return aResult;
}
複製程式碼

16. 獲取 url 中的引數

指定引數名稱,返回該引數的值 或者 空字串 不指定引數名稱,返回全部的引數物件 或者 {} 如果存在多個同名引數,則返回陣列

例子:

getUrlParam('http://www.nowcoder.com?key=1&key=2&key=3&test=4#hehe', 'key');

function getUrlParam(sUrl, sKey) {
  var res,
    param = {};
  sUrl.replace(/[\?&]?(\w+)=(\w+)[&#]/g, function($0, $1, $2) {
    param[$1] = param[$1] === undefined ? $2 : [].concat(param[$1], $2);
  });
  res = sKey === undefined || "" ? param : param[sKey] || "";
  return res;
}
複製程式碼

17.查詢兩個節點的最近公共父節點

oNode1 和 oNode2 在同一文件中,且不會為相同的節點
  • 第一種解法比較簡單,分別從兩個節點開始,沿著 parent 指標走向根節點,得到兩個棧,然後求兩個棧的第一個公共節點;
function commonParentNode(oNode1, oNode2) {
  var nodes1 = [],
    nodes2 = [];
  while (oNode1) {
    nodes1.push(oNode1);
    oNode1 = oNode1.parentNode;
  }
  while (oNode2) {
    nodes2.push(oNode2);
    oNode2 = oNode2.parentNode;
  }
  while ((a = nodes1.pop()) === nodes2.pop()) {
    node = a;
  }

  return node;
}
複製程式碼
  • 第二種方法是藉助 contains 方法,通過 for 迴圈遍歷查詢
function commonParentNode(oNode1, oNode2) {
  if (!oNode1 || !oNode2) {
    return null;
  }
  for (; oNode1; oNode1 = oNode1.parentNode) {
    if (oNode1.contains(oNode2)) {
      return oNode1;
    }
  }
}
複製程式碼

18.根據包名,在指定空間中建立物件

輸入描述:
namespace({a: {test: 1, b: 2}}, 'a.b.c.d')
輸出描述:
{a: {test: 1, b: {c: {d: {}}}}}
複製程式碼

方法一:

function namespace(oNamespace, sPackage) {
  var arr = sPackage.split(".");
  var res = oNamespace; // 保留對原始物件的引用
  for (var i = 0, len = arr.length; i < len; i++) {
    if (arr[i] in oNamespace) {
      // 空間名在物件中
      if (typeof oNamespace[arr[i]] !== "object") {
        // 為原始值
        oNamespace[arr[i]] = {}; // 將此屬性設為空物件
      }
    } else {
      // 空間名不在物件中,建立此空間名屬性,賦值為空
      oNamespace[arr[i]] = {};
    }
    oNamespace = oNamespace[arr[i]]; // 將指標指向下一個空間名屬性;
  }
  return res;
}
複製程式碼

方法二遞迴

function namespace(oNamespace, sPackage) {
  if (sPackage.length <= 0) return;
  var pointer = oNamespace;
  if (sPackage[0] in oNamespace) {
    if (typeof oNamespace[sPackage[0]] !== "object") {
      oNamespace[sPackage[0]] = {};
    }
  } else {
    oNamespace[sPackage[0]] = {};
  }
  oNamespace = oNamespace[sPackage[0]];
  namespace(oNamespace, sPackage.slice(2));
  return pointer;
}
複製程式碼

19. 時間格式化輸出

輸入例子: formatDate(new Date(1409894060000), 'yyyy-MM-dd HH:mm:ss 星期 w')
輸出例子: 2014-09-05 13:14:20 星期五

function formatDate(oDate, sFormation) {
  var obj = {
    yyyy: oDate.getFullYear(),
    yy: ("" + oDate.getFullYear()).slice(-2),
    MM: ("0" + (oDate.getMonth() + 1)).slice(-2),
    M: oDate.getMonth() + 1,
    dd: ("0" + oDate.getDate()).slice(-2),
    d: oDate.getDate(),
    HH: ("0" + oDate.getHours()).slice(-2),
    H: oDate.getHours(),
    hh: ("0" + (oDate.getHours() % 12)).slice(-2),
    h: oDate.getHours() % 12,
    mm: ("0" + oDate.getMinutes()).slice(-2),
    m: oDate.getMinutes(),
    ss: ("0" + oDate.getSeconds()).slice(-2),
    s: oDate.getSeconds(),
    w: ["日", "一", "二", "三", "四", "五", "六"][oDate.getDay()]
  };
  return sFormation.replace(/([a-z]+)/gi, function($1) {
    return obj[$1];
  });
}
複製程式碼

20. 獲取字串的長度

如果第二個引數 bUnicode255For1 === true,則所有字元長度為 1 否則如果字元 Unicode 編碼 > 255 則長度為 2

//unicode編碼大於255的字元可以通過
s.charCodeAt(i) > 255;

或者;

s.match(/[\u0256-\uffff]/g);

function strLength(s, bUnicode255For1) {
  //如果bUnicode255For1為false,返回s的長度加正則匹配\u0256-\uffff的長度
  return (
    s.length +
    (bUnicode255For1 ? 0 : (s.match(/[\u0256-\uffff]/g) || []).length)
  );
}
複製程式碼

###21. 顏色字串轉換

將 rgb 顏色字串轉換為十六進位制的形式,如 rgb(255, 255, 255) 轉為 #ffffff rgb 中每個 , 後面的空格數量不固定 十六進位制表示式使用六位小寫字母 如果輸入不符合 rgb 格式,返回原始輸入

輸入例子: rgb2hex('rgb(255, 255, 255)') 輸出例子: #ffffff

利用 Number 物件的 toString 方法,toString() 方法以指定的基數返回該物件的字串表示.

function rgb2hex(sRGB) {
  var res = [];
  var reg = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/g;
  if (!reg.test(sRGB)) {
    return sRGB;
  } else {
    var reg2 = /(\d+)/g;
    sRGB.replace(reg2, function($0) {
      res.push(("0" + (+$0).toString(16)).slice(-2));
    });
  }
  return "#" + res.join("");
}
複製程式碼

22.將字串轉換為駝峰形式

css 中經常有類似 background-image 這種通過 - 連線的字元,通過 javascript 設定樣式的時候需要將這種樣式轉換成 backgroundImage 駝峰格式,請完成此轉換功能

以 - 為分隔符,將第二個起的非空單詞首字母轉為大寫 -webkit-border-image 轉換後的結果為 webkitBorderImage 輸入例子:

cssStyle2DomStyle('font-size') 輸出例子:

fontSize 使用 String.replace 方法,注意這裡 replace 的函式包含三個引數,第一個是匹配到的字串,第二個和第三個 slash 為子匹配;而 js 傳參是值傳遞,因此通過 word, slash1, slash2 只能獲得值,對匹配到的值進行修改之後,一定記得要 return 回去;

function cssStyle2DomStyle(sName) {
  var reg = /(\w)?-+(.)?/g;
  return sName.replace(reg, function(word, slash1, slash2) {
    if (!slash1) return slash2;
    else {
      return slash2 ? slash1 + slash2.toUpperCase() : slash1 + "";
    }
  });
}
複製程式碼

24. 將數字轉為大寫

編寫一個函式 fn(Number n),將數字轉為大寫輸出;

輸入 123 輸出 一百二十三 解題思路: 首先設定大寫度量單位'千百十億千百十萬千百十個' 將數字轉為大寫同時加上度量單位 eg:二千零五十四億零二百萬二千一百零三 將零(千|百|十)替換成零,將多個零替換成 1 個零,將零(億|萬)替換成(億|萬);

function fn(n) {
  if (isNaN(n)) {
    return "非法資料";
  }
  var unit = "千百十億千百十萬千百十個";
  if (n.length > unit.length) {
    return "資料過長";
  }
  var newStr = "";
  var nlength = n.length;
  unit = unit.substr(unit.length - nlength);
  for (var i = 0; i < nlength; i++) {
    newStr += "零一二三四五六七八九".charAt(n[i]) + unit.charAt(i);
  }
  newStr = newStr.substr(0, newStr.length - 1);
  console.log(newStr);
  newStr = newStr
    .replace(/零(千|百|十)/g, "零")
    .replace(/(零)+/g, "零")
    .replace(/零(億|萬)/g, "$1");
  return newStr;
}
複製程式碼

25.js 陣列去重

1.最簡單的方法(es6)

ES6 允許按照一定模式,從陣列和物件中提取值,對變數進行賦值,這被稱為解構(Destructuring);

以前,為變數賦值,只能直接指定值;

function unique(arr) {
  const seen = new Map();
  return arr.filter(a => !seen.has(a) && seen.set(a, 1));
}
// or
function unique(arr) {
  return Array.from(new Set(arr));
}
複製程式碼

2.如果是已知排序的 array,或者不在乎去重之後的結果順序; 可以做一次迴圈,判斷當前的 item 是否等於前面那個 item,如果不等於或不存在前面的 item,就 push 到 result 中;(這個很快)

Array.prototype.uniq = function() {
  if (!this.length || this.length === 0) {
    return this;
  }
  this.sort();
  var res = [this[0]];
  for (var i = 1, len = this.length; i < len; i++) {
    if (this[i] !== this[i - 1]) {
      res.push(this[i]);
    }
  }
  return res;
};
複製程式碼

3.或者採用兩個指標 l 和 r,l 記錄不重複元素的位置,r 從 l 的下一個開始遍歷陣列,如果 r 位置的數字等於 l 位置的數字,說明該數字重複出現,不予處理;如果 r 位置的數字不等於 l 位置的數字,說明該數字沒有重複,需要放到 l 的下一位置,並使 l 加 1;

時間複雜度:O(n)

空間複雜度:O(1)

Array.prototype.uniq = function() {
  if (!this.length || this.length === 0) {
    return this;
  }
  this.sort();
  var left = 0,
    right;
  for (right = left + 1, len = this.length; right < len; right++) {
    if (this[left] !== this[right]) {
      this[++left] = this[right];
    }
  }
  return this.slice(0, left);
};
複製程式碼

4.如果順序雜亂,可以做一次迴圈,用一個物件標記該 item 是否存在,如果不存在,就 push 到 result 中;這裡使用一個 hashtable 的結構記錄已有的元素,這樣就可以避免內層迴圈;不過,這樣的話,假如陣列非常龐大,效能會差;

if (!Array.prototype.unique) {
  Array.prototype.unique = function() {
    var hash = {},
      result = [],
      item;
    for (var i = 0; i < this.length; i++) {
      item = this[i];
      var key = Object.prototype.toString.call(item) + item;
      if (!hash[key]) {
        hash[key] = true;
        result.push(item);
      }
    }
    return result;
  };
}
複製程式碼

注意 js 中 hash 的鍵值是以字元儲存的,所以這裡將陣列元素作為 hash 索引時需要加上型別,否則無法區分數字 1 和字元 1;
如果陣列中存在 function,直接判斷是否相等是不行的,可以 toString()一下,再進行比較;

如果碰到 Object,就繼續做迴圈;

遍歷陣列,建立新陣列,利用 indexOf 判斷是否存在於新陣列中,不存在則 push 到新陣列,最後返回新陣列

時間複雜度 o(n^2)

Array.prototype.indexOf =
  Array.prototype.indexOf ||
  function(item) {
    for (var i = 0, j = this.length; i < j; i++) {
      if (this[i] === item) {
        return i;
      }
    }
    return -1;
  };
function removeDuplicatedItem(ar) {
  var ret = [];
  for (var i = 0, j = ar.length; i < j; i++) {
    if (ret.indexOf(ar[i]) === -1) {
      ret.push(ar[i]);
    }
  }
  return ret;
}
複製程式碼

在 JavaScript 裡使用 typeof 來判斷資料型別,只能區分基本型別,即 "number","string","undefined","boolean","object"五種;

對於陣列、函式、物件來說,其關係錯綜複雜,使用 typeof 都會統一返回 "object"字串;

要想區別物件、陣列、函式單純使用 typeof 是不行的,JavaScript 中,通過 Object.prototype.toString 方法,判斷某個物件值屬於哪種內建型別;

26.陣列各種排序演算法

1.插入排序 思路: 外層迴圈從第二個開始迴圈,陣列[2,1,5,4,9,8];
第一輪 第二個和第一個,如果第二個小於第一個交換位置;[1,2,5,4,9,8];
第二輪 第三個和第二個比,如果第二個小於第一個交換位置;[1,2,5,4,9,8];
第三輪 第四個和第三個比,如果第二個小於第一個交換位置;[1,2,4,5,9,8];
以此類推:外層迴圈完就能排出大小;

function insert(arr) {
    var s;
    for (var i = 1; i < arr.length; i++) {
        for (var j = i; j > 0; j--) {
        if (arr[j] < arr[j - 1]) {
            s=arr[j];
            arr[j]=arr[j-1]
            arr[j-1]=s
            console.log(arr)   //可以列印出來每一個改變的步驟
        }
        }
    }
    return arr
}
console.log(insert([3,3,4,2,1,4,4,3,7,8]))
複製程式碼

2.氣泡排序

程式碼演示:

    function bubble(arr) {
        var s
        for (var i =0;i<arr.length;i++) {
            for (var j = 0; j < arr.length; j++) {
                if (arr[j] > arr[j + 1]) {
                    s = arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=s;
                }
            }
        }
        return arr
    }
    console.log(bubble([1,2,3,4,5,7,8]));
    
複製程式碼

3.選擇排序

先找出陣列中的最小項,放入新陣列,同時確定這個最小項的索引,根據這個索引,當前陣列刪除這個最小項 以上的操作,只能進行一次,所以我們在外面加一層的迴圈,讓他執行多次,次數就是陣列的長度

程式碼演示:

    function select(arr) {
        var result = []
        for (var i = 0, len = arr.length; i < len; i++) {
            var min = Math.min.apply(null, arr)
            result.push(min)
            arr.forEach(function(item, index) {
                if (item == min) {
                    arr.splice(index, 1)
                }
            })
        }
        return result
    }
    var arr = [3, 2, 5, 4, 8, 7, 1]]
    var res = select(arr)
    console.log(res)
複製程式碼

27.為什麼 ["1", "2", "3"].map(parseInt) 返回 [1,NaN,NaN]?

parseInt() 函式

parseInt(string, radix)
string 必需要被解析的字串.
radix 可選。表示要解析的數字的基數。該介於 2 ~ 36 之間。

返回值

返回解析後的數字。

map 方法

對陣列的每個元素呼叫定義的回撥函式並返回包含結果的陣列.
array1.map(callbackfn[, thisArg])
array1 必需。一個陣列物件。
callbackfn 必需。一個接受最多三個引數的函式。對於陣列中的每個元素,‘map‘ 方法都會呼叫 ‘callbackfn‘ 函式一次。
thisArg 可選。可在 ‘callbackfn‘ 函式中為其引用 ‘this‘ 關鍵字的物件。如果省略 ‘thisArg‘,則 ‘undefined‘ 將用作 ‘this‘ 值。

返回值

其中的每個元素均為關聯的原始陣列元素的回撥函式返回值的新陣列

異常

如果 callbackfn 引數不是函式物件,則將引發 TypeError 異常。 備註

對於陣列中的每個元素,map 方法都會呼叫 callbackfn 函式一次(採用升序索引順序)。 不為陣列中缺少的元素呼叫該回撥函式。

除了陣列物件之外,map 方法可由具有 length 屬性且具有已按數字編制索引的屬性名的任何物件使用

返回[1,NaN,NaN]原因

我們從新定義一下parseInt

var parseInt = function(string, radix) {
    return string + "-" + radix;
};
 
["1", "2", "3"].map(parseInt);
//輸出結果為:
["1-0", "2-1", "3-2"]
 
//在加一個引數 

var parseInt = function(string, radix, obj) {
    return string + "-" + radix + "-" + obj;
};
 
["1", "2", "3"].map(parseInt);
//輸出結果:

["1-0-1,2,3", "2-1-1,2,3", "3-2-1,2,3"];

//我們再繼續增加引數:

var parseInt = function(string, radix, obj, other) {
    return string + "-" + radix + "-" + obj + "-" + other;
};
 
["1", "2", "3"].map(parseInt);

複製程式碼

結論:["1", "2", "3"].map(parseInt) 應該對應的是: [parseInt("1", 0), parseInt("2", 1), parseInt("3", 2)] parseInt("3", 2) 的第二個引數是界於 2-36 之間的,之所以返回 NaN 是因為 字串 "3" 裡面沒有合法的二進位制數,所以 NaN

28.JS的forEach和map方法的區別

1、語法:

//forEach
array.forEach(callback(currentValue, index, array){
    //do something
}, this)
 
//或者
array.forEach(callback(currentValue, index, array){
    //do something
})&emsp;&emsp;
 
//map:
var new_array = arr.map(callback[, thisArg])&emsp;
 
//$.each()
$(selector).each(function(index,element))
複製程式碼

區別

1.forEach()返回值是undefined,不可以鏈式呼叫。

2.map()返回一個新陣列,原陣列不會改變。

3.沒有辦法終止或者跳出forEach()迴圈,除非丟擲異常,所以想執行一個陣列是否滿足什麼條件,返回布林值,可以用一般的for迴圈實現,或者用Array.every()或者Array.some();

4.$.each()方法規定為每個匹配元素規定執行的函式,可以返回 false 可用於及早停止迴圈。

迴圈方法效能排行

for迴圈遍歷 < for...of遍歷 < forEach遍歷 < for...in遍歷 < map遍歷

29.Array的push與unshift方法效能比較分析

Array的push與unshift方法都能給當前陣列新增元素,不同的是,push是在末尾新增,而unshift則是在開頭新增。 從原理就可以知道,unshift的效率是較低的。原因是,它每新增一個元素,都要把現有元素往下移一個位置。兩者的效率差異有多大呢?下面來測試一下

var arr=[],s = +new Date;
//push效能測試
for(var i=0;i<50000;i++){
    arr.push(i);
}
console.log(+new Date - s);

s= +new Date;
arr=[];
//unshift效能測試
for(var i=0;i<50000;i++){
    arr.unshift(i);
}
console.log(+new Date - s);
複製程式碼

結果為
6
238

原理: 因為頭部加入元素的話,陣列原來的位置將會被打亂陣列會重新排序,效能就會較差,如果是尾部刪除和新增的話,原來的下標位置將不會被打亂,位置資訊將不會被打亂,效能較好;

若必須達到unshift的效果,可以先用push,再使用陣列的reverse方法反轉陣列。如:

for (var i = 0; i < 50000; i++) { 
&emsp;&emsp;arr.push(i); 
} 
arr.reverse(); 

//reverse的效能又怎樣呢?執行下面的程式碼

var arr = [ ], s = +new Date; 
for (var i = 0; i < 50000; i++) { 
&emsp;&emsp;arr.push(i); 
} 
arr.reverse(); 
console.log(+new Date - s); 

複製程式碼

結果為:8
可見reverse效能很高,基本沒有影響

30.JavaScript:(a==1 && a==2 && a==3)能輸出ture麼?

const a = {
  num: 0,
  valueOf: function() {
    return this.num += 1
  }
};
const equality = (a==1 && a==2 && a==3);
console.log(equality); // true
複製程式碼

有什麼竅門呢?

其實也沒有,能有的就是js中的兩個概念:

  • 隱式轉換
  • object的valueOf函式

隱式轉換

注意:這題裡面我們用的是 == 而不是===,在js中==代表的是等於而不是全等,那麼就存在變數的隱式轉化問題。這就意味著結果會比我們所期望的更多的可能性。對於js的隱式轉化,真的有很多文章,我推薦一下以下幾篇部落格,如果你想要了解,可以點進去:

記住下面兩點:

  • 使用相等操作符,js會做強制型別轉化
  • 我們的物件每次呼叫valueOf()它的值會增加1

31.手動實現淺拷貝

遍歷物件,然後把屬性和屬性值都放在一個新的物件不就好了~

    var shallowCopy = function(obj) {
        // 只拷貝物件
        if (typeof obj !== 'object') return;
        // 根據obj的型別判斷是新建一個陣列還是物件
        var newObj = obj instanceof Array ? [] : {};
        // 遍歷obj,並且判斷是obj的屬性才拷貝
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                newObj[key] = obj[key];
            }
        }
        return newObj;
    }
複製程式碼

33.深拷貝的實現

那如何實現一個深拷貝呢?說起來也好簡單,我們在拷貝的時候判斷一下屬性值的型別,如果是物件,我們遞迴呼叫深拷貝函式不就好了~

    var deepCopy = function(obj) {
        if (typeof obj !== 'object') return;
        var newObj = obj instanceof Array ? [] : {};
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
            }
        }
        return newObj;
    }
複製程式碼

32.實現一個javascript new的功能

要想實現new的功能,我們首先要知道new是幹什麼的呢?

在js中 var a = new A() 後

1.建立一個空物件obj{}

2.將a的this指向obj{}

3.將a的_proto_指向A的prototype

4.執行A的建構函式

5.返回obj

程式碼級實現:

    function Test (name) {
        this.name = name;
        this.test = '????';
    }
    
    Test.prototype.test2 = function () {
        console.log('測試原型鏈上的方法!');
    }
    
    function _new () {
        let newObj = {};
        let Constructor = Array.prototype.shift.call(arguments);
        newObj.__proto__ = Constructor.prototype;
        Constructor.apply(newObj,arguments);
        return newObj;
    }

    var t = _new(Test,'yz');
    t.test2();
複製程式碼

相關文章