《前端JavaScript面試技巧》

《前端JavaScript面試技巧》筆記一

思考:javascript

  • 拿到一個面試題,你第一時間看到的是什麼 -> 考點
  • 又如何看待網上搜出來的永遠也看不完的題海 -> 不變應萬變
  • 如何對待接下來遇到的面試題 -> 題目到知識再到題目

知識體系:html

JS基礎知識前端

1、變量類型和計算java

題目:jquery

  • JS中使用typeof能獲得哪些類型?
  • 什麼時候使用 === 什麼時候使用 == ?
  • JS中有哪些內置函數
  • JS變量按照存儲方式區分爲哪些類型,並描述其特色
  • 如何理解JSON

知識點:面試

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*1、變量類型*/
   /*值類型vs引用類型*/
   //從內存來講,值類型是把每一個值分塊存放在內存中,而引用類型是好幾個變量公用一個內存塊,節省內存空間。
   //引用類型包括:對象、數組、函數。引用類型的屬性是能夠無限擴展的,屬性多了,就會佔用更多的內存空間,因此引用類型是爲了公用內存空間。
   //值類型
   var  a = 100;
   var  b = a;
   a = 200;
   console.log(b);  //100
   //引用類型
   var  a = {age:20};
   var  b = a;
   b.age = 21;
   console.log(a.age);  //21
 
   /*typeof運算符詳解*/
   //typeof只能區分值類型,引用類型也只能區分function,由於function的定位很是高。
   typeof  undefined  //undefined (值類型)
   typeof  'abc'  //string (值類型)
   typeof  123  //number (值類型)
   typeof  true  //boolean (值類型)
   typeof  {}  //object (引用類型)
   typeof  []  //object (引用類型)
   typeof  null  //object (引用類型)
   typeof  console.log  //function (引用類型)
/*2、變量計算---強制類型轉換*/
   //字符串拼接
   var  a = 100+10;  //110
   var  b = 100+ '10' //'10010'
   //運算符
   //==會把兩邊的值轉換爲true或false
   100 ==  '100' //true
   0 ==  '' //true
   null  == undefined;  //true
   //if語句
   //if會把括號裏面的值轉換爲true或false
   var  a =  true ;
   if (a){...}
     var  b = 100;
   if (b){...}
     var  c =  '' ;
   if (c){...}
   //邏輯運算
   console.log(10 && 0);  //0
   console.log( ''  ||  'abc' );  //'abc'
   console.log(!window.abc);  //true
   //判斷一個變量會被看成true仍是false
   var  a = 100;
   console.log(!!a);

解題:ajax

JS中使用typeof能獲得哪些類型?正則表達式

  undefined、string、number、boolean、object、functionjson

什麼時候使用 === 什麼時候使用 == ?api

1
2
3
4
if (obj.a== null ){
    //這裏至關於 obj.a === null || obj.a ===undefined ,簡寫形式
    //這是 jquery 源碼中推薦的寫法
}

  雙等會進行強制類型轉換,三等不會進行強制類型轉換。

  除了上面的例子用雙等,其它的都用三等。

JS中有哪些內置函數 

  數據封裝類對象
  Object
  Array
  Boolean
  Number
  String
  Function
  Date
  RegExp :正則表達式
  Error

JS變量按照存儲方式區分爲哪些類型,並描述其特色

  值類型和引用類型。

  從內存來講,值類型是把每一個值分塊存放在內存中,而引用類型是好幾個變量公用一個內存塊,節省內存空間。

如何理解JSON  

  JSON只不過是一個JS對象,也是一種數據格式。
  JSON基本的api只有兩個:
  JSON.stringify({a:10,b:20}); //對象轉字符串
  JSON.parse('{"a":10,"b":20}'); //字符串轉對象

 

 2、原型和原型鏈

題目:

  • 如何準確判斷一個變量是數組類型
  • 寫一個原型鏈繼承的例子
  • 描述new一個對象的過程
  • zepto(或其餘框架)源碼中如何使用原型鏈

 知識點:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//大寫開頭的函數基本都是構造函數,這麼寫提升代碼可讀性
 
/*1、構造函數*/
   function  Foo(name,age){
     this .name = name;
     this .age = age;
     this . class  'class-1' ;
     //return this //默認有這一行,最好不要寫
   }
   var  f =  new  Foo( 'zhangsan' ,20);
   //var f1 = new Foo('lisi',22); //建立多個對象
   //new時把參數傳進去。new函數執行時裏面的this會變成空對象,給this賦值後,再把this給return回來,return回來就把值賦值給了f,這時f就具有f.name = 'zhangsan',f.age = 20,f.class ='' 'class-1'
   
 
