ES6 学习笔记

461 阅读25分钟

新的变量声明方式

let - 块级变量

<!--ES5-->
console.log(a)
var a = 20; // undefined
<!--ES6-->
{
    let a = 20;
}
console.log(a) // a is not defined

解构赋值

// 基本
let [a,b,c] = [1,2,3]; //a => 1 b => 2 c => 3
// 可忽略
let [a,,b] = [1,2,3]; //a => 1 b => 3
// 剩余运算符
let [a,...b] = [1,2,3];//a => 1 b => [2,3]
// 字符串
let [a, b, c, d, e] = 'hello'; // a => 'h' b => 'e' c => 'l' d => 'l' e => 'o'
// 默认赋值
let [a = 3, b = a] = []; // a => 3 b => 3
// 当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果
let [a = 3, b = a] = [];     // a = 3, b = 3
let [a = 3, b = a] = [1];    // a = 1, b = 1
let [a = 3, b = a] = [1, 2]; // a = 1, b = 2

任何获取对象属性的场景都可以是使用解构赋值来减少我们的代码量 - 如数组

var arr = [1,2,3];
var [a,b,c] = arr;
console.log(a) // 1
console.log(b) // 2
console.log(c) // 3

对象模型

const props = {
    className = 'tigger-button',
    loading:false,
    clicked:true,
    disabled:'disabled'
}
//ES5 当我们需要 loading 与 clicked 
var loading = props.loading;
var clicked = props.clicked;
//ES6 支持给一个默认值 def = false解析结构
const { loading,clicked,def=false } = props;
console.log(className) // tigger-button
console.log(loading) // false
// 对象解构
let { baz : foo } = { baz : 'ddd' }; // foo => dd
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { y }] } = obj;
// x = 'hello'
// y = 'world'
// y = 'world'

字符串

判断字符串是否包含子串 includes、startsWith、endsWith

includes、startsWith、endsWith 可接收两个参数 ('需要搜索的字符串','可选的搜索起始位置索引')
includes - 返回布尔值,判断是否找到参数字符串

'apple'.includes('p') // true

startsWith - 返回布尔值,判断参数字符串是否在头部

'apple'.startsWith('a') // true

endsWith - 返回布尔值,判断参数字符串是否在尾部

'apple'.endsWith('e') // true

字符串重复 repeat()

返回新的字符串,表示将字符串重复指定次数返回

console.log('hello,'.repeat(2)) // "hello,hello"
// 参数是小数 向下取整
// 参数是0至-1、NaN 等同于0
// 参数小于-1 或者 是Infinity 会报错
// 传入字符串,自动将参数转化为数字

字符串补全 padStart、padEnd

padStart、padEnd可接收两个参数 ('指定生成的字符串的最小长度','用来补全的字符串,如不传默认用空格填充')
padStart - 返回新的字符串 表示用参数字符串从头部补全原字符串

console.log("h".padStart(5,"o")); // "ooooh"

padStart - 返回新的字符串 表示用参数字符串从尾部补全原字符串

console.log("h".padStart(5,"o")); // "hoooo"
// 如果指定的长度大于或者等于原字符串的长度,则返回原字符串
console.log("hello".padStart(5,"A"));  // "hello"
// 如果原字符串加上补全字符串大于指定长度,则截去超出位数的补全字符串
console.log("hello".padEnd(10,",world!"));  // "hello,worl"
console.log("hello".padEnd(14,",world!")); // "hello,world!,w"

模板字符串

使用``将整个字符串包裹起来,而在其中使用${}来包裹一个变量或者一个表达式
可应用在多语言 或者 防止用户输入恶意内容

//ES5
var a = 20;
var b = 30;
var string = a + "+" + b + "=" + (a + b);
//ES6
var a = 20;
var b = 30;
var string = `算法:${a}+${b}=${a+b}`; // '算法:'20+30=50
alert`Hello world!`;  // 等价于 alert('Hello world!');
// 字符串中调用函数
function test(){
    return 'test'
}
var str = `this is ${test()}`;
console.log(str) // "this is test"
function say(stringArr,...values){
    var result = "";
    // stringArr 模板字符串 数组
    // values 模板变量 数组
    for(var i=0;i<stringArr.length;i++){
        result += stringArr[i];
        if(values[i]){
            result += values[i];
        }
    }
    return result
}
var name = 'kay';
var age = 27;
console.log(say`My name is ${name}, i am ${age} years`) // "My name is kay, i am 27 years"
// 等价于
console.log(say(['My name is ',', i am ',' years'],'kay',27)); // "My name is kay, i am 27 years"

数组

Array.of()

将参数中所有值作为元素形成数组

console.log(Array.of(1,2,3,4)); // [1,2,3,4]
console.log(Array.of(1, '2', true)); // [1, '2', true]
console.log(Array.of()); // []

Array.from();

将类数组对象或可迭代对象转化为数组
参数 Array.from('类数组','map函数对每个元素进行处理,放入数组的是处理后的元素','指定map函数执行时的this对象')

// 参数为数组 返回与原数组一样的数组
console.log(Array.from([1, 2])); // [1, 2]
// 对每个元素进行*2处理
console.log(Array.from([1, 2, 3], (n) => n * 2)); // [2, 4, 6]
// 用指定map函数执行时的this对象
let mapFun = {
    fo:(n) => n * 2
}
let arrayLike = [1,2,3];
console.log(Array.from(arrayLike,(n)=> this.do(n),mapFuns)) // [2,4,6]
// 类数组 一个类数组必须含有length 属性,且元素属性名必须是数值或者可转换为数值的字符
// 没有length属性,则返回空数组
// 元素属性名不为数值且无法转换为数值,返回长度为length,元素值为undefined的数组
var arr = Array.from({
    0:'1',
    1:'2',
    2:3,
    length:3
})
console.log(arr) // [1,2,3]
// 转换map
let map = new Map();
map.set('key0','value0');
map.set('key1','value1');
console.log(Array.from(map)) // [['key0','value0'],['key1','value1']]
// 转换set
let arr = [1,2,3];
let set = new Set(arr);
console.log(Array.from(set)); // [1,2,3]
// 转换字符串
let str = 'abc'
console.log(Array.from(str)) // ["a","b","c"]

Array.find

接受一个回调函数 返回一个满足过滤条件的元素 如匹配不到则返回一个undefined 查找到第一个满足条件的元素后就结束执行

const characters = [
    { id: 1, name: 'ironman' },
    { id: 2, name: 'black_widow' },
    { id: 3, name: 'captain_america' },
    { id: 4, name: 'captain_america' },
];
function getCharacter(name) {
    return character => character.name === name;
}
console.log(characters.find(getCharacter('captain_america')));
//{ id: 3, name: 'captain_america' }

Array.findIndex

查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引
查找到第一个满足条件的元素后就结束执行

// 参数1:回调函数
// 参数2(可选):指定回调函数中的 this 值
let arr = Array.of(1, 2, 1, 3);
console.log(arr.findIndex(item => item = 1)); // 0

Array.fill()

将一定范围索引的数组元素内容填充为单个指定的值

// 参数1:用来填充的值
// 参数2:被填充的起始索引
// 参数3(可选):被填充的结束索引,默认为数组末尾
let arr = Array.of(1,2,3,4);
console.log(arr.fill(0,1,2)) // [1,0,3,4]

Array.copyWithin()

将一定范围索引的数组元素修改为此数组另一指定范围索引的元素

// 参数1:被修改的起始索引 可以为负数,从后面数起
// 参数2:被用来覆盖的数据的起始索引
// 参数3(可选):被用来覆盖的数据的结束索引,默认为数组末尾
console.log([1, 2, 3, 4].copyWithin(0,2,4)); //
console.log([1, 2, 3, 4].copyWithin(-2, 0)); //
console.log([1, 2, ,4].copyWithin(0, 2, 4)); //

entries()

遍历键值对

for(let [key,value] of ['a','b'].entries()){
    console.log(key,value);
    // 0 "a"
    // 1 "b"
}
// 不使用 for ... of循环
let entries = ['a','b'].entries();
console.log(entries.next().value); // [0, "a"]
console.log(entries.next().value); // [1, "b"]
// 数组含空位
console.log([...[,'a'].entries()]); // [[0, undefined], [1, "a"]]

keys()

遍历键名

