JavaScript学习笔记(八)--ES6

121 阅读7分钟

JavaScript面向对象

两大编程思想:

  • 面向过程

    • 面向过程:POP(Process-oriented programming)

      • 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。
  • 面向对象

    • 面向对象:OOP (Object Oriented Programming)

      • 面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。

面向对象三大特性

  • 封装性【已经把扫把功能准备好,负责开即可】
  • 继承性【继承与拖拉机,会开拖拉机就会弄这个,继承自拖拉机】
  • 多态性【可以放到一起,也可以单独拿下来,而且那个扫把坏了换哪个不影响其他的】

面向对象和过程优缺点

  • 面向过程:

    • 优点:性能比面向对象高,步骤练习紧密

    • 缺点:不好维护,不易多次使用及扩展

  • 面向对象:

    • 优点:易维护,可复用,可扩展,灵活性高

    • 缺点性能没有面向过程高

  • 面向过程就是一份蛋炒饭,味道均匀,但是假如有的人不喜欢吃鸡蛋,没办法分开
  • 面向对象就是一个盖浇饭,但是味道不均匀,而不想吃某种味道,可以分开

  • 简单程序面向过程,复杂程序用面向对象

ES6(es2015)

  • let 和 const 关键字

    • 我们之前定义变量的关键字是var,它定义的变量有很多奇怪的特点,如下有两点比较突出:
  • 变量先使用再定义------ 变量提升。

      console.log(a);
      var a = 1;
      console.log(a)
  • 缺少块级作用域。
      // 此处为了使用循环,定义一个循环变量i。
      for(var i=1; i<10;i++){}
      // i在循环体之外也能使用。
      console.info(i);

这两个问题都容易让初学者混淆,也容易让从其它语言转过来的程序员混淆。为了解决这些问题,ES6中新增了let。 而ES6引入const的原因是,因为ES5中并没有提供常量的功能。

let 变量

  • 作用:定义变量。(var也是用来定义变量)

  • let基本使用

    • 格式: let 变量名 = 变量值; 它用来定义变量,基本使用格式与var关键字一样。在可以在使用var 的地方改成let。
  • 与var的区别

    • 不能重复定义
    • 没有变量提升(var定义的变量是有变量提升的),必须先定义再使用
    • 全局变量不会附加到window对象的属性中
    • 具有块级作用域
  • 代码演示

    • 不能重复定义

// 1. let 定义变量,变量不可以再次定义
let name = 'zhangsan';
name = 'lisi';
console.log(name); // lisi
let name = 'wangwu'; // 再次定义,报错:Identifier 'name' has already been declared

  • 块级作用域

    • 作用域
      • 全局作用域
      • 局部作用域
      • 块级作用域(ES6中提出的)
  • 如果是通过let关键字来定义变量的,则js代码中就可以通过{}来切分作用域了。

// 具有块级作用域,块就是大括号
{
    let myAge = 18;
    console.log(myAge); // 18
}
console.log(myAge); // 报错,此作用域中没有myAge的定义

// 块级作用域的嵌套
{
    let a = 1;
    {
        let a = 2;
        console.log(a); // 2
        a = 3;
        console.log(a); // 3
    }
    console.log(a); // 1
}

// 隐藏的块级作用域
for (let i = 0; i < 10; i++) {
    // i 只能在此范围内使用,因为有块级作用域
}
console.log(i);  // 报错,此作用域中没有i的定义
  • 没有变量提升
    • 通过let定义的变量,不再有没有变量提升,必须先定义再使用
console.log(myGender); // 报错,此时还没有定义myGender
let myGender = '男'; 

console.log(myAge); // 不报错,输出undefined
var myAge = 13; 

  • 全局变量不会赋值加在顶级对象中

    • 浏览器环境中顶级对象为window
    • nodejs环境中顶级对象为global
  • let声明的全局变量不会以属性的形式附加在window对象中,全局变量与window对象再无关系。

// 浏览器环境下代码:
let hobby = '吃饭';
var a = 1
console.log(window.hobby);  // undefined
console.log(window.a); // 1

// nodejs环境下代码:
let hobby = '吃饭';
var a = 1
console.log(window.hobby);  // undefined
console.log(window.a); // 1
  • ES6中保留了对var的支持,你可以在代码中同时使用var和let。当然,建议使用let。

const 常量

  • 使用场景

程序员在协同开发项目时,会遇到一种场景:有一些数据大家都需要用到,但是都不能修改,即数据是只读的。举个例子:在开发公司的网站时,公司的基本信息:地址,公司名,电话等等信息可以在多个地方都需要使用,但不允许去修改。 显示,使用变量来保存这些信息是不适合的,因为变量不是只读的。这种情况下, 我们就可以把这些数据保存在常量中。

