ES6 复习,知识补充

本文详细介绍了JavaScript ES6中的核心特性,包括Promise对象用于处理异步操作,Set和Map数据结构,类与继承的概念和用法,以及Symbol类型的介绍。此外,还探讨了迭代器、生成器和模块化等高级特性,帮助开发者深入理解ES6的语法和功能。
摘要由CSDN通过智能技术生成

ES6

知识补充

概念

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。
它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。

提到 ES6 的地方,一般是指 ES2015 标准,但有时也是泛指“下一代 JavaScript 语言”。

ECMAScript是JavaScript的规格、规范
JavaScript是其的一种实现

Babel

1、概述

Babel 是一个工具链,主要用于在旧的浏览器或环境中将ESCMAScript 2015+ 代码转换为向后兼容版本的 JavaScript代码。 

通俗说:实现es5之前版本向es6之后版本的转换

------------------
2、安装

npm install --save-dev @babel/core @babel/cli @babel/preset-env 

npm install --save @babel/polyfill

--------------------------
3、配置

打开package.json文件,将以下内容添加:

{
    //预设置版本
    "presets" : [
        //最后版本
        "latest"
    ],
     // 插件
    "plugins" : [],
     // 开发描述
    "devDependencies" : {
        // babel 命令提示符
        "babel-cli" : "^6.0.0",
        "babel-preset-latest" : "^6.24.1"
    },
    "scripts" : {
        // 构造
        // 将src(es6之后版本)目录和lib(es5之前版本)目录,语法版本转换
        "build" : "babel src -d lib"
    }
}

---------------------------------
4、运行(实现转码转换)

npm run build

-------------------
5、导入使用,已转好的lib目录下生成的js文件

块级作用域

1、在 ES5版本没有块级作用域

例如:
if (true) {
    // 因es5没有块级,所以变量v是全局变量
    var v = 'jine'
}
console.log(v)
/*
	结果:100
*/


-------------------------------------------------------
2ES6 提供了块级作用域

if (true) {
    // ES6语法有块级,所以定义的变量只能在当前块级作用域中被访问
    let es6 = 'jine'
}
console.log(es6)
/*
	结果:es6 is not defined
*/

============================================
for 循环中用var声明的变量,在全局可以访问到,因此可以使用let关键字声明变量,成为块级作用域

基础语法

let

letvar 区别:


let 特点:

1、没有变量提升

例如:
console.log(age);
let age=38;
/*直接报错,age变量undefined*/

-----------------------------------------------------------------------------------------
2、有块级作用域

例如:
for(let i=0;i<10;i++){
    //块级作用域
}
console.log(i)
/*直接报错,i变量undefined*/


ES6 允许块级作用域的任意嵌套,例如:
{{{{
  let insane = 'Hello World';
  {let insane = 'Hello World'}
}}}};
/*内层作用域可以定义外层作用域的同名变量。*/


-----------------------------------------------------------------------------------------
3、不能重复声明
let num=10;
let num=20;
console.log(num)
/*直接报错,不可重新声明变量*/


function fun(a){
    let a = 10 //相当于重复声明,会报错
    console.log(a)
}
fun(100)
"注意""在ES6中函数的参数相当于使用let关键字定义的局部变量"

----------------------------------------------------------------------------------------=
4、可以重新赋值
let num=10;
num=20;
console.log(num)
/*结果为:20*/

-----------------------------------------------------------------------------------------
5、暂时性死区

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

var tmp = 123;

if (true) {
  // let 关键字声明的变量将当前环境封闭,所以并不会去找外面相同的变量
  tmp = 'abc'; // ReferenceError
  let tmp;
}

上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。

ES6 明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

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

var 特点:
1、有变量提升

例如:
console.log(es5)
var es5 = 'jine'
/*
    结果:undefined
    原因:使用var关键字声明的变量,存在声明提前
    相当于以下代码:
                var es5
                console.log(es5)
                es5 = 'jine'
*/

--------------------------
2、没有块级作用域,是函数作用域
3、可以重复声明
4、可以重新赋值

const

const 关键字声明的常量(一旦声明就无法更改)

1、没有变量提升
2、有块级作用域
3、不能重复声明
4、不能重新赋值(声明必须要有初始值)
5、同样存在暂时性死区


const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

上面代码中,常量foo储存的是一个地址,这个地址指向一个对象。
不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。

解构赋值

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

对象可以不按顺序赋值,只要找到对应的属性便可以

1ES6写法

let obj={
    name:"jine",
    age:22,
    mvp:"hha"
}

let {name:name1,age:age1}=obj

-----------------------------------------------------------------------------------------
变量名可与对象属性名一致
let {name:name,age:age}=obj

-----------------------------------------------------------------------------------------
如果声明的变量名和对象属性名一致,可以简写,例如:
let{name,age}=obj

-----------------------------------------------------------------------------------------
若出现变量名没有和对象属性名一致,只会声明一个空变量,例如:
let{name,age,other}=obj
console.log(name,age,other)
/*
	结果: jine 22  undefined
*/

-----------------------------------------------------------------------------------------
若出现变量名和对象属性名有的相同,有的不同,可以一下写法,例如:
let{name,age,mvp:other}=obj
console.log(name,age,other)
/*
	结果: jine 22  haha
*/

-----------------------------------------------------------------------------------------
若对象属性值已有,在声明相同的变量来进行赋值并不会覆盖(如下age)
若声明的变量没有对应的对象属性,可以给这个变量初始化值,便不会出现undefined(如下height),例如:
let{name,age=18,other,height=175}=obj
console.log(name,age,height)
/*
	结果为:jine 22 175
*/


-----------------------------------------------------
let{name,...obj2}=obj
console.log(obj2)
/*
	结果为:22  haha
	
	这写法是不包含obj中name属性,其他成员全部赋值给obj
*/

=========================================================================================
2ES5写法

var obj={
    name:"jine",
    age:22
}

var name1=obj.name
var age1=obj.age
数组解构赋值
数组顺序进行依次赋值

1ES6写法

let arr=[10,20,30]
let [num1,num2,num3]=arr
console.log(num1,num2,num3)
/*结果:10 20 30*/

let [num1,num2,num3,num4]=arr
console.log(num1,num2,num3,num4)
/*结果:10 20 30 undefined*/

let [num1,num2,num3,num4=40]=arr
console.log(num1,num2,num3,num4)
/*结果:10 20 30 40*/

let [num1=100,num2,num3,num4=40]=arr
console.log(num1,num2,num3,num4)
/*结果:10 20 30 40*/



// ES6 底层将为变量赋值的值与undefined进行比较,若有指定的默认值便用默认,没有则用undefined
let [num1,num2=100] = [10,undefined]
console.log(num1,num2)
/*结果:10  100 */


=========================================================================================
2ES5写法

var arr=[1,2,3]
var a=arr[0]
var b=arr[1]
var c=arr[2]
字符串解构赋值
变量的数量与字符串中字符的数量进行一一对应

例如:

let [a,b,c,d,e] = "Hello"
console.log(a,b,c,d,e)

/* H e l l o */
解构赋值结合函数声明
function test1({name,age}){
	console.log(name,age)
}

test1({
    name:"jine",
    age:20,
})

模板字符串

语法: ``(使用反引号标识)
作用:类似于python的多行字符串(ES5是没有多行字符串)
特点:1、可以在模板字符串中调用变量或函数。2、模板字符串中的所有的空格和缩进也都会保留。

可以在模板字符串中调用变量,例如:

let author="----jine"
let strs=
`
我要开始输入啦,
这是第二段的字符串输入,
这便是多行字符串的输入.  Noproblem~
                     ${author}
`
console.log(strs)
/*
结果:
	我要开始输入啦,
这是第二段的字符串输入,
这便是多行字符串的输入.  Noproblem~
                     ----jine
*/


-----------------------------------------------------------------------------------------
也可以在模板字符串中写调用函数,例如

let fun=()=>"我是一个小函数~"
console.log(`我也可以调用函数哟:${fun()}`)
字符串的扩展
1、includes() 

用来判断一个字符串是否包含在另一个字符串中,返回布尔值
'注意:是区分大小写的'

let str = 'woshihhaoee'

console.log(str.includes('o',8))
/* true */
console.log(str.includes('o',9))
/*false */


------------------------------------------------------------
2、startsWith()

用来判断当前字符串的指定索引值(默认为 0)是否以另外一个给定的字符串 '开头' 的 , 返回布尔值

'注意:是区分大小写的'

例如:

let str = 'woshihhaoee'
console.log(str.startsWith('wo'))
/* true */
console.log(str.startsWith('hi',3))
/* true */

----------------------------------------------------------
3、endsWith()

用来判断当前字符串的指定索引值(默认为 0)是否以另外一个给定的字符串 '结尾' 的 , 返回布尔值


-----------------------------------------------------------
4、repeat()

用于将原字符串重复n次,返回一个新字符串
返回的是一个新字符串,并不会改变原有字符串内容

str.repeat(number)

number 参数有以下几种情况:

若number参数为小数,则会向下取整
若number参数为负数或无穷大,则会报错
若number参数为NaN,则为0(不报错,不会有任何输出)
若number参数为字符串,则会先转换为数字值


例如:

let str = 'woshihhaoee'
res=str.repeat(3)
console.log(str,res)
/* 
    woshihhaoee 
    woshihhaoeewoshihhaoeewoshihhaoee
*/

------------------------------------------------------------
5、原始字符串

let str = 'woshihhaoee'

function fun(arg){
    console.log(arg)
}

fun `this is ${str} ok`

/*
    结果:[ 'this is ', ' ok' ]
    原因:在函数的第一个参数中,都存在raw属性(获取模板字符串的原始字符串)
        原始字符串:是模板字符串被定义时的内容,而不处理之后的内容
 */

fun(`this is ${str} ok `)

/*
    结果:this is woshihhaoee ok 
         将模板字符串进行了处理
*/



-----------------------------------------------------------------------------
6、raw()

ES6 还为原生的 String 对象,提供了一个`raw()`方法。该方法返回每一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法。

例如:
console.log(`hello\n${2+1}`)
/*
	hello
	3
*/

console.log(String.raw`hello\n${2+1}`)
/*
	hello\n3
*/

展开运算符

对象展开
let Person={
    name:"jine",
    age:22,
    printf(){
        console.log(this.name,this.age)
    }
}

let Fun={
    song:"歌唱",
    jump:"跳跳"
}


let Jine={
    ...Person, //将Person对象中所有的成员拿过来
    ...Fun,
    jump:"不跳了" //此时jump把前面Fun对象传入的jump覆盖
}

console.log(Jine)
/*
结果:
	{
      name: 'jine',
      age: 22,
      printf: [Function: printf],
      song: '歌唱',
      jump: '不跳了'
	}
*/
数组展开
数组展开做的是深复制(修改一个内容,不会改变另外一个内容)

1、可以做数组拼接


let arr1=[1,2,3]

let arr2=[4,5,6]

let arr3=[...arr1,...arr2]
console.log(arr3)
/* 
	[1,2,3,4,5,6]
	此结果将俩个数组,做了合并
*/

------------------------------------------------------------
2、也可求max值等

let max1=Math.max(arr3)
console.log(max1)
/* NaN */

let max2=Math.max(...arr3)
console.log(max2)
/* 6 */


----------------------------------------------------------
3、 解构赋值和展开运算符配合使用
let num = [1,2,3,4,5,6]
let [arr1,...arr2] = num
console.log('arr1:'+arr1,'arr2:'+arr2)
/*
    arr1:1 
    arr2:2,3,4,5,6
 */

----------------------------------------------------------------
4、字符串配合数组展开

let sttr = 'Hello World'
console.log([...sttr]) 
/*
    ['H', 'e', 'l', 'l','o', ' ', 'W', 'o','r', 'l', 'd']
 */

函数新特性

箭头函数
匿名函数的简写,但是这样写法的this指向和以前写法的this指向不一样

简写规则:

a.function改成=>     => 可以读成goesto
b.如果只有一个形参,那就可以省略形参小括号.
C.如果不是一个形参,0个或者多个形参,那就不能省略这个形参小括号了
d.如果函数体只有一句话, 那就可以省略函数体的大括号
e.如果函数体只有一句话,并且这一句话是return返回值,return也要省略
f.如果函数体不是一句话,那就不能省略这个大括号.

-----------------------------------------------------------------------------------------
由规则b,d,例如:
let fun=function(name){
    console.log("name:"+name)
}
/*ES5写法*/

let fun=name=>console.log("name:"+name)
/*ES6箭头函数*/

fun("jine")

-----------------------------------------------------------------------------------------
由规则c,d,例如:
let fun=function(name,age){
    console.log(name,age)
}
/*ES5写法*/

let fun=(name,age)=>console.log(name,age)
/*ES6箭头函数*/

fun("jine",22)

-----------------------------------------------------------------------------------------
由规则a,d,e,例如:
let fun=function(name){return name}
/*ES5写法*/

let fun=name=>name
/*ES6箭头函数*/

console.log(fun("jine"))

-----------------------------------------------------------------------------------------
由规则c,f,例如:
let fun=function(name,age){
    console.log(name)
    console.log(age)
}
/*ES5写法*/

let fun=(name,age)=>{
    console.log(name)
    console.log(age)
}
/*ES6写法*/

fun("jine",22)

-----------------------------------------------------------------------------------------

"需要注意的是:
let fun=name=>
console.log(name)

"这样也是可以的,虽然换行了,但是因为有箭头,所以不是结束,所以还会输出name"
箭头函数 this
ES5 定义函数时,使用的this,指向'调用函数时'的上下文对象

ES6 定义箭头函数时 ,使用的this,指向'定义箭头函数时'的上下文对象
* 不要用new 关键字去调用箭头函数
* 不可使用argument对象,该对象在函数体内不存在。若用,可以使用rest参数代替

例如:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>箭头函数this</title>
</head>
<body>
    <button id="btn">点我呀~</button>
    <script>
        //function函数写法
        // document.getElementById('btn').οnclick=function(){
        //     console.log(this)
                /*btn元素节点*/
        //     console.log(this.textContent)
                /*btn中的文本内容*/
        // }

        // //箭头函数写法
        document.getElementById('btn').onclick=()=>{
            console.log(this)
            /*Window*/
            console.log(this.textContent)
            /*undefined*/
        }
    </script>
</body>

</html>
函数参数指定默认值
ES5标准不允许对函数的参数设置其默认值。若定义了形成,而不传递实参时,会导致形成的值为undefined。

例如;

function fun(arg){
    console.log(arg)
}

fun()
/*undefined */


function fu(arg){
    //通过其他方式设置默认值
    arg = arg || 0
    console.log(arg)
}

fu()
/*0 */


-------------------------------------------------------------------
Es6 新增可以给形参设置默认值