for(let key of ['a', 'b'].keys()){
    console.log(key);
    // 0
    // 1
}
// 数组含空位
console.log([...[,'a'].keys()]); // [0, 1]

values()

遍历键值

for(let value of ['a', 'b'].values()){
    console.log(value);
    // "a"
    // "b"
}
// 数组含空位
console.log([...[,'a'].values()]); // [undefined, "a"]

Array.includes

检查数组是否包含某个元素 返回 true false
与Set和Map的has方法区分,Set的has方法用于查找值;Map的has方法用于查找键名

// 参数1:包含的指定值
var arr = [1,2,3,4,5,5];
arr.includes(1); // true
arr.includes(6); // false
// 参数2:可选,搜索的起始索引,默认为0
[1, 2, 3].includes(1, 2); // false
// NaN 的包含判断
[1, NaN, 3].includes(NaN); // true

flat()

嵌套数组转一维数组

console.log([1 ,[2, 3]].flat()); // [1, 2, 3]
// 指定转换的嵌套层数
console.log([1, [2, [3, [4, 5]]]].flat(2)); // [1, 2, 3, [4, 5]]
// 不管嵌套多少层
console.log([1, [2, [3, [4, 5]]]].flat(Infinity)); // [1, 2, 3, 4, 5]
// 自动跳过空位
console.log([1, [2, , 3]].flat());// [1, 2, 3]

flatMap()

先对数组中每个元素进行了处理,在对数组执行flat()方法

// 参数1:遍历函数,该遍历函数可接受3个参数:当前元素,当前元素索引,园数组
// 参数2:指定遍历函数中this的指向
console.log([1, 2, 3].flatMap(n => [n * 2])); // [2, 4, 6]

Array.filter

接受一个回调函数 返回一个满足过滤条件的新数组 如匹配到返回一个新数组

const characters = [
    { id: 1, name: 'ironman' },
    { id: 2, name: 'black_widow' },
    { id: 3, name: 'captain_america' },
    { id: 4, name: 'captain_america' },
];
function getCharacter(name) {
    return character => character.name === name;
}
console.log(characters.filter(getCharacter('captain_america')));
//[{ id: 3, name: 'captain_america' },{ id: 4, name: 'captain_america' },]

Array.some

接受一个回调函数 返回是否包含满足过滤条件 true false

const characters = [
  { id: 1, name: 'ironman', env: 'marvel' },
  { id: 2, name: 'black_widow', env: 'marvel' },
  { id: 3, name: 'wonder_woman', env: 'dc_comics' },
];
function hasCharacterFrom(env) {
  return character => character.env === env;
}
console.log(characters.some(hasCharacterFrom('marvel')));// true

Array.reduce

方法接收一个函数作为累加器,数组中的每个之从左到右开始循环 最终为一个值

// previousValue 上一次调用回调返回的值 或者是提供的初始值
// currentValue 数组中当前处理的元素
// index 当前元素所在数组的牵引
// array 调用reduce的数组
// initialValue 放在最后 初始值
[0,1,2,3,4].reduce(function(previousValue, currentValue, index, array){
    console.log(previousValue)
    console.log(currentValue)
    return previousValue + currentValue;
    // 10
});

扩展运算符

ES6中使用... 来表示展开运算符,它可以将数组方法或者对象进行展开。

<!--数组-->
var arr1 = [1,2,3];
var arr2 = [...arr1,10,20,30] //[1,2,3,10,20,30]

<!--对象-->
var obj1 = {
    a:1,
    b:2,
    c:3
}
var obj2 = {
    ...obj1,
    d:4,
    e:5,
    f:6
}
// obj2 {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}

展开运算符还经常运用在解析结构之中,如不确定props还有多少个数据,就可以利用展开运算符来处理数据

const props = {
    size:1,
    src:'xxx',
    mode:'si'
}
const { size, ...others } = props;
console.log(others) //{src: "xxx", mode: "si"}

运用在函数的参数中 表示函数的不确定参数,只有放在最后才能作为函数的不定参数,否则会报错

var add = (a,b,...more) =>{
    return more.reduce((m,n) => m + n) + a + b;
}
console.log(add(1,2,3,4,5,6,7,8,9,10)) // 55
// 复制数组
let arr = [1,2],
    arr1 = [...arr];
console.log(arr1); // [1,2]
// 数组含空位
let arr2 = [1,,3],
    arr3 = [...arr2];
console.log(arr3); // [1,undefined,3]
// 合并数组
console.log([0,...[1,2],...[3,4]]) // [0,1,2,3,4]

数值

Number.isFinite()

用于检查一个数值是否为有限的( finite ),即不是 Infinity

console.log( Number.isFinite(1));   // true
console.log( Number.isFinite(0.1)); // true

// NaN 不是有限的
console.log( Number.isFinite(NaN)); // false
console.log( Number.isFinite(Infinity));  // false
console.log( Number.isFinite(-Infinity)); // false

// Number.isFinate 没有隐式的 Number() 类型转换,所有非数值都返回 false
console.log( Number.isFinite('foo')); // false
console.log( Number.isFinite('15'));  // false
console.log( Number.isFinite(true));  // false

Number.isNaN()

用于检查一个值是否为 NaN 。

console.log(Number.isNaN(NaN));      // true
console.log(Number.isNaN('true'/0)); // true

// 在全局的 isNaN() 中,以下皆返回 true,因为在判断前会将非数值向数值转换
// 而 Number.isNaN() 不存在隐式的 Number() 类型转换,非 NaN 全部返回 false
Number.isNaN("NaN");      // false
Number.isNaN(undefined);  // false
Number.isNaN({});         // false
Number.isNaN("true");     // false

Number.isInteger()

用于判断给定的参数是否为整数。

Number.isInteger(0); // true
// JavaScript 内部,整数和浮点数采用的是同样的储存方法,因此 1 与 1.0 被视为相同的值
Number.isInteger(1);   // true
Number.isInteger(1.0); // true

Number.isInteger(1.1);     // false
Number.isInteger(Math.PI); // false

// NaN 和正负 Infinity 不是整数
Number.isInteger(NaN);       // false
Number.isInteger(Infinity);  // false
Number.isInteger(-Infinity); // false
 
Number.isInteger("10");  // false
Number.isInteger(true);  // false
Number.isInteger(false); // false
Number.isInteger([1]);   // false

// 数值的精度超过 53 个二进制位时,由于第 54 位及后面的位被丢弃,会产生误判
Number.isInteger(1.0000000000000001) // true

// 一个数值的绝对值小于 Number.MIN_VALUE(5E-324),即小于 JavaScript 能够分辨的最小值,会被自动转为 0,也会产生误判
Number.isInteger(5E-324); // false
Number.isInteger(5E-325); // true
Number.isSafeInteger()
用于判断数值是否在安全范围内。
Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1); // false
Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1); // false

Math 对象的扩展

ES6 在 Math 对象上新增了 17 个数学相关的静态方法,这些方法只能在 Math 中调用。

Math.cbrt

用于计算一个数的立方根。

Math.cbrt(1);  // 1
Math.cbrt(0);  // 0
Math.cbrt(-1); // -1
// 会对非数值进行转换
Math.cbrt('1'); // 1
 
// 非数值且无法转换为数值时返回 NaN
Math.cbrt('hhh'); // NaN

Math.imul

两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。

// 大多数情况下,结果与 a * b 相同 
Math.imul(1, 2);   // 2
 
// 用于正确返回大数乘法结果中的低位数值
Math.imul(0x7fffffff, 0x7fffffff); // 1

Math.hypot

用于计算所有参数的平方和的平方根。

Math.hypot(3, 4); // 5
 
// 非数值会先被转换为数值后进行计算
Math.hypot(1, 2, '3'); // 3.741657386773941
Math.hypot(true);      // 1
Math.hypot(false);     // 0
 
// 空值会被转换为 0
Math.hypot();   // 0
Math.hypot([]); // 0
 
// 参数为 Infinity 或 -Infinity 返回 Infinity
Math.hypot(Infinity); // Infinity
Math.hypot(-Infinity); // Infinity
 
// 参数中存在无法转换为数值的参数时返回 NaN
Math.hypot(NaN);         // NaN
Math.hypot(3, 4, 'foo'); // NaN
Math.hypot({});          // NaN

Math.clz32

用于返回数字的32 位无符号整数形式的前导0的个数。