/*2、構造函數-擴展*/
   /*
   var a = {} 實際上是 var a = new Object() 的語法糖。 構造函數是Object函數。
   var a = [] 實際上是 var a = new Array() 的語法糖。 構造函數是Array函數。
   function Foo(){...} 實際上是 var Foo = new Function(...)。 構造函數是Function。
   推薦前面的書寫方式。
   使用instanceof判斷一個函數是不是一個變量的構造函數。好比:判斷一個變量是否爲「數組」:變量 instanceof Array
   */
 
/*3、原型規則和示例*/
   //5條原型規則-原型規則是學習原型鏈的基礎
   //一、全部的引用類型(數組、對象、函數),都具備對象特性,便可自由擴展屬性(除了null之外)
   var  obj = {}; obj.a = 100;
   var  arr = []; arr.a = 100;
   function  fn(){}; fn.a = 100;
   //二、全部的引用類型(數組、對象、函數),都有一個__proto__屬性,屬性值是一個普通的對象。 __proto__ 隱式原型
   console.log(obj.__proto__);
   console.log(arr.__proto__);
   console.log(fn.__proto__);
   //三、全部的函數,都有一個prototype屬性,屬性值也是一個普通的對象。  prototype顯式原型
   console.log(fn.prototype);
   //四、全部的引用類型(數組、對象、函數),__proto__屬性值指向它的構造函數的"prototype"屬性值。
   console.log(obj.__proto__ === Object.prototype);
   //五、當試圖獲得一個對象的某個屬性時,若是這個對象自己沒有這個屬性,那麼會去它的 __proto__(即它的構造函數的prototype)中尋找。
   //構造函數
   function  Foo(name,age){
     this .name = name;
   }
   Foo.prototype.alertName =  function (){
     alert( this .name);
   }
   //建立示例
   var  f =  new  Foo( 'zhangsan' );
   f.printName =  function (){
     console.log( this .name);
   }
   //測試
   f.printName();  //zhangsan
   f.alertName();  //zhangsan
 
   //補充 -- 循環對象自身的屬性
   var  item;
   for (item  in  f){
     //高級瀏覽器已經在 for in 中屏蔽了來自原型的屬性
     //可是這裏建議仍是加上這個判斷,保證程序的健壯性
     if (f.hasOwnProperty(item)){
       console.log(item);
     }
   }
 
 
/*4、原型鏈*/
   //構造函數
   function  Foo(name,age){
     this .name = name;
   }
   Foo.prototype.alertName =  function (){
     alert( this .name);
   }
   //建立示例
   var  f =  new  Foo( 'zhangsan' );
   f.printName =  function (){
     console.log( this .name);
   }
   //測試
   f.printName();  //zhangsan
   f.alertName();  //zhangsan
   f.toString();  //要去f.__proto__.__proto__中查找
 
 
/*5、instanceof*/
   //用於判斷引用類型屬於哪一個構造函數的方法
   //構造函數
   function  Foo(name,age){
     this .name = name;
   }
   Foo.prototype.alertName =  function (){
     alert( this .name);
   }
   //建立示例
   var  f =  new  Foo( 'zhangsan' );
   f.printName =  function (){
     console.log( this .name);
   }
   //測試
   f.printName();  //zhangsan
   f.alertName();  //zhangsan
   f.toString();  //要去f.__proto__.__proto__中查找
 
   //instanceof
   //f instanceof Foo 的判斷邏輯是:
   //一、f 的 __proto__ 一層一層往上,可否對應到 Foo.prototype
   //二、再試着判斷 f instanceof Object

 

  

 

 

解題:

如何準確判斷一個變量是數組類型 

1
2
3
var  arr = [];
arr  instanceof  Array;  //true
typeof  arr;  //object   typeof是沒法判斷是不是數組的 

寫一個原型鏈繼承的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//動物
  function  Animal(){
      this .eat =  function (){
          console.log( 'animal eat' );
      }
  }
  
  //狗
  function  Dog(){
      this .bark =  function (){
          console.log( 'dog bark' );
      }
  }
  Dog.prototype =  new  Animal();
  //哈士奇
  var  hashiqi =  new  Dog();
 
  /*面試時千萬不要寫這個例子,要寫更貼近於實戰的原型鏈例子*/
1
/*用下面的例子*/

//寫一個封裝DOM查詢的例子
function Elem(id){
  this.elem = document.getElementById(id);
}