语法格式及命名规范

  • 作用:定义一个只读的常量。

  • 格式: const 常量名 = 常量值;

  • 示例:var COMPANY_NAME = "XXX公司"

    • 注意:
      • 区别于变量名,常量名推荐采用全大写的方式,多个单词之间使用_下划线分隔。
  • const特点

    • 所谓常量,就是不能修改,即:
      • 一旦定义就不能修改;
      • 一旦声明,就必须立即初始化;
 const a = 1;
 a = 2; // 报错

其它与let相似的特点

  • 具有块级作用域

  • 没有变量提升,必须先定义再使用

  • 常量也是独立的,定义后不会添加为window对象的属性

    • 本质
      • const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存单元中保存的数据不得改动
const obj = { a:1 };
obj.a = 2;
console.log(obj); // 问题:obj对象的属性值a是否修改了。
  • 对于简单类型数据(数值、字符串、布尔值),值就保存在变量指向的那个内存单元,因此等同于常量值。
  • 对于复杂类型数据(如对象和数组),变量指向的内存单元,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就不能控制了。因此,将一个对象声明为常量必须非常小心,它的属性是可以修改的。

20208241

如果你真的希望定义一个不能修改的对象(属性不能添加,修改,删除),你可以使用Object.freeze()。下面是一段参考代码:它一个可以把一个对象全部冻结的函数(深冻结):

// 设置函数,对一个对象进行深冻结(用于防止复杂数据被修改)
function deepFreeze(obj) {
  Object.freeze(obj);
  for (var k in obj) {
    if (typeof obj[k] === 'object') {
      deepFreeze(obj[k]);
    }
  }
}

// 声明要冻结的对象
const obj = {
  name: 'jack',
  age: 18,
  hobbies: {
    eat: '吃',
    drink: '喝',
    run: '跑步',
    playBall: ['篮球', '弹球', '悠悠球']
  }
};

// 调用函数冻结对象obj
deepFreeze(obj);
obj.name = 'rose';
obj.hobbies.eat = '各种吃';
obj.hobbies.playBall[2] = '各种弹';
console.log(obj); // obj没有变化,说明冻结完成

总结:

20208242

20208243

解构赋值

  • 我们经常会遇到一个场景:从对象中取出它的属性值,并保存在一个变量中。

    • 例如:
    // 这里的item是一个对象{},其中包含属性name和age
      let name = item.name; 
      let age = item.age;
    
    
    • 另外,如ajax请求的回调函数
    $.ajax({
      // ..省略其它参数
      success : function(result){
      	// 这里的result是一个对象,其中有code属性和data属性,data是一个数组
      	if(result.code === 200){
      		let data = result.data;
      		data.forEach(); // ..省略内部操作
      	}
      }
    })
    
    

    ES6提供了一个更加方便的方式从对象中取出属性值来,这就解构赋值。

定义

  • ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

    • 它有两个动作:
    • 解构:意思是把有结构的数据分解开成为一个一个的值
    • 赋值:把解构之后的值保存到变量
  • 基本示例:

     let arr = [1, 2, 3, 4];
       // 需求: 快速地从数组中取出值来赋值给变量a, b, c, d
       let a = arr[0], b = arr[1], c = arr[2], d = arr[3];
       console.log(a, b, c, d); // 1 2 3 4
    
    

数组的解构赋值

  • 它能够快速从数组中取出值保存到变量中。它的本质是给变量赋值。

    • 内容:

      - 语法格式及解构规则
      - 常规使用:变量个数等于数组长值
      
    • 非常规使用

      • 变量个数大于数组长度
      • 变量个数小于数组长度
    • 高级使用

      • 用空跳过不需要的值
      • 剩余值
      • 复杂嵌套场景
      • 小面试题
  • 语法格式及解构规则

    • 格式:
      let [变量1=默认值1, 变量2=默认值2, 变量n=默认值n] = [数组元素1, 数组元素2, 数组元素n];
      

注:默认值是可选的,可以设置,也可以不设置;

