es6入门API,相关操作方法

367 阅读18分钟

es6入门

一、es6 简介

1.什么是es6?

Es6是简称,全称是ECMAScript 6.0。由于es6是2015年6月份发布的标准。又可以称之为ECMAScript 2015,或es2015。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

2.ECMAScript和JavaScript的关系

请问二者相等吗?

ECMAScript != JavaScript

稍微回顾js的历史。

ECMAScript是一个语言标准。

JavaScript则是这个语言标准的一个具体实现。在浏览器环境中的具体实现。

再如,微软的Jscript。Flash中的ActionScript,服务器端的node。

ECMAScript只是提供了js语法。

JavaScript通常是指浏览器的ECMAScript的实现,除了有ECMAScript语法之外,还有浏览器相应的接口,比如documnet.getElmentById。

由于js和ECMAScript并不是同一个概念,所以在es6之后,就不再叫javascript了,而是回归到本来的名称。

3.ECMAScript的历史

ES6从开始制定到最后发布,整整用了15年。

1997年发布ECMAScript 1.0

1998年6月发布ECMAScript 2.0

1999年12月发布 ECMAScript 3.0(巨大成功,奠定js的基本语法)

2000年,其实有一个4.0版本,由于激进,没有获得通过。

2009年12月,ECMAScript5.0 版正式发布

2015年6月,ECMAScript 6.0 正式通过,成为国际标准。

ECMA决定:从2015年开始,每一年发布一个新的版本。

2016年6月,小幅修订的《ECMAScript 2016标准》即 ES6.1 版

根据计划,2017年6月发布 ES2017 标准。

还有es6、es7和es8的说法。

ES6 既是一个历史名词,也是一个泛指,含义是5.1版以后的 JavaScript 的下一代标准,涵盖了ES2015、ES2016、ES2017等等,有时也泛指“下一代 JavaScript 语言

4.为什么要学习ES6?

es6的出现,给前端开发人员带来了新的惊喜,它包含了一些很棒的新特性,可以更加方便的实现很多复杂的操作,提高开发人员的效率。

二、let

let的作用,和var是类似的。是用来声明变量的

let声明的变量有一些特性:

  • 块作用域

  • 不能声明提前

  • 在同一个块作用域中,不允许重复声明变量

  • 暂时性死区

(1)基本使用

let a=200;
console.log(a);

a=400;
console.log(a);
// 从这个层面来讲,和var是一样的效果

(2)块级作用域

首先需要搞清楚,在es5中,有没有块作用域?

没有,在es5中,变量的作用域只有两种:

  • 全局变量

  • 局部变量

它们是以函数作为分界线的。

以单层函数为例:函数外部的,就是全局变量,如果是在函数内部声明的变量,就是局部变量。

Let则提供了块作用域的用法。

也就说,在es6中,使用let来声明变量,就有三种作用域:

  • 全局作用域

  • 局部作用域

  • 块级作用域

定义一个块级的变量如下:

<script>
    {
        let b = 20;
        const c = 30;
        console.log(b); 
        console.log(c);
    
    }
    console.log(b);
    
</script>

使用let声明的变量,只在当前块有效果

//在进行for循环时,使用let来声明计数器变量,如下:
for (let i = 0; i < 10; i++) {
		 .......      
 }	
 console.log(i)

此处说明,这个i并不是一个全局变量,而是块级变量。

小结:*let声明的变量,局部块级作用域特性,具体体现在两个地方:*

  • {}

  • for循环中声明的计数器

(3)不能声明提前

和var对比,var可以声明提前,但是let不可以。

{
		  console.log(s);
		  let  s = "let es6"
		  console.log(s)
		}

所以,在使用let声明变量时,必须要先声明,然后才能使用发,否则报错

(4)不能重复声明

在同一个块级作用域中,不能重复声明

{
			let a=100;
			let a=200;
			console.log(a);
}	

在不同的作用域中,我们是可以声明的。

let a=400;  //全局的
console.log(a) 
	{
			let a=100;  //块级的
			console.log(a); 
	}	

(5)暂时性死区--(面试)

只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”,temporal dead zone,简称 *TDZ*

