# javascript 学习总结

## 基础
* 输入`prompt(...)`
* 比较
  * 如果两边任意一边出现 `true` 或者 `false` 使用 ===
  * 如果两边任意一边可能是特定值`0,"", []` 使用 ===
  * 剩下的所有情况，使用 ==
  * 对于引用类型来说，比较仅仅是比较其引用是否是同一个，另外在和字符串比较时，数组会自动转成字符串
  ```js
  var a = [1,2,3]
  var b = [1,2,3]
  var c = "1,2,3"
  console.log(a == b) // false
  console.log(a == c) // true
  console.log(b == c) // true
  ```
  * switch语句
  ```js
  switch(a) {
    case 2:
    case 10:
      // 2 或者 10 执行
      break;
    default:
      // 条件都不满足默认执行
  }
  ``` 

## ES6
* let 尽量将变量声明放在代码块开始，避免在(Temproal Dead Zone, TDZ)临时死亡区使用而报错
  * 尽量使用for...let，在每次循环中都会声明一个新的变量，生成了一个闭包。避免for里边的函数在调用的时候引用不到正确的变量。
* const声明，相对于保护其中的内容更注重其语意所带来的规范性
* 如果把方法定义在{}内，那么在块外执行会报ReferenceError
* spread/rest`...`放在数组前用于展开数组，放在变量前就是收集变量到一个数组里，例如 `function a(...arg){} a(a,b,c) -> arg = [a,b,c]`
* 解构 `var [a, b, c] = foo()` 如果foo返回的是一个长度为三的数组其内容将会被依次赋值给abc
  * 对象属性复制模式`var {a, b, c} = foo()`这里省略的其实是`a:, b:, c:`，对象的属性名要和函数返回的属性名相同，然后将值一一赋值给属性值`var {a: x, b: y, c: z} = foo()`最后使用的是`xyz`而不是`abc`
    * 对于这个形式，如果省略声明符就需要用小括号括起来`({a, b, c} = foo())`
  * 对象属性名的计算属性`var which = 'w'; o1 = {which: 'w'}; => {which: 'w'}   o2 = {[which]: 'w'}; => {w: 'w'}`使用中括号即可
  * 简单的交换两个元素`[a, b] = [b, a]`'
  * 允许出现多次列出同一个源属性`{a: {x: X, x: Y}, a} = {a: {x: 1}} => X == X == 1, a = {x :1}`
  * 不必都写在一行里，解构的目的不是为了打字更少，而是为了可读性更强
  * 赋值的时候用不到的东西可以抛弃`[,a,b] = [1, 2, 3] => a = 2, b = 3`
  * 赋值的时候多余的东西会赋值undefined`[a, b] = [1] => a = 1, b = undefined`
  * 赋值的时候可以使用spread/rest`...`进行收集操作`[...a,b] = [1, 2, 3] => a = [1, 2], b = 3`
  * 赋值的时候可以使用默认值`{a, b: WW = 20} = {a: 10}  => a = 10, WW = 20`
  * 赋值的时候可以嵌套结构
  * 在形参中使用这个就接近了命名参数，得到了任意位置的可选参数功能，设置默认参数`{x = 10} = {}`
* 对象字面量扩展
  * 简洁属性`{x}`
  * 简洁方法`{x(){}}`
    * 这个操作会生成一个匿名的函数，如果需要递归请不要使用
* 模板字面量``...${foo(`${name}s`)}``
  ```js
    function foo(strings, ...args) {
      console.log(strings)
      console.log(args)
    }
    var desc = "awesome"
    foo`Every thing is ${desc}!`
    // ["Every this is ", "!"]
    // ["awesome"]
    // 配合Strng.resuce可以用来处理字符串
  ```
* 箭头函数
  * 合理使用，函数越短越适合用，函数长了反而会使函数边界模糊
  * 主要的功能是为了this的规范化而不是减少代码量
  * 尽可能减少非必要情况下的箭头函数使用