规则:

  • 赋值符号左右两边都是数组,把右边的数组按下标从小到大取出来,放入左边的数组中。

  • 如果左边的变量并没有分配到某一个值:

    • 有默认值,使用其默认值。
    • 无默认值,其值是undefined
  • 最开始的let可选,也可写成var或省略,与解构赋值功能无关,仅代表变量的声明方式。

    • 基本示例
    //最简单的场景: 变量个数和数组中元素的是相等的
      let arr = [5, 9, 10];
      let [a, b, c] = arr;
      console.log(a, b, c); // 输出 5 9 10
      // 等价于:
      let a = arr[0];
      let b = arr[1];
      let c = arr[2];
    
    

    注意:

    • “=”左右两边的格式要统一。
  • 闯关练习(重要)

    • 我们可以通过如下练习来掌握数组的解构赋值,搞定这些练习,解构赋值操作就没有什么问题啦~
    // 1 变量多,值少:
    	let arr = [5, 9, 10];
    	let [a, b, c, d] = arr;
    	console.log(a, b, c, d); // 5 9 10 undefined
    	// 结论:没有匹配上的值是undefined
        
    	// 2 默认值:
    	let arr = [5, 9];
    	let [a, b, c, d = 4] = arr;
    	console.log(a, b, c, d); // 5 9 undefined 4
    	// 结论:对于没有匹配上的变量,有默认值就使用默认值,否则就是 undefined,
        
    	// 3 变量少,值多:
    	let arr = [5, 9, 10, 8, 3, 2];
    	let [a, b] = arr;
    	console.log(a, b); // 5 9
    	// 结论:多余的忽略
        
    	// 4 用空跳过不需要的值:
    	let arr = [5, 9, 10, 8, 3, 2];
    	let [, , a, , b] = arr; 
    	console.log(a, b); // 10 3
    	// 结论:不需要用变量接收的值,用空位占位
        
    	// 5 复杂的场景,只要符合模式,即可解构:
    	let arr = ['zhangsan', 18, ['175cm', '65kg']];
    	// 5.1 如何让a的值是175cm,b的值是65kg?
    	let [, , [a, b]] = arr;
    	console.log(a, b); // '175cm' '65kg'
    	// 5.2 如何让wxyz对应arr中的四个数据呢?
    	let [w, x, [y, z]] = arr;
    	console.log(w, x, y, z); // 'zhangsan' 18 '175cm' '65kg'
    
    

剩余值

 let arr = [5, 9, 10, 8, 3, 2];
 let [a, b, ...c] = arr; // ...c 接收剩余的其他值,得到的c是一个数组
 console.log(a, b, c); // 结果为: 5 9 [10, 8, 3, 2]
 // 等价于:
 // let a = 5, 
 // let b = 9, 
 // let c = [10, 8, 3, 2]
  • 注意:

    • ... 只能用在最后一个变量上。这个变量一定是一个数组。
    • 在解构赋值的过程中,如果有出现数组元素个数大于变量个数的情况,它将会把多余的参数起来,保存在这个数组。如果元素个数不足(没有剩余元素),也会返回空数组。
  • 一个解构赋值的小面试题

    • 交换两个变量的值?
      let a = 1, b = 2;
      // 写代码,实现互换a,b的值
      // ???? 
      [a, b] = [b, a];
      console.log(a,b); // 要求输出 2, 1
      

对象的解构赋值

  • 作用:快速从对象中获取值保存到变量中。它的本质是给变量赋值。

  • 使用格式及规则

    • 完整格式
    let {"属性名1":变量名1=默认值1, "属性名2":变量名2=默认值2,... } = {"属性名1":属性值1,"属性名2":属性值2,...}
    
  • 解析规则:

    • 默认值是可选的。你可以指定默认值,也可以不指定。

    • 右边的"属性名"与左边的“属性名” 一致,则把右边的属性值赋值给左边的变量名。

    • 如果右边的匹配不成立,看看是否有使用默认值,有默认值就使用默认值,没有就是undefined。

    • 精简格式

      • 如果左侧对象中属性名与变量名相同,则可左侧合并: ```javascript
      let {变量名1=默认值1,变量名2=默认值2} = {"属性名1":属性值1,"属性名2":属性值2,...}
      
      ```
      
  • 解析规则:

    • 右边的"属性名"与左边的变量名 一致,则把右边的属性值赋值给左边的变量名。
  • 基本使用

    • 场景1,变量名和属性名一样

      // 场景1,默认要求变量名和属性名一样
      	let { name, age } = {age: 27, name: 'jack'};
      	console.log(name, age); // 'jack' 27
          
      	let {a, c} = {a: 'hello', b: 'world'};
      	console.log(a, c); // 'hello' undefined  
      
  • 注意:

    • “=” 左右两边的格式一致。
    • 对象是属性的无序集合,所以不需要管顺序
  • 场景2,变量改名

	// 场景2,可以通过:为变量改名
	let {b, name:a} = {name: '李雷'};
	console.log(b, a, name); // undefined '李雷' undefined
	// 解释:操作中,将属性name的值保存给变量a,所以只有a有值
  • 闯关练习

	// 默认值:
	var {b=1, name:a,age:b=20 } = {name: '韩梅梅'};
	console.log(b, a, name); // 2 '韩梅梅' undefined

	// 复杂的嵌套,只要符合模式,即可解构
	let obj = {
    	name: '小红',
    	age: 22,
    dog: {
        name: '小明',
        gender: '男'
      }
  };
	// 如何才能把age和name解析出来
	let {name, age, dog: {name:dogName, gender}} = obj;
	console.log(name, age, dogName, gender); // '小红' 22 '小明' '男'


	// 假设从服务器上获取的数据如下
	let response = {
    	data: ['a', 'b', 'c'],
    	meta: {
        	code: 200,
        	msg: '获取数据成功'
      }
  }
	// 如何获取到 code 和 msg
	let { meta: { code, msg } } = response;
	console.log(code, msg); // 200 '获取数据成功'
    
   let {max,min,PI} = Math;