<script>
var world = 'globalWorld';
		
		function fn(){
			console.log(s1);
			let world = 'location';
			console.log(world)
			
		}
		
		fn()
</script>

var world = 'globalWorld';
		
		function fn(){
		//暂时性死区开始了
			//console.log(s1);
			//暂时性死区结束了
			let world = 'location';
			console.log(world)
			
		}
		
		fn()

let还有哪些应用呢?

//给li循环绑定点击事件,当点击的时候,分别弹出0,1,2,3
//一般可能会这么写?

var lis = document.getElementsByTagName("li");	
	for(var i=0;i<lis.length;i++){
		lis[i].onclick=function(){
			alert(i)
		}
	}	

//执行之后,发现每一个li点击之后弹出的都是4,并不是我们期望的0,1,2和3。

原因何在?

其实,我们一定要搞清楚,代码的编写和执行是分开的。换言之,事件的执行一定是分两个过程:

  • 注册过程,绑定过程

  • 触发了,执行注册好代码

在注册的过程中,for语句一定会执行。但是函数中的alert是不会执行的

循环完毕,相当于是如下代码:

       lis[0].onclick=function(){
		     alert(i)
	    }
		lis[1].onclick=function(){
			alert(i)
		}
		lis[2].onclick=function(){
			alert(i)
		}
		lis[3].onclick=function(){
			alert(i)
		}
		

其中的i,由于没有执行,仍然是i.

循环完毕,i的值已经是4了.

然后,当我们点击具体的某一个li时,才会真正的执行function代码.

此时,i的值就是外部的i,都会弹出4.

解决方法有二:

  • 人为的给每一个li元素对象增加一个索引值

  • 利用自执行函数或闭包

a.增加索引

var lis = document.getElementsByTagName("li");	
	for(var i=0;i<lis.length;i++){
		lis[i].index = i;
		lis[i].onclick=function(){
			alert(this.index)
		}
	}
//测试,ok	

b.自执行函数和闭包

for(var i=0;i<lis.length;i++){
		  lis[i].onclick=function(n){
			 return function(){
				 alert(n);
			 }
		  }(i)
	  }	
//测试,ok 

这里之所以,可以就是因为针对每一个li绑定的处理函数中,都有一个自己是局部变量,值依次是i循环的值,分别是0,1,2和3。

c.let 的方式

for(let i=0;i<lis.length;i++){
		lis[i].onclick=function(){
			alert(i)
		}
	}
//测试,ok	

尽管现在看到只有一个i,但是这个i不是全局的i,而是每一个事件处理函数自己的i。

实际上,let 的出现,其实就是为了解决这一类的问题。

三、const

作用:是用于定义常量的。

在es5中,js中是没有常量的概念。所有的都是通过var来定义

const PI=3.1415926;
	console.log(PI);
	PI=3.14;
	console.log(PI)

和let对比,let定义的变化,其值是可以修改的。而const定义的量是不能修改的。

  • const在声明的时候必须要赋值。

    const num;
    	num = 20.5;
    	console.log(num);
    

除了上述两个区别,其他let局部的几大特性,const也都具备

  • 不存在声明提前

  • 只在当前的块级作用域内有效

  • 不能重复声明

  • 存在暂时性死区

注意:*常量的本质*

const定义的常量是不能修改的

对于应用数据类型:

  • 直接修改了指向
const person = {
		name:'lucky',
		age:20,
		address:'中山西路666号'
	}
	person = {}
	
	console.log(person)

  • 只是修改了属性,
const person = {
		name:'lucky',
		age:20,
		address:'中山西路666号'
	}
	person.age=21;
	console.log(person)

实际上,我们说const真正不变的是常量保存的内容。

如果是基本数据类型,就是值

如果是引用数据类型,就是指对象的地址,地址不变就ok。

到了es6中,大家在编写代码的时候,就不要再写var了。

需要使用let或者const。

如果这个值需要变化,就使用let。

如果这个值不会变化,就使用const

四、try catch

我们编译运行程序出错的时候,编译器就会抛出异常。同时程序下面的代码不能执行,这对开发不是很好,但是有一种更为合理的语法结构 try..catch,它会在捕捉到异常的同时不会使得代码停止执行而是可以做一些更为合理的操作。

