let

一、声明变量

let a;
let b , c;
let d = 100;
let e = 6 , f = 'string' , obj = {} , arr = [];

二、特点:

1. 变量不能重复声明
2. 具有块级作用域(全局、函数、eval)
3. 不存在变量提升
4. 不影响作用域链

const(常量)

一、声明变量

const SCHOOl = "菜鸟学院"

二、特点:

1. 必须赋初始值
2. 常量的值不可修改
3. 具有块级作用域
4. 对于数组和对象的元素修改,不算做对常量的修改,不会报错
5. 不影响作用域链
6. 正常情况下,常量使用大写

解构赋值

一、概念:

ES6 允许按照一定模式中从数组和对象中提取值,对变量进行赋值

二、数组解构

// 定义数组
const arr = [1,6,7,2];
// 将原有的数组解构,重新定义一个新的数组
const newArr = [...arr];
// 按顺序解构数组
const [a,b,c,d] = arr;

三、对象解构

// 定义对象
const obj = {
    name:'小猴子',
    age:18,
    fn:()=>{
        console.log('函数')
    }
};

// 将原来的对象解构,并定义一个新的数组
const newObj = {...obj};

// 按 obj 里面的属性解构对象
const { name , age , fn } = obj;

注意:数组和对象逐个解构方式不同,数组逐个解构按照下标顺序进行解构,而对象逐个解构是按照 key 进行解构

模板字符串( )

一、可以用作声明变量时使用

const str = `string`;

console.log(str)

二、作用

  1. 内容中可以直接出现换行
const str =  `
    <ul>
        <li>小笨猪</li>
        <li>小可爱</li>
        <li>小天使</li>
    </ul>
`;
  1. 变量拼接
const num = 111
const str = 'abcd'
const newStr = `${num}   ${tr}`;

箭头函数

一、使用

const fn = ( a , b ) => {
    console.log( a , b )
};

二、特点:

1. this 是静态的,this 始终指向函数声明时所在的作用域
2. 不能作为构造函数实例化对象
3. 不能使用 arguments (将外部传进来的实参形成伪数组)变量

三、箭头函数的简写方式

  • 省略小括号

当形参有且只有一个的时候

const fn = n => {
    return n ;
}
  • 省略花括号和小括号

当形参只有一个,并且代码只有一条语句时,此时,可以省略花括号和小括号,return 此时必须省略,且语句的执行结果就时函数的返回值

const fn = n => n + 1;

四、箭头函数的使用

箭头函数 适合与 this无关的回调;如:定时器、数组的方法回调等

    箭头函数 不适合 this有关的回调;如:事件回调等

参数默认值

一、形参赋初始值,具有默认值的参数,一般位置靠后

function fn ( a , b , c = 10 ) {
    return a + b + c
}

const result = fn( 1 , 5 )
console.log( result )

二、与解构赋值结合

function fn ( { a = 1 , b , c } ) {
    console.log( a );
    console.log( b );
    console.log( c );
};

const obj = {
    // a : 'A',
    b : 4,
    c : ()=>{}
};
fn( obj )

reset参数

一、概念

用于获取函数的参数,代替 arguments

二、使用

function date(...args){
	console.log(args);
}

三、rest参数必须放到参数最后

四、reset参数 与 arguments的区别:

  • reset参数:

可以使用filter、map、some等数组里面的方法,可以在 箭头函数中使用

  • arguments:

不能使用filter、map、some等数组里面的方法,也不可以在箭头函数中使用

(…)扩展运算符

一、对象的扩展运算符

对象中扩展运算符(...)用于去除参数对象中的所有可遍历属性,拷贝到当前对象之中
const obj = {
	a:1,
	b:'str'
};
const newObj = { ...obj };// { a : 1 ,b : 'str'}

上述方法实际上等价于

const obj = {
	a:1,
	b:'str'
}
const newObj = Object.assign({},bar);// { a : 1 ,b : 'str'}

Object.assign() 方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
Object.assign() 方法的第一个参数使目标对象,后面的参数都是源对象。(如果目标对象和源对象有相同属性名(key值),或多个源对象有相同属性名(key值),则后面的属性会覆盖前面的属性)。
同样,如果用户定义的属性,放在扩展运算符后面,则扩展运算符内部的相同属性名(key值)会被覆盖掉

const obj = {
	a:1,
	b:'str'
};
const newObj = { ...obj , a : 299 };// { a : 299 , b : 'str' }
注意:
	(...)扩展运算符 和 Object.assign() 都是一个种浅拷贝

二、数组的扩展运算符

  • 可以将数组转换为参数序列
function add (x,y){
	return x + y;
}
const arr = [ 3 , 67 ];
add( ...arr ); // 70
  • 可以复制数组
    数组是复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆一个全新的数组。
const a1 = [1, 2];
const a2 = a1;

a2[0] = 2;
a1 // [2, 2]

上面代码中,a2并不是a1的克隆,而是指向同一份数据的另一个指针。修改a2,会直接导致a1的变化。

  1. ES5只能用变通方法来复制数组。
const a1 = [1, 2];
const a2 = a1.concat();
	