Math.clz32(0); // 32
Math.clz32(1); // 31
Math.clz32(0b01000000000100000000000000000000); // 1
 
// 当参数为小数时,只考虑整数部分
Math.clz32(0.5); // 32
 
// 对于空值或非数值,会转化为数值再进行计算
Math.clz32('1');       // 31
Math.clz32();          // 32
Math.clz32([]);        // 32
Math.clz32({});        // 32
Math.clz32(NaN);       // 32
Math.clz32(Infinity);  // 32
Math.clz32(-Infinity); // 32
Math.clz32(undefined); // 32
Math.clz32('hhh');     // 32

Math.trunc

用于返回数字的整数部分。

Math.trunc(12.3); // 12
Math.trunc(12);   // 12
 
// 整数部分为 0 时也会判断符号
Math.trunc(-0.5); // -0
Math.trunc(0.5);  // 0
 
// Math.trunc 会将非数值转为数值再进行处理
Math.trunc("12.3"); // 12
 
// 空值或无法转化为数值时时返回 NaN
Math.trunc();           // NaN
Math.trunc(NaN);        // NaN
Math.trunc("hhh");      // NaN
Math.trunc("123.2hhh"); // NaN

Math.fround

用于获取数字的32位单精度浮点数形式。

// 对于 2 的 24 次方取负至 2 的 24 次方之间的整数(不含两个端点),返回结果与参数本身一致
Math.fround(-(2**24)+1);  // -16777215
Math.fround(2 ** 24 - 1); // 16777215
 
// 用于将 64 位双精度浮点数转为 32 位单精度浮点数
Math.fround(1.234) // 1.125
// 当小数的精度超过 24 个二进制位,会丢失精度
Math.fround(0.3); // 0.30000001192092896
// 参数为 NaN 或 Infinity 时返回本身
Math.fround(NaN)      // NaN
Math.fround(Infinity) // Infinity
 
// 参数为其他非数值类型时会将参数进行转换 
Math.fround('5');  // 5
Math.fround(true); // 1
Math.fround(null); // 0
Math.fround([]);   // 0
Math.fround({});   // NaN

Math.sign

判断数字的符号(正、负、0)。

Math.sign(1);  // 1
Math.sign(-1); // -1
 
// 参数为 0 时,不同符号的返回不同
Math.sign(0);  // 0
Math.sign(-0); // -0
 
// 判断前会对非数值进行转换
Math.sign('1');  // 1
Math.sign('-1'); // -1  
 
// 参数为非数值(无法转换为数值)时返回 NaN
Math.sign(NaN);   // NaN 
Math.sign('hhh'); // NaN

Math.expm1()

用于计算 e 的 x 次方减 1 的结果,即 Math.exp(x) - 1 。

Math.expm1(1);  // 1.718281828459045
Math.expm1(0);  // 0
Math.expm1(-1); // -0.6321205588285577
// 会对非数值进行转换
Math.expm1('0'); //0
// 参数不为数值且无法转换为数值时返回 NaN
Math.expm1(NaN); // NaN

Math.log1p(x)

用于计算1 + x 的自然对数,即 Math.log(1 + x)

Math.log1p(1);  // 0.6931471805599453
Math.log1p(0);  // 0
Math.log1p(-1); // -Infinity
// 参数小于 -1 时返回 NaN
Math.log1p(-2); // NaN

Math.log10(x)

用于计算以 10 为底的 x 的对数。

Math.log10(1);   // 0
// 计算前对非数值进行转换
Math.log10('1'); // 0
// 参数为0时返回 -Infinity
Math.log10(0);   // -Infinity
// 参数小于0或参数不为数值(且无法转换为数值)时返回 NaN
Math.log10(-1);  // NaN

Math.log2()

用于计算 2 为底的 x 的对数。

Math.log2(1);   // 0
// 计算前对非数值进行转换
Math.log2('1'); // 0
// 参数为0时返回 -Infinity
Math.log2(0);   // -Infinity
// 参数小于0或参数不为数值(且无法转换为数值)时返回 NaN
Math.log2(-1);  // NaN

双曲函数方法

Math.sinh(x): 用于计算双曲正弦。
Math.cosh(x): 用于计算双曲余弦。
Math.tanh(x): 用于计算双曲正切。
Math.asinh(x): 用于计算反双曲正弦。
Math.acosh(x): 用于计算反双曲余弦。
Math.atanh(x): 用于计算反双曲正切。

指数运算符

1 ** 2; // 1
// 右结合,从右至左计算
2 ** (2 ** 3); // 256 2的8次方 
// **=
let exam = 2;
exam ** = 2; // 4

ES6 对象

对象字面量

当属性与值得变量同名时间

const name = "jana";
const age = 20;
//ES6 简写
var person1 = {
    name,
    age,
    getName(){
        return this.name
    }
}
//ES5
var person2 = {
    name:name,
    age:age,
    getName:function getName(){
        return this.name
    }
}
// 如果是Generator 函数,则要在前面加一个星号
const obj = {
  * myGenerator() {
    yield 'hello world';
  }
};
//等同于
const obj = {
  myGenerator: function* () {
    yield 'hello world';
  }
};

属性名表达式 对象字面量中可以使用中括号作为属性,表示属性也能是一个变量

const obj = {
 ["he"+"llo"](){
   return "Hi";
  }
}
obj.hello();  //"Hi"
// 属性的简洁表示法和属性名表达式不能同时使用,否则会报错
const hello = "Hello";
const obj = {
 [hello]
};
obj  //SyntaxError: Unexpected token }
const hello = "Hello";
const obj = {
 [hello+"2"]:"world"
};
obj  //{Hello2: "world"}
const name = 'kay';
const age = 20;
const person = {
    [name]:true,
    [age]:true
}
console.log(perosn) // { kay:true, 20:true }

对象的拓展运算符

拓展运算符(...)用于取出参数对象所有可遍历属性然后拷贝到当前对象。
自定义的属性在拓展运算符后面,则拓展运算符对象内部同名的属性将被覆盖掉。
自定义的属性在拓展运算度前面,则变成设置新对象默认属性值。
拓展运算符后面是空对象,没有任何效果也不会报错。
拓展运算符后面是null或者undefined,没有效果也不会报错。

let person = {name: "Amy", age: 15};
let someone = { ...person };
someone;  //{name: "Amy", age: 15}
// 可用于合并两个对象
let age = {age: 15};
let name = {name: "Amy"};
let person = {...age, ...name};
person;  //{age: 15, name: "Amy"}

Object.assign

用于将源对象的所有可枚举属性复制到目标对象中
如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性
null 和 undefined 不能转化为对象,所以会报错

let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target,object2,object3);  
// 第一个参数是目标对象,后面的参数是源对象
target;  // {a: 1, b: 2, c: 3}
// 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回
var a = Object.assign(3);
typeof Object.assign(3);  // "object"
console.log(a) // Number {3}
// assign 的属性拷贝是浅拷贝
let sourceObj = { a: { b: 1}};
let targetObj = {c: 3};
var obj = Object.assign(targetObj, sourceObj);
console.log(obj.a) // {b: 1}
targetObj.a.b = 2;
console.log(obj.a) // {b: 2}
console.log(targetObj.a) // {b: 2}
// 数组的处理
// 会将数组处理成对象,所以先将 [2,3] 转为 {0:2,1:3} ,然后再进行属性复制,所以源对象的 0 号属性覆盖了目标对象的 0。
Object.assign([2,3], [5]);  // [5,3]

Object.is(value1, value2)

用来比较两个值是否严格相等,与(===)基本类似。

Object.is("q","q");      // true
Object.is(1,1);          // true
Object.is([1],[1]);      // false
Object.is({q:1},{q:1});  // false
// 与(===)的区别
// 一是+0不等于-0
Object.is(+0,-0);  //false
+0 === -0  //true
//二是NaN等于本身
Object.is(NaN,NaN); //true
NaN === NaN  //false

函数

箭头函数

箭头函数需要注意this的指向问题,因为箭头函数this指向不能被改变的特性,因此react组件常常利用这个特性来在不同的组件进行传值会更加方便。
没有 this、super、arguments 和 new.target 绑定
箭头函数里面没有this对象 此时的this是外层的this对象,即window