let funny = (arg=1) => console.log(arg)
funny()
/* 1 */



===============================================================
let v = 100
let func = (arg=v) => {
    let v = 200 
    console.log(arg)
}

func()
/*
    结果:100
    结论:加载当前函数的形参,并且设置其默认值时,形成一个独立的作用域
          当前独立作用域既不是全局作用域,而不是函数作用域,也不是块级作用域
          当为形参设置默认值时,当前只能访问全局变量v
          当前独立作用域,会在函数声明结束后,自动消失
*/
rest 参数
ES6 新增rest 参数,用来获取函数多余的参数,替代arguments对象,与rest参数配合的变量是一个数组,该变量将多余的参数放入数组中。

'注意:rest参数之后不能再有其他参数(只能是最后一个参数),否则会报错
函数的length属性,不包含rest参数。

'


例如:

let fun = (arg,...rest) =>{
    console.log(arg,rest)
}

fun(1,2,3,4,5,6,7,8)

/*
    结果:1 [2, 3, 4, 5,6, 7, 8]
    结论: rest 接受多余的形参,成为一个数组
 */

对象新特性

对象成员的简写
let name="jine"
let age=20

let obj={
	name, //相当于name:name
    age,
    printf(){ //相对于 printf:function(){}
        console.loe(name,age)
    }
}
obj.printf()


-----------------------------------------------------------------------------------------
以下写法会报错,例如:
let obj={
    name,
    age,
    score,
    printf(){
        console.log(name,age,score)
    }
}

obj.printf()
/*因为全局作用域没有声明score,所以会报错*/

-----------------------------------------------------------------------------------------
let obj={
    name:1,
    age,
    score:age+58,
    printf(){
        console.log(this.name,age,this.score)
    }
}

obj.printf()
/*结果:1  20  78*/
Object.is()
Object.is() 就是对同值相等算法的具体实现


'ES5'

console.log( +0 === -0 ) // true
console.log(NaN === NaN ) // false


'ES6'
console.log(Object.is(+0 ,-0 )) // false
console.log(Object.is(NaN,NaN)) // true
Object.assign()
用于将所有可枚举属性的值从一个或多个源对象复制到目标对象

Object.assign(target,...sources)

* target : 目标对像
* sources : 源对象
* 返回值 :  目标对象

是一个深复制操作	
super
ES6 新增关键字用于指定向当前对象的原型对象


'注意:super 关键字表示原型对象时,只能在对象的方法之中,用在其他地方都会报错'


let pro = {
    age : 22
}

let obj = {
    name : 'jine',
    printf(){
        // 这里等价于 this.age ,只不过用Es6新增的super关键字,更明了清晰,表达了原型对象
        console.log(super.age)
    }
}

// 将pro对象作为obj的原型对象
Object.setPrototypeOf(obj,pro)
obj.printf()

数组新特性

—引用类型—
Array.from()
可以从一个伪数组或可迭代对象中创建一个新的数组实例

应用:伪数组对象和对象不可直接用展开运算符展开,可以借助Array.from() 构建一个新伪数组对象,再使用展开运算符

当然对象展开也可以以下这样操作:

let obj = {
    0 : 'jine',
    1 : 22,
    2: 'code',
    length : 3
}
let obj2 = {} 
console.log(obj2 = {...obj})
/*
    { '0': 'jine', '1': 22, '2': 'code', length: 3 }
*/



Array.from() 应用如下,例如:

let obj = {
    0 : 'jine',
    1 : 22,
    2: 'code',
    length : 3
}

for (let i=0 ; i<obj.length; i++) {
    console.log(obj[i])
    /*jine  22  code */
}

let arry = Array.from(obj)

console.log(typeof arry)
/* object */
console.log(arry instanceof Array)
/* true */

console.log(...arry)
/*jine 22 code */

/*
	虽然arry是数组,但是一个伪数组对象,不可用Array中的方法,但可以用展开运算符
*/
Array.of()
用于创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型

可以测试代码,得结果,如下:

console.log(new Array(1))
/* [ <1 empty item> ] */
console.log(new Array(1,2))
/* [ 1, 2 ] */

console.log(Array.of(1))
/* [ 1 ] */
console.log(Array.of(1,2))
/* [ 1, 2 ] */


/*

结论:
 * new Array() 若传入一个参数时,表示length
 * Array.of() 若传入一个参数,表示一个元素内容
 * 而 Array.of() 是为了,弥补 new Array() 的不足
*/ 


"注意:若没有传递参数,则返回一个空数组"
—实例方法—
forEach()
forEach() 可以替换以前用的for循环
作用:循环遍历,把遍历出来的每一项交给回调函数,无返回值

例如:
let arr=[1,2,3]
let ret=arr.forEach(function(item,index){
    //item 是遍历出来的每一项
    //index 是遍历出来每一项对应的索引
    console.log(index,item)
    return "返回值"
})
console.log(ret)
/*
结果:
	0 1
	1 2
	2 3
	undefined
*/


-----------------------------------------------------------------------------------------
map()
map() 循环遍历,与forEach()类似,唯一不同的是map()有返回值

let arr=[1,2,3]
let ret=arr.map(function(item,index){
    console.log(index,item)
    return item*item
})
console.log(ret)
/*
结果:
	0 1
	1 2
	2 3
	[ 1, 4, 9 ]
*/

返回属性的值,例如:
let arr=[{name:"jine",age:18},{name:"ren",age:22},{other:"其他",age:23}]
let ret=arr.map((item,index)=>item.name)
console.log(ret)
/*
结果:[ 'jine', 'ren', undefined ]
返回所有name属性的值,第三个没有name实现,所以返回undefined
 */


-----------------------------------------------------------------------------------------
filter
filter() 过滤器
作用:返回一个新的数组,新的数组中的元素是通过检查后符合条件的元素

过滤偶数,例如:
let arr=[1,2,3,4,5,6]
let ret=arr.filter((item,index)=>item%2==0)
console.log(ret)
/*[2,4,6]*/


过滤指定元素,例如:
let arr=[{name:"jine",age:18},{name:"ren",age:22},{other:"其他",age:23}]
let ret=arr.filter((item,index)=>item.name)
console.log(ret)
/*
结果:
    [ { name: 'jine', age: 18 }, { name: 'ren', age: 22 } ]
*/
copyWithin()
用于浅复制数组的一部分到统一数组中的另一个位置,并返回它,而不修改其大小

copyWithin(target,start,end)
    * target:必需。复制到指定目标索引位置。
	* start : 可选。元素复制的起始位置。若省略,则复制默认从开始位置进行
	* end: 	可选。停止复制的索引位置 (默认为 array.length)。如果为负值,表示倒数。若省略,则复制到最后 (注意:不包含结束索引位置)

    
例如:

let arry = [0,1,2,3,4,5]

console.log(arry.copyWithin(1,0,3))
/*
 * 第一个参数,也就是target=1,指定索引目标复制并替换元素 
 * 第二个参数,也就是start=0 , 开始索引位置
 * 第三个参数,也是就是end=3,  结束索引位置
 * 
 * 结果:[ 0, 0, 1, 2, 4, 5 ] 
 * 
 *  结果可看出,target=1,索引1的位置开始替换,而start是复制元素的开始索引位置,end是复制元素结束的索引位置(不包含结束索引位置)
 * 
 */

'注意:不能改变数组的长度,而且修改了原有数组'
find() 与findIndex()
1、find() 
返回数组中满足提供的测试函数的第一个元素的值,否则返回undefined。

arr.find(callback[,thisArg])