Elem.prototype.html = function(val){
  var elem = this.elem;
  if(val){
    elem.innerHTML = val;
    return this; //鏈式操做
  }else {
    return elem.innerHTML;
  }
}

Elem.prototype.on = function(type,fn){
  var elem = this.elem;
  elem.addEventListener(type,fn);
  return this;
}

var div1 = new Elem('div1');
console.log(div1.html());
div1.html('<h2>新內容</h2>').on('click',function(){
alert(div1.html());
}).html('<h2>新內容新內容新內容</h2>').on('click',function(){
alert('第二次');
}); //鏈式操做


/*div1.html('<h2>新內容</h2>')
div1.on('click',function(){
alert(div1.html());
})*/

1
  

描述new一個對象的過程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
   一、建立一個新對象
   二、this指向這個新對象
   三、執行代碼,即對this賦值
   四、返回this
  */
  /*構造函數*/
   function  Foo(name,age){
     this .name = name;
     this .age = age;
     this . class  'class-1' ;
     //return this //默認有這一行
   }
   var  f =  new  Foo( 'zhangsan' ,20);
   //var f1 = new Foo('lisi',22); //建立多個對象

zepto(或其餘框架)源碼中如何使用原型鏈

  1. 閱讀源碼是高效提升技能的方式。如zepto
  2. 但不能「埋頭苦鑽」,有技巧在其中
  3. 慕課網搜索「zepto設計和源碼分析」

 

3、做用域和閉包

題目:

  • 說一下對變量提高的理解
  • 說明this幾種不一樣的使用場景
  • 建立10個<a>標籤,點擊時彈出對應的序號
  • 如何理解做用域
  • 實際開發中閉包的應用

知識點:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*1、執行上下文*/
  //範圍:一段<script>或者一個函數
  //全局:變量定義、函數聲明(一段<script>)
  //函數:變量定義、函數聲明、this、arguments(函數)
  //PS:注意「函數聲明」和「函數表達式」的區別
  //實際代碼中不要下面這種寫法,都是先定義,再執行,提升代碼可讀性。
  console.log(a);  //undefined
  var  a = 100;
 
  fn( 'zhangsan' );  //'zhangsan' 20
  function  fn(name){  //函數聲明
   age = 20;
   console.log(name,age);
   var  age;
  }
  var  fn =  function (){}  //函數表達式
 
 
/*2、this*/
     //this要在執行時才能確認值,定義時沒法確認。
     //做爲構造函數執行
     //做爲對象屬性執行
     //做爲普通函數執行
     //call、apply、bind
     var  a = {
         name: 'A' ,
         fn: function (){
             console.log( this .name);
         }
     }
     a.fn();  //this===a
     a.fn.call({name: 'B' });  //this==={name:'B'}
     var  fn1 = a.fn;
     fn1();  //this===window
 
 
/*做用域*/
     /*無塊級做用域*/
     //沒有塊級做用域,寫在裏面和外面是同樣的。不建議下面這種寫法,程序不易讀。
     if ( true ){
         var  name =  'zhangsan' ;
     }
     console.log(name);  //zhangsan
     /*函數和全局做用域*/
     var  a = 100;  //全局變量
     function  fn(){
         var  a = 200;  //局部變量
         console.log( 'fn' ,a);
     }
     console.log( 'global' ,a);
     fn();
 
 
/*做用域鏈*/
     //函數的父級做用域是函數定義時的做用域,不是函數執行時的做用域。
     var  a = 100;
     function  fn(){
         var  b = 200;
 
         //當前做用域沒有定義的變量,即「自由變量」
         console.log(a);  //100
         console.log(b);  //200
     }
     fn();
     //例子
     var  a = 100;
     function  F1(){
         var  b = 200;
         function  F2(){
             var  c = 300;
             console.log(a);  //100 //自由變量
             console.log(b);  //200 //自由變量
             console.log(c);  //300
         }
         F2();
     }
     F1();
 
 
 
/*閉包*/
     function  F1(){
         var  a = 100;
 
         //返回一個函數(函數做爲返回值)
         return  function (){
             console.log(a);
         }
     }
     //f1獲得一個函數
     var  f1 = F1();
     var  a = 200;
     f1();
     //f1執行的是return裏的函數,return裏的a是個自由變量,要去父級做用域尋找,父級做用域F1裏面定義了a,因此打印出來的a的值是100.
     /*閉包的使用場景
     一、函數做爲返回值(上一個demo)
     二、函數做爲函數傳遞(下面這個例子)
     */
     function  F1(){
         var  a = 100;
         return  function (){
             console.log(a);
         }
     }
     var  f1 = F1();
     function  F2(fn){
         var  a = 200;
         fn();
     }
     F2(f1);