<!--ES5-->
var fn = function(a,b){
    return a + b;
}
var foo = function(){
    var a = 20;
    var b = 30;
    return a + b;
}
<!--ES6-->
// 当函数直接被return时,可以省略函数体的括号
// 当只有一行语句,并且需要返回结果时,可以省略{},结果会自动返回
var fn = (a,b) => a + b;
const foo = () =>{
    const a = 20;
    const b = 30;
    return a + b;
}
// 当箭头函数函数体有多行语句,用{}包裹起来,表示代码块
// 当箭头函数要返回对象的时候,为了区分于代码块,要用()将对象包裹起来
var f = (id,name) => ({id: id, name: name});
f(6,2);  // {id: 6, name: 2}
// 不适

函数默认参数

在实际开发中给参数添加适当的默认值,可以让我们对函数的参数类型有一个直观的认识。
使用函数默认参数时,不允许有同名参数
只有在未传递参数,或者参数为 undefined 时,才会使用默认参数,null 值被认为是有效的值传递

// x,y都有默认值
var add = (x=30,y=30) => x + y;

不定参数

不定参数用来表示不确定参数个数,如...变量名。具名参数只能放在参数组的最后,并且有且只有一个不定参数

function f(...value){
    console.log(value);
}
f(1,2,3,4) // [1,2,3,4] 数组

Symbol

ES5中对象的属性名都是字符串容易造成属性名的冲突
Symbol函数前不能使用new命令 否则会报错
是ES6 新引入的原始数据Symbol表示独一无二的值,最大的用法是用来定义对象的唯一属性名

基本用法

let sy = Symbol("kk");
let sy1 = Symbol("kk");
console.log(sy) // Symbol(kk)
typeof(sy) // "symbol"
// 相同参数Symbol()返回的值不相等
sy === sy1 // false
// 定义对象属性名用法
let sy = Symbol("key1");
// 写法1
let syObject = {};
syObject[sy] = "kk";
console.log(syObject);    // {Symbol(key1): "kk"}
// 写法2
let syObject = {
  [sy]: "kk"
};
console.log(syObject);    // {Symbol(key1): "kk"}
// 写法3
let syObject = {};
Object.defineProperty(syObject, sy, {value: "kk"});
console.log(syObject);   // {Symbol(key1): "kk"}
// 访问属性 需要使用方括号
syObject[sy];  // "kk"
// for in ,for of , Object.keys() Object.getOwnPropertyNames() 是获取不了 Symbol属性的 ,只能用过Object.getOwnPropertySymbols()和Reflect.ownKeys> () 获取
Object.keys(syObject);                     // []
Object.getOwnPropertySymbols(syObject);    // [Symbol(key1)]
Reflect.ownKeys(syObject);                 // [Symbol(key1)]
// Symbol.for() 首先会在全局搜索被登记的 Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值,若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索
let yellow = Symbol("Yellow");
let yellow1 = Symbol.for("Yellow");
yellow === yellow1;      // false

let yellow2 = Symbol.for("Yellow");
yellow1 === yellow2;     // true

// Symbol.keyFor()  返回一个已登记的 Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被登记
let yellow1 = Symbol.for("Yellow");
Symbol.keyFor(yellow1);    // "Yellow"

Proxy

可以对目标对象的读取,函数调用等操作进行拦截,然后进行操作处理,他不直接操作对象 而是像代理模式,通过对象的代理对象进行操作。在操作时可以添加额外的操作
有 target(目标对象) 和 handler(一个对象,声明代理target的指定行为) 这个两个参数
handler 对象可以为空 相当于不设置拦截操作 直接访问目标对象

// 在严格模式下 set 没有 return true 就会报错  
var target = {
    name:'tom',
    age:24
}
var handler = {
    // 用于 target 对象上 propKey 的读取操作
    get:function(target,key){
        console.log('getting '+key);
        return target[key]
    },
    // 用于拦截 target 对象上的 propKey 的赋值操作 ,如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用
    // 在set 中 可设置4个参数
    set:function(target,key,value,receiver){

        console.log(target)     // obj 对象
        console.log(key)        // 对象调用的key值
        console.log(value)      // 对象调用的value 值
        console.log(receiver)   // 表示原始操作行为所在对象 一般就是Proxy实例本身

        console.log('setting '+key);
        target[key] = value
    }
}
let proxy = new Proxy(target, handler)
proxy.name // 实际调用 handler.get
proxy.age = 25 // 实际调用 handler.set

// handler对象也可以设置为空,相当于不设置拦截操作,直接访问目标对象

// 通过构造函数新建实例时其实是对目标对象进行了浅拷贝,因此目标对象与代理对象会互相影响
var targetEpt = {}
var proxyEpt = new Proxy(targetEpt, handler)
proxyEpt.name = 'kay'
console.log(proxyEpt) // key
targetEpt.name="haha"
console.log(proxyEpt) // haha

// get 方法也可以继承
let proxy = new Proxy({}, {
  get(target, propKey, receiver) {
      // 实现私有属性读取保护
      if(propKey[0] === '_'){
          throw new Erro(`Invalid attempt to get private "${propKey}"`);
      }
      console.log('Getting ' + propKey);
      return target[propKey];
  }
});
let obj = Object.create(proxy);
obj.name

// set方法 可用在设置对象的时候 添加 判断操作 
var validator = {
    set: (obj, prop, value,receiver) => {
        if (prop === 'age') {
            if (!Number.isInteger(value)) {
                throw new TypeError('The age is not an integer');
            }
            if (value > 200) {
                throw new RangeError('The age seems invalid');
            }
        }
        // 对于满足条件的 age 属性以及其他属性,直接保存
        obj[prop] = value;
    }
}
const proxy = new Proxy({},validator);
proxy.age = 100

apply(target, ctx, args)

用于拦截函数的调用、call 和 reply 操作。

function sub(a,b){
    return a - b;
}
let handler = {
    apply:function(target,ctx,args){
        console.log(target) // target 表示目标对象
        console.log(ctx)    // ctx 表示目标对象上下文
        console.log(args)   // args 表示目标对象的参数数组
        return Reflect.apply(...arguments);
    }
}
let proxy = new Proxy(sub,handler)
console.log(proxy(2,1))
// handle apply
// 1

has(target, propKey)

用于拦截 HasProperty 操作,即在判断 target 对象是否存在 propKey 属性时,会被这个方法拦截。此方法不判断一个属性是对象自身的属性,还是继承的属性
此方法不拦截 for ... in 循环

let  handler = {
    has: function(target, propKey){
        console.log("handle has");
        return propKey in target;
    }
}
let exam = {name: "Tom"}
let proxy = new Proxy(exam, handler)
console.log('name' in proxy)
// handle has
// true

construct(target, args)

用于拦截 new 命令。返回值必须为对象

let handler = {
    construct: function(target, args, newTarget){
        console.log("handle construct");
        return Reflect.construct(target, args, newTarget);
    } }
class exam = {
    constructor(name){
        this.name = name;
    }
}
let proxy = new Proxy(exam,handler)
console.log(new proxy("Tom"))
// handle construct
// exam {name: "Tom"}

deleteProperty(target, propKey)

用于拦截 delete 操作,如果这个方法抛出错误或者返回 false ,propKey 属性就无法被 delete 命令删除

defineProperty(target, propKey, propDesc)

用于拦截 Object.definePro若目标对象不可扩展,增加目标对象上不存在的属性会报错;若属性不可写或不可配置,则不能改变这些属性。

let handler = {
    defineProperty: function(target, propKey, propDesc){
        console.log("handle defineProperty");
        return true;
    }
}
let target = {}
let proxy = new Proxy(target, handler)
proxy.name = "Tom"  // handle defineProperty
console.log(target) // {name: "Tom"}

// defineProperty 返回值为false,添加属性操作无效
let handler1 = {
    defineProperty: function(target, propKey, propDesc){
        console.log("handle defineProperty");
        return true;
    }
}
let target1 = {}
let proxy1 = new Proxy(target1, handler1)
proxy1.name = "Jerry"
console.log(target1) // {}

getOwnPropertyDescriptor(target, propKey)

用于拦截 Object.getOwnPropertyD() 返回值为属性描述对象或者 undefined 。

let handler = {
    getOwnPropertyDescriptor: function(target, propKey){
        return Object.getOwnPropertyDescriptor(target, propKey);
    }
}
let target = {name: "Tom"}
let proxy = new Proxy(target, handler)
console.log(Object.getOwnPropertyDescriptor(proxy, 'name'))
// {value: "Tom", writable: true, enumerable: true, configurable: true}