剩余值

	// 把其它的属性全收集起来
	let obj = {name:'zs', age:20, gender:'男'};
	let {name, ...a} = obj;
	console.log(name, a);
	// 结果:
	// name = zs	
	// a = {age: 20, gender: "男"};

定义对象的简洁方式

  • 对属性名的简化

  • 对方法的简化,

    • 回想js高级课程class中的方法设置,写法是一样的。
  • 注意

    • {} 是专门解构对象使用的
    • [] 是专门解构数组使用的
    • 不能混用
	let name = 'zhangsan', age = 20, gender = '女';
	let obj = {
    	name: name, // 原来的写法
    	age, // 对象属性和变量名相同,可以省略后面的 “:age”,下面的gender同理
    	gender,
      fn1 : function(){  // 常规写法
        console.log(123);
      },
      fn2 () { // function可以省略 
        console.log(456);
      }
   };
  console.log(obj.age); // 20
  obj.fn2(); // 456


函数的拓展

es6对函数的功能进行了一些拓展,补充了很多功能。学习这个部分的内容我们就可以更加灵活优雅地使用函数啦。

参数默认值

在定义一个函数时,我们可以给形参设置默认值:当用户不传入对应实参时,我们有一个保底的值可以使用。这个特性在ES6之前,是不支持的,我们以前会通过一些变通的方式来实现。

传统的默认值设置方式

参数的默认值在之前的课程内容已经涉及,例如在xhr.open(请求类型,请求地址,是否异步)方法中,第3个参数默认是true,即表示异步ajax。

  • 默认值的意思是:
    • 如果传入对应实参,就用你传的值。
    • 如果不传,就使用某个特殊的、事先定义好的值。这个值也就是默认值。
  • 示例代码:(以ajax课程中的ajax函数封装为示例)

// ES5 中给参数设置默认值的变通做法,以ajax函数部分封装为示例:
function ajax (type, url, isAsync) {
	// 基本写法:  
  if(isAsync === undefined){
      isAsync = true;
  }
  // 简化写法:
	// isAsync = isAsync || true;
  
  console.log(type, url, isAsync);
	//.. 其他代码略
}
// 下面这两句是等价的
open("get","common/get"); // 参数3使用默认值true
open("get","common/get",true);

open("get","common/get",false);

  • 以上代码是利用了形参的一个特点:没有传入对应实参的话,其默认值是undefined。
  • 观察后发现,代码是可以工作的,但是显得很累赘,es6提供了更简单的实现。

ES6的实现

  • 格式
function 函数名 (参数名1=默认值1,参数名2=默认值2,参数名3=默认值3){
    // 函数体
}

  • 示例
function open(type, url, isAsync = true) {
    console.log(type, url, isAsync);
}
// 下面这两句是等价的
open("get","common/get");// // 参数3使用默认值true
open("get","common/get",true);

open("get","common/get",false); 

  • 思考与回忆:

    • 能否跳过isAsync,url,而只给type设置默认值?
  • 注意:

    • 带默认值的形参放在形参列表的最右边。
  • 练习


	function f(a = 1,b = 2){
   	console.log(a, b);
   }
   	f(10);
   	f(10,20);
   	f();
   function f2(a = 1,b){
   	console.log(a, b);
   }
   	f2(10);
   	f2(10,20);
   	f2(,3); // 报错:Uncaught SyntaxError: Unexpected token
   	f2(); 
   	// 与解析赋值一起使用:
   	function f1({a = 1, b = 2} = {}){
  		console.log(a, b);
	}	
   	f1({a:10,b:20});
   	f1({a:20});
   	f1({c:1});
   	f1(); // 注意:如果形参位置不设置 = {},会出现报错

rest 参数

  • rest (其它的,剩余的)参数 用于获取函数多余参数,把它们放在一个数组中。

    • 语法格式
      • rest参数不能设置默认值,且必须设置在参数列表最后位置