function rand(){

​      var i = Math.random()*10;

​      if(i<5){

​        throw Error ("发生了错误")

​      }else{

​        return i

​      }

​    }

​    try {

​    console.log(rand())

​    } catch (error) {

​    console.log(error)

​    }

​    console.log(22)

它按照以下步骤执行:

  1. 首先,执行 try {...} 里面的代码。
  2. 如果执行过程中没有异常,那么忽略 catch(err) 里面的代码,try 里面的代码执行完之后跳出该代码块。
  3. 如果执行过程中发生异常,控制流就到了 catch(err) 的开头。变量 err(可以取其他任何的名称)是一个包含了异常信息的对象。

五、变量解构赋值

1.*什么是解构赋值*

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

包括:*数组、对象、函数参数*、字符串、数值和布尔值。null和undefined不行。

在实际开发时,真正使用比较多的是数组、对象和函数参数的解构。

解构,通俗的讲法,*就是批量定义变量*。本质是定义变量的。

可以理解为变量的取出

  • 数组的解构赋值
<script>
    var arr = [1,2,3];
    //var a = arr[0],b = arr[1], c = arr[2];
    [a,b,c] = arr;
    console.log(a,b,c)
</script>

上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。 本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。 如果解构失败,变量的值等于undefined。

<script>
let [foo, bar, baz] = [1, 2, 3];
foo // 1
bar // 2
baz // 3

let [ , , third] = ["foo", "bar", "baz"];
third // "baz"

let [x, , y] = [1, 2, 3];
x // 1
y // 3

let [x, y, z] = ['a'];
x // a
y // undefined
z // undefined
</script>
  • 对象的解构赋值

解构不仅可以用于数组,还可以用于对象。对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。如果解构失败,变量的值等于undefined。

<script>
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"

let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
</script>

六、箭头函数

箭头函数 ES6 允许使用“箭头”(=>)定义函数。箭头函数实际还是函数 箭头函数的写法

<script>
var f = v => v;

// 等同于
var f = function (v) {
    return v;
};
</script>
  1. 不带参数的写法
<script>
  var f = (a) =>  a 
</script>
  1. 带一个参数的写法
<script>
  var f = a => a
</script>
  1. 带多个参数的写法
<script>
  var f = (a,b) => a+b
</script>
  1. return 多行写法
<script>
var f = (a,b) => {
    return a+b;
}
</script>

  1. 箭头函数的this指向 settimeout会改变this的指向 如果我们用箭头函数 箭头函数就指向父级。 在setInterval和setTimeout中传入函数时,函数中的this会指向window对象。
<script>
var obj = {
    num : 1,
    add:function(){
        setTimeout(() => {
            console.log(this);
        },300)
    }
};
obj.add();
</script>

七、函数默认值

在ES6之前,不能直接为函数的参数指定默认值,只能采取变通的方法。

<script>
function log(x,y){
    y = y||'world';
    console.log(x,y);
}
log('hello'); //hello world

// es6 写法
function log(x ,y="world"){
    console.log(x,y);
}
log('hello'); //hello  world
</script>

数组的扩展

经典面试题:请列出常见的数组方法,不少于10个。

*push、pop*、shift、unshif、join、reverse、sort、*slice、splice*、concat、indexOf.

es5中新增数组方法:

在es5中,新增了如下几个方法:

  • Array.isArray()

  • forEach()

  • *map()*

  • *filter()*

  • reduce

  • some()

  • every()

除了isArray方法之外,其他的方法都是用于对数组进行遍历操作。

八、数组的静态操作方法

(1)Array.isArray

​ 作用:判断某个数据是否是数组 返回布尔值

  function fun(a,b){
		console.log( Array.isArray(arguments));  //false 伪数组
	}	
	fun(2,3)

(2)Array.from

Array.from方法用于将类数组对象转为真正的数组(类数组对象比如arguments) 类数组对象特点 表现像数组 却没有数组该有的方法 比如push