getPrototypeOf(target)

用于拦截获取对象原型的操作
返回值必须是对象或者 null ,否则报错。另外,如果目标对象不可扩展(non-extensible),getPrototypeOf 方法必须返回目标对象的原型对象,包括以下操作

  • Object.prototype.proto
  • Object.prototype.isPrototypeOf()
  • Object.getPrototypeOf()
  • Reflect.getPrototypeOf()
  • instanceof
let exam = {}
let proxy = new Proxy({},{
    getPrototypeOf: function(target){
        return exam;
    }
})
console.log(Object.getPrototypeOf(proxy))
console.log(Object)

isExtensible(target)

用于拦截 Object.isExtensible 操作
该方法只能返回布尔值,否则返回值会被自动转为布尔值
它的返回值必须与目标对象的isExtensible属性保持一致,否则会抛出错误

let proxy = new Proxy({},{
    isExtensible:function(target){
        return true;
    }
})
console.log(Object.isExtensible(proxy)) // true

ownKeys(target)

用于拦截对象自身属性的读取操作,包括以下操作

  • Object.getOwnPropertyNames()
  • Object.getOwnPropertySymbols()
  • Object.keys()
  • or...in
    方法返回的数组成员,只能是字符串或 Symbol 值,否则会报错。
    若目标对象中含有不可配置的属性,则必须将这些属性在结果中返回,否则就会报错。
    若目标对象不可扩展,则必须全部返回且只能返回目标对象包含的所有属性,不能包含不存在的属性,否则也会报错。
let proxy = new Proxy( {
  name: "Tom",
  age: 24
}, {
    ownKeys(target) {
        return ['name'];
    }
});
console.log(Object.keys(proxy))
// [ 'name' ]f返回结果中,三类属性会被过滤:
//          - 目标对象上没有的属性
//          - 属性名为 Symbol 值的属性
//          - 不可遍历的属性
let target = {
  name: "Tom",
  [Symbol.for('age')]: 24,
};
// 添加不可遍历属性 'gender'
Object.defineProperty(target, 'gender', {
  enumerable: false,
  configurable: true,
  writable: true,
  value: 'male'
});
let handler = {
    ownKeys(target) {
        return ['name', 'parent', Symbol.for('age'), 'gender'];
    }
};
let proxy = new Proxy(target, handler);
console.log(Object.keys(proxy))
// [ 'name' ]

preventExtensions(target)

拦截 Object.preventExtensions 操作
该方法必须返回一个布尔值,否则会自动转为布尔值

// 只有目标对象不可扩展时(即 Object.isExtensible(proxy) 为 false ),
// proxy.preventExtensions 才能返回 true ,否则会报错
var proxy = new Proxy({}, {
  preventExtensions: function(target) {
    return true;
  }
});
// 由于 proxy.preventExtensions 返回 true,此处也会返回 true,因此会报错
Object.preventExtensions(proxy) 被// TypeError: 'preventExtensions' on proxy: trap returned truish but // the proxy target is extensible
 
// 解决方案
 var proxy = new Proxy({}, {
  preventExtensions: function(target) {
    // 返回前先调用 Object.preventExtensions
    Object.preventExtensions(target);
    return true;
  }
});
Object.preventExtensions(proxy)
// Proxy {}

setPrototypeOf

主要用来拦截 Object.setPrototypeOf 方法。
返回值必须为布尔值,否则会被自动转为布尔值。
若目标对象不可扩展,setPrototypeOf 方法不得改变目标对象的原型。

let proto = {}
let proxy = new Proxy(function () {}, {
    setPrototypeOf: function(target, proto) {
        console.log("setPrototypeOf");
        return true;
    }
}
);
Object.setPrototypeOf(proxy, proto);
// setPrototypeOf

Proxy.revocable()

用于返回一个可取消的 Proxy 实例。

let {proxy, revoke} = Proxy.revocable({}, {});
proxy.name = "Tom";
revoke();
proxy.name 
// TypeError: Cannot perform 'get' on a proxy that has been revoked

Reflect

ES6 中将 Object 的一些明显属于语言内部的方法移植到了 Reflect 对象上(当前某些方法会同时存在于 Object 和 Reflect 对象上),未来的新方法会只部署在 Reflect 对象上
Reflect 对象对某些方法的返回结果进行了修改,使其更合理。
Reflect 对象使用函数的方式实现了 Object 的命令式操作。

Reflect.get(target, name, receiver)

查找并返回 target 对象的 name 属性。

let exam = {
    name: "Tom",
    age: 24,
    get info(){
        return this.name + this.age;
    }
}
console.log(Reflect.get(exam, 'name')); // "Tom"
// 当 target 对象中存在 name 属性的 getter 方法, getter 方法的 this 会绑定 
// 把this 绑定到 receiver
let receiver = {
    name: "Jerry",
    age: 20
}
console.log(Reflect.get(exam, 'info', receiver)); // Jerry20
console.log(Reflect.get(exam, 'info')); // Tom24
// 当 name 为不存在于 target 对象的属性时,返回 undefined
console.log(Reflect.get(exam, 'birth')); // undefined
// 当 target 不是对象时,会报错
console.log(Reflect.get(1, 'name')); // TypeError

Reflect.set(target, name, value, receiver)

将 target 的 name 属性设置为 value。返回值为 boolean ,true 表示修改成功,false 表示失败。当 target 为不存在的对象时,会报错

let exam = {
    name: "Tom",
    age: 24,
    set info(value){
        return this.age = value;
    }
}
console.log(exam.age); // 24
console.log(Reflect.set(exam, 'age', 25)); // true
console.log(exam.age); // 25
// value 为空时会将 name 属性清除
Reflect.set(exam, 'age', ); // true
exam.age; // undefined
// 当 target 对象中存在 name 属性 setter 方法时,setter 方法中的 this 会绑定 
// 把this 绑定到 receiver 
let receiver = {
    age: 18
}
Reflect.set(exam, 'info', 1, receiver); // true
// 所以修改的实际上是 receiver 的属性。
console.log(receiver.age); // 1
console.log(exam.age); // undefined
let receiver1 = {
    name: 'oppps'
}
Reflect.set(exam, 'info', 1, receiver1);
receiver1.age; // 1

Reflect.has(obj, name)

是 name in obj 指令的函数化,用于查找 name 属性在 obj 对象中是否存在。返回值为 boolean。如果 obj 不是对象则会报错 TypeError。

let exam = {
    name: "Tom",
    age: 24
}
console.log(Reflect.has(exam, 'name')); // true
console.log(Reflect.has(exam, 'home')); // false

Reflect.deleteProperty(obj, property)

是 delete obj[property] 的函数化,用于删除 obj 对象的 property 属性,返回值为 boolean。如果 obj 不是对象则会报错 TypeError。

let exam = {
    name: "Tom",
    age: 24
}
console.log(Reflect.deleteProperty(exam , 'name')); // true
console.log(exam) // {age: 24} 
// property 不存在时,也会返回 true
console.log(Reflect.deleteProperty(exam , 'name')); // true
console.log(Reflect.deleteProperty(exam , 'age')); // true
console.log(exam) // '{}'

Reflect.construct(obj, args)

等同于 new target(...args)。

function exam(name){
    this.name = name;
}
console.log(Reflect.construct(exam, ['Tom'])); // exam {name: "Tom"}

Reflect.getPrototypeOf(obj)

用于读取 obj 的 proto 属性。在 obj 不是对象时不会像 Object 一样把 obj 转为对象,而是会报错。

class Exam{}
let obj = new Exam()
console.log(Reflect.getPrototypeOf(obj) === Exam.prototype) // true

Reflect.setPrototypeOf(obj, newProto)

用于设置目标对象的 prototype。

let obj ={}
console.log(Reflect.setPrototypeOf(obj, Array.prototype)); // true

Reflect.apply(func, thisArg, args)

等同于 Function.prototype.apply.call(func, thisArg, args) 。func 表示目标函数;thisArg 表示目标函数绑定的 this 对象;args 表示目标函数调用时传入的参数列表,可以是数组或类似数组的对象。若目标> 函数无法调用,会抛出 TypeError 。