在定义函数时,在最后一个参数前面加上..., 则这个参数就是剩余参数;

let fn = function(参数1,参数2,...rest参数){}
let fn = (参数1,参数2,...rest参数) => { }; 

只是在定义函数时,在形参列表中区别一下,而在调用函数时并无区别。

  • 示例

    • 回答如下问题
 function f2 (x,...y){
     console.log(x,y)
 }
 f2(1,2);
 f2(2,3,4);

 function f1 (x,y){
    console.log(x,y)
 }
 f1(1,2);
 f1(2,3,4);

  • 应用--代替arguments

    • 问题:编写一个函数,求所有参数之和;

      • 方法一:arguments

      • 方法二:rest参数

	function getSum (){
   	 //  在这里写你的代码
    	var sum = 0 ; 
    	for(var i = 0; i < arguments.length; i++){
        	console.info( arguemnts[i])
        	sum += arguments[i];
    	}
	}

  • 如果以箭头函数 的方式去定义这个函数,则内部不可以使用arguments这个对象了。此时,我们就可以使用
  • rest 参数,它可以替代 arguments 的使用。 代码如下:

      // 参数很多,不确定多少个,可以使用剩余参数
      const  getSum = (...values) => {
      	var sum = 0 ; 
      	for(var i = 0; i < values.length; i++){
          	console.info( values[i])
          	sum += values[i];
      	}
      }
      // 调用
      console.log(fn(6, 1, 100, 9, 10));
    

- 与arguments相比,它是一个真正的数组,可以使用全部的数组的方法。



**箭头函数**

- 什么是箭头函数

   - 箭头函数本质上是 匿名函数的简化写法。

- ES6 中允许使用箭头函数的方式来定义一个函数。前面学习的定义一个函数有两种方式:

   -  函数声明式
   - 函数表达式

- 现在有了一个新的方式:

   3. 箭头函数
  - 格式
  ```javasript
  	let 函数名 = (形参1,...形参n) => {
   	// 函数体
   };

  • 定义一个箭头函数
    
    	// 1. 函数声明式
    	function fu1(x){
    	return x * 2;
    	}
    	// 2. 函数表达式
    	let fn2 = function (x) {
    	 return x * 2;
    };
    
    	// 3. 箭头函数 (赋值给变量,作为函数表达式使用)
    	let fn3 = (x) => {
    	return x * 2;
    };
    
    	// 对比2、3两种写法会发现,箭头函数的写法要比传统匿名函数简洁。
    
    

格式小结:

  1. 去除function关键字
  2. 在形参和函数体之间设置 =>
  • 注意:

    • =>是一个整体,不要加空格

    • 箭头函数只在书写格式上有些区别,但在函数调用方式上,是没有区别的。

  • 简化写法

    • 当形参有且只有一个,可以省略小括号
    	let f = (x) => {console.log(x)}
      	// 可以简化成:
          let f = x => {console.log(x)}
    
    
    • 当函数体只有一条语句,可以省略大括号;
    	let f = x => {console.log(x);}
          // 可以简化成:
          let f = x => console.log(x); 
    
    • 当函数体只有一条语句,并且就是return语句,则可以省略return和大括号。

      • 如果省略了大括号,则必须省略return,否则报错
        let f = x => {return x*2; }
      		// 可以简化成:
        let f = x => x*2;
      
      
    • 注意如果返回值是一个对象,要加()

        let f = x => { return {a:1};  }
        // 可以简化成:
        let f = x => {a:1}; // 如果不加,{}会被认为是函数的代码块,不会被解析			器当成对象处理
        let f = x => ({a:1});
      
      

箭头函数与普通函数的区别

  • 内部没有this

  • 内部没有arguments(了解)

  • 不能作为构造器(了解)

    • 内部的this对象,指向定义时所在的对象,而不是使用时所在的对象。
      	// 箭头函数中this的问题:
      let obj = {
      	name: 'jack',
      	age: 18,
      sayHi() {
      	// 方法中直接访问this:
      	console.log(this); // 对象obj
      
      // 普通调用的函数中的this:
      var f1 = () => {
      	console.log(this); // 对象obj
      	};
      f1();
      
      // 同上:
      setTimeout(() => {
       console.log(this); // 对象obj
      }, 0);
      
      // 另一个对象的方法中的this
      let obj2 = { name: 'rose' };
      	obj2.sayHehe = () => {
      console.log(this); // 对象obj
      };
      obj2.sayHaha = function () {
      	console.log(this); // 对象obj2,因为没有使用箭头函数
      };
      obj2.sayHehe();
      obj2.sayHaha();
      
      	}
      };
      obj.sayHi();
      
      
      

没有 arguments

  • 因为es6中提供了函数的rest剩余值功能,可以替代arguments

let fn = (a,b) => {
    console.log(arguments); // 报错,arguments is not defined
};
fn(1, 2);

  • 箭头函数不能作为构造函数
	let Person = () => {
	// ..代码
	};
	let obj = new Person(); // 报错,Person is not a constructor
	// 换个角度理解,箭头函数中都没有自己的this,无法处理成员,所以不能当构造函数

在javascript中,函数的功能太多了,除了起到最基本的封装代码之外,还可以当做构造器来使用。ES6中提出的箭头函数让函数减负了,只适合代码的封装操作。

数组的扩展

数组对象是js中非常重要的对象,它本身提供了非常多的方法,例如:push,pop,shift,unshift,splice,concat,sort,indexOf,join,map,forEach,filter,every,some,reduce... 。由于前端的主要工作内容之一是与后端进行数据交互,而数据交互的载体大多是数组和对象,所以我们对数组的操作要求也非常

扩展运算符

  • 功能:它的作用是把数组中的元素一项项地展开:把一个整体的数组拆开成单个的元素。

  • 格式:...数组

    • 基本用法
     let arr = [1, 2, 3, 4, 5]
      console.log(...arr) // 1 2 3 4 5 
    
  • 数组合并

    • 从把一个数组中的值全取出来,放在另一个数组中的
      	var arr0 = ['a', 'b'];
      	    var arr1 = [1, 2, 3];
      	    var arr2 = [...arr1];
      		var arr3 = [...arr0, ...arr1];   
    
  • 对象合并

    	let obj = {
    	name: 'Jack',
    	age: 18
      }
      let obj2 = {
    	...obj,
    	gender: '男'
      }
      console.log(obj2)
    
    
  • 在函数传递参数的时候也可以使用

	let arr = [1, 2, 3]
	function fn(a, b, c) {
  	console.log(a)
  	console.log(b)
  	console.log(c)
	}
	fn(...arr)
		// 等价于 fn(1, 2, 3)

  • Math.max()
Math.max(1,3,4,6);
var arr = [1,3,4,6];
Math.max(...arr);
// 或者 Math.max.apply(this, [1, 2, 3, 566]);

  • Array.from()
    • 功能:把其它伪数组的对象转成数组。

    • 格式: 数组 = Array.from(伪数组对象)

      • 它的实参有三种情况:

      1.自定义的,类似数组格式的对象。 ``` let fakeArr = { 0: 'a', 1: 'b', 2: 'c', length: 3 };

      2. arguments对象
        3. DOM 操作返回的 NodeList 集合
      
      

