【面試筆記】js面試25題筆記

 

自測連接>> 在js25題js21題或者js85題測驗你的知識掌握。javascript

js25題筆記html

1. 使用typeof bar === "object" 可能遇到的陷阱和解決方法java

在 JavaScript 裏使用 typeof 來判斷數據類型,只能區分基本類型,即 「number」,」string」,」undefined」,」boolean」,」object」 五種。jquery

陷阱:若是bar是null,js會視之爲object類型。數組

解決:判斷bar是object但非null: 安全

console.log((bar !== null) && (typeof bar === "object"));

擴展:判斷bar是數組:  typeof只能區分基本類型,它將function,array,object都視爲object類型,因此要區分這三類對象就不能用typeof方法了,能夠用toString.call()的返回值來判斷:閉包

console.log((bar !== null) && (typeof bar === "object") && (! $.isArray(bar)));

或者用jq的isArray() isFunction()來判斷:app

onsole.log((bar !== null) && (typeof bar === "object") && (toString.call(bar) !== "[object Array]"));

ECMA 5.1 中關於Object.prototype.toString.call() 的描述:函數

When the toString method is called, the following steps are taken:this

If the this value is undefined, return 「[object Undefined]「.

If the this value is null, return 「[object Null]「.

Let O be the result of calling ToObject passing the this value as the argument.

Let class be the value of the [[Class]] internal property of O.

Return the String value that is the result of concatenating the three Strings 「[object ", class, and "]「.

對全部值類型應用 Object.prototype.toString.call() 方法結果以下:

console.log(Object.prototype.toString.call(123)) //[object Number]
console.log(Object.prototype.toString.call('123')) //[object String]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(function(){})) //[object Function]

2.不要寫容易引發誤會的代碼

(function(){
  var a = b = 3;
})();

console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));

陷阱: 誤覺得函數內至關於

var b = 3;
var a = b;

事實上是

b = 3;
var a = b;

所以若是不是在strict mode下,b被當成全局變量。因此應該是輸出b有定義,a無定義。

3.在內聯函數中,ECMA5以前this指向window,而在ECMA5 內聯函數的this變成undefined。

4.閉包的應用

閉包的例子:

function f1(){
   var privateA = 1;   
   function f2(){//至關於getter方法
       return privateA;
   };
   f3 = function(n){//至關於setter方法,注意,這裏的f3是一個全局變量
       privateA = n;
   };
   return f2;
}
var f4 = f1();
console.log(f4());//1
f3(2);
console.log(f4());//2

閉包的特色和做用:

1.保護函數內的變量安全,限定訪問接口,實現JS私有屬性和私有方法。f1的變量只有f2能訪問,f2至關於f1私有變量的getter方法。

2.在內存中維持一個變量。f4持有f2,f2引用着f1,所以f4存在的時候,f2和f1也會駐留在內存中(GC不會回收,所以使用閉包時也要留意內存泄露的問題)

5.jQ爲了處理多庫共存,使用noConflict()來轉移$使用權,而當即調用表達式能夠在加載時就初始化,造成一個單例模式的效果,故而使用當即調用表達式能夠解決js變量污染問題。若是jq的$使用權轉移以後還想用jq,結合當即調用表達式的做用,下面這個方法使咱們仍然能使用jq:

(function($) { /* jQuery plugin code referencing $ */ } )(jQuery);

示例:

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>

<div id="foo">
    
</div>
</body>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">
    $.noConflict();
    ;(function  ($) {
        $("#foo").html("我仍是能使用jq!")
    })(jQuery);
    $("#foo").html("這裏就用不了jq了!")//報錯,$找不到
</script>
</html>

6.use strict 使用嚴格模式有什麼好處

7.return的陷阱

當return單獨處於一行時,javascript會自動補全爲return;

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
<script type="text/javascript">
   function foo1()
{
  return {
      bar: "hello"
  };
}

function foo2()
{
  return 
  {
      bar: "hello"
  };
}
console.log("foo1 returns:");
console.log(foo1());//Object {bar: "hello"}
console.log("foo2 returns:");
console.log(foo2()) //!!foo2的返回值爲 undefined
</script>
</body>
</html>

8.NaN的陷阱

NaN表示一個非數字類型的值。與任何值比較都返回false,包括它本身。用數學函數操做都會返回NaN。

var a = NaN;
console.log(a);//NaN
console.log(a===a);//false
console.log(a>0);//false
console.log(Math.max(a));//NaN

可是使用typeof比較時卻會判斷它爲數值類型。

console.log(typeof NaN === "number");  // "true"

判斷NaN的方法能夠用isNaN(),雖然這個方法存在不足, 有一個更好的方法是使用Number.isNaN()

Number.isNaN(NaN);        // true
Number.isNaN(Number.NaN); // true
Number.isNaN(0 / 0)       // true

// 使用全局的isNaN(),下面會返回true
Number.isNaN("NaN");      // false
Number.isNaN(undefined);  // false
Number.isNaN({});         // false
Number.isNaN("blabla");   // false
isNaN("NaN");      // true
isNaN(undefined);  // true
isNaN({});         // true
isNaN("blabla");   // true

