### 變量類型和計算
#### 值類型、引用類型
```javascript
// 值類型
let a = 100
let b = a
a = 200
console.log(b) // 100
// 引用類型
let c = { age: 18 }
let d = c
d.age = 20
console.log(c) // {age:20}
```
> 值類型存儲于 **棧**
>
> 引用類型存儲于 **堆**
>
> 應用類型存儲的是內存地址,d = c,表示d和c指向同一塊內存
```javascript
// 常見引用類型
const obj = { x:100 }
const arr = ['a','b','c']
const n = null // 特殊引用類型,指針指向為空指針
// 特殊引用類型,但不用于存儲數據,所以沒有拷貝,復制函數
function fun() {}
```
#### typeof
* 識別所有值類型
* 識別函數
* 判斷是否是引用類型(不可再細分、**不能區分數組、對象、null**)
```javascript
// 區分數組 和 對象
[] instanceof Array --> true
```

#### 深拷貝
```javascript
/**
* 深拷貝
* @param {Object} obj 要拷貝的對象
*/
function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj == null) {
// obj 是 null ,或者不是對象和數組,直接返回
return obj
}
// 初始化返回結果
let result
if (obj instanceof Array) {
result = []
} else {
result = {}
}
for (let key in obj) {
// 保證 key 不是原型的屬性
if (obj.hasOwnProperty(key)) {
// 遞歸調用,解決多級對象\數組
result[key] = deepClone(obj[key])
}
}
// 返回結果
return result
}
```
#### 變量計算-類型轉換
##### 字符串拼接
```javascript
100 + 10 // 110 number
100 + '10' // '10010' string
true + '1' // 'true1' 字符串
true + 1 // 2 number
```
##### == 運算符
```javascript
100 == '100' // true
0 == '' // true
0 == false // true
false == '' // true
null == undefined // true
null == false // false
```
```javascript
// 日常使用中 除了 == null 之外,其他都一律使用 ===
const obj = { x: 100 }
if (obj.a == null) {
// 相當于 if (obj.a === null || obj.a === undefined)
}
```
##### truly變量、falsely變量
* truly 變量:!!a === true 的變量
* false 變量:!!a === false 的變量
```javascript
// 舉例、1 兩次去反、1 是 truly 變量
!1 = false
!!1 = !false => ture
```
```javascript
// 以下是 falsely 變量。除此之外都是 truly 變量
!!0 === false
!!NaN === false
!!'' === false
!!null === false
!!undefined === false
!!false === false
```
* if 語句判斷就是 truly 變量;如 `if('')1;else 0; // 0`
### 原型和原型鏈
#### 類型判斷--instanceof
```javascript
// Object 是所有類的父類,基類
class People {
// ……
}
class Student extends People {
// ……
}
const xialuo = new Student()
console.log(xialuo instanceof Student) // true
console.log(xialuo instanceof People) // true
console.log(xialuo instanceof Object) // true
console.log([] instanceof Array) // true
console.log([] instanceof Object) // true
console.log([] instanceof Object) // true
```
#### 原型--隱士原型、顯示原型
```javascript
// class 實際上是函數 ,語法糖
typeof People // 'function'
console.log( xialuo.__proto__) // 隱式原型
console.log( Student.prototype ) // 顯示原型
xialuo.__proto__ === Student.prototype // ture
```

* 每個 class 都有顯示原型 `prototype`
* 每個實例都有隱式原型 `__proto__`
* 實例的 `__proto__` 執行對應 class 的 `prototype`
#### 基于原型和執行規則
* 獲取屬性 xialuo.name 或執行方法 xialuo.sayhi() 時
* 先在自身屬性和方法尋找
* 找不到,去 `__proto__` 總查找
#### 原型鏈
```javascript
// class Student 是 class People 的子類
People.prototype === Student.prototype.__proto__
```

```javascript
xialuo.hasOwnProperty('name') // true
xialuo.hasOwnProperty('sayHi') // false
xialuo.hasOwnProperty('hasOwnProperty') // false
```
#### instanceof 原理
```javascript
xialuo instanceof People // true
// 按照上圖 xialuo 的原型鏈,去找 People 的顯示原型 prototype 能找到 true
```
#### 提示
* class 是 ES6 語法規范,由 ECMA 委員會發布
* ECMA 只規定語法規則,即只規定代碼的書寫規范,不規定如何使用
* 以上實現方式 為 V8 引擎的實現方式,也是主流實現方式
### 作用域和閉包
#### 作用域、自由變量
```javascript
// 創建 10 個 `<a>` 標簽,點擊的時候彈出來對應的序號
let a
for (let i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}
```

* 全局作用域
* 函數作用域
* 塊級作用域(ES6新增)
##### 自由變量
* 一個變量在當前作用域沒有定義,但被使用了
* 向上級作用域,一層一層一次尋找,指導找到為止
* 如果找到全局作用域都沒找到,則報錯 xx is not defined
#### 閉包
* 作用域應用的特殊情況,有兩種表現
* 函數作為參數被傳遞
* 函數作用返回值被返回
```javascript
// 函數作為返回值
function create() {
let a = 100
return function () {
console.log(a)
}
}
let a = 200
let fn = create()
fn() // 100
// create()()
```
```javascript
// 函數作為參數
function print(fnn) {
let b = 200
fnn()
}
let b = 100
function fnn() {
console.log(b)
}
print(fnn) // 100
```
> 閉包:自由變量從函數定義的地方開始找,依次向上級作用域查找。
##### 閉包的實際應用
* 隱藏數據
```javascript
function createCache() {
const data = {} // 閉包中的數據,被隱藏,不被外界訪問
return {
set: function (key, val) {
data[key] = val
},
get: function (key) {
return data[key]
},
}
}
const c = createCache()
c.set('a', 100)
// 在外部無法直接訪問data中的數據
// console.log(data.a) // data is not defined
console.log(c.get('a'))
```
#### this 賦值問題
* 常見情況
* 作為普通函數
* 使用 call apply bind
* 作為對象方法被調用
* 在 class 方法中調用
* 箭頭函數
* **this 在各個場景中取值:函數執行的時候確定的,不是函數定義的時候確認的**
```javascript
function fn1() {
console.log(this)
}
fn1() // window
fn1.call({ x:100 }) //{ x: 100 }
const fn2 = fn1.bind({ x: 200 })
fn2() // {x: 200}
```

##### call apply bind 簡介-區別
> 改變函數執行時的上下文,即改變函數運行時的 this 指向
* call 和 apply
```javascript
obj.call(thisObj, arg1, arg2, ...);
obj.apply(thisObj, [arg1, arg2, ...]);
```
> 兩者作用一致,都是把`obj`(即this)綁定到`thisObj`,這時候`thisObj`具備了`obj`的屬性和方法。或者說`thisObj`『繼承』了`obj`的屬性和方法。綁定后會立即執行函數。
>
> 唯一區別是**apply接受的是數組參數**,**call接受的是連續參數**。
bind的使用
```javascript
obj.bind(thisObj, arg1, arg2, ...);
```
> 把obj綁定到thisObj,這時候thisObj具備了obj的屬性和方法。與call和apply不同的是,bind綁定后不會立即執行。需要調用