find方法

在实际的开发中,我们经常会遇到一种场景:从一个数组中找出符合条件的元素。我们要的讨论的重点是如何从数组中找出符合条件的元素,当然,我们可以通过手写循环的方式来实现这个功能,但是现在es6中提供了现成的功能。find/findIndex

  • 作用:从数组中找出我们符合条件的第一个元素(或者是下标)。

    • 格式

      • find和findIndex的格式是一致的。
      
      	let result = [].find(function(item,index,self){ 
         //...
         	// 如果满足查找的条件
         return true;
         });
      
      
      • 回调函数有三个参数,分别表示:数组元素的值、索引及整个数组

      • 如果某次循环返回的是true,find和findIndex方法的返回值就是满足这个条件的第一个元素或索引

  • 执行流程

    • find和findIndex方法,会遍历传递进来的数组

    • 如果在回调函数体内,某个时刻return true,则表示查找过程结果,返回值就是本轮循环中的元素(或者是下标);如果全部的循环结束,也没有return true,则表示没有找到,没有找到会返回undefined。

    • findIndex 找到数组中第一个满足条件的成员并返回该成员的索引,如果找不到返回 -1。

    	let arr = [1, 2, 4, 0, -4, 3, -2, 9];
    	arr.find(function (item, index, self) {
    		console.log(item); // 数组中的每个值
    		console.log(index); // 数组中的每个索引/下标
    		console.log(self); // 当前的数组
    	});
    
    
  • 简单示例

	// 用法:找数组中第一个小于0的数字
   	let arr = [1, 2, 4, 0, -4, 3, -2, 9];
   	let result = arr.find(function (item) {
   	return item < 0; //遍历过程中,根据这个条件去查找
   	});
   	console.log(result); // -4

- 注意通过箭头函数来简化代码。

  • 从一个复杂的对象数组中找出符合条件的对象。
		let data = [
         {id:2,name:'严高',age:15},
 		{id:3,name:'徐阶',age:17},
 		{id:4,name:'高拱',age:18},
 		{id:1,name:'张居正',age:12},
 	];


