#### 块级作用域
```
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之后