<script>
    function aa(a,b){
        console.log(arguments) //Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
        arguments.push(3);
        console.log(arguments) //arguments.push is not a function
    }
    aa(1,2)
    //  想让类数组对象使用数组该有的方法 Array.from转换
     function aa(a,b){
        console.log(arguments) //Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
        var arr = Array.from(arguments)
        arr.push(3);
        console.log(arr) //arguments.push is not a function
    }
    aa(1,2)
    
</script>
var obj1={
		0:'html',
		1:'css',
		2:'js',
		3:'vue',
		length:4 //注意
	}		
			
  console.log(Array.from(obj1)); 

(3)Array.of

作用:将一组值转换为数组。

它的出现,主要目的是弥补构造器****Array()的不足****

   const arr1 = new Array(3);  //3xempty
	const arr2 = new Array(3,5); //3,5	  
	const arr3 = new Array("3",'html');	   //3,"html"
	console.log(arr1,arr2,arr3);		

在使用Array构造器的时候,根据参数类型的变化,得到的结果完全不一样。

在es6中,就提出了Array.of,来替代new Array的用法。

    const arr4 = Array.of(3); 
	const arr5 = Array.of(3,5);
	const arr6 = Array.of("3","html");
	console.log(arr4,arr5,arr6);		

使用Array.of之后,表现就统一了,都是定义数组的元素。

九、数组常见操作方法

常用的数组的操作 map、filter、forEach、some、every、includs、find、findIndex 、reduce

(1)forEach

​ 作用:用****于遍历数组,对数组中的每一个元素进行某个操作****。没有返回值,也不需要返回值。

​ 格式:

\ 数组.forEach(**function****(value,index,arr){**

​ *\ })*

其中,value就是数组元素

Index,就是元素对应的索引

Arr就是表示当前数组,这个不常用

foreach 相当于for循环 对数据进行便利

	var arr=[1,2,3,4,'html','css','js'];
		
	arr.forEach(function(el,index){
		console.log(el); //1,2,3,4,'html','css','js'
		console.log(index);//0,1,2,3,4,5,6,7
	})
//箭头函数的写法
arr.forEach(el=>console.log(el+1));
//结果:
//2,3,4,5,'html1','css1','js1'

var array1 = ['a', 'b', 'c'];
array1.forEach(function(element) {
  console.log(element);
});
//结果:
//  "a"
//  "b"
//  "c"

(2)map

map() JavaScript 数组map()方法主要创建一个新的数组使用调用此数组中的每个元素上所提供的函数的结果。即对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。对数据进行操作 返回新的数据

格式:

数组.map(function(value,index,arr){

})

参数含义和forEach一致。

<script>
var list = [1,2,3,4];
var newList = list.map(ele =>{
    return ele*2
});
console.log(list,newList) // [1,2,3,4] [2,4,6,8]
</script>

在使用map的时候,注意两点:

  • 在回调函数中,一定要使用return返回

  • map方法调用之后,会返回一个全新的数组,原来的数组不变。

(3) filter

作用:*就是用于进行过滤,筛选出符合条件的元素,组成一个新的数组返回*

格式:

数组.filter(function(value,index,arr){})

基本上和map是差不多的。

filter 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素

<script>

var list = [1,2,3,4];
var newList = list.filter(ele => ele > 2);
console.log(list,newList) // [1,2,3,4] [3,4]

</script>

(4)find/findIndex

  • find 数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回 undefined。 find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
    var ele = [1, 5, 10, 15].find(function(value, index, arr) {
      return value > 9;
    }) 
    console.log(ele) // 10

  • findIndex 数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的下标,如果所有成员都不符合条件,则返回-1。
<script>
    [1, 5, 10, 15].findIndex(function(value, index, arr) {
      return value > 9;
    }) // 2
</script>
  
const foods=[
		{
			id:98,
			name:'哈根达斯',
			num:10,
			price:98
		},
		{
			id:56,
			name:'八喜',
			num:1,
			price:48
		},
		{
			id:102,
			name:'冰雪皇后',
			num:1,
			price:29
		},
		{
			id:106,
			name:'蒙牛',
			num:10,
			price:4
		}
	]

const item = foods.find(function(el,index){
		 // return el.id==102;
		 return el.name=="八喜"
	   })
console.log(item) ;
//返回的结果
  //{
	//		id:56,
	//		name:'八喜',
	//		num:1,
	//		price:48
	//	}