console.log(Reflect.apply(Math.max, Math, [1, 3, 5, 3, 1])) // 5

Reflect.defineProperty(target, propertyKey, attributes)

用于为目标对象定义属性。如果 target 不是对象,会抛出错误。

var myDate = {}
Reflect.defineProperty(myDate, 'now', {
  value: () => Date.now()
}); // true

Reflect.getOwnPropertyDescriptor(target, propertyKey)

用于得到 target 对象的 propertyKey 属性的描述对象。在 target 不是对象时,会抛出错误表示参数非法,不会将非对象转换为对象。

var exam = {}
Reflect.defineProperty(exam, 'name', {
  value: true,
  enumerable: false,
})
Reflect.getOwnPropertyDescriptor(exam, 'name')
// { configurable: false, enumerable: false, value: true, writable:false}
// propertyKey 属性在 target 对象中不存在时,返回 undefined属性在 target 对象中不存在时,返回 undefined
Reflect.getOwnPropertyDescriptor(exam, 'age') // undefined

Reflect.isExtensible(target)

用于判断 target 对象是否可扩展。返回值为 boolean 。如果 target 参数不是对象,会抛出错误。

let exam = {}
Reflect.isExtensible(exam) // true

Reflect.preventExtensions(target)

用于让 target 对象变为不可扩展。如果 target 参数不是对象,会抛出错误。

let exam = {}
Reflect.preventExtensions(exam) // true

Reflect.ownKeys(target)

用于返回 target 对象的所有属性,等同于 Object.getOwnPropertyNames 与Object.getOwnPropertySymbols 之和。

var exam = {
  name: 1,
  [Symbol.for('age')]: 4
}
Reflect.ownKeys(exam) // ["name", Symbol(age)]

Reflect 与 Proxy组合使用

Reflect 对象的方法与 Proxy 对象的方法是一一对应的。所以 Proxy 对象的方法可以通过调用 Reflect 对象的方法获取默认行为,然后进行额外操作。

let exam = {
    name: "Tom",
    age: 24
}
let handler = {
    get: function(target, key){
        console.log("getting "+key);
        return Reflect.get(target,key);
    },
    set: function(target, key, value){
        console.log("setting "+key+" to "+value)
        Reflect.set(target, key, value);
    }
}
let proxy = new Proxy(exam, handler)
console.log(proxy.name = "Jerry") // setting name to Jerry
console.log(proxy.name)           // getting name // "Jerry"

使用场景 实现观察者模式

const queuedObservers = new Set();
// 把观察者函数都放入 Set 集合中
const observe = fn => queuedObservers.add(fn);
// observable 返回原始对象的代理,拦截赋值操作
const observable = obj => new Proxy(obj, {set});
function set(target, key, value, receiver) {
  // 获取对象的赋值操作
  const result = Reflect.set(target, key, value, receiver);
  // 执行所有观察者
  queuedObservers.forEach(observer => observer());
  // 执行赋值操作
  return result;
}

Map 对象

Map 对象保存键值对,任何值都可以作为一个键或一个值
Map 的键值对从size属性获取

// key是字符串
var myMap = new Map();
var keyString = "a string";
myMap.set(keyString,"和键'a string'关联的值");
// 因为keyString === 'a string'
console.log(myMap.get(keyString));    // "和键'a string'关联的值"
console.log(myMap.get("a string"));   // "和键'a string'关联的值"

// key是对象
var myMap = new Map();
var keyObj = {};
myMap.set(keyObj, "和键 keyObj 关联的值");
myMap.get(keyObj); // "和键 keyObj 关联的值"
myMap.get({}); // undefined, 因为 keyObj !== {}

// key是函数
var myMap = new Map();
var keyFunc = function () {}, // 函数
myMap.set(keyObj, "和键 keyObj 关联的值");
myMap.get(keyFunc); // "和键 keyFunc 关联的值"
myMap.get(function() {}) // undefined, 因为 keyFunc !== function () {}

// key是NaN
var myMap = new Map();
myMap.set(NaN, "not a number");
myMap.get(NaN); // "not a number"
var otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"

Map 迭代 for of 和 foreach

var myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
// 将会显示两个 log。 一个是 "0 = zero" 另一个是 "1 = one"
for (var [key, value] of myMap) {
  console.log(key + " = " + value);
}
for (var [key, value] of myMap.entries()) {
  console.log(key + " = " + value);
}
/* 这个 entries 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的 [key, value] 数组。 */
// 将会显示两个log。 一个是 "0" 另一个是 "1"
for (var key of myMap.keys()) {
  console.log(key);
}
/* 这个 keys 方法返回一个新的 Iterator 对象, 它按插入顺序包含了 Map 对象中每个元素的键。 */
// 将会显示两个log。 一个是 "zero" 另一个是 "one"
for (var value of myMap.values()) {
  console.log(value);
}
/* 这个 values 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的值。 */

var myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one"); 
// 将会显示两个 logs。 一个是 "0 = zero" 另一个是 "1 = one"
myMap.forEach(function(value, key) {
  console.log(key + " = " + value);
}, myMap)

Map 对象的操作

Map 与 Array的转换

var kvArray = [["key1", "value1"], ["key2", "value2"]];
// Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象
var myMap = new Map(kvArray);
console.log(myMap)
// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组
var outArray = Array.from(myMap)
console.log(outArray)

Map的克隆

var myMap1 = new Map([["key1", "value1"], ["key2", "value2"]]);
var myMap2 = new Map(myMap1);
console.log(original === clone); 
// 打印 false。 Map 对象构造函数生成实例,迭代出新的对象。

Map的合并

var first = new Map([[1, 'one'], [2, 'two'], [3, 'three'],]);
var second = new Map([[1, 'uno'], [2, 'dos']]);
// 合并两个 Map 对象时,如果有重复的键值,则后面的会覆盖前面的,对应值即 uno,dos, three
var merged = new Map([...first, ...second]);

Set

set 对象 允许你存储任何类型的唯一值,无论是原始值或者是对象引用

// 储存的值总是唯一的 ,需要判断 两个只是否恒等 , +0/-0  undefined/undefined NaN/NaN 在这属于恒等
var mySet = new Set();
mySet.add(1) // {1}
mySet.add(2) // {1,2}
mySet.add(1) // {1,2} 这里因为已经有一个值恒等了。
mySetadd{a:1,b:2}
mySetadd{a:1,b:2}  // 对象之间引用即使相同值 也是不恒等的。

类型转换

// 数组转 set
var mySet = new Set(["value1", "value2", "value3"]);
// set 转 array
var myArray = [...mySet];
// string 转 set 
var mySet = new Set('hello'); // {'h','e','l','l','o'}

set对象作用

数组去重

var mySet = new Set([1, 2, 3, 4, 4]);
[...mySet]; // [1, 2, 3, 4]

并集

var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var union = new Set([...a, ...b]); // {1, 2, 3, 4}

交集

var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var intersect = new Set([...a].filter(x => b.has(x))); // {2, 3}

差集

var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var difference = new Set([...a].filter(x => !b.has(x))); // {1}

export import 模块

// 输出常量 
export const common = 'xxx';
// 输出函数
export function add(x){
    return x + x
}
// 输出多个
export { myName, myAge, myfn, myClass } 

// 引入模块
import add  from './xxx';
// 引入多个模块
import { common,add } from './xxx';
console.log(common) // 'xxx'
add(1) 

as 用法

let myName = "Tom";
export { myName as exportName }
// 导入的变量名,须和导出的接口名称相同
import { exportName } from "./test.js";

// 不同模块导出接口名称命名重复, 使用 as 重新定义变量名。
// test1.js
let myName = "Tom";
export { myName }
// test2.js
let myName = "Jerry";
export { myName }
// 区分 myName
import { myName as name1 } from "./test1.js";
import { myName as name2 } from "./test2.js";
console.log(name1);// Tom
console.log(name2);// Jerry

import 命令特点
只读属性:不允许在加载模块的脚步里面改写接口的引用指向,即可以改写import变量类型为对象的属性值,不能改写import变量类型为基本类型的值。

import {a} from 'xxx.js';
a = {}; // errpr
import {a} from 'xxx.js';
a.foo = 'hello'; // a = {foo:'hello'}

单例模式 多次重复执行同一句 import 语句,那么只会执行一次,而不会执行多次。import 同一模块,声明不同接口引用,会声明对应变量,但只执行一次 import