findIndex()

  • findIndex 的使用和 find 类似,只不过它查找的不是数组中的元素,而是元素的下标。

includes()

  • 功能:判断数组是否包含某个值,返回 true / false

  • 格式:数组.includes(参数1,参数2)

    • 参数1,必须,表示查找的内容
    • 参数2,可选,表示开始查找的位置,0表示从第一个元素开始找。默认值是0。
let arr = [1, 4, 3, 9];
console.log(arr.includes(4)); // true
console.log(arr.includes(4, 2)); // false, 从2的位置开始查,所以没有找到4
console.log(arr.includes(5)); // false

Map 和 Set

  • 是 ES6 新增的两个数据类型
  • 都是属于内置构造函数
  • 使用 new 的方式来实例化使用

Set

  • 使用方式就是和 new 连用
const s = new Set()
console.log(s)

/*
	Set(0) {}
        size: (...)
        __proto__: Set
        [[Entries]]: Array(0)
        length: 0
*/

  • 就是一个数据集合
  • 我们可以在 new 的时候直接向内部添加数据

// 实例化的时候直接添加数据要以数组的形式添加
const s = new Set([1, 2, 3, {}, function () {}, true, 'hwllo'])
console.log(s)

/*
	Set(7) {1, 2, 3, {…}, ƒ, …}
        size: (...)
        __proto__: Set
        [[Entries]]: Array(7)
        0: 1
        1: 2
        2: 3
        3: Object
        4: function () {}
        5: true
        6: "hwllo"
        length: 7
*/

  • 看上去是一个类似数组的数据结构,但是不是,就是 Set 数据结构

常用方法和属性

  • size : 用来获取该数据结构中有多少数据的
	const s = new Set([1, 2, 3, {}, function () {}, true, 'hwllo'])
	console.log(s.size) // 7

看上去是一个和数组数据类型差不多的数据结构,而且我们也看到了 length 属性 但是不能使用,想要获取该数据类型中的成员数量,需要使用 size 属性

  • add : 用来向该数据类型中追加数据
const s = new Set()
s.add(0)
s.add({})
s.add(function () {})
console.log(s.size) // 3
  • 这个方法就是向该数据类型中追加数据使用的

  • delete : 是删除该数据结构中的某一个数据

	const s = new Set()
	s.add(0)
	s.add({})
	s.add(function () {})

	s.delete(0)

	console.log(s.size) // 2
  • clear : 清空数据结构中的所有数据

	const s = new Set()
	s.add(0)
	s.add({})
	s.add(function () {})

	s.clear()

	console.log(s.size) // 0

  • has : 查询数据解构中有没有某一个数据

	const s = new Set()
	s.add(0)
	s.add({})
	s.add(function () {})

	console.log(s.has(0)) // true

  • forEach : 用来遍历 Set 数据结构的方法

	 const s = new Set()
	s.add(0)
	s.add({})
	s.add(function () {})

	s.forEach(item => {
    	console.log(item) // 0   {}   function () {}
	})

  • 方法介绍的差不多了,有一个问题出现了,那就是
  • 我们的方法要么是添加,要么是删除,要么是查询,没有获取
  • 因为要获取 Set 结构里面的数据需要借助一个 ... 展开运算符
  • 把他里面的东西都放到一个数组里面去,然后再获取
 	const s = new Set([1, 2, 3, 4, 5, 6])
	const a = [...s]
	console.log(a) // (6) [1, 2, 3, 4, 5, 6]

	console.log(a[0]) // 1
	console.log([...s][0]) // 1

  • 又一个问题出现了,new 的时候需要以数组的形式传递
  • 然后获取的时候又要转成数组的形式获取
  • 那么我为什么不一开始就定义数组,要这个 Set 数据类型干什么
  • 这就不得不提到一个 Set 的特点
  • Set 不允许存储重复的数据
  const s = new Set([1, 2, 3])

  s.add(4)  // 此时 size 是 4
  s.add(1)  // 此时 size 是 4
  s.add(2)  // 此时 size 是 4
  s.add(3)  // 此时 size 是 4
  

数组去重

let arr = [1,1,2,3,3];
console.log([...new Set(arr)])

Map

  • 也是要和 new 连用
  • 是一个数据集合,是一个很类似于 对象 的数据集合
	const m = new Map()
    console.log(m)

/*
	Map(0) {}
        size: (...)
        __proto__: Map
        [[Entries]]: Array(0)
        length: 0
*/


  • 我们的对象中不管存储什么,key 一定是一个字符串类型
  • 但是再 Map 里面,我们的 key 可以为任意数据类型
  • 我们也管 Map 叫做 (值 = 值 的数据类型)

