JavaScript ES6

let & const

  • 变量严格只在作用域里有效
  • 不能多次重复声明
  • 内层可以声明外层的变量, 但改变不了外部的变量
  • 必须先声明再使用(死区)
  • (let, const都遵循)

const指向的地址, 如果const指向了一个对象, 那么对象不能被替换(因为地址变了), 但对象的内容可以变化 比如指向了一个给对象, 那么对象的属性可以改变, 指向一个数组, 数组可以增删 但都不能真能赋新值

如果要冻结对象:

const foo = Object.freeze({});
// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;
将一个对象连同属性彻底冻结:
var constantize = (obj) => {
   Object.freeze(obj);
   Object.keys(obj).forEach( (key, value) => {
     if ( typeof obj[key] === 'object' ) {
       constantize( obj[key] );
     }
   });
};

全局对象

  • 在浏览器中指的是 windows 对象, 在 node.js 中指的是 global 对象
  • ES6中, let, const, class定义的对象, 不再是全局对象

解构赋值

let [head, ...tail] = [1, 2, 3, 4];
// head // 1
// tail // [2, 3, 4]

let [x, y, ...z] = ['a'];
// x // "a"
// y // undefined // 解构不成功的会成为undefined
// z // []
  • 事实上,只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值。否则就不行
function* fibs() {
   var a = 0;
   var b = 1;
   while (true) {
     yield a;
     [a, b] = [b, a + b];
   }
 }
 
 var [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5
  • 解构赋值可以有默认值, 默认值也可以是函数, 但是是惰性求值的, 如果有值, 则不会触发函数
function f() {
   console.log('aaa');
}
 
let [x = f()] = [1]; // f()根本不会执行 // 定义一个数组,并且给里面一个元素命名为x
  • 解构不仅可以用于数组,还可以用于对象。
var { foo, bar } = { foo: "aaa", bar: "bbb" };
// foo: "aaa"
// bar: "bbb"
// 给对象赋值要注意
var { foo: baz } = { foo: "aaa", bar: "bbb" };
// baz: “aaa” // foo的结果用baz接,然后我们传的是aaa
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
// f: 'hello'
// last: 'world'
// 干脆这么理解,我要一个f,一个l,值分别来自于obj对象的first和last
  • 对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。 这一节比较猛,多留意一下
let { log, sin, cos } = Math;

上面代码将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多。

  • 属性赋值:
let {length : len} = 'hello';
// len: 5
// length: 3 (???) 难道不是我要一个len,值来自于'hello`字符串的lengh属性吗?
  • 函数参数的解构也可以使用默认值。
function move({x = 0, y = 0} = {}) { // 双重默认值,飒,即给一个空值就默认为传{}
 return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

// 为了理解深刻点,我们把不传时的默认值改一下
function move({x = 0, y = 0} = {y:77}) { // 双重默认值,飒,即给一个空值就默认为传{}
 return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 77] // ««« 唯一的区别在这里

上面代码中,函数move的参数是一个对象,通过对这个对象进行解构,得到变量x和y的值。如果解构失败,x和y等于默认值。 注意,下面的写法会得到不一样的结果。

function move({x, y} = { x: 0, y: 0 }) {
    return [x, y];
}

// 这一次,不给对象赋默认值了,只有完全不给对象,才会给默认值 
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

所以我说双重默认值,一个是空对象的默认值,一个是有对象的话,里面的属性的默认值

  • undefined就会触发函数参数的默认值。
[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]

模板字符串嵌套

const tmpl = addrs => `
   <table>
   ${addrs.map(addr => `
     <tr><td>${addr.first}</td></tr>
     <tr><td>${addr.last}</td></tr>
   `).join('')}
   </table>
 `;
  • 如果需要引用模板字符串本身,在需要时执行,可以像下面这样写。
// 写法一
let str = 'return ' + '`Hello ${name}!`';
let func = new Function('name', str);
func('Jack') // "Hello Jack!"

 // 写法二
let str = '(name) => `Hello ${name}!`';
let func = eval.call(null, str);
func('Jack') // "Hello Jack!"

我只能看懂写法2...

正则

  • 点(.)字符在正则表达式中,含义是除了换行符以外的任意单个字符。
  • 对于码点大于0xFFFFUnicode字符,点字符不能识别,必须加上u修饰符。
var s = '𠮷';

/^.$/.test(s) // false
/^.$/u.test(s) // true
  • y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。
    • 同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。
var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;
 
r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]

r1.exec(s) // ["aa"]
r2.exec(s) // null
  • 注意, 是对exec方法而言!!!
  • 进一步说,y修饰符号隐含了头部匹配的标志^。
/b/y.exec('aba')
// null

上面代码由于不能保证头部匹配,所以返回null。y修饰符的设计本意,就是让头部匹配的标志^在全局匹配中都有效。

单单一个y修饰符对match方法,只能返回第一个匹配,必须与g修饰符联用,才能返回所有匹配。(我们会以为例子中的值是a\d连续的, 所以会被y匹配到, 并不是)

'a1a2a3'.match(/a\d/y) // ["a1"]
'a1a2a3'.match(/a\d/gy) // ["a1", "a2", "a3"]

数组

  • Array.from方法则是还支持类似数组的对象。
    • 所谓类似数组的对象,本质特征只有一点,即必须有length属性。
    • 因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。
Array.from({ length: 3 });
// [ undefined, undefined, undefinded ]
Array.from("hello");
// ['h', 'e', 'l', 'l', 'o']
  • Array.of方法用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
  • 这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。
Array() // []
Array(3) // [, , ,] // (3)理解为了个数
Array(3, 11, 8) // [3, 11, 8] // (3)理解为了值
  • 注意判断NaN的用法
[NaN].findIndex(y => Object.is(NaN, y))

如何import

import _ from "lodash";

这种简略的表达方法等价于

import {default as _} from "lodash"

refs: Spread Syntax