思考: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(或其餘框架)源碼中如何使用原型鏈
- 閱讀源碼是高效提升技能的方式。如zepto
- 但不能「埋頭苦鑽」,有技巧在其中
- 慕課網搜索「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
|
前端使用異步的場景有哪些
- 定時任務:setTimeout、setInterval
- 網絡請求:ajax請求、動態<img>加載
- 事件綁定
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);
})
|