//箭头函数写法
const item = foods.find(el=>el.id==100);
console.log(item); //结果为:   undefined

const index = foods.findIndex(el=>el.id==98);
 console.log(index); //结果为: 0

(5)every与some

every()与some()方法都是JS中数组的迭代方法。

every()是对数组中每一项运行给定函数,如果该函数对每一项返回true,则返回true。

some()是对数组中每一项运行给定函数,如果该函数对任一项返回true,则返回true。

<script>
var arr = [ 1, 2, 3, 4, 5, 6 ]; 
 
console.log( arr.some( function( item, index, array ){ 
    return item > 3; 
}));   // true 

console.log( arr.every( function( item, index, array ){ 
    return item > 3; 
}));  // false
</script>

(6)includes

includes 方法用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false。

[1, 2, 3].includes(2);     // true
[1, 2, 3].includes(4);     // false
[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true
[1, 2, NaN].includes(NaN); // true

(7)reduce

作用:将****前一项**后一项****的值进行运算,返回累积的结果

格式:数组.reduce(function(prev,next){...})

其中,prev表示前一项,next表示后一项。

运算规则:

默认情况下,会把数组的第一个元素作为prev的初始值。

每循环一次,把累积的结果赋给prev,next就变为下一个数组元素

var arr3 = [10,22,23,25,50];
	
  const total = arr3.reduce(function(pre,next){
		console.log(pre+"----"+next);
		return pre+next;
	})
	console.log(total);

实际上,reduce方法还有第二个参数,*如果传递了第二个参数,就作为prev的初始值*。同时next就是数组的第一个元素。

<script>

    const total = arr3.reduce(function(pre,next){
		console.log(pre+"----"+next);
		return pre+next;
	},100)
	console.log(total);

</script>

小结一下:请列出js中的常用的数组方法。

*push、pop、shift、unshift、sort、slice、splice、reverse、join、concat*

*forEach、map、filter、reduce、some、every、find、findIndex、fill、*

*Array.isArray、Array.from、Array.of*

十、字符串扩展

(1)字符串模板

字符串拼接是开发时一个必不可少的环节,也是很恶心的一个环节,尤其是又臭又长的html字符串拼接。

为什么说html字符串拼接很恶心呢,主要有以下几点:

  1. 传统的字符串拼接不能正常换行
  2. 传统的字符串拼接不能友好的插入变量 ${}
  3. 传统的字符串拼接不能友好的处理单引号、双引号互相嵌套的问题。 es6的模板字符串解决了以上问题
  • 拼接字符串
<script>
    // 以前拼接字符串
    var html = '<ul>'+
        '<li cla="aaa">'+1+'</li>'+
        '<li>2</li>'+
    '</ul>'
    // 现在拼接字符串
    // esc 下面的一个键
    ``
    var html = `<ul>
        <li>1</li>
        <li>2</li>
    </ul>`
</script>
  • 插入变量
<script>
    var s1 = `hello vue`;
    var html = `xxx ${s1} xxx` 
    console.log(html) //xxx hello vue xxx
</script>

补位,repeat

扩展运算符 扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列

<script>
console.log(...[1, 2, 3])
// 1 2 3

console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5

// 常用场景 合并两个数组
var arr = [1,2],arrs = [3,4];
var newArr = [...arr,...arrs];
console.log(newArr) // [1,2,3,4]
</script>

(2)repeat

作用:按照指定此时重复指定的内容

格式:*字符串.repeat(n)*

var str="你好"
console.log(str.repeat(3)) //你好你好你好

十一、对象的扩展

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。 Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。Object.assign有返回值 返回值是目标对象(target)

  const target = { a: 1 };
    const source1 = { b: 2 };
    const source2 = { c: 3 };

    Object.assign(target, source1, source2);
    console.log(target) // {a:1, b:2, c:3}

浅拷贝 Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

  const obj1 = {a: {b: 1}};
    const obj2 = Object.assign({}, obj1);

    obj1.a.b = 2;
    obj2.a.b // 2

十二、Symbol

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。

  • Symbol 的值都是唯一的 永不相等
<script>
    var s1 = Symbol();
    var s2 = Symbol();
    console.log(s1 === s2); // false
</script>
  • 标识 Symbol函数接受一个可选参数,可以添加一段文本描述即将创建的Symbol,这段属描述不可用于属性访问,但是建议每次创建Symbol时都添加一段描述,便于阅读代码和调试Symbol程序。
<script>
    var s1 = Symbol("我是s1的描述");  //  作用是方便理解这个定义的值是做什么用处
    var s2 = Symbol("我是s2的描述");
    console.log(s1 === s2); // false

    var s3 = Symbol("不凡学院");
    var s4 = Symbol("不凡学院");
    console.log(s3 === s4); // false
</script>
  • 获取Symbol里面的描述信息 .description
<script>
    var s3 = Symbol("不凡学院");
    console.log(s3.description); // 不凡学院
</script>
  • 作为属性名的 Symbol 由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。 注意,Symbol 值作为对象属性名时,不能用点运算符。
    • 赋值的第一种方式
<script>
    var s1 = Symbol();
    var obj = {
        name : "张三"
    }
    obj[s1] = 50;
    console.log(obj[s1])
</script>
  • 赋值的第二种方式
<script>
    var s1 = Symbol("我是s1")
    var obj = {
        name : "张三",
        [s1] : 40
    }
    console.log(obj[s1])
</script>

十三、Set 和 Map 数据结构

  • Set ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。 Set 本身是一个构造函数,用来生成 Set 数据结构。
<script>
var s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
console.log(s)
// [2 3 5 4]
</script>

上面代码通过add方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值。

  • size属性 Set.prototype.size:返回Set实例的成员总数。array.length
  • size 方法 Set.prototype.add(value):添加某个值,返回 Set 结构本身。 Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。 // 删除成功 返回true 否则 false Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。 Set.prototype.clear():清除 清空set数据结构
<script>
    var s = new Set([1,23,4]);
    s.add(5); // Set(4) {1, 23, 4, 5}
    console.log(s.size) // 4 
    s.delete(1) //
    console.log(s) // Set(3) {23, 4, 5}
    var sets = s.has(23) // 
    console.log(sets) // true
    s.clear() ;
    console.log(s) //Set(0)
</script>
  • Set实现数组去重
<script>
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);
</script>
  • Map

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应(对象,数字,函数都可以作为作为键值),Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。