const m = new Map([[{}, {}], [function () {}, function () {}], [true, 1]])
console.log(m)

/*
	Map(3) {{…} => {…}, ƒ => ƒ, true => 1}
        size: (...)
        __proto__: Map
        [[Entries]]: Array(3)
        0: {Object => Object}
            key: {}
            value: {}
        1: {function () {} => function () {}}
            key: ƒ ()
            value: ƒ ()
        2: {true => 1}
            key: true
            value: 1
        length: 3

*/

常用方法和属性

  • size : 用来获取该数据类型中数据的个数
	const m = new Map([[{}, {}], [function () {}, function () {}], [true, 	1]])
   
   	console.log(m.size) // 3
  • delete : 用来删除该数据集合中的某一个数据
 	const m = new Map([[{}, {}], [function () {}, function () {}], [true, 	1]])
   	m.delete(true)
       
   	console.log(m.size) // 2
  • set : 用来向该数据集合中添加数据使用

    	const m = new Map()
       m.set({ name: 'Jack' }, { age: 18 })
       console.log(m.size) // 1
    
    
  • get : 用来获取该数据集合中的某一个数据

    
    	const m = new Map()
    
       m.set({ name: 'Jack' }, { age: 18 })
       m.set(true, function () {})
       console.log(m.get(true)) // function () {}
    
    
  • clear : 清除数据集合中的所有数据

    
    	const m = new Map()
    
    	m.set({ name: 'Jack' }, { age: 18 })
    	m.set(true, function () {})
    
    	m.clear()
    
    	console.log(m.size) // 0
    
    
  • has : 用来判断数据集合中是否存在某一个数据

    	const m = new Map()
    
    	m.set({ name: 'Jack' }, { age: 18 })
    	m.set(true, function () {})
    
    	console.log(m.has(true)) // true
    
    

String的扩展String的扩展

es6中对字符串提供了新的特性,我们介绍其中几个方法:

  • 模板字符串
  • includes
  • startsWith
  • endsWith
  • repeat

模板字符串

在做字符串拼接时,使用+来拼接复杂内容是很麻烦的,而模板字符串可以解决这个问题。

  • 格式:${变量} ${表达式}

  • 语法:

    • 模板字 符串使用反引号 ` 把内容括起来,类似于普通字符串的""。

    • ${}充当界定符的作用,其中可以写变量名,表达式等。

    • 允许字符串内部进行换行,代码更好读更好的体验

      
      let name = 'zs';
      	let age = 18;
      	// 拼接多个变量,在模板字符串中使用占位的方式,更易懂
      	let str = `我是${name},今年${age}`;
      
      	// 内容过多可以直接换行
      	let obj = [{name: 'flex', age: 20},{name: 'james', age: 21}];
      
      	let arr = ['175cm', '60kg'];
      	let html = `
      		<div>
      			<ul>
      				<li>${obj.name}</li>
      				<li>${obj.age}</li>
      				<li>${arr[0]}</li>
      				<li>${arr[1]}</li>
                	</ul>
            	</div>`;
      
      

includes()

  • 格式:str.includes(searchString, [position])
  • 功能:返回布尔值,表示是否找到了参数字符串
    • position: 从当前字符串的哪个索引位置开始搜寻子字符串,默认值为0。

startsWith()

  • 格式:str.startsWidth(searchString, [position])
  • 功能:返回布尔值,表示参数字符串是否在原字符串的头部或指定位置
    • position: 在 str 中搜索 searchString 的开始位置,默认值为 0,也就是真正的字符串开头处。

endsWith()

  • 格式:str.endsWith(searchString, [len])
  • 功能:返回布尔值,表示参数字符串是否在原字符串的尾部或指定位置.
    • len:可选。作为 str 的长度。默认值为 str.length。

repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。

  • 语法:str.repeat(n)

    	let html = '<li>itheima</li>';
    	html = html.repeat(10);
    
    

ECMAScript 6 降级处理

ES 6 的兼容性问题

  • ES6 (2015年10月)虽好,但是有兼容性问题,IE7-IE11 基本不支持 ES6 ES6 兼容性列表
  • 在最新的现代浏览器、移动端、Node.js 中都支持 ES6
  • 后续我们会讲解如何处理 ES6 的兼容性
  • 因为 ES 6 有浏览器兼容性问题,可以使用一些工具进行降级(把ES6的代码转成ES5的代码·),例如:babel

降级

babel官网](www.babeljs.cn/)

实时转码:babeljs.io/repl