解題:

說一下對變量提高的理解 

變量定義
函數聲明(注意和函數表達式的區別)
執行上下文的概念。
各個函數中,它的變量的聲明和定義,以及函數的聲明都會提早,放在前面,由此就是變量提高主觀上、形象上的理解。

說明this幾種不一樣的使用場景

做爲構造函數執行

做爲對象屬性執行

做爲普通函數執行

call、apply、bind

建立10個<a>標籤,點擊時彈出對應的序號

1
2
3
4
5
6
7
8
9
10
11
12
var  i;
for (i=0;i<10;i++){
     ( function (i){
         var  a = document.createElement( 'a' );
         a.innerHTML = i+ '<br>' ;
         a.addEventListener( 'click' , function (e){
             e.preventDefault();
             alert(i);
         })
         document.body.appendChild(a);
     })(i);
}

如何理解做用域

要領:
一、自由變量
二、做用域鏈,即自由變量的查找
三、閉包的兩個場景

實際開發中閉包的應用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//閉包實際應用中主要用於封裝變量,收斂權限
     function  isFirstLoad(){
         var  _list = [];
         return  function (id){
             if (_list.indexOf(id)>=0){
                 return  false ;
             } else  {
                 _list.push(id);
                 return  true ;
             }
         }
     }
     //使用
     var  firstLoad = isFirstLoad();
     firstLoad(10);  //true
     firstLoad(10);  //false
     firstLoad(20);  //true//在 isFirstLoad 函數外面,根本不可能修改掉 _list 的值

  

4、異步和單線程

題目: 

  • 同步和異步的區別是什麼?分別舉一個同步和異步的例子
  • 一個關於setTimeout的筆試題
  • 前端使用異步的場景有哪些

知識點:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/*什麼是異步(對比同步)*/
     //同時執行,不會阻塞程序執行
     console.log(100);
     setTimeout( function (){
         console.log(200);
     },1000);
     console.log(300);
     setTimeout( function (){
         console.log(400);
     },1000);
     //下面這個例子相似同步
     console.log(100);
     alert(200);
     console.log(300);
 
 
 
/*前端使用異步的場景*/
     /*什麼時候須要異步:
     在可能發生等待的狀況
     等待過程當中不能像alert同樣阻塞程序進行
     所以,全部的「等待的狀況」都須要異步
     一、定時任務:setTimeout、setInterval
     二、網絡請求:ajax請求、動態<img>加載
     三、事件綁定
     */
     //ajax請求代碼示例
     console.log( 'start' );
     $.get( './data1.json' , function (data1){
         console.log(data1);
     })
     console.log( 'end' );
     //<img>加載示例
     console.log( 'start' );
     var  img = document.createElement( 'img' );
     img.onload =  function (){
         console.log( 'loaded' );
     }
     img.src =  '/xxx.png' ;
     console.log( 'end' );
     //事件綁定示例
     console.log( 'start' );
     document.getElementById( 'btn1' ).addEventListener( 'click' , function (){
         alert( 'clicked' );
     })
     console.log( 'end' );
     
 
 
 
/*異步和單線程*/
     console.log(100);
   setTimeout( function (){
     console.log(200);
   });
   console.log(300);
   //執行結果:100、300、200
   //setTimeout是異步,最後執行。這個例子的setTimeout沒有等待時間,因此待其它執行完以後立馬執行setTimeout。
   //單線程就是一次只能作一件事
   /*解析:
   一、執行第一行,打印100
   二、執行setTimeout後,傳入setTimeout的函數會被暫存起來,不會當即執行(單線程的特色,不能同時幹兩件事)
   三、執行最後一行,打印300
   四、待全部程序執行完,處於空閒狀態時,會立馬看有沒有暫存起來的要執行。
   五、發現暫存起來的setTimeout中的函數無需等待時間,就當即拿過來執行。
   */

 

解題:

同步和異步的區別是什麼?分別舉一個同步和異步的例子

同步會阻塞代碼執行,而異步不會。
alert是同步,setTimeout是異步。

例子:

1
2
3
4
5
6
7
8
9
10
//異步
     console.log(100);
     setTimeout( function (){
         console.log(200);
     },1000);
     console.log(300);
//同步
     console.log(100);
     alert(200);
     console.log(300);

 

一個關於setTimeout的筆試題

1
2
3
4
5
6
7
8
9
10
11
console.log(1);
   setTimeout( function (){
     console.log(2);
   },0)
   console.log(3);
   setTimeout( function (){
     console.log(4);
   },1000)
   console.log(5);
   
   //1 3 5 2 4

 