<script>
const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false
</script>

Map实例的属性和操作方法

  • size 属性 size属性返回 Map 结构的成员总数。
  • Map.prototype.set(key, value) set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。
<script>
    var map = new Map();
    map.set("aa","100");
    console.log(map) // Map(1) {"aa" => "100"}
</script>
  • Map.prototype.get(key) get方法读取key对应的键值,如果找不到key,返回undefined。
<script>
    var map = new Map();
    map.set("aa","100");
    console.log(map.get("aa")) // 100
</script>
  • Map.prototype.has(key) has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
<script>
    var map = new Map();
    map.set("aa","100");
    console.log(map.has("aa")) // true
</script>
  • Map.prototype.delete(key) delete方法删除某个键,返回true。如果删除失败,返回false。
<script>
    var map = new Map();
    map.set("aa","100");
    console.log(map.delete("aa")) // true
</script>
  • Map.prototype.clear() clear方法清除所有成员,没有返回值。
<script>
    var map = new Map();
    map.set("aa","100");
    map.clear()
    console.log(map) // Map(0) {}
</script>

十四、Proxy

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。

var proxy = new Proxy(target, handler);
// 例
var obj = {
    name:""
}
var proxy = new Proxy(obj, {
  get: function(target, propKey) {
    return 35;
  }
});

proxy.time // 35
proxy.name // 35
proxy.title // 35

上面代码中,作为构造函数,Proxy接受两个参数。第一个参数是所要代理的目标对象,即如果没有Proxy的介入,操作原来要访问的就是这个对象;第二个参数是一个配置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。比如,上面代码中,配置对象有一个get方法,用来拦截对目标对象属性的访问请求。get方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回35,所以访问任何属性都得到35

注意**,要使得Proxy起作用,必须针对Proxy实例(上例是proxy对象)进行操作,而不是针对目标对象(上例是空对象)进行操作。**