// 使用Number.isNaN()和全局isNaN()都返回false
Number.isNaN(true);
Number.isNaN(null);
Number.isNaN(37);
Number.isNaN("37");
Number.isNaN("37.37");
Number.isNaN("");
Number.isNaN(" ");

9.for循環中變量做用域的問題

for (var i = 0; i < 5; i++) {
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  btn.addEventListener('click', (function(i) {
    return function() { console.log(i); };
  })(i));
  document.body.appendChild(btn);
}

學ECMAScript 6的時候遇過這個問題,點擊任意按鈕都是輸出5,由於在onclick觸發前,for循環已經結束了,點擊按鈕時輸出的i是for循環結束後的i,因此都是5.爲了輸出0,1,2,3這樣的值,咱們能夠從i的做做用域和onclick的執行時機下手。如下有三個方法,其中前兩個方法是基於i的做用域處理,後一個則是限制onclick的執行時機(用了當即調用方式) 

for (let i = 0; i < 5; i++) { //let讓i的做用域在每次執行循環時都獨立出來 
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  btn.addEventListener('click', (function(i) {
    return function() { console.log(i); };
  })(i));
  document.body.appendChild(btn);
}
['a', 'b', 'c', 'd', 'e'].forEach(function (value, i) { //['a', 'b', 'c', 'd', 'e']一樣是將每次循環的i分隔開來
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  btn.addEventListener('click', function() { console.log(i); });
  document.body.appendChild(btn);
});
for (var i = 0; i < 5; i++) {
  var btn = document.createElement('button');
  btn.appendChild(document.createTextNode('Button ' + i));
  (function (i) {
    btn.addEventListener('click', function() { console.log(i); });
  })(i);//當即調用表達式使btn.addEventListener在循環中完成初始化並加載
  document.body.appendChild(btn);
}

10.reverse,push,concat的使用

var arr1 = "john".split('');
var arr2 = arr1.reverse();
var arr3 = "jones".split('');
arr2.push(arr3);
console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));
console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));
//輸出:
//"array 1: length=5 last=j,o,n,e,s"
//"array 2: length=5 last=j,o,n,e,s"

經過上面這段代碼來鞏固reverse,push和concat的使用。

首先,reverse會改變數組自己(arr1)而且返回一個數組引用(不是返回數組arr1,是返回arr1的引用),所以arr2 = arr1.reverse() 使得arr2指向arr1,從而對arr2的操做也會映射到arr1上。

此時arr1與arr2指向同一個對象,值爲[ 'n' , 'h' , 'o' , 'j' ]。

其次,push在插入一個數組時,會把數組當成一個元素插進去,並且直接改變數組。所以arr2.push(arr3)的結果是[ 'n' , 'h' , 'o' , 'j' ,['j' ,'o', 'n' ,'e' ,'s']]。從中咱們知道push和concat的區別就是concat是將數組中的元素一個一個拼接到前一個數組中,並且不直接改變對象,要將拼接後的對象從新返回給原對象(使用時 arr = arr.concat(xxx);  <== > arr.push(xxx);)。而push是把元素整個放入對象結尾,因此遇到數組時push是往數組中放入array對象,concat是拼接數組。

所以在上面這段代碼中,輸出arr1最後一個元素時便輸出了arr3這個數組(push時arr3被看成最後一個元素加入arr1指向的數組對象),而arr1和arr2指向的對象相同,因此輸出二者內容都是同樣的。

 11.利用事件隊列解決堆棧溢出

下面這份代碼在list特別大的時候會致使堆棧溢出

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        nextListItem();
    }
};

 能夠巧妙地用事件隊列解決這個問題

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        setTimeout( nextListItem, 0);
    }
};

經過上面的方法,用事件隊列處理遞歸,而不是調用堆棧。當運行nextListItem時,若是item不爲空(未到列表最後一個),這個計時方法(nextListItem)會被加入事件隊列,同時該方法也就退出了調用堆棧,這樣nextListItem方法會被一直加入到事件隊列,知道item爲空時開始逐個執行事件隊列裏的方法。與此同時,調用堆棧一直爲空。

12. || 與 &&

0 || 1 = 1  //0爲false,繼續計算下一個操做數1,最終獲得1
1 || 2 = 1  //1爲true,忽略下一個操做數2,最終獲得1
0 && 1 = 0 //0爲false,忽略下一個操做數1,最終獲得0
1 && 2 = 2 //1爲true,繼續計算下一個操做數1,最終獲得2

13. == 與 ===

==判斷值相等,===判斷值與類型均相同。

14.js在設置對象屬性時,會自動吧參數字符串化。

var a={},
    b={key:'b'},
    c={key:'c'};

a[b]=123;
a[c]=456;

console.log(a[b]); //456

緣由:b和c都是object,放入a時字符串化獲得[object object] 。所以a[b],a[c]其實都是a["[object object]"],輸出的值天然就是最後賦值的那一個了。

15.bind的用法(IE678不支持)

var hero = {
    _name: 'John Doe',
    getSecretIdentity: function (){
        return this._name;
    }
};

var stoleSecretIdentity = hero.getSecretIdentity;//undefined

console.log(stoleSecretIdentity());//John Doe

var stoleSecretIdentity2 = hero.getSecretIdentity.bind(hero);//John Doe
相關文章
相關標籤/搜索