callback: 回调函数,有以下三个参数:

    * element : 当前遍历到的元素
	* index : 当前遍历到的索引值
    * array : 数组本身
    
* thisArg : 可选,指定callback的this参数

例如:

let arry = [1,2,3,4,5]

let fun = arry.find((element,index,array) => {
    console.log('element:'+ element)
})

console.log('fun: '+ fun)

/*
    结果:    element:1
            element:2
            element:3
            element:4
            element:5
            fun: undefined
 */

let res = arry.find((element,index,arry) => {
    return element < 3;
})
console.log('res: '+ res);

/*
  结果:res: 1
  结论: 因为element < 3 ,分别有:1,2  ,但是返回数组中满足提供的测试函数的第一个元素的值,所以res结果为1
 */


--------------------------------------------------------------
2、findIndex()

返回数组中满足提供的测试函数的第一个元素的索引值,否则返回-1

因其用法与find()基本类似,只是作用不同,所以不进行代码测试
fill()
用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引值。(浅复制填充)

fill(value[,start[,end]])

* value : 用来填充数组元素的值
* start : 可选项,起始索引,默认值为0
* end : 可选项,终止索引,默认值为 this.length (不包含结束索引)
* 返回值 :修改后的数组

let arry = [1,2,3,4,5]

let results = arry.fill(6)
console.log(arry,results)
/* 
    结果:[ 6, 6, 6, 6, 6 ]
         [ 6, 6, 6, 6, 6 ]
    分析:因其没有指定start和end,所以fill() 默认会将所有元素添加
    
*/

results[0] = 1
console.log(results,arry)

/*
    结果:[ 1, 6, 6, 6, 6 ] [ 1, 6, 6, 6, 6 ]
    结论:fill() 是浅复制填充
 */


let newArry = [1,2,3,4,5]
let fillArry = newArry.fill(6,1,3)
console.log(fillArry)

/*
    结果: [ 1, 6, 6, 4, 5 ]
    结论: fill(6),填充数字为6,start为1,end为3(不包含结束索引位置),所以将第1索引~2索引填充替换为6
*/
includes()
用来判断一个数组是否包含一个指定的值。

根据情况,若包含则返回true,否则返回false

includes(searchElement,fromIndex)

* searchElement : 需要查找的元素值
* fromIndex : 可选项,从该索引处查找searchElement。若为负值,则按升序从 array.length - fromIndex 的索引开始搜索。默认为0

例如:

let arr = [1,2,3,4,5]
console.log(arr.includes(3))
/*true*/
console.log(arr.includes(3,4))
/*false*/

数组应用

数组降维
let arr=[1,2,3,[4,5,6],7,8]

let arrNew=[]

arr.forEach(v=>{
    if(Array.isArray(v)){
        arrNew.push(...v)
    }else{
        arrNew.push(v)
    }
})

console.log(arrNew)
/*由二维降为了一维*/
数组去重
1、排序后判断法

let arr=[10,20,30,30,20,40,50,10,20]
let arrNew=[]
arr.sort((a,b)=>{
    return a-b
})

// [10, 10, 20, 20, 20,30, 30, 40, 50]
console.log(arr)

arr.forEach((item,index)=>{
    //若下一个和上一个不相等
    if(item!=arr[index+1]){
        arrNew.push(item)
    }
})

// [ 10, 20, 30, 40, 50 ]
console.log(arrNew)

=========================================================================================

2、使用对象法,数组去重

let arr=[10,20,30,30,20,40,50,10,20]
let arrNew=[]

let obj={}
arr.forEach(item=>{

    /**
     * 利用对象重复赋值的方法
     * 若要加入obj对象的item属性不是undefined,便加到对象中
     * 若有重复的item属性便不会加到对象中,也就不会执行if中的arrNew.push()
     */
    if(obj[item]==undefined){
        arrNew.push(item)
        obj[item]='value'
    }
})
console.log(arrNew)
/* [ 10, 20, 30, 40, 50 ] */
数组升维
//需求:将下列数组arr,根据类别type升为二维数组
let arr =[
{type:'电子产品',name:'iPhone',price: 8888},
{type:'家具',name:'桌子',price: 100},
{type:'食品',name:'瓜子',price: 10},
{type:'家具',name:'椅子',price: 380},
{type:'电子产品',name:'小米手机',price: 1380}, 
{type :'食品',name:'辣条',price: 5},
{type: '食品',name:'咖啡',price: 50}
];

let obj=[]
let arrNew=[]

arr.forEach(v=>{
    //把第一次type和第一次的值加入arrNew
    if(obj[v.type]==undefined){
        obj[v.type]='value'
        arrNew.push({
            type:v.type,
            data:[v]
        })
    }
    //第二的值加入arrNew中对应的type中
    else{
        arrNew.forEach((v2,j)=>{
            if(v.type==v2.type){
                arrNew[j].data.push(v)
            }
        })
    }
})
console.log(arrNew)


Set 集合

Set 对象是值的集合,可以按照插入的顺序迭代它的元素。
Set 集合中的元素只会出现一次,即Set集合中的元素是唯一的。


'NaN、undefined等值允许被存储在Set集合中‘
'NaN值在set集合中被认为是相等的'

let set = new Set([NaN,NaN,undefined,undefined,null,null])
/*
	结果:{ NaN,undefined,null}
*/


===============================================================

Set 集合中存储复杂数据类型(数组、对象及函数等)
多个空数组和空对象、多个函数 表示多个值
    
let set = new Set([[],[],{},{},function(){},function(){}])
console.log(set)
/*
    Set { [], [], {}, {}, [Function], [Function] }
*/
作用:和数组类似,和数组不同的是不能存放重复的元素
应用场景:数组去重


Set测试,例如:

let set1=new Set([10,20,30,10,20,40])
console.log(set1)
/*
结果是一个Set对象:
    Set { 10, 20, 30, 40 }
*/

-----------------------------------------------------------------------------------------
数组去重,例如:

let arr=[1,2,3,1,2,4,5]
let set2=new Set(arr)
console.log(set2)
/*
    结果:Set { 1, 2, 3, 4, 5 }
*/

let newArr=[...set2]
console.log(newArr)
/*
    去重结果:[ 1, 2, 3, 4, 5 ]
*/
Set 集合中的属性与方法
1、size 属性用于返回Set对象值的个数

例如:

let set = new Set([1,2,3,4,5])
console.log(set.size)
/* 5 */


====================================================================
2、操作Set集合的方法

(1)add(value)
在Set集合尾部添加一个元素,返回该Set对象

(2)delete(value) 
从Set集合中删除指定的元素。返回布尔值,表示是否删除成功

(3)has(value)
检索Set集合是否包含指定的元素。返回布尔值,表示是否包含

(4)clear()
清除Set集合中所有元素,没有返回值




==================================================================
3、遍历Set集合的方法

(1)values()
返回一个新的迭代器对象(可遍历),该对象包含Set集合中的按插入顺序排列的所有元素的值

let set = new Set([1,2,3,4,5])
let values = set.values()
for (let i of values) {
    console.log(i)
}
/* 1 2 3 4 5 */


'没有length属性值,常规的循环语句无法使用'
'只能使用for...of进行遍历'

(2)keys()
与values()方法相同

let set = new Set([1,2,3,4,5])
let keys = set.keys()
for (let i of keys) {
    console.log(i)
}
/* 1 2 3 4 5 */

(3)entries()
返回一个新的迭代器对象,该对象包含Set集合中的按插入顺序排列的所有元素的值的[value,value]数组