a2[0] = 2;
a1 // [1, 2]
  1. 扩展运算符提供了复制数组的简便写法。
const arr1 = [ 43 , 14 , 4 , 26 , 1];
const arr2 = [ ...arr1 ];
  • 扩展运算符可以与解构复制结合起来,用于生成数组
const [ first , ...rest ] = [ 1 , 3 , 5 , 2 , 6 ];
first // 1 
rest // [ 3 , 5 , 2 , 6 ]

注意:

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错
const [...rest, last] = [1, 2, 3, 4, 5];
// 报错
const [first, ...rest, last] = [1, 2, 3, 4, 5];
// 报错

es6 数组map 方法_es6 数组map 方法

  • 合并数组
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

不过,这两种方法都是浅拷贝,使用的时候需要注意。

  • 扩展运算符还可以将字符串转为真正的数组
const str = 'hello'
const arr = [ ...str ]; // ['h','e','l','l','o'];

任何 Iterator接口的对象,都可以用扩展运算符转为真正的数组( Iteration 接口请参考阮一峰老师的ECMAScript 6入门教程中的Iterator(遍历器)的概念)

Symbol

一、概述

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它属于 JavaScript 语言的数据类型之一,其他数据类型是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)、大整数(BigInt)。

二、基本使用

  • Symbol 值通过Symbol()函数生成。
let s = Symbol();

typeof s
// "symbol"

变量 s 就是一个独一无二的值。
typeof运算符的结果,表明变量s是 Symbol 数据类型,而不是字符串之类的其他类型

注意:Symbol函数前不能使用 new 命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

  • Symbol函数可以接受一个字符串作为参数
let s1 = Symbol('foo');
let s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

  • Symbol 的参数是一个对象
const obj = {
  toString() {
    return 'abc';
  }
};
const sym = Symbol(obj);
sym // Symbol(abc)

如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。

注意:Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。

  • Symbol 值不能与其他类型的值进行运算,会报错
let sym = Symbol('My symbol');

"your symbol is " + sym
// TypeError: can't convert symbol to string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string
  • Symbol 值可以显式转为字符串
let sym = Symbol('My symbol');

String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'
  • Symbol 值也可以转为布尔值,但是不能转为数值。
let sym = Symbol();
Boolean(sym) // true
!sym  // false

if (sym) {
  // ...
}

Number(sym) // TypeError
sym + 2 // TypeError

三、特点

1. Symbol 的值是 唯一的,用来解决命名冲突的问题
2. symbol 的值不能与其他数据进行运算
3. Symbol 定义的对象属性不能使用 for…in…循环遍历,但是可以使用
4. Reflect.ownKeys来获取对象的所有键名

四、内置方法

  • Symbol.hasInstance:当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法
  • Symbol.isConcatSpreadable:对象的 Symbol.isConcatSpreadable属性等于的一个布尔值,表示该对象用 Array.prototype.concat()时,是否可以展开
  • Symbol.unscopables:该对象指定了使用 with 关键字时,哪些属性会被 with 环境排除
  • Symbol.match:当执行 str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值
  • Symbol.replace:当该对象被 str.replace(myObject)方法调用时,会返回该方法的返回值。
  • Symbol.search:当该对象被 str.search(myObject)方法调用时,会返回该返回值
  • Symbol.split:当该对象被 str.split(myObject)方法调用时,会返回该方法
  • Symbol.iterator:对象进行 for…of循环时,会调用 Symbol.iterator方法,返回该对象的默认遍历器
  • Symbol.toPrimitive:该对象转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值
  • Symbol.toStringTag:在该对象上面调用的值时,会调用这个方法,返回该对象的原始类型值
  • Symbol.species:创建衍生对象时,会使用该属性

详细了解请参考阮一峰老师的ECMAScript 6入门教程中的Symbol章节

迭代器

一、概念

1、迭代器(Iterator)是一种接口,为各种不同的数据解构统一的访问机制
2、Iterator 接口主要供 for…of…消费

二、原生具备 Iterator 接口的数据

Array、Arguments、Set、Map、String、TypeDTypedArray、NodeList

三、for…of… 和 for…in… 的区别

  • 相同点:

for…of… 和 for…in… 两者都可以用于遍历

  • 不同点:

for…in…

for…in…更适合遍历对象,当然也可以遍历数组,但是会存在一些问题

  1. index索引为字符串型数字,不能直接进行几何运算
var arr = [1,2,3]
    
for (let index in arr) {
  let res = index + 1
  console.log(res); // 01 11 21
}
  1. 遍历顺序有可能不是按照实际数组的内部顺序

使用for…in…会遍历数组所有的可枚举属性,包括原型,如果不想遍历原型方法和属性的话,可以在循环内部判断一下,使用hasOwnProperty()方法可以判断某属性是不是该对象的实例属性

var arr = [1,2,3]
Array.prototype.a = 123
    
for (let index in arr) {
  let res = arr[index]
  console.log(res)
}
//1 2 3 123

for(let index in arr) {
    if(arr.hasOwnProperty(index)){
        let res = arr[index]
  		console.log(res)
    }
}
// 1 2 3