import { a } "./xxx.js";
import { a } "./xxx.js";
// 相当于 import { a } "./xxx.js";
 
import { a } from "./xxx.js";
import { b } from "./xxx.js";
// 相当于 import { a, b } from "./xxx.js";

静态执行特性:import
是静态执行,所以不能使用表达式和变量

export default 命令

在一个文件或模块中,export import 可以有多个,export default 仅有一个
export default 中的default是对应的到处接口变量
通过export方式到处,在导入时要加{ },export default 则不需要
export default 向外暴露的成员,可以使用任意变量来接收

var a = "My name is Tom!";
export default a; // 仅有一个
export default var c = "error"; 
// error,default 已经是对应的导出变量,不能跟着变量声明语句
 
import b from "./xxx.js"; // 不需要加{}, 使用任意变量接收

复合使用

export 与 import可以在同一模块使用
可以将导出接口改名,包括 default
复合使用 export 与 import ,也可以导出全部,当前模块导出的接口会覆盖继承导出的

export { foo,bar } from 'methods';
// 约等于下面两段语句,不过上面导入导出方式该模块没有导入 foo 与 bar
import { foo,bar } from "methods"
export { foo,bar };
// ------- 特点 1 --------
// 普通改名
export { foo as bar } from "methods";
// 将 foo 转导成 default
export { foo as default } from "methods";
// 将 default 转导成 foo
export { default as foo } from "methods";
// ------- 特点 2 --------
export * from "methods";

Class 语法 - ES6创建对象提供的新的语法糖

在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。
class 的本质是 function。
它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
不可重复声明
类定义不会被提升,这意味着,必须在访问前对类进行定义,否则就会报错。
类中方法不需要 function 关键字。
方法间不能加分号。
Class实例化必须通过new关键字 否则报错。

//ES5 - 构造函数
function Person(name,age){
    this.name = name;
    this.age = age;
}
Person.prototype.getName = function(){
    return this.name
}
var person = new Person('kay',20);

//ES6
// 类定义 - 类表达式可以为匿名或命名
// 匿名类
let Example = class {
    constructor(a) {
        this.a = a;
    }
}
// 命名类
let Example = class Example {
    constructor(a) {
        this.a = a;
    }
}
class Person {
    // 构造函数
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    // 将方法添加到原型中
    getName(){
        return this.name
    }
    // 静态属性
    // 等同于 Person.a = 20
    static a = 20;
    // 表示在构造函数中添加属性 在构造函数中等同于 this.c = 20
    c = 20; 
    // 箭头函数的写法表示在构造函数中添加方法,在构造函数中等同于 this.getAge = function(){}
    getAge = () => this.age
}
var a = new Person('kay',20);
var b = new Person('haha',19);
console.log(Person.a)   // 20
console.log(a)          // {c:20, getAge:function(), name:'kay',age:20}
console.log(a.getName())// kay
console.log(a.getAge()) // 20
// 共享原型对象
a.__proto__ == b.__proto___ // true
a.__proto__.sub = function(){
    return this.a + this.b
}
a.sub(); // 3
b.sub(); // 3

// prototype 仍旧存在,虽然可以直接自类中定义方法,但是其实方法还是定义在 prototype 上的。 覆盖方法 / 初始化时添加方法
Example.prototype.test = 'xxxx';

实例属性:定义在实例对象( this )上的属性
class Example {
    a = 2;
    constructor () {
        console.log(this.a); // 2
    }
}
var ss = new Example()

静态属性

静态属性:class 本身的属性,即直接定义在类内部的属性( Class.propname ),不需要实例化。 ES6 中规定,Class 内部只有静态方法,没有静态属性
静态方法通常用于创建 实用/工具 函数。

class Person {
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    // 属性
    static test = 2;
    // 方法
    static sum(a, b) {
        console.log(a+b);
    }
    add(){
        console.log(this.name)
    }
}
var a = new Person('kay',20);
Person.add(); // 报错
console.log(a.test)      // undefined
console.log(Person.test) // 2
Person.prototype.a = 2;
console.log(a.a)
Person.sum(1,1); // 22 

name

返回跟在 class 后的类名(存在时)

let Example = class Exam {
    constructor(a) {
        this.a = a;
    }
}
console.log(Example.name); // Exam

let Example = class {
    constructor(a) {
        this.a = a;
    }
}
console.log(Example.name); // Example

constructor

构造函数 constructor 方法是类的默认方法,创建类的实例化对象时被调用

class Test {
    constructor() {
        // 默认返回实例对象 this
    }
}
console.log(new Test() instanceof Test); // true

class Example {
    constructor() {
        // 指定返回对象
        return new Test();
    }
}
console.log(new Example() instanceof Example); // false
console.log(new Example() instanceof Test); // true

getter / setter

getter 不可单独出现
getter与setter必须同级出现

class Example{
    constructor(a, b) {
        this.a = a; // 实例化时调用 set 方法
        this.b = b;
    }
    get a(){
        console.log('getter');
        return this.a; // 一直获取a 重复调用。
    }
    set a(a){
        console.log('setter');
        this.a = a; // 自身递归重复调用,一直调用set
    }
}
let exam = new Example(1,2); // 不断输出 setter ,最终导致 RangeError
exam.a; // 不断输出getter ,导致 RangeError

继承

在继承的构造函数中,必须调用一次super方法,它表示构造函数的继承,与ES5中利用call/apply继承构造函数是一样的功能。
通过extends实现类的继承
调用父类构造函数,只能出现在子类的构造函数constructor中
子类 constructor 方法中必须有 super ,且必须出现在 this 之前
不可继承常规对象 如 var Father = {}
ES5 Person.call(this) 进行继承

extends

用于基础父类,使用extends可以扩展一个内置的对象(如Date),也可以是自定义对象,或者是null

class Father {
    constructor() {}
}
class Child extends Father {
    constructor(a) {
        super();
    }
}
let test = new Child(1);

super

调用父类方法, super 作为对象,在普通方法中,指向父类的原型对象,在静态方法中,指向父类 用于调用父对象上的函数。super.prop和super[expr]表达式在类和对象字面量中的任何方法定义中都有效 子类继承父类 必须调用super方法,因为子类没有自己的this对象,而是继承父类的this对象。如果不调用super函数,子类就得不到this对象 ,super()作为父类的构造函数,只能出现在子类的constructor()中;但是super指向父类的原型对象,可以调用父类的属性和方法

class Father {
    constructor(name) {
        this.name = name
    }
    static test(){
        return 1
    }
}
class Child extends Father {
    constructor(name,a) {
        super(name);
        this.a = a;
    }
    static childTest(){
        return super.test() + 1 
    }
}
let test = new Child('kay',1);
console.log(Child.childTest()); // 2

Promise

Promise 是一个构造函数接收一个参数 - 函数 并传入两个参数 resolve,reject 分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数
Promise 对象有三种状态
pending: 等待中,或者进行中,表示还没有得到结果
resolve: 已经完成,表示得到了我们想要的结果,可以继续往下执行
reject: 也表示得到结果,但是由于结果并非我们所愿,因此拒绝执行
Promise 对象中的then 方法可以接收构造函数中处理的状态变化,then的参数是两个函数 第一个函数接收resolve状态的执行,第二个函数接收reject状态的执行

function fn(num){
    return new Promise(function(resolve,reject){
        if(typeof num == 'number'){
            resolve();
        }else{
            reject();
        }
    })
    .then(function(){
        console.log('参数是一个number值')
    },function(){
        console.log('参数不是一个number值')
    })
    .then(function(){
        alert('可以完成上一步之后再进行下一个方法,解决回调地狱')
    })
}

then方法的执行结果也回放回一个Promise对象,因此我们可以进行then的链式执行,也是解决回调地狱的主要方式

