# JavaScript
## 概念
### AJAX
廣義上的 AJAX 是指 XMLHttpRequest,也就是 ECMAScript 中用于網絡交互的操作。
現在的 AJAX 大多數指的是 JQuery 中的 AJAX 函數。
JQuery 中的 AJAX 封裝了大量網絡交互操作,使其更加方便快捷。
### this 關鍵字
首先介紹一下函數不同的調用方法:
普通函數調用;作為方法來調用;作為構造函數來調用;使用apply/call方法來調用;Function.prototype.bind方法;es6箭頭函數。
> 誰調用了方法,this 就指向誰。
**普通函數調用**
```javascript
function person() {
this.name = "xiaoming";
console.log(this);
console.log(this.name);
}
person(); //print window, xiaoming
```
在這段代碼中,person() 作為普通函數調用,實際上 person 是作為全局對象 window 的一個方法來進行調用的,即 window.person()。
所以在這里,是 window 調用了 person 方法,那么 person() 中的 this 就指向 window,并且 window 還有了一個屬性 name,值為 xiaoming。
同樣的:
```javascript
var name = "xiaoming"
function person() {
console.log(this);
console.log(this.name);
}
person(); // print window, xiaoming
```
和上邊代碼的結果是相同的,因為 this 指向 window, 所以 this.name 的值還是 xiaomimg。
**作為方法來調用**
```javascript
var name = "xiao hong";
var person = {
name: "xiao ming",
printName: function() {
console.log(this.name);
},
}
// print xiao ming
// person 對象調用 printName() 方法,所以 this 指向 person 對象,所以打印 xiao ming。
person.printName();
var printName = person.showName; // 注意沒有 ()
// print xiao hong
// 將 person 類中的方法 printName() 賦給了 printName 變量,當直接調用 printName() 的時候,this 指向的是 window,所以打印 xiao hong。
printName();
```
**作為構造函數來調用**
```javascript
function Person(name) {
this.name = name;
}
var person1 = Person("xiao ming");
console.log(person1.name); // error:undefine
// 因為沒有使用 new 操作,等同于 window 調用了 Person() 方法,因此 this 指向的是 window對象。
console.log(window.name); // xiao ming
var person2 = new Person("xiao hong")
console.log(person2.name); // xiao hong
console.log(window.name); // xiao ming
```
下邊講解一下 new 操作:
```javascript
function Person(name) {
var o = {},
o.__proto__ = Person.prototype; // 原型繼承
Person.call(o, name);
return o;
}
// 注意,此處為偽代碼,并不能實際運行,如此寫是為了讓讀者更明白 new 操作符的作用。
```
**call/apply 方法的調用**
call、apply 方法的作用是改變 this 的作用域。
```javascript
var name = "xiao hong";
var Person = {
name: "xiao ming",
printName: function() {
console.log(this.name);
}
}
Person.printName(); // xiao ming
Persin.printName.call(); // xiao hong
```
**箭頭函數(ES6)**
箭頭函數的 this 指向固定化,始終指向外部對象,因為箭頭函數沒有 this,因此它自身不能進行 new 操作實例化,同時也不能使用 call、apply、bind 來改變 this 的指向。
## 操作符
### == && ===
例:value1 == value2 , value3 === value4,
== :
1. 如果兩個值類型相同,再進行三個等號(===)的比較。
2. 如果兩個值類型不同,也有可能相等,需根據以下規則進行類型轉換在比較:
1. 如果一個是null,一個是undefined,那么相等。
2. 如果一個是字符串,一個是數值,把字符串轉換成數值之后再進行比較。
===:
1. 如果類型不同,則一定不相等。
2. 如果兩個都是數值并且是同一個值,那么相等。
3. 如果一個是數值一個是 NaN,那么不相等。
4. 如果都是true 或者 false 相等。
5. 如果都是 null 或者 undefined,那么相等。注意,null == undefined => true, null === undefined => false.
總結:
1. string、number 等基礎類型,== 與 === 的區別在于,== 會轉換類型再進行比較,=== 不會轉換類型比較。
2. array、object 等高級類型,== 與 === 沒有區別。
3. 基礎類型與高級類型比較,區別在于,== 將高級轉換為基礎類型,再進行比較,=== 因為類型不同,直接返回false
### equals
JavaScript 中字符串沒有 equals 方法,但是可以自己實現。
## 函數
### ready() && onload()
- document.ready()
是DOM結構繪制完成之后即可執行,不需要相關聯資源下載完成,例如圖片DOM結構繪制完成,而圖片可能沒有下載完成,其函數同樣會執行。
- document.onload()
相關資源都加載完畢之后,執行,一個頁面中只能有一個onload
- load函數
常用在圖片的加載,比如 $('idName').load(),load同樣是資源全部加載完成之后才會執行。
> JavaScript 沒有 document.ready(),但是有 document.readyState,返回當前頁面情況。
### apply && call && bind
#### 相同點:
- 都是用來改變函數的this對象的指向的
- 第一個參數都是this要指向的對象
- 都可以繼續傳遞參數
```javascript
var xb = {
name: '小冰',
gender: '女',
say: function(){
alert(this.name + ',' + this.gender);
}
}
var other = {
name: '小東',
gender: '男',
}
xb.say(); ===> 小冰,女
xb.say.call(other); ==> 小東,男
xb.say.apply(other);
xb.say.bind(other)();
```
#### 三個方法的不同點
- `call`和 `apply`方法,都是對函數的直接調用,但是 `bind()` 方法需要加上()來執行
- `call`和 `apply`方法傳參的方法不同
```javascript
xb.say.call(other,'斯坦福','3')。
xb.say.apply(other, 'sitanfu','third');
```
- `bind` 返回的是方法,所以需要后邊加上()才能執行。
#### 實現 bind 方法
```javascript
if (!function() {}.bind) {
Function.prototype.bind = function(context) {
var self = this,
args = Array.prototype.slice.call(arguments);
return function() {
return self.apply(context, args.slice(1));
}
};
}
```
### Typeof
Typeof 把參數的類型信息作為字符串返回。
可能的值有:
- number
- string
- boolean
- object
- function
- undefined
> 注意:typeof(null) 返回object。
### 定時器
- `setTimeout(.... , time)` 在指定的時間(time) 后調用函數或者計算表達式。
- `setInterval(code,millisec,lang)`, lang傳遞給執行函數的其他參數。會在指定的周期(millisec)不斷調用函數(code),直到 clearInterval() 清除。
### slice && splice (操作數組)
#### slice (截取數組,不改變原數組)
> 接收一個或兩個參數,它可以創建一個由當前數組中的一項或多項組成的新數組,也就是說它不會修改原來數組的值。
用法:
- slice( para1 ),會截取從para1開始的到原數組最后的部分;
- slice(para1,para2)會截取原數組的從para1開始的para2-para1個數組。
> 注意:當兩個參數中存在負數時,用原數組的長度加上兩個負數的參數作為相應的參數來計算,并且得到的第一個參數必須小于第二個參數,否則返回一個空數組。
```javascript
var a = [1,2,3,4,5,6];
a.slice(-2,-3) //return []
a.slice(-3,-2) //return [4]
```
#### splice (刪除/替換 數組中的某幾位,返回刪除的數組)
用法:
- splice( para1,para2 ) : 刪除數組中任意數量的項,從para1開始的para2項。
> 注意的是用splice刪除數組中的值會直接將某幾項從數組中完全刪除,會導致數組length值的改變,這與delete的刪除置為undefined是不一樣的。
- splice( para1,para2,val1,val2… ):向數組中添加和刪除項,para1表示可以添加的項數,para2表示刪除的項數,后面的變量表示要添加的項的值,注意是從para1之后開始刪除和添加的。
> 參數為負數的問題,如果para1為負數,則會加上數組的長度作為para1的值,而para2為負數或0的話不會執行刪除操作,此時從 para1 當前位置開始添加變量。
### split (操作字符串)
根據特定的字符切割字符串并返回切割后生成數組。
> 如果 a.split(''), 則切割每一個字符。
```javascript
var string = 'abcde';
var array = string.split('');
// array = ['a','b','c','d','e']
```
### reverse (反轉數組中的元素)
```javascript
var a = [1,2,3,4];
a.reverse();
//a = [4,3,2,1]
```
### join (用于將數組轉換為字符串)
> 不改變原數組
```javacript
var a = [1,2,3,4];
a.join('') // return '1234'
a.join('.') // return '1.2.3.4'
//此時,a = [1,2,3,4]
```
## 綜合
### 垃圾回收機制
Js具有自動回收垃圾的機制,垃圾收集器會按照固定的時間間隔周期性執行。
- 標記清除
工作原理:當變量進入環境時,將這個變量標記為“進入環境”,當變量離開環境時,則將其標記為“離開環境”。標記為“離開環境”的就會被回收。
- 引用計數
跟蹤記錄每個值被引用的次數,當一個值被引用的次數為0時,垃圾收集器下次運行時就會將值回收。
### 內存泄漏
| 問題 | 解決辦法|
|-------------------------------------------------------|---------------|
|全局變量引起的內存泄漏 |嚴格使用全局變量|
|閉包引起的內存泄漏。閉包可以維持函數內部變量,使其得不到釋放|避免閉包,或者在外部事件處理函數中刪除對DOM的引用|
|被遺忘的定時器或者回調函數 |刪除定時器|
### JS對象屬性遍歷
1. 使用 Object.keys(objectName).forEach(function(element,index){ .... } 便利
2. for( var i in object ) {...}
3. Object.getOwnPropertyNames(), 返回一個數組,包含自身的所有屬性(不包含symbol屬性,但是包含不可枚舉屬性)。
4. Reflect.ownKeys(obj) ,包含所有屬性,包含symbol屬性和不可枚舉屬性
```javascript
var obj = {
a: 'aaaaa',
b: 'bbbbb',
c: 'ccccc',
}
for ( var i in obj ) {
console.log(i, obj[i]);
}
//輸出結果
//a aaaaa
//b bbbbb
//c ccccc
Object.keys(obj).forEach(
function(ele,index) {
console.log(ele,obj[ele],index)
})
//輸出結果
a dfafda 0
b afdaf 1
c ssssss 2
```
### cookies && storage
`cookies`、`localStorage`、`sessionStorage` 都是在客戶端以鍵值對存儲的存儲機制,并且只能將值存儲為字符串。
`sessionStorage`常用操作方法:
- session.clear(), 清空
- session.setItem('key','value')
- session.getItem('key')
- session.removeItem('key')
- session.length,
| |cookies | localStorage | sessionStorage|
|--------------|--------|--------------|---------------|
|初始化 |客戶端或服務器,服務器可使用Set-Cookies請求頭|客戶端|客戶端|
|過期時間 |手動設置|永不過期 |當前頁面刷新、關閉|
|與域名關聯 |是 |否 |否|
|容量 |4kb |5mb |5mb|
|隨請求發給服務器|是 |否 |否|
### 冒泡/捕獲/事件委托/事件代理
`事件委托` 就是利用事件冒泡,只指定一個事件處理程序,就可以管理某一類型的所有事件。
舉例:
有三個同事預計會在周一收到快遞。為簽收快遞,有兩種辦法:一是三個人在公司門口等快遞;二是委托給前臺MM代為簽收。現實當中,我們大都采用委托的方案(公司也不會容忍那么多員工站在門口就為了等快遞)。前臺MM收到快遞后,她會判斷收件人是誰,然后按照收件人的要求簽收,甚至代為付款。這種方案還有一個優勢,那就是即使公司里來了新員工(不管多少),前臺MM也會在收到寄給新員工的快遞后核實并代為簽收。
1. 現在委托前臺的同事是可以代為簽收的,即程序中的現有的dom節點是有事件的;
2. 新員工也是可以被前臺MM代為簽收的,即程序中新添加的dom節點也是有事件的。
發生事件的元素可以使用 `ev.target` 獲取到,利用事件冒泡。
[參考文章](https://www.cnblogs.com/liugang-vip/p/5616484.html)
> 注意:$("ul").on("click","li",function(){});這樣寫有事件委托。$("ul li").on();這樣寫法則沒有。
### 瀏覽器事件模型
1. 每個瀏覽器都有自己的事件模型,W3C為了兼顧標準,定義了三個階段:捕獲階段,目標階段,冒泡階段。
2. elem.addEventListener("eventType", fn , boolean);
3. 參數說明: 事件類型,函數,true,表示在捕獲階段執行,false為在冒泡階段執行(默認為false).
4. stopPropagation()可以阻止事件的傳播,可以是捕獲階段也可以是冒泡階段。
### 模塊化
Commonjs、AMD、CMD、es6 modules
- CommonJS
- 核心思想就是通過 require 方法來同步加載所要依賴的其他模塊,然后通過 exports 或者 module.exports 來導出需要暴露的接口
- 同步加載
- 用于nodejs服務器端
- AMD規范
- require.js
- 非同步加載,允許指定回調函數
- require([module],callback),引用函數
- define(id, [dependences], callback), 定義函數
- 支持 Commonjs 的導出方式
- 只是提前加載所有依賴,不是按需加載
- CMD規范
- sea.js
- 按需加載
- 依賴spm打包
- ES6模塊化
- import
- es6 modules
- 前端框架中常用
### 閉包
簡答的說,如果一個function能除了能訪問它的參數、局部變量或函數之外,還能訪問外部環境變量(包括全局變量、DOM、外部函數的變量或者函數),那么它就可以成為一個閉包。
```javascript
// 看代碼猜結果:
function fun(n,o) {
console.log(o);
return {
fun:function(m){
return fun(m,n);
}
};
}
1. var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,0,0,0
2. var b = fun(0).fun(1).fun(2).fun(3);//undefined,0,1,2,
3. var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,0,1,1
```
> [參考文章](https://segmentfault.com/a/1190000009886713)
## 元素獲取
### 原生
- getElementById()
- getElementsByName()
- getElementsByTagName()
### 模擬復雜元素獲取方式
- getElementByClassNameAndTag
根據 `tagName` 和 `className`獲取元素
```javascript
function getElementsByClassNameAndTag(tagName, className){
var tags = document.getElemetsByTagName(tagName);
var resultTags = [];
for(var i = 0; i < tags.length; i++){
if(tags[i].classname.indexOf(className) != -1){ /* important, indexOf */
resultTags[resultTags.length] = tags[i];
}
}
return resultTags;
}
```
- getElementsByClassName
只根據 `className` 來獲取元素
```javascript
function getElementsByClassName(className){
var resultTags = []
if( !document.getElementsByClassName ) {
var allTags = document.getElementsByTagName('*');
for ( var i = 0; i < allTags.length; i++ ) {
if( allTags[i].className.indexOf(className) != -1 ) {
resultTags[resultTags.length] = allTags[i];
}
}
}else {
var tags = document.getElementsByClassName(className);
for ( var i = 0; i < tags.length; i++) {
resultTags.push(tags[i]);
}
}
return resultTags;
}
```
- 1. HTML
- 1.1 HTML 標簽
- 1.2 HTML 屬性
- 1.3 HTML5
- 2. CSS/CSS3
- 2.1 CSS3
- 2.2 Less
- 2.3 Sass
- 3. JavaScript
- 3.1 JQuery
- 3.2 javascript code
- 3.3 es6
- 4. 前端框架
- 4.1 Angular4+
- 4.2 React
- 4.3 Vue
- 5. 綜合知識
- 5.1 HTTP
- 5.2 websocket
- 5.3 綜合問題集合
- 5.4 前端優化
- 6. 附加知識
- 6.1 TCP/IP
- 6.2 數據結構
- 6.3 前端開發
- 7. 相關工具
- 7.1 Git
- 7.2 調試
- 7.3 Linux
- 8. 其他需要了解的內容
- 8.1 Python3
- 8.2 Java
- 8.3 數據庫