for…of…

for…of…遍历的是数组元素值,而且 for…of 遍历的只是数组内的元素,不包括原型属和索引

var arr = [1,2,3]
arr.a = 123
Array.prototype.a = 123
    
for (let value of arr) {
  console.log(value)
}
//1 2 3

for …of… 适用遍历数组 / 数组对象 / 字符串 / map / set 等拥有迭代器对象(iterator)的集合,但是 不能遍历对象,因为没有迭代器对象,但如果想遍历对象的属性,你可以用for…in…循环(这也是它的本职工作)或用内建的Object.keys()方法

四、小结

for…in…遍历的是数组的索引(即键名),而for of遍历的是数组元素值
for…in…总是得到对象的key或数组、字符串的下标
for…of…总是得到对象的value或数组、字符串的值

Generator函数(生成器)

一、概念

1、生成器就是通过构造函数 Generator创建出来的对象,生成器是一个迭代器,同时又是一个可迭代的对象
2、生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
3、生成器其实就是一个特殊的函数

二、分析 Generator的执行过程

  • 第一次调用,Generator函数开始执行,知道遇到第一个 yield表达式为止
  • 第二次调用,Generator函数从上次yield 表达式停下的地方,一直执行到下个 yield表达式
  • 第 n-1 次调用,Generator函数从上次 yield表达式停下的地方,一直执行到 return语句(如果没有return,就执行到函数结束)
  • 第 n 次调用,Generator函数已经执行完毕,next方法返回对象的 value属性为 undefined,done属性为true

三、注意

  • JavaScript的Generator只有调用next是才执行当前yield的代码
  • yield只能出现的Generator函数里面,不然会报错
  • yield表达式如果用在另一个表达式之中,必须放在圆括号里面console.log(‘Hello’ + (yield 123))
  • next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
  • for…of循环可以自动遍历 Generator 函数运行时生成的Iterator对象,且此时不再需要调用next方法
  • yield 函数代码的分隔符

四、其他操作

1、与 Iterator 接口的关系

任意一个对象的Symbol.iterator方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象

由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。

var myIterable = {};
myIterable[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

[...myIterable] // [1, 2, 3]

上面代码中,Generator 函数赋值给Symbol.iterator属性,从而使得myIterable对象具有了 Iterator 接口,可以被…运算符遍历了。

2、next 方法的参数

yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

function* f() {
  for(var i = 0; true; i++) {
    var reset = yield i;
    if(reset) { i = -1; }
  }
}

var g = f();

g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
g.next(true) // { value: 0, done: false }

上面代码先定义了一个可以无限运行的 Generator 函数f,如果next方法没有参数,每次运行到yield表达式,变量reset的值总是undefined。当next方法带一个参数true时,变量reset就被重置为这个参数(即true),因此i会等于-1,下一轮循环就会从-1开始递增。

这个功能有很重要的语法意义。Generator 函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。通过next方法的参数,就有办法在 Generator 函数开始运行之后,继续向函数体内部注入值。也就是说,可以在 Generator 函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }

上面代码中,第二次运行next方法的时候不带参数,导致 y 的值等于2 * undefined(即NaN),除以 3 以后还是NaN,因此返回对象的value属性也等于NaN。第三次运行Next方法的时候不带参数,所以z等于undefined,返回对象的value属性等于5 + NaN + undefined,即NaN。
如果向next方法提供参数,返回结果就完全不一样了。上面代码第一次调用b的next方法时,返回x+1的值6;第二次调用next方法,将上一次yield表达式的值设为12,因此y等于24,返回y / 3的值8;第三次调用next方法,将上一次yield表达式的值设为13,因此z等于13,这时x等于5,y等于24,所以return语句的值等于42。

注意:由于next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的。V8 引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的。从语义上讲,第一个next方法用来启动遍历器对象,所以不用带有参数。

for…of…循环

for…of循环可以自动遍历 Generator 函数运行时生成的Iterator对象,且此时不再需要调用next方法。

function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
  console.log(v);
}
// 1 2 3 4 5

上面代码使用for…of循环,依次显示 5 个yield表达式的值。这里需要注意,一旦next方法的返回对象的done属性为true,for…of循环就会中止,且不包含该返回对象,所以上面代码的return语句返回的6,不包括在for…of循环之中。

除了for…of循环以外,扩展运算符(…)、解构赋值和Array.from方法内部调用的,都是遍历器接口。这意味着,它们都可以将 Generator 函数返回的 Iterator 对象,作为参数。

内置方法

  1. Generator.prototype.throw([newError()]):在函数体外抛出错误,然后在Generator函数体内(try…catch)捕获
  2. Generator.prototype.return(value):返回给定的值,并且终结遍历Generator函数
  3. 让Generator函数恢复执行,并且使用不同的语句替换yield表达式
  • next()是将yield表达式替换成一个值
  • throw()是将yield表达式替换成一个throw语句
  • return()是将yield表达式替换成一个return语句
  1. yield* 表达式:在一个Generator函数里面执行另一个Generator函数