* for...of循环
  * for...in 遍历数组的时候遍历的是index for...of遍历的是值
  * 适用所有带迭代器的变量
  * 自带迭代器的有Arrays,Strings,Generators,Collections/TypedArrays
  * ...可以是赋值表达式也可以是声明
* 正则匹配
  * 定点标识
    * `re = /d/ re.lastIndex`
    * 定点定位`/f../y`会从lastIndex开始搜索，不会更新lastIndex
    * g模式使用exec 会更新lastIndex
    * ^ 总是指向输入起始处的锚点，不受lastIndex的影响
    * y 加上 ^ 加上lastIndex > 0 是一个不兼容的组合，总是会导致失败
  * flags
    * `re = /foo/ig re.flags => gi`查看应用的哪些标识
    * 顺序总是`gimuy`
* 数字字面量扩展
  * `doc = 12`
  * `oct = 0o52`
  * `hex = 0x2a`
  * `bin = 0b101010`
  * `a = 42 a.toString(10 || 8 || 6 || 2)`可以进行转换
* Unicode
  * `var snowman = '\u2603'` \u转义
  * `var gclef = '\uD834\uDD1E'` 替代对
  * `var gclef = '\u{1D11E}'` 码点转义
  * 精确判断字符串长度
    * `[...str].length`
    * `Array.from(str).length`
* 符号`symbol`
  * 例子`var sym = Symbol('something')`
  * 不应该使用new
  * 使用typeof识别
  * 获取全局符号（如果全局不存在则新建一个放在全局）`Symbol.for("exec.name")`
  * 通过符号获取字符串`Symbol.keyFor(sym)`
  * 设置为对象的属性之后不可枚举，但是可以通过`Object.getOwnPropertySymbols(sym)`获取
* 迭代器
  * 可以用来满足消费者生产者模型
  * 必须拥有一个next()
  * 可选return()用于消费者向生产者发送所有消费过程完成的信号，生产者应进行销毁程序
  * 可选throw()用于向生产者发送错误信号，终止生产
  * for of 可以消费迭代器，使用break退出会触发return
  * 通过rest/gather运算符可以消耗迭代器`it = arr[Symbol.iterator]() var [x, y] = it; var [z, ...w] = it;` it最终会被消耗光
* 生成器
  * `function *foo() {yield 1;} it = foo() it.next() // value 1 ` next里边的参数会替换掉 yield后边的东西
  * 就是用迭代器来控制生成器
  * 可以用throw()配合try catch实现错误的双向或 内外双向传递
  * 每次被调用的时候都会是一个新的生成器
  * 加* 
* 模块
  * `import` 和 `export` 必须在使用它们的最顶层作用域
  * 命名导出
  ```js
  // 方式一
  export var bar = [1,3,4]; export function foo() {}
  // 方式二
  var bar = []; function foo() {}; export {bar, foo}
  ```
  * 导出的是在模块内的最后的值
  ```js
  var a = 1
  export { a }
  a = 2
  // -> 最后导出的是2而不是1
  ```
  * 导出可以指定名称
  ```js
  var a = 1
  export { a as b }
  // -> 只有b可以被导入，a被隐藏
  ```
  * 导入不是赋值操作，而是类似指针一样的绑定
  * es6倾向于一个文件一个export，可以使import变得简单
  * 默认导出的两种形式
  ```js
  export default function foo() {} // -> 导出的是表达式的绑定
  function foo() {} export default foo // -> 同上,即`export default`接受的是一个表达式
  function foo() {} export { foo as default } // -> 导出的是标识符的绑定，也就是导出foo在文件中最后的值
  ```
  * 一个模块只能有一个default
  ```js
  // 方式一
  export default function foo() {}
  export function bar() {}
  // 方式二
  function foo() {}
  function bar() {}
  export { foo as default, bar }
  ```
  * 可以导出别的模块的导出 -> 感觉可以用来集成模块，，也没啥用
  ```js
  export { foo, bar } from 'baz'
  export { foo as FOO, bar as BAR } from 'baz'
  export * from 'baz'
  ```
  * 导入模块如果有default`import foo from "baz"`引入那个default
  * 对导入进行重命名`import { foo as Foo } from "baz" -> Foo()`
  * 对复合导入`import FOOFN, { bar, baz } from "baz"`
  * 命名空间导入`import * as foo from "foo" -> foo.bar()`当前只能全部导入
    * 如果有默认导出，就是`foo.default()`
    * 默认+命名空间`import foofn, * as hello from "world"` `foofn -> hello.default` 尽量不要用这种迷惑操作
  * 所有导入的绑定都是只读的，所有的赋值操作都会报错
  * 所有导入都会被提升，全局访问
