#### 塊級作用域
```
var a = 3;
let a = 4; //error: has already been declared
var a = 3;
{
let a = 4;
a; //4
}
a; //3
```
let不允許聲明提升
```
console.log(a); //undefined
console.log(b); //ReferenceError
typeof a; //undefined
typeof b; //ReferenceError
var a;
let b;
```
循環引用問題
```
//用let代替
//方案1
for(let i = 0; i < 10; i++){ ... }
//方案2
for(var i = 0; i < 10l i++){ let j = i; .../*操作j*/ }
```
const對于非單值來說,它保證的是地址不變,而不是值不變。
#### spread/rest
```
//spread
function foo(x, y, z) { console.log(x, y, z) }
foo(...[1, 2, 3]); //1 2 3
var a = [2, 3, 4];
var b = [1, ...a, 5]; //[1, 2, 3, 4, 5]
//rest
function foo(x, y, ...z) { console.log(x, y, z) }
foo(1, 2, 3, 4, 5); //1 2 [3, 4, 5]
```
```
//es6把形參轉化為數組
function foo(...args) { ... }
//es5把形參轉化為數組
function foo() { var args = Array.prototype.slice.call(arguments); }
```
#### 默認參數值
```
//es6的默認值相當于x !== undefined ? x : 默認值
function foo(x = 1, y = 2) { ... }
//默認參數的賦值作用域位于()中,()中沒有w的聲明所以向外找到了w,x + 1語句執行時x已經由w + 1生成了,沒問題。但是到z = z + 1語句時,z在函數內部無法找到,而z本身又是形參,因此永遠不會向外找到那個為2的z。
var w = 1, z = 2;
function foo(x = w + 1, y = x + 1, z = z + 1) { console.log(x, y, z); }
foo(); //ReferenceError, z is not defined
```
#### 解構
```
var [a, b, c] = [1, 2, 3];
var {x, y, z} = {x: 1, y: 2, z: 3}
//原理
var {x: a, y: b, z: c} = {x: 1, y: 2, z: 3}
console.log(x, y, z); //ReferenceError
console.log(a, b, c); //1 2 3
```
```
var {x, y, z} = {x: 1, y: 2, z: 3}
//必須用()包起來,否則{}將會被當作是一個塊級作用域,跟{} + [] -> 0一個道理
var x, y, z;
({x, y, z} = {x: 1, y: 2, z: 3});
```
巧用解構來交換兩個變量而不用臨時變量
```
var x = 10, y = 20;
[y, x] = [x, y];
console.log(x, y); //20 10
//[y, x] = [10, 20]; y = 10, x = 20
```
數組重排序
```
var a1 = [1, 2, 3], a2 = [];
[a2[2], a2[0], a2[3]] = a1;
a2; //[2, 3, 1]
```
解構允許重復(key重復)
```
var {a: X, a: Y} = {a: 1};
console.log(X, Y); //1 1
var {a: {x: X, x: Y}, a} = {a: {x: 1}};
X; //1
Y; //1
a; //{x: 1}
({a: X, a: Y, a: [Z]} = {a: [1]});
X.push(2);
Y[0] = 10;
X; //[10, 2]
Y; //[10, 2]
Z; //1
```
解構完成后,返回值依舊是原對象/數組
```
//數組同理
var o = {a: 1, b: 2, c: 3};
var p = {a, b, c} = o; //{a, b, c} = o解構完成后返回o
p === o; //true
var o = {a: 1, b: 2, c: 3};
var a, b, c;
({a} = {b, c} = o);
console.log(a, b, c); //1 2 3
//{b, c} = o解構后返回o -> {a} = o
var p = [4, 5, 4];
[x, y] = [z] = p;
console.log(x, y, z); //4 5 4
```
#### 太多 太少 剛剛好
```
var [a, b] = [1, 2, 3, 4, 5];
a; //1
b; //2
var [, , c] = [1, 2, 3, 4, 5];
c; //3
//多余的值會被設為undefined
var [a, b, c] = [1];
b; //undefined
c; //undefined
```
如果...出現在解構的位置,則執行`集合`操作
```
var a = [2, 3, 4];
//...必須出現在最后的位置
var [b, ...c] = a;
b; //2
c; //[3, 4]
```
默認解構值
```
var [a, b, c = 4] = [1];
a; //1
b; //undefined
c; //4
```
嵌套解構
```
var a1 = [1, [2, 3, 4], 5];
var o1 = {x: {y: {z: 6}}};
var [a, [b, c, d], e] = a1;
var {x: {y: {z: w}}} = o1;
a; //1
b; //2
c; //3
d; //4
e; //5
w; //6
//xyz均為ReferenceError not defined
```
```
var a = [];
var [b, c, ...d] = a;
b; //undefined
c; //undefined
a; //[]
```
```
var defaults = {
options: {
remove: false,
enable: false,
instance: {}
},
log: {
warn: true,
error: true
}
}
var config = {
options: {
remove: false,
instance: null
}
}
//es5
config.options = config.options || {}
config.options.remove = (config.options.remove !== undefined) ? config.options.remove : defaults.options.remove;
config.options.enable = (config.options.enable !== undefined) ? config.options.enable : defaults.options.enable;
...
//es6
let {
options: {
remove = defaults.options.remove,
enable = defaults.options.enable,
instance = defaults.options.instance
} = {},
log: {
warn = defaults.log.warn,
error = defaults.log.error
} = {}
} = config;
config = {
options: {remove, enable, instance},
log: {warn, error}
}
```
#### 對象字面量擴展
```
var x = 2, y = 3;
//簡寫
var o = {x, y};
//簡寫
var o = {w(), z()};
//生成器的簡寫
var o = {*foo(){}};
```
計算屬性
```
//es5
var o = {}, prefix = "user_"
o[prefix + "foo"] = function() { ... };
o[prefix + "bar"] = function() { ... };
//es6
var o = {
[prefix + "foo"]: function() { ... },
[prefix + "bar"]: function() { ... },
*[prefix + "baz"]: function() { ... }
}
```
設定prototype
```
//es6方式1
var a = {};
var o = { __proto__: a };
//es6方式2
var o = {};
Object.setPrototypeOf(o, a);
```
super對象
```
var o1 = { foo() { console.log("o1,foo") } };
var o2 = { foo() { super.foo();console.log("o2.foo"); } };
Object.setPrototypeOf(o2, o1);
o2.foo(); //o1.foo o2.foo
```
#### 模板字符串
```
//模板字符串中回車會自動轉為換行符
var text = `Now is time for
all good men
to come here
`
text; //Now is time for
// all good men
//to come here
function upper(s) { return s.toUpperCase(); }
var who = "reader";
var text = `A very ${upper(`${who}`)} welcome`;
// A very READER welcome
```
標簽模板字面量
```
function foo(strings, ...values) {
console.log(strings);
console.log(values);
}
var desc = "awesome";
foo`Everything is ${desc}!`;
//[ "Everything is", "!" ];
//[ "awesome" ];
```
原始字符串
```
var str = "hello\nworld";
str; //hello
//world
String.raw`hello\nworld`;
//hello\nworld;
String.raw`hello\nworld`.length; //12
```
#### 箭頭函數
箭頭函數的主要設計目的就是以特定的方式改變this的行為,而不是僅僅減少代碼量。
箭頭函數的this綁定不是動態的,而是詞法的。箭頭函數沒有自己的this, 它的this繼承的是父執行上下文里面的this
```
var obj = {
say: function () {
setTimeout(() => {
console.log(this)
});
}
}
obj.say(); // obj
//此時的 this繼承自obj, 指的是定義它的對象obj, 而不是 window!
```
```
var obj = {
say: function () {
var f1 = () => {
console.log(this); // obj
setTimeout(() => {
console.log(this); // obj
})
}
f1();
}
}
obj.say();
//因為f1定義時所處的函數 中的 this是指的 obj,
//setTimeout中的箭頭函數this繼承自f1, 所以不管有多層嵌套,都是 obj
```
```
var obj = {
say: function () {
var f1 = function () {
console.log(this); // window, f1調用時,沒有宿主對象,默認是window
setTimeout(() => {
console.log(this); // window
})
};
f1();
}
}
obj.say();
//都是 window,因為 箭頭函數在定義的時候它所處的環境相當于是window,
//所以在箭頭函數內部的this函數window
```
#### for of循環
for in循環的結果是key,for of循環的結果是value
```
var a = [1, 2, 3, 4];
for(var val of a){
console.log(val); //1 2 3 4
}
//原理
var a = [1, 2, 3, 4];
for(var val, ret, it = a[Symbol.iterator](); (ret = it.next()) && !ret.done);){
val = ret.value;
console.log(val);
}
```
#### 數字字面量擴展
```
//二進制和八進制表示法
var dec = 42;
var oct = 0o52;
var hex = 0x2a;
var bin = 0b101010;
num.toString(n)代表把num轉換成n進制的數字的字符串
```
#### 符號
js新的原生類型:symbol
```
//不能也不應該用new,它并不是構造器
//參數是可選的,它應該是一個Symbol用途的描述
//參數只能是字符串,即便是其他類型,也會強轉為字符串
var sym = Symbol("some description");
typeof sym; //"symbol"
sym.toString(); //"Symbol(some description)"
```
如同str不是String的實例一樣,sym也不是Symbol的實例
```
sym instanceof Symbol; //false
var symObj = Object(sym);
symObj instanceOf Symbol; //true
symObj.valueOf() === sym; //true
```
Symbol的主要意義就是獨一無二的值
```
//由于每一個 Symbol 值都是不相等的,這意味著 Symbol 值可以作為標識符。
const EVT_LOGIN = Symbol("event.login");
//用于對象的屬性名,就能保證不會出現同名的屬性。這對于一個對象由多個模塊構成的情況非常有用,能防止某一個鍵被不小心改寫或覆蓋。
let ms = Symbol();
const o = {};
o[ms] = "a symbol";
//ms這一屬性無法被覆蓋
```
Symbol.for(全局符號注冊)
Symbol.for(str)會在全局中搜索內容為str的symbol,有的話將其返回,沒有的話創建一個并返回
```
var s = Symbol("regist");
var ms = Symbol.for("login");
ms; //Symbol(login)
//直接Symbol()創建的symbol是無法獲取內部的內容的
Symbol.keyFor(s); //undefined
//使用Symbol.for創建的symbol可以獲取內部內容
Symbol.keyFor(ms); //login
```
Symbol作為對象的屬性,雖然確保了該屬性不會被覆蓋,但該屬性也不會出現在一般的枚舉中,除非使用`getOwnPropertySymbols()`
```
var o = { [Symbol("bar")]: "hello", foo: 1, bar: 2 };
Object.getOwnPropertyNames(o); //["foo", "bar"];
Object.getOwnPropertySymbols(o); //[Symbol(bar)]
```
- 你不知道的JS上
- 第一部分 第三章 函數作用域和塊作用域
- 第一部分 第四章 提升
- 第一部分 第五章 閉包
- 第二部分 第一章 關于this
- 第二部分 第二章 this全面解析
- 第二部分 第三章 對象
- 第二部分 第五章 原型
- 第二部分 第六章 行為委托
- 你不知道的JS中
- 第一部分 第二章 值
- 第一部分 第三章 原生函數
- 第一部分 第四章 強制類型轉換
- 第一部分 第五章 語法
- 第二部分 第一章 異步
- 第二部分 第三章 Promise
- 第二部分 第四章 生成器
- 第二部分 第五章 性能
- 你不知道的JS下
- 第一部分 總結
- 第二部分 第二章 語法
- 第二部分 第三章 代碼組織
- 第二部分 第四章 Promise
- 第二部分 第五章 集合
- 第二部分 第六章 新增API
- 第二部分 第七章 元編程
- 第二部分 第八章 ES6之后