function fn(num){
    return new Promise(function(resolve,reject){
        if(typeof num == 'number'){
            resolve();
        }else{
            reject();
        }
    })
    .then(function(){
        console.log('参数是一个number值')
    })
    .then(null,function(){
        console.log('参数不是一个number值')
    })
}
fn(111)
fn('xxx')
// catch(function() {}) 等同于 .then(onFulfilled,onRejected) 中的onRejected 失败的方法
// fn('x').then(function(res){
//     alert(res)
// }).catch(function(err) {
//     alert(err)
// })
<!--Promise ajax 封装例子-->
var url = 'https://xxx/api/xxx';
function getJson(url){
    return new Promise(function(resolve,reject){
        var XHR = new XMLHttpRequest();
        XHR.open('GET',url,true);
        XHR.send();
        XHR.onreadystatechange = function(){
            if(XHR.readyState == 4){
                if(XHR.status == 200){
                    try{
                        var response = JSON.parse(XHR.responseText);
                        resolve(response)
                    }catch(e){
                        reject(e)
                    }
                }else{
                    reject(new Error(XHR.statusText));
                }
            }
        }
    })
}
getJson(url).then(resp => console.log(resp); )

Promise.all Promise.all 接收一个Promise对象组成的数组作为参数,当这个数组所有的Promise对象状态都变成resolve 或 reject 才执行 可用于ajax请求它的参数需要其他的请求都有返回结果之后才能确定,就需要用到Promise.all

var p1 = new Promise(function (resolve) {
    setTimeout(function () {
        resolve("Hello");
    }, 3000);
});
var p2 = new Promise(function (resolve) {
    setTimeout(function () {
        resolve("World");
    }, 1000);
});
// 当p1 与 p2 都返回 resolve 或 reject 后才执行
Promise.all([p1, p2]).then(function (result) {
    alert(result); // ["Hello", "World"]
});

Promise.race 与Promise.all 一样接收一个Promise对象组成的数组作为参数,分别在于当其中一个Promise状态变成resolve 或 reject就可以调用.then方法

迭代器 Iterator

迭代器是一个统一的接口,使各种数据结构可被便捷的访问,通过一个键为Symbol.iterator的方法来实现
迭代器是用于遍历数据结构元素的指针(如数据库中的游标)

let items = ["zero","one"];
// 通过Symbol.iterator创建一个迭代器,指向当前数据结构的起始位置
// 随后通过next方法进行向下迭代指向下一个位置,next方法会返回当前位置的对象,包含value(当前属性的值)和done(判断是否遍历结束) 
// 当donetrue时则遍历结束
let it = items[Symbol.iterator]();
console.log(it.next())  // {value: "zero", done: false}
console.log(it.next())  // {value: "one", done: false}
console.log(it.next())  // {value: undefined, done: true}

for...of 循环

ES6新引入的循环,用于替代for...in 和 forEach(),并且支持新的迭代协议,可用于迭代常规的数据类型 Array String Map Set
of 操作数必须是可迭代,如果是普通对象无法进行迭代,数据结构是类数组结构,可借助Array.from()进行转换迭代
使用let和const,每次迭代将会创建一个新的储存空间,这样可以保证作用域在迭代的内部,只有在循环体内能访问。 如果是var 将会作用于全局在循环体外也能访问,只是结果是需要循环的最后一个。

// Array
for(var num of ["zero", "one", "two"]){
    console.log(num)
    // zero
    // one
    // two
}

// String
const str = "zero";
for (let item of str) {
  console.log(item);
}

// myMap
let myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
myMap.set(2, "two");
// 遍历 key 和 value
for (let [key, value] of myMap) {
  console.log(key + " = " + value);
}

// Set
let mySet = new Set();
mySet.add("zero");
mySet.add("one");
mySet.add("two");
// 遍历整个 set
for (let item of mySet) {
  console.log(item);
}
// 只遍历 key 值
for (let key of mySet.keys()) {
  console.log(key);
}
// 只遍历 value
for (let value of mySet.values()) {
  console.log(value);
}
// 遍历 key 和 value ,两者会相等
for (let [key, value] of mySet.entries()) {
  console.log(key + " = " + value);
}

Generator 函数

可以通过yidid关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案
与普通函数的分别在于 Generator函数在函数名之前有* 在function后面加*, 函数内部有yield表达式
执行机制 在函数后面加上()即可,会返回一个指向内部状态对象的指针,所以需要调用next 方法 指针就会从函数头部或者上一次停下来的地方开始执行。
当调用next方法时 会将yield后边的表达式的值作为返回对象的value属性值,如果yield返回值为空,那value值就为undefined 如果函数还没有执行完,done属性值是false ,当继续调用next方法时,done为true 此时函数已经执行完了,vaule属性值是undefined

function* func(){
    console.log("one");
    yield '1';
    console.log("two");
    yield '2'; 
    console.log("three");
    return '3';
}
var f = func();
console.log(f.next()); // one  {value: "1", done: false}
console.log(f.next()); // two  {value: "2", done: false}
console.log(f.next()); // three  {value: "3", done: false}
console.log(f.next()); // {value: undefined, done: true}

next 传参 yield

next不传参数的是,yield表达式的返回值是undefined,当传入参数的时候,该参数会作为上一步yield的返回值

function* sendParameter(){
    console.log("strat");
    var x = yield '2';
    console.log("one:" + x);
    var y = yield '3';
    console.log("two:" + y);
    console.log("total:" + (x + y));
}
var sendp1 = sendParameter();
sendp1.next(10); // strat
sendp1.next(20); // one 20
sendp1.next(30); // two 30  // total 50

return

返回给定值,并结束遍历Generator函数
return 方法提供参数时,返回该参数,不提供参数时,返回undefined

function* foo(){
    yield 1;
    yield 2;
    yield 3;    
}
var f = foo();
console.log(f.next()); // {value: 1, done: false}
console.log(f.return("foo")) // {value: "foo", done: true}
console.log(f.next()) // {value: undefined, done: true}

throw

可以在Generator函数体外抛出异常,在函数体内部捕获

// 遍历器抛出两个错误,第一个被Generator函数内部捕获,第二个因为函数体内的catch函数已经执行过了,不会再捕获这个错误,所以这个错误就抛出Generator函数内部,调用另一个Generator。
var g = function* (){
    try {
        yield;
    }catch(e){
        console.log('catch inner', e);
    }
}
var i = g();
i.next();
try{
    i.throw('a');
    i.throw('b');   
}catch(e){
    console.log('catch outside', e);
}
// catch inner a
// catch outside b

yield* 表达式

yield* 表达式表示yield返回一个遍历器对象,用于在Generator函数内部,调用另一个Generator函数

function* callee() {
    console.log('callee: ' + (yield));
}
function* caller() {
    while (true) {
        yield* callee();
    }
}
const callerObj = caller();
callerObj.next();  // {value: undefined, done: false}
callerObj.next("a");  // callee: a  // {value: undefined, done: false}
callerObj.next("b");  // callee: b  // {value: undefined, done: false}
// 等同于
function* caller(){
    while(true){
        for(var value of callee){
            yield value;
        }
    }
}

使用场景

为不具备Irerator接口的对象提供遍历方法

function* objectEntries(obj){
    const propKeys = Reflect.ownKeys(obj);
    for(const propKey of propKeys){
        yield [propKey,obj[propKey]]
    }
}
const jane = {first:'jane',last:'Doe'};
for(const [key,value] of objectEntries(jane)){
    console.log(`${key}:${value}`);
    // first: Jane
    // last: Doe
}

async

ES7才有,与异步操作有关的关键字,与Promise,Generator有点类似
async 函数返回一个Promise对象,可以使用then方法添加回调函数

// 语法
async function name([param[, param[, ... param]]]) { statements }
// name 函数名称
// param 要传递给函数的参数的名称
// statements 函数体语句
async function helloAsync(){
    return "helloAsync";
}
// 返回Promise 对象
console.log(helloAsync())
// 可以使用then方法添加回调函数
helloAsync().then((v)=>{
    console.log(v)
})

await

用于等待一个Promise对象 正常情况下await命令后面是一个Promise对象,但是也可以是字符串 布尔值 数值 普通函数,如果是Promise对象需要在resolve后才执行下一步,非Promise对象,直接返回对于的值 在async函数执行时,如果遇到await会先暂停执行,等到触发异步操作完成后,恢复async函数的执行并返回解析值
await仅在async function中有效,如果在async function 函数体外使用await 会报错

function testAwait(){
    return new Promise((resolve)=>{
        setTimeout(function(){
            console.log('testAwait');
            resolve();
        },1000)
    })
}
async function helloAsync(){
    console.log('startFun')
    await testAwait();
    console.log('helloAsync');
}
helloAsync();
// startFun  执行testAwait方法 等该方法完成后 才执行下一步