如果handler没有设置任何拦截,那就等同于直接通向原对象。

Proxy常见操作

  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']

    get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。

    // 如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回undefined。
    var person = {
      name: "张三"
    };
    
    var proxy = new Proxy(person, {
      get: function(target, propKey) {
        if (propKey in target) {
          return target[propKey];
        } else {
          throw new ReferenceError("Prop name " + propKey + " does not exist.");
        }
      }
    });
    
    proxy.name // "张三"
    proxy.age // 抛出一个错误
    
  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。

    set`方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。

    var person = {
    
    ​    name: "张三",
    
    ​    age:20
    
       };
    
    
    
       var proxy = new Proxy(person, {
    
    ​    set(target , key , val){
    
    ​      if(key == "age"){
    
    ​        if(val>150){
    
    ​          throw new Error("年龄不符合要求")
    
    ​        }else{
    
    ​          target[key] = val
    
    ​        }
    
    ​      }
    
    ​    }
    
       });
    
       proxy.age = 200;
    

    proxy实现双向绑定

    <div id="app">
          <h2>input框的值为:<span id="val"></span></h2>
          <input type="text" id="ipt" />
        </div>
        <script>
          var ipt = document.getElementById("ipt");
          var span = document.getElementById("val");
          var obj = {
            val: ipt.value
          };
          var proxy = new Proxy(obj, {
            set(target, key, val) {
              span.innerText = val;
              ipt.value = val;
            },
            get(target, propKey) {
              span.innerText = target[propKey];
              return target[propKey];
            }
          });
          document.getElementById("ipt").oninput = function() {
            proxy.val = document.getElementById("ipt").value;
          };
    

    十五、class

    引用类型的数据在使用typeof判断类型的时候返回的都是object 如果向单独的判断引用数据的类型可以使用instanceof来进行判断(某个对象是不是另一个对象的实例) constructor 属性返回对创建此对象的数组函数的引用,指向生成这个实例的函数。。 constructor 返回的是构造函数 而instanceof返回的是true或false

    • es6中的类和对象
      • 对象 对象的定义是一组无序的相关属性和方法的集合,所有的事物都是对象

      • class类 在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。class 的本质是 function (class Person() ; typeof Person == Function)。 它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。

      • 对象和类的区别 类抽象了对象的公共部分(封装了公共的属性和方法),他泛指某一大类(class), 对象特指某一个,通过类实例化一个具体的对象 他俩之间的关系可以理解为设计图(类)和实物(对象)的关系

      • 类的使用 上面是es6 class的写法 下面是es5构造函数的写法

    <script>
            class Person { // 创建一个person类 类名大写
                constructor(name){ 
                    this.name = name
                }
                ale(){
                    alert("我是",this.name)
                }
            }
            var p1 = new person("不凡君"); // 创建person类的实例 需要注意的是使用创建的类必须通过实例化的方式
            console.log(p1.ale())
    
            // function person(name){
            //     this.name = name;
            // }
            // person.prototype.ale = function(){
            //     alert(this.name)
            // }
            // var p1 = new person("不凡君");
            // console.log(p1.ale())
    </script>
    
    • 类的注意事项及特点
      • 构造函数为了与普通函数更好的区分 一般构造函数的首字母都是大写
      • 类里面的constructor(构造函数)函数 可以接收实例传递过来的参数 同时返回实例对象
      • constructor函数 只要new生成实例时 就会自动调用这个函数 即使不写这个函数 也会自动生成
      • 生成实例的时候 new关键字不能省略
      • 类里面的函数 不能加function
      • 类里面的多个函数之间不需要加,
      • 类里面的所有方法都是定义在原型上面
    • 类的继承 利用原型让一个引用类型继承另一个引用类型的属性和方法,即让原型对象等于另一个类型的实例(即要继承的对象的实例) Class 可以通过extends关键字实现继承
    <script>
        class Point {  }
    
        class ColorPoint extends Point { 
            constructor() {
                // 关键字super,指向当前实例对象的原型对象
                super() 
                
            }
        }
        
       class  Animal{
    		constructor(name,age,color) {
    		    this.name=name;
    			this.age=age;
    			this.color=color;
    		}
    		play(){
    			console.log('playing......')
    		}
    		eat(){
    			console.log('吃啥啥不剩.....')
    		}
    		sleep(){
    			console.log('呼呼大睡.....')
    		}
    	}	 
        
        class Bird extends Animal{
    		constructor(name,age,color,cicle) {
    			super ();
    			this.name = name;
    			this.age = age;
    			this.color=color;
    		    this.cicle = cicle;
    		}
    		play(){
    			console.log('飞到天上玩.....')
    		}
    		//静态方法
    	  static fly(){
    			console.log('我要飞的更高。。。。')
    		}
    	}
        
       var b1 = new Bird('黄鹂鸟',1,'yellow','是个球')
    	console.log(b1);
    	b1.eat();
    	//如果,子类重构了父类的方法,这是使用自己的方法
    	b1.play();
    	//静态方法必须用类去调用
    	Bird.fly()   
        
    </script>
    

    十六、Promise

    Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

    所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

    <script>
        var p = new Promise(function(resolve , reject){
        setTimeout(() => {
            var num = Math.random()*10;
            if(num>6){
            resolve(num)
            }else{
            reject("小于6")
            }
        }, 1000);
        
        })
        p.then(function(val){
        console.log(val)
        }).catch(function(val){
        console.log(val)
        })
    </script>
    

    Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

    resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

    Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

    Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了 解决了层层嵌套

    <script>
        var status = 1,isLogin=false;
           var login = (resolve , reject)=>{
               setTimeout(()=>{
                   if(status == 1){
                        isLogin = true
                        resolve({
                            code : 1,
                            token:"ad31nu891nv",
                            msg:"登陆成功!"
                        })
                    }else{
                        isLogin = false
                        reject("失败")
                    }
               },2000)
                
           };
           var getInfo = (resolve , reject)=>{
                setTimeout(()=>{
                    if(isLogin){
                        resolve("获取用户信息成功!")
                    }else{
                        reject("获取失败")
                    }
                },1000)
           };
           new Promise(login)
           .then(res =>{
               console.log(res);
               return new Promise(getInfo);
           })
           .then(res =>{
                console.log(res);
           })
    </script>
    

    十七、async await

    ES2017(es7) 标准引入了 async 函数,使得异步操作变得更加方便。

    • async await async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。 async await 最大的好处是让前端有了能力 以同步的方式写异步的代码
      • async async函数返回一个 Promise 对象 语义上理解 当函数前面加上async表示函数内部有异步操作
    <script>
        async function main(){ // 声明一个异步函数
            return 1  // return的结果 相当于resolve出去的结果
        }
        console.log(main()) // Promise {<resolved>: 1}
        // 获取async返回的结果通过.then获取
        main().then(res => console.log(res)); // 1
    </script>
    
    • await await 关键字要在 async 关键字函数的内部,await 写在外面会报错。 await右侧如果是函数,那么函数的return值就是「表达式的结果」 await右侧如果是一个 'hello' 或者什么值,那表达式的结果就是 'hello' await关键字会阻塞后面代码的运行(通过阻塞特点 控制ajax 或者settimeout的执行顺序)
    <script>
        async function async1() {
            console.log( 'async1 start' )
            await async2() // 使用await关键字之后 await下面的代码会被阻塞 也就是说会先跳出当前的async1函数  先执行 console.log( 'script start' )
            console.log( 'async1 end' )
        }
        async function async2() {
            console.log( 'async2' )
        }
        async1()
        console.log( 'script start' )
        //  结果会是 
        // async1 start
        // async2
        // script start
        // async1 end
        
    </script>
    

    async await 重构ajax

     function getUser(){
    		  return $.ajax({
    			  url:'http://bufantec.com/api/test/user/list?start=10'
    		  })
    	  }
    	 
    	function getuserInfo(val){
    		return  $.ajax({
    			  url:`http://bufantec.com/api/test/user/detail?id=${val}`
    		  })
    	} 
    	 
      async	function getResult(){
    		const result1 = await getUser();
    		const result2 = await getuserInfo(result1.data.list[1].userId);
    		console.log(result2);
    	} 
    	 
    	 getResult()