let set = new Set([1,2,3,4,5])
let entries = set.entries()
for (let i of entries) {
    console.log(i)
}

/*
    [ 1, 1 ]
    [ 2, 2 ]
    [ 3, 3 ]
    [ 4, 4 ]
    [ 5, 5 ]
*/


(4)forEach(value,key,set)

*value:值
*key:键
*set集合

按照插入顺序,为Set集合中每一个元素调一次callback函数

let set = new Set([1,2,3,4,5])
set.forEach((value,key,set) => {
    console.log(key,value,set)
})

/*
    1 1 Set { 1, 2, 3, 4, 5 }
    2 2 Set { 1, 2, 3, 4, 5 }
    3 3 Set { 1, 2, 3, 4, 5 }
    4 4 Set { 1, 2, 3, 4, 5 }
    5 5 Set { 1, 2, 3, 4, 5 }
*/


/*
	以上结果可以看出,在set集合中key和value是相等的
*/
Set集合与Array对比
1、数组中用于判断元素是否存在的indexOf() 效率低下 (不如Set集合中has()2、Set对象允许根据值删除元素,而数组中必须使用基于下标的splice()

3、数组的indexOf()方法无法找到NaN值,而Set集合可以

4、Set对象存储不重复的值,所以不需要手动处理包含重复值的情况

WeakSet 集合

WeakSet 对象是一些对象的集合,并且其中每个对象值只能出现一次

WeakSet集合与Set集合区别有俩点:

  *只能存放对象引用,不能存放值。而Set集合可以

  *存储的对象值都是被弱引用的。若没有其他变量或属性引用这个对象值,则这个对象值会被当成垃圾回收掉。因此,WeakSet无法被枚举,无法拿到WeakSet集合包含的所有元素



WeakSet集合提供的方法和Set集合中一样,如下:

(1)add(value)
在WeakSet集合尾部添加一个元素,返回该WeakSet对象

(2delete(value) 
从WeakSet集合中删除指定的元素。返回布尔值,表示是否删除成功

(3)has(value)
检索WeakSet集合是否包含指定的元素。返回布尔值,表示是否包含

(4)clear()
清除WeakSet集合中所有元素,没有返回值

Map 集合

是键值对的集合。任何值都可以作为Map 集合中的键值对。
可以按照插入的顺序迭代它的元素。

与Set集合的操作方法和遍历方法相同,都继承与同一原型

与Set集合唯一的区别是:set(key,value)

例如:

let map = new Map()

let obj = {
    name : 'jine',
    age : 22,
    printf: ()=>{
        console.log("yes")
    }
}

map.set('name',obj.name)
map.set('age',obj.age)
map.set('fun',obj.printf)

console.log(map)

/*
    Map { 'name' => 'jine', 'age' => 22, 'fun' => [Function: printf] }
*/

Map集合与Object对比
Object 的键均为String类型,但在Map里键可以是任意类型
必须手动计算Object的尺寸,但可以很容易获取使用Map迭代尺寸
Map的遍历遵循元素的插入顺序
Object 有原型,所以映射中有一些缺省的键

Promise (解决回调地狱)

Promise 概述

Promise是异步编程的一种解决方案,比传统的解决方案一回调函数和事件一更合理和更强大。

它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。

所谓Promise,简单说就是一个容器, 里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

从语法上说,Promise是一个对象,从它可以获取异步操作的消息。

Promise 提供统一的API,各种异步操作都可以用同样的方法进行处理。

Promise 对象代表一个异步操作:
有三种状态:pending(进行中)、fulfilled (已成功)和rejected (已失败)Promise对象的状态改变,只有两种可能:
1、成功:从pending变为fulfilled
2、失败:从pending变为rejected。

只有异步操作的结果,可以决定当前是哪一种状态, 任何其他操作都无法改变这个状态。

这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。


ajxa和axios也会返回promise对象(有些方法自身便会返回promise对象,不用去手写promise对象)

Promise 语法

ES6规定,Promise对象是一个构造函数,用来生成Promise实例

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

异步操作成功后,调用resolve()方法,其内部调用了then()里面的第一个参数函数
异步操作失败后,调用reject()方法,其内部调用了then()里面的第二个参数函数

例如:

const fs=require('fs')

let p=new Promise((resolve,reject)=>{
    //写异步操作(读文件)
    fs.readFile(`${__dirname}/file/aa.txt`,'utf-8',(err,data)=>{
        if(err){
            //文件读取失败
            reject(err)  // 调用then() 里面第二个参数函数
        }else{
            //文件读取成功
            resolve(data)//调用then() 里面第一个参数函数
        }
    })
})

p.then((data)=>{
    console.log(data)
},(err)=>{
    console.log(err)
})

/*上面p对象中写异步操作,这个对象会直接执行。*/

-----------------------------------------------------------------------------------------当然也可以为其封装一个函数,以后调用时再执行,例如:

function getPromise(){
    return new Promise((resolve,reject)=>{
    //写异步操作(读文件)
    fs.readFile(`${__dirname}/file/aa.txt`,'utf-8',(err,data)=>{
        if(err){
            //文件读取失败
            reject(err)  // 调用then() 里面第二个参数函数
        }else{
            //文件读取成功
            resolve(data)//调用then() 里面第一个参数函数
        }
    })
})
}

getPromise().then((data)=>{
    console.log(data)
},(err)=>{
    console.log(err)
})

回调地狱概述

假如需求是依次去执行异步代码(异步操作无顺序),而且是顺序执行

那么做法是在异步请求成功后的回调函数中,再去执行下一个异步请求

但是会出现回调地狱,例如:

/*
补充知识:
在一个回调函数中嵌套回调函数,不断的进行嵌套,代码阅读性地,维护不变,让人觉得害怕,这样就是回调地狱
*/

//导包
const fs=require('fs')

//读a文件
fs.readFile(`${__dirname}/file/a.txt`,'utf-8',(err,data)=>{
    if(err){
        console.log(err);
    }else{
        console.log(data); 
        
        //读b文件
        fs.readFile(`${__dirname}/file/b.txt`,'utf-8',(err,data)=>{
            if(err){
                console.log(err);
            }else{
                console.log(data);  

                //读c文件
                fs.readFile(`${__dirname}/file/c.txt`,'utf-8',(err,data)=>{
                    if(err){
                        console.log(err);
                    }else{
                        console.log(data);  
                    }
                })
            }
        })
    }
})


解决回调地狱

//导包
const fs=require('fs')

function getPromise(filename){
    return new Promise((resolve,reject)=>{
    //写异步操作(读文件)
    fs.readFile(`${__dirname}/file/${filename}.txt`,'utf-8',(err,data)=>{
        if(err){
            //文件读取失败
            reject(err)  // 调用then() 里面第二个参数函数
        }else{
            //文件读取成功
            resolve(data)//调用then() 里面第一个参数函数
        }
    })
})
}

getPromise('a').then((data)=>{
    console.log(data)
    
    //调用getPromise(),此时传入的参数是b文件,并返回
    //return 返回的整体再去执行下一个then
    return getPromise('b')
},(err)=>{
    console.log(err)
}).then((data)=>{
    console.log(data)
    return getPromise('c')
}).then((data)=>{
    console.log(data)
},(err)=>{
    console.log(err)
})

Promise 方法补充

catch()
抓取错误信息,这样在每个文件后面省略了 (err)=>{console.log(err)}
只需要再最后写个文件后面 catch((err)=>{console.log(err)})

例如:

//导包
const fs=require('fs')

function getPromise(filename){
    return new Promise((resolve,reject)=>{
    //写异步操作(读文件)
    fs.readFile(`${__dirname}/file/${filename}.txt`,'utf-8',(err,data)=>{
        if(err){
            //文件读取失败
            reject(err)  // 调用then() 里面第二个参数函数
        }else{
            //文件读取成功
            resolve(data)//调用then() 里面第一个参数函数
        }
    })
})
}

getPromise('a').then((data)=>{
    console.log(data)
    
    //调用getPromise(),此时传入的参数是b文件,并返回
    //return 返回的整体再去执行下一个then
    return getPromise('b1')
}).then((data)=>{
    console.log(data)
    return getPromise('c')
}).then((data)=>{
    console.log(data)
}).catch((err)=>{
    console.log(err)
})

finally()
返回一个Promise对象,在执行 then() 和 catch() 后,都会执行finally指定的回调函数。这样可以避免同样的语句在then() 和 catch() 中各写一次的情况。

'finally() 是ES2018 引入的标准'
all()
用于将多个promise实例,包装成一个新的promise实例
所有promise对象执行成功,all才会成功(相对于并且)

//导包
const fs=require('fs')

function getPromise(filename){
    return new Promise((resolve,reject)=>{
    //写异步操作(读文件)
    fs.readFile(`${__dirname}/file/${filename}.txt`,'utf-8',(err,data)=>{
        if(err){
            //文件读取失败
            reject(err)  // 调用then() 里面第二个参数函数
        }else{
            //文件读取成功
            resolve(data)//调用then() 里面第一个参数函数
        }
    })
})
}

let p1=getPromise('a')
let p2=getPromise('b')
let p3=getPromise('c')

let pAll=Promise.all([p1,p2,p3])
//每一个promise对象(p1,p2,p3)都读取成功,最后才会成功
pAll.then((data)=>{
    console.log(data)
})
race()
此方法和all()类似,但是只要一个promise执行成功,那么race就成功(相当于或者)

例如:

//导包
const fs=require('fs')

function getPromise(filename){
    return new Promise((resolve,reject)=>{
    //写异步操作(读文件)
    fs.readFile(`${__dirname}/file/${filename}.txt`,'utf-8',(err,data)=>{
        if(err){
            //文件读取失败
            reject(err)  // 调用then() 里面第二个参数函数
        }else{
            //文件读取成功
            resolve(data)//调用then() 里面第一个参数函数
        }
    })
})
}

let p1=getPromise('a')
let p2=getPromise('b')
let p3=getPromise('c')

let pAll=Promise.race([p1,p2,p3])
//每一个promise对象(p1,p2,p3)有一个读取成功,race便会成功(每次执行后便会随机读一个文件)
pAll.then((data)=>{
    console.log(data)
})

async 函数

ES7 标准引入。该函数主要目的简化使用Promise异步调用的操作,并对一组Promise 执行某些操作。Promise类似于结构化回调,async/await 类似于组合生成器和Promise。


1async 关键字
在函数前面 加上关键字 async ,js底层便自动给这个函数生成一个Promise

例如:

let  fun = async () => {return 'ok'}
fun().then(value => console.log(value))

'注意:上述代码,return语句中没有await表达式,因此异步函数的返回值被隐式的传递给Promise.resolve'


-------------------------------------------------------------------
2await 关键字

await 操作符用于等待一个 Promise 对象, 它只能在异步函数 async function 内部使用。

返回 Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。

若 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。


let promise = new Promise((resolve,rejects) => {
    return resolve('Promise is ok ')
})

console.log('同步代码1')

let fun = async () => {
    console.log('start...')
    let result1 = await 'async中的同步代码'
    let result2 = await promise
    console.log(result1)
    console.log(result2)
    console.log('end...')
}
fun()
console.log('同步代码2')

/*
    结果:
        同步代码1
        start...
        同步代码2
        async中的同步代码
        Promise is ok 
        end...

    结论:
    	只要async函数体中有await关键字,那么其关键字后面代码将都被阻塞,处于等待状态。
        将先处理async函数外面的同步代码之后,再返回依次处理
*/




-------------------------------------------------------------------------------------
3、利用 try...catch 捕获 Promise中的rejects   
    
let promise = status =>{
    return new Promise((resolve,rejects) => {
        if(status){
            return resolve('Promise is ok ')
        }
        return rejects('Promise is failed')
    })
}

let fun = async () => {
    console.log('start...')
    
    try{
        let result = await promise(false)
    }catch(err){
        console.log(err)
    }
}

fun()

/*
    结果:
        start...
        Promise is failed

    结论: 用 await关键字时,最好使用 try...catch 来捕获异常错误,因为Promise可能会返回的是rejects

*/

Symbol 类型

概述

ES6 新增第六种原始类型 Symbol(符号)类型。

Symbol 类型是唯一的,并且可以用来作为Object的key值

'注意:Symbol 类型是原始类型,是不能通过 new Symbol() 来创建对象'

例如:

let symbol = Symbol('jine')
let sym = Symbol('jine')

console.log(symbol)
/* Symbol(jine) */
console.log(sym)
/* Symbol(jine) */

console.log(sym===symbol)
/* 
    false
    因此可以说,类型是唯一的
*/

Symbol.for()

该方法会根据给定的键Key,来从运行时的symbol注册表中找到对应的symbol。若找到,则返回,否则,新建一个与该键关联的symbol,并放入全局symbol注册表中。

let sym = Symbol.for('jine')
console.log(sym)

/*
    Symbol(jine)
*/


Symbols 在 for...in 迭代中不可枚举。
可以使用Object.getOwnPropertySymbols() 得到

迭代器

ES5	规范表示集合的数据结构有数组(Array)和对象(Object)

ES6 规范又新增了 Set和Map 俩种集合。

这样在JavaScript中就有四种集合,需要一种统一的机制进行操作。

迭代器便是这种机制,为各种不同的数据结构提供统一的访问机制。

在任何数据结构只要部署 Iterator 接口,便可以完成遍历操作

迭代器具有三种作用:
* 为各种数据结构,提供一个统一的、简便的访问接口
* 使得数据结构的成员能够按某种次序排列
* ES6 新增了 for...of 循环语句,用于遍历迭代器



在 JavaScript中迭代器是一个对象,该对象提供next()方法用于返回序列中的下一项,该方法返回包含 done 和 value 俩个属性的对象



以下便是迭代器,例如:

/*
    迭代器: 返回迭代对象
        具备条件:
                * 该迭代对象必须具有next()方法
        作用:用于返回序列的下一项
        返回值:是一个对象
            * done属性:表示是否迭代完毕(true,false)
            * value属性 :表示当前迭代的值

*/

let fun = arr => {
    let index = 0;
    return {
        next : () => {
            return index < arr.length ? {
                done : false ,
                value : arr[index++]
            }:{
                done : true
            }
        }
    }
}

let arr = [1,2,3,4,5,6]

let results = fun(arr)

for (let i=0 ; i<arr.length ; i++){
    console.log(results.next())
    /*
        { done: false, value: 1 }
        { done: false, value: 2 }
        { done: false, value: 3 }
        { done: false, value: 4 }
        { done: false, value: 5 }
        { done: false, value: 6 }  
    */
}

Iterator

一种数据只要部署了Iterator 接口 ,就可以称这种数据结构是'可遍历的'ES6 规范规定默认的Iterator 接口部署在数据结构的  Symbol:iterator 属性。 (一个数据结构只要具有Symbol.iterator 属性,就可以认为是'可遍历的')

Symbol.iterator 属性本身是一个函数,就可以当成数据结构默认的迭代器生成函数。执行这个函数就会返回一个迭代器。


for…of

ES6 引入 for...of 语句用于遍历迭代器。

一个数据结构只要部署了Symbol.iterator 属性,就被视为具有iterator接口,就可以用for...of 循环遍历它的成员。

for(variable of iterable) {
    // satements
}

* variable:在每次迭代中,将不同属性的值分配给变量
* iterable: 被迭代枚举其属性的对象

for…of 和 forEach 区别

* forEach() 方法无法跳出循环。break语句和continue语句无效。
* for ... of 语句可以,而且还能配合 return 语句

for…of 和 for…in 区别

* for...in 不仅遍历自身,还会遍历手动添加的,甚至包括原型链的

* 若用于遍历数组的话,遍历得到的键名为字符串类型的数字值

生成器

Generator 函数

虽然可以自定义一个迭代器,但自定义的迭代器需要显式地维护其内部状态。而生成器提供了另一个强大的选择,其提供了允许定义一个包含自有迭代算法的函数,同时可以自动维护其内部状态。

Generator函数可以作为生成一个迭代器的特殊函数,该函数被调用时返回一个Generator对象,该对象是符合可迭代协议和迭代器协议的。


Generator 函数与普通函数区别:

* function* 这种声明方式会定义一个生成器函数,它返回一个Generator对象
* yield 关键字用来暂停和恢复一个生成器函数


例如:

// 定义一个生成器函数
function * fun (arry){
    for (let i=0; i<arry.length; i++){
        yield arr[i]
    }  
}

let arr = ['jine','age','hha']

// 生成器函数调用返回生成器对象
let result = fun(arr)

// 生成器对象就是ES6提供的迭代器
console.log(result.next())
/*
    { value: 'jine', done: false }
*/
console.log(result.next())
/*
    { value: 'age', done: false }
*/
console.log(result.next())
/*
    { value: 'hha', done: false }
*/

yield * 表达式

用于委托给另一个Generator 或可迭代对象

yield * [[expression]]

* expression: 返回一个可迭代对象的表达式


例如:

function * funny (){
    yield 'funny'
    yield 'ok'
}

// 定义一个生成器函数
function * fun (arry){
    for (let i=0; i<arry.length; i++){
        yield arr[i]
        yield * funny()
    }  
}

let arr = ['jine','age','hha']

// 生成器函数调用返回生成器对象
let result = fun(arr)

console.log(result.next())
console.log(result.next())
console.log(result.next())
console.log(result.next())
console.log(result.next())
console.log(result.next())
console.log(result.next())
console.log(result.next())
console.log(result.next())


/*
    { value: 'jine', done: false }
    { value: 'funny', done: false }
    { value: 'ok', done: false }
    { value: 'age', done: false }
    { value: 'funny', done: false }
    { value: 'ok', done: false }
    { value: 'hha', done: false }
    { value: 'funny', done: false }
    { value: 'ok', done: false }

 */

Generator 对象的方法

1next()
返回一个包含属性 done 和 value 的对象。该方法也可以接受一个参数用以向生成器传值

2return()

返回给定的值并结束生成器

3throw()

用于向生成器抛出异常,并恢复生成器的执行,返回带有done和value俩个属性的对象

这三个方法本质的作用都是让Generator 函数恢复执行,并且使用不同的语句替换yield表达式

Class 关键字

ES6 提供了更接近传统开发语言的写法,引入了类(Class)的概念。
类作为对象的模板,只是一个语法糖。
Class关键字只是让对象原型的写法更加清晰、更加面向对象编程的语法而已。

类的声明

有俩种方式:

1、类的声明方式

Class Name {
    // 这样方式声明的类,不允许再次声明已经存在的类,否则会抛出一个类型错误。
}


2、类的表达式

const MyClass = class [classname]{
    // 和函数表达式相同,类的表达式的命名也可以说匿名的。
    // 若命名类表达式,这个名字只能在类体内才能访问到
}


'注意:声明类时,不允许声明提前,也不允许重复声明'

Constructor

构造器,是用于创建和初始化类中创建对象的一种特殊方法。

* 在一个类中只能有一个名为 'Constructor' 的特殊方法。一个类中多次出现将会抛出SyntaxError错误。

* 在一个构造方法中可以使用 super 关键字来调用父类的构造方法

* 若没有显示指定构造方法,则会添加默认的constructor方法


例如:

// 类定义
class People {
    // 构造器
    constructor (){
        this.name = 'jine'
        this.age = 22 
        this.print = () => {
            console.log(this.name,this.age)
        }
    }
}

let jine = new People()

jine.print()
/* jine 22 */


class Person {
    constructor (name,age) {
        this.name = name 
        this.age = age 
        this.printf = () => {
            console.log(this.name,this.age)
        }
    }
}

let people = new Person('jine',22)

people.printf()
/* jine 22 */

getter 与 setter

class People {
    constructor (name,age) {
        this.name  = name
        this.age = age
        this.printf = () => {
            console.log(this.name,this.age)
        }
    }
}

let  jine = new People ('jine',22)

jine.printf()
/* jine 22 */

jine.name = 'ren'
jine.age = 18 
jine.printf()
/*
    结果: ren 18
    结论; 这样因为,在ES6中当用构造函数初始的对象,当进行属性的操作时,都会默认提供getter 和 setter 
           在ES5 中 , 并没有默认提供,而是需要自己进行定义get和set 方法,才能进行修改其属性

*/

static

static 关键字为一个类定义的静态方法。不会在类的实例上被调用,相反被类本身调用

'注意:在static中的this,指向的是类,而不是当前对象'

class People {
    constructor (name,age) {
        this.name = name
        this.age = age
    }

    static print(name,age){
        console.log(name,age)
    }

    static printf(name,age){
        // 此时的this可不是对象,而是类(因为静态方法只能用类名调用)
        this.print(name,age)
    }
}   

let jine = new People('jine',22)

console.log(jine)

/*
    People { name: 'jine', age: 22 }
    可以看出并没有打印出,static定义的静态方法
*/

People.printf('static',18)
/*  
    static 18
    注意: 在static 定义的方法中,其 this 指的是类,而不是当前的对象。(因为static定义的方法,只能通过类名来调用)
*/

类的继承

extends 关键字用于类声明或者类表达式中,以创建一个类,该类作为另一个类的子类

class Person {
    constructor (){
      this.name = 'jine'  
    }
}


class People extends Person {
	 constructor (){
     	super()
        this.age = 2
    }
}

'注意:继承的prototype 必须是一个 Object 或者 null '

'注意:在子类的构造器中需要加 super() 关键字 ,此时super()关键字的含义是:指向当前子类的父类构造器'



====================================================================
    
类继承不只是可以继承constructor中的内容,是全部继承下来(包括普通方法和静态方法)


class Person {
    constructor (name){
        this.name = name
        this.printf = () => {
            console.log(this.name)
        }
    }

    fun(){
        console.log('fun() ... ')
    }

    static staMethod(){
        console.log('staMethod() ... ')
    }
}

class People extends Person {
    constructor () {
        this.age =22 
        super()
    }
}

let jine = new Person('jine')

console.log(jine)
/* Person { name: 'jine', printf: [Function] } */

jine.printf()
/* jine */
jine.fun()
/* fun() ...  */
People.staMethod()
/* staMethod() ...  */


/*
    结论:以上结果,可看出,子类继承于父类的,不只是constructor() 中的属性和方法,其普通方法和静态方法也可以继承下来
*/

super关键字

1、在constructor中使用 super() 表示:指向当前类的父类构造器。


'注意:super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B的实例,因此super()在这里相当于A.prototype.constructor.call(this)'


例如:

class Person {
    constructor (name) {
        this.name = name
        this.printf = () => {
            console.log(this.name)
        }
    }
}

class People extends Person {
    constructor (name) {
        // 此时的super() 指向的是当前类的父类构造器
        super(name)
    }
}

let jine = new People('jine')

jine.printf()
/* jine */



----------------------------------------------------------------
2super 作为对象时,在普通方法中,指向父类的原型对象。在静态方法中,指向父类(不是父类的实例)。

'注意: 由于super指向父类的原型对象,所以定义在父类实例方法上的方法和属性,是无法通过super 调用的。'


例如:

class A {
    p() {
      return 2;
    }
}
  
class B extends A {
    fun(){
        console.log(super.p()); // 2
    }
}
  
let b = new B();

console.log(A.prototype.p())
/* 2 */

b.fun()
/* 
    2
    说明此时的super.p() 指向的是A.prototype
*/



'需要注意,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。'

例如:

class A {
    constructor(){
        this.name = 'jine'
    }
}

A.prototype.age = 22

class B extends A {
    printfName(){
        console.log(super.name)
    }
    printfAge(){
        console.log(super.age)
    }
}
  
let b = new B();

b.printfName()
/* 
    undefined
    上面代码中,name是父类A实例的属性,super.p就引用不到它。
    如果属性定义在父类的原型对象上,super就可以取到,如下面的age
 */

b.printfAge()

/*
    22
    因其在A.prototype上绑定了age 
*/


------------------------------------------------------------
ES6 规定,在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例。

class A {
  constructor() {
    this.x = 1;
  }
  print() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  m() {
    super.print();
  }
}

let b = new B();
b.m() // 2

'上面代码中,super.print()虽然调用的是A.prototype.print(),但是A.prototype.print()内部的this指向子类B的实例,导致输出的是2,而不是1。也就是说,实际上执行的是super.print.call(this)。'



其他的再多扩展无非离不开super的俩种情况,更多的super说明参考阮一峰的第22.Class的继承中的super关键字

Proxy(代理)

概述

Proxy 也就是代理,可以帮助我们完成很多事情,例如对数据的处理,对构造函数的处理,对数据的验证,说白了,就是在我们访问对象前添加了一层拦截,可以过滤很多操作,而这些过滤,由你来定义。


'此Proxy内容作为了解使用,只需要知道语法和大体机制,暂不深究'

语法

 let pro= new Proxy(target, handler);


* target:需要使用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)

* handler属性是当执行一个操作时定义代理的行为的函数(可以理解为某种触发器),具体的handler相关函数请查阅官网



get行为例子:

let obj = {
    name : 'jine'
}

// 定义代理对象
let pro = new Proxy(obj,{
    // target: obj , key : attr
    get(target,key){
        if(key in target){
            return target[key]
        }else{
            throw ReferenceError('此对象属性不存在')
        }
    },
    fun(){
        console.log("aaa")
    }
})



console.log(pro.name)
/*
     jine
*/

console.log(pro.age)
/*
     throw ReferenceError('此对象属性不存在')
            ^
     ReferenceError: 此对象属性不存在
        at Object.get (c:\Users\lenovo\Desktop\my_code_space\ren_chunjin\Daily code practice\10、29  类\proxy.js:10:19)
        at Object.<anonymous> (c:\Users\lenovo\Desktop\my_code_space\ren_chunjin\Daily code practice\10、29  类\proxy.js:16:17)
        at Module._compile (internal/modules/cjs/loader.js:1138:30)
        at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
        at Module.load (internal/modules/cjs/loader.js:986:32)
        at Function.Module._load (internal/modules/cjs/loader.js:879:14)
        at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
        at internal/main/run_main_module.js:17:47
*/

console.log(pro.fun())
/*
     throw ReferenceError('此对象属性不存在')
            ^
     ReferenceError: 此对象属性不存在
        at Object.get (c:\Users\lenovo\Desktop\my_code_space\ren_chunjin\Daily code practice\10、29  类\proxy.js:10:19)
        at Object.<anonymous> (c:\Users\lenovo\Desktop\my_code_space\ren_chunjin\Daily code practice\10、29  类\proxy.js:16:17)
        at Module._compile (internal/modules/cjs/loader.js:1138:30)
        at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
        at Module.load (internal/modules/cjs/loader.js:986:32)
        at Function.Module._load (internal/modules/cjs/loader.js:879:14)
        at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
        at internal/main/run_main_module.js:17:47
*/




set行为,例如:

let obj = {}

let pro = new Proxy(obj,{
    get(target,key){
        if(key in target){
            return target[key]
        }else{
            return new ReferenceError('此对象中没有此属性')
        }
    },
    set(target,key,value){
        if(key === 'age' && value > 22) {
            return new ReferenceError('输入年龄太大')
        }else{
            target[key] = value
            return '添加成功'
        }
    }
})

pro.age = 18
pro.age = 23
console.log(pro.age)
/*
    结果:18
    结论: 因在set行为中拦截了输入的value > 22 , 所以 pro.age = 23 是无效的。
           最后 pro.age 也可以看出结果为18
*/

Reflect(反射)

概述

'此内容作为了解,有个大概体系即可,此内容来自 阮一峰ES6'

Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect对象的设计目的有这样几个。

(1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。

(2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。

(3) 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。

(4)Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。

Proxy(target, {
  set: function(target, name, value, receiver) {
    var success = Reflect.set(target, name, value, receiver);
    if (success) {
      console.log('property ' + name + ' on ' + target + ' set to ' + value);
    }
    return success;
  }
});
上面代码中,Proxy方法拦截target对象的属性赋值行为。
它采用Reflect.set方法将值赋值给对象的属性,确保完成原有的行为,然后再部署额外的功能。


有了Reflect对象以后,很多操作会更易读。

// 老写法
Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1

// 新写法
Reflect.apply(Math.floor, undefined, [1.75]) // 1



Reflect 对象中的方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。

模块化

在这里插入图片描述

概述

在这里插入图片描述

Module 模块

服务器和浏览器都会支持 ES6 模块格式。
不再需要对象作为命名空间(比如Math对象),未来这些功能可以通过模块提供。

ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";export命令:用于规定模块对外的接口
➢外部能够读取模块内部的某个变量、函数、类
➢使用as关键字重命名
➢该命令可以出现在模块的任何位置,只要处于模块顶层即可,除了块作用域内(import也是如此)import命令:用于输入其它模块提供的功能
➢变量、函数
➢使用as关键字
➢输入的变量都是只读的
➢Import命令具有提升效果


浏览器加载ES6模块
<script type='module' src='xxx.js'> </script>

了解

<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>
上面代码中,<script>标签打开defer或async属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。

defer与async的区别是:defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

览器对于带有type="module"<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性。


如果网页有多个<script type="module">,它们会按照在页面出现的顺序依次执行。

<script>标签的async属性也可以打开,这时只要加载完成,渲染引擎就会中断渲染立即执行。执行完成后,再恢复渲染。

<script type="module" src="./foo.js" async></script>
一旦使用了async属性,<script type="module">就不会按照在页面出现的顺序执行,而是只要该模块加载完成,就执行该模块
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值