* 类
  * 使用`class`声明类，类名大写
  ```js
  class Foo {
    constructor(a, b) {
      this.x = a;
      this.y = b;
    }
    gimmeXY() {
      return this.x * this.y
    }
  }
  ```
  * 不能使用call所以只能通过new实例化
  * 类不能被提升，所以实例化之前必须声明
  * 声明一个class并没有创建一个同名的全局对象属性
  * class本身并不是一个实体，而是一个包裹着其他像函数和属性这样的具体实体并把它们组合到一起的元概念
  * class也可以是一个表达式
  ```js
  var x = class Y {} // -> 对于把类定义(严格说是构造器本身)作为函数参数传递，或者把它赋值给一个对象属性的时候特别管用
  ```
  * 把类理解为一个宏，用于自动产生一个prototype对象
  * 类对extends以及super提供语法糖
  ```js
  class Bar extends Foo {
    constructor(a, b, c) {
      super(a, b)
      this.z = c
    }
    gimmeXYZ() {
      return super.gimmeXY() * this.z
    }
  }
  ```
  * 如果子类不定义构造函数就会
  ```js
  constructor(...args) {
    super(...args)
  }
  ```
  * 子类不在构造器中使用`super`是无法使用`this`的
  * 可以拓展原生类
  ```js
  class MyCoolArray extends Array {
    first() {return this[0]}
    last() {return this[this.length -1 ]}
  }
  a = new MyCoolArray(1, 2, 3)
  a.first()
  ```
  * `new.target`在构造器中访问new直接调用的构造器
* Promise
  * 决议的结果只可能是完成或者拒绝，附带一个可选的单个值，如果拒绝，那么最终的值称为原因
  * 只能被完成或者拒绝一次，之后完成或者拒绝的操作都会被忽略
  * 创建一个Promise
  ```js
  var p = new Promise(function(resolve, reject) {
    // 成功
    resolve(arg)
    // 采用另一个Promise的决议值
    resolve(promise)
    // 拒绝
    reject(why)
  })
  // 调用
  // then接受两个函数第一个是完成，第二个是拒绝时执行
  p().then(
    function fulfilled(res) {},
    funcrion rejected(err){},
  )
  ```
  * 不论是`.then`还是`.catch`都会返回一个Promise，可以用`.then`进行处理
  * 只要是函数里边包含`then`方法都会被认为是一个`thenable`所以不要盲目信任自称是Promise的东西
  * Promise API
    * `Promise.resolve`
    ```js
    var p1 = Promise.resolve(42)
    var p2 = new Promise(function pr(resolve){resolve(42)})
    // p1 和 p2 的最终行为方式是相同的
    ```
    这个接口提供了一个规范化Promise的方法，可以将刚刚不可信任的thenable放进去规范化
    * `Promise.reject`
      * 和上边的一样创建一个立即被拒绝的promise，但是如果他的参数是Promise，它不会理会传进来得Promise的决议值
    * `Promise.all([])`
      * 接受一个或者多个值的数组，比如立即值、Promise、thenable，它返回一个promise，如果里边的所有值都是完成则决议结果是完成，其中某一个被拒绝这个Promise就立即被拒绝
      * 是等待所有都完成或者第一个拒绝
    * `Promise.rase([])`
      * 等待第一个完成或者拒绝
      * 也就是说返回第一个完成的决议值
    * 