前端使用異步的場景有哪些  

  1. 定時任務:setTimeout、setInterval
  2. 網絡請求:ajax請求、動態<img>加載
  3. 事件綁定

 

5、其它知識點

題目:

  • 獲取2017-06-10格式的日期
  • 獲取隨機數,要求是長度一致的字符串格式
  • 寫一個能遍歷對象和數組的通用forEach函數

知識點: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/*日期*/
  Date.now();  //獲取當前時間毫秒數
  var  dt =  new  Date();
  dt.getTime();  //獲取毫秒數
  dt.getFullYear();  //年
  dt.getMonth();  //月(0-11)
  dt.getDate();  //日(0-31)
  dt.getHours();  //小時(0-23)
  dt.getMinutes();  //分鐘(0-59)
  dt.getSeconds();  //秒(0-59)
 
 
 
/*Math*/
     Math.random();  //獲取隨機數。(返回的是 >0 和 <1 的小數)。有清除緩存的做用。
 
 
 
/*數組API*/
     //forEach //遍歷全部元素
     var  arr = [1,2,3];
     arr.forEach( function (item,index){
         //遍歷數組的全部元素。item:元素的值。index:元素的位置
         console.log(index,item);
     })
     //every   //判斷全部元素是否都符合條件
     var  arr = [1,2,3];
     // var arr = [1,2,3,4,5];
     var  result = arr.every( function (item,index){
         if (item<4){
             return  true ;
         }
     })
     console.log(result);  //true
     //some    //判斷是否有至少一個元素符合條件
     var  arr = [1,2,3];
     var  result = arr.some( function (item,index){
         if (item<2){
             return  true ;
         }
     })
     console.log(result);  //true
     //sort    //排序
     var  arr = [1,4,2,5,3];
     var  arr2 = arr.sort( function (a,b){
         //從小到大排序
         return  a -b;
 
         //從大到小排序
         //return b-a;
     })
     console.log(arr2);
     //map     //對元素從新組裝,生成新數組
     var  arr = [1,2,3,4];
     var  arr2 = arr.map( function (item,index){
         return  '<b>'  + item +  '</b>' ;
     })
     console.log(arr2);
     //filter  //過濾符合條件的元素
     var  arr = [1,2,3,4];
     var  arr2 = arr.filter( function (item,index){
         if (item>=2){
             return  true ;
         }
     })
     console.log(arr2);
 
 
 
/*對象API*/
     var  obj = {
         x:100,
         y:200,
         z:300
     }
     var  key;
     for (key  in  obj){  //key是obj的屬性名
         //注意這裏的 hasOwnProperty ,再講原型鏈時候講過了
         if (obj.hasOwnProperty(key)){  //判斷key是obj原生的屬性,而不是原型裏面的屬性
             console.log(key,obj[key]);
         }
     }

 

解題:

獲取2017-06-10格式的日期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function  formatDate(dt){
   if (!dt){
     dt =  new  Date();
   }
   var  year = dt.getFullYear();
   var  month = dt.getMonth() + 1;
   var  date = dt.getDate();
   if (month<10){
     //強制類型轉換
     month =  '0' +month;
   }
   if (date<10){
     //強制類型轉換
     date =  '0' +date;
   }
   //強制類型轉換
   return  year +  '-'  + month +  '-'  + date;
}
var  dt =  new  Date();
var  formatDate = formatDate();
console.log(formatDate);

獲取隨機數,要求是長度一致的字符串格式

1
2
3
4
var  random = Math.random();
var  random = random +  '0000000000' //後面加上10個零,爲了確保長度一致
var  random = random.slice(0,10);  //截取前10位
console.log(random);

寫一個能遍歷對象和數組的通用forEach函數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function  forEach(obj,fn){
     var  key;
     if (obj  instanceof  Array){
       //準確判斷是否是數組
       obj.forEach( function (item,index){
         fn(index,item);
       })
     } else {
       //不是數組就是對象,對象用for in循環
       for (key  in  obj){
         fn(key,obj[key]);
       }
     }
   }
   var  arr = [1,2,3];
   //注意,這裏參數的順序換了,爲了和對象的遍歷格式一致
   forEach(arr, function (index,item){
     console.log(index,item);
   })
   var  obj = {x:100,y:200};
   forEach(obj, function (key,value){
     console.log(key,value);
   })

 

 

 

 

 

 
 
標籤:  js面試前端
相關文章
相關標籤/搜索