ES6-ES11相关资料 持续更新中…

ES6语法

ES6相关概念

什么是ES6

ES 的全称是 ECMAScript , 它是由 ECMA 国际标准化组织,制定的一项脚本语言的标准化规范。

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

为什么使用 ES6 ?

每一次标准的诞生都意味着语言的完善,功能的加强。JavaScript语言本身也有一些令人不满意的地方。

  • 变量提升特性增加了程序运行时的不可预测性

  • 语法过于松散,实现相同的功能,不同的人可能会写出不同的代码

ES6新增语法

let

ES6中新增了用于声明变量的关键字

let声明的变量只在所处于的块级有效

 if (true) { 
     let a = 10;
 }
console.log(a) // a is not defined
​
{
  let a = 10;
  var b = 1;
}
​
a // ReferenceError: a is not defined.
b // 1
​
let声明的变量只在它所在的代码块有效。

注意:使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域特性。

不存在变量提升

// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
​
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

上面代码中,变量foovar命令声明,会发生变量提升,即脚本开始运行时,变量foo已经存在了,但是没有值,所以会输出undefined。变量barlet命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。

暂时性死区

利用let声明的变量会绑定在这个块级作用域,不会受外界的影响

var tmp = 123;
 if (true) { 
     tmp = 'abc';
     let tmp; 
 } 
 
​
上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。
​
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
​
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

例1

var arr = [];
 for (var i = 0; i < 3; i++) {
     arr[i] = function () {
         console.log(i); 
     }
 }
 arr[0]();
 arr[1]();
 arr[2]();
​

***此题的关键点在于变量i是全局的,函数执行时输出的都是全局作用域下的i值。

 let arr = [];
 for (let i = 0; i < 3; i++) {
     arr[i] = function () {
         console.log(i); 
     }
 }
 arr[0]();
 arr[1]();
 arr[2]();
​

此题的关键点在于每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的i值.

小结

  • let关键字就是用来声明变量的

  • 使用let关键字声明的变量具有块级作用域

  • 在一个大括号中 使用let关键字声明的变量才具有块级作用域 var关键字是不具备这个特点的

  • 防止循环变量变成全局变量

  • 使用let关键字声明的变量没有变量提升

  • 使用let关键字声明的变量具有暂时性死区特性

const

声明常量,常量就是值(内存地址)不能变化的量

具有块级作用域

 if (true) { 
     const a = 10;
 }
console.log(a) // a is not defined

声明常量时必须赋值

const PI; // Missing initializer in const declaration

常量赋值后,值不能修改

const PI = 3.1415;
PI = 100; // Assignment to constant variable.
​
const ary = [111, 222];
ary[0] = 'a';
ary[1] = 'b';
console.log(ary); // ['a', 'b']; 
ary = ['a', 'b']; // Assignment to constant variable.

小结

  • const声明的变量是一个常量

  • 既然是常量不能重新进行赋值,如果是基本数据类型,不能更改值,如果是复杂数据类型,不能更改地址值

  • 声明 const时候必须要给定值

let、const、var 的区别

  • 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象

  • 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升

  • 使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值

解构赋值

ES6中允许从数组中提取值,按照对应位置,对变量赋值,对象也可以实现解构

数组解构

 let [a, b, c] = [1, 2, 3];
 console.log(a)//1
 console.log(b)//2
 console.log(c)//3
//如果解构不成功,变量的值为undefined
​
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 [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
​
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

对象解构

let person = { name: 'zhangsan', age: 20 }; 
 let { name, age } = person;
 console.log(name); // 'zhangsan' 
 console.log(age); // 20
​
 let {name: myName, age: myAge} = person; // myName myAge 属于别名
 console.log(myName); // 'zhangsan' 
 console.log(myAge); // 20
​

小结

  • 解构赋值就是把数据结构分解,然后给变量进行赋值

  • 如果结构不成功,变量跟数值个数不匹配的时候,变量的值为undefined

  • 数组解构用中括号包裹,多个变量用逗号隔开,对象解构用花括号包裹,多个变量用逗号隔开

  • 利用解构赋值能够让我们方便的去取对象中的属性跟方法

箭头函数

ES6中新增的定义函数的方式。

() => {} //():代表是函数; =>:必须要的符号,指向哪一个代码块;{}:函数体
const fn = () => {}//代表把一个函数赋值给fn

函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号

 function sum(num1, num2) { 
     return num1 + num2; 
 }
 //es6写法
 const sum = (num1, num2) => num1 + num2; 
​

如果形参只有一个,可以省略小括号

 function fn (v) {
     return v;
 } 
//es6写法
 const fn = v => v;
​

箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this

const obj = { name: '张三'} 
 function fn () { 
     console.log(this);//this 指向 是obj对象
     return () => { 
         console.log(this);//this 指向 的是箭头函数定义的位置,那么这个箭头函数定义在fn里面,而这个fn指向是的obj对象,所以这个this也指向是obj对象
     } 
 } 
 const resFn = fn.call(obj); 
 resFn();
 

小结

  • 箭头函数中不绑定this,箭头函数中的this指向是它所定义的位置,可以简单理解成,定义箭头函数中的作用域的this指向谁,它就指向谁

  • 箭头函数的优点在于解决了this执行环境所造成的一些问题。比如:解决了匿名函数this指向的问题(匿名函数的执行环境具有全局性),包括setTimeout和setInterval中使用this所造成的问题

例1

var age = 80;
​
var obj = {
    age: 20,
    say: () => {
        alert(this.age)
    }
}
​
obj.say();//箭头函数this指向的是被声明的作用域里面,而对象没有作用域的,所以箭头函数虽然在对象中被定义,但是this指向的是全局作用域

剩余参数

剩余参数语法允许我们将一个不定数量的参数表示为一个数组,不定参数定义方式,这种方式很方便的去声明不知道参数情况下的一个函数

function sum (first, ...args) {
     console.log(first); // 10
     console.log(args); // [20, 30] 
 }
 sum(10, 20, 30)

剩余参数和解构配合使用

let students = ['wangwu', 'zhangsan', 'lisi'];
let [s1, ...s2] = students; 
console.log(s1);  // 'wangwu' 
console.log(s2);  // ['zhangsan', 'lisi']

ES6 的内置对象扩展

Array 的扩展方法

扩展运算符(展开语法)

扩展运算符可以将数组或者对象转为用逗号分隔的参数序列

 let ary = [1, 2, 3];
 ...ary  // 1, 2, 3
 console.log(...ary);    // 1 2 3,相当于下面的代码
 console.log(1,2,3);

扩展运算符可以应用于合并数组

// 方法一 
 let ary1 = [1, 2, 3];
 let ary2 = [3, 4, 5];
 let ary3 = [...ary1, ...ary2];
 // 方法二 
 ary1.push(...ary2);

将类数组或可遍历对象转换为真正的数组

let oDivs = document.getElementsByTagName('div'); 
oDivs = [...oDivs];

构造函数方法:Array.from()

将伪数组或可遍历对象转换为真正的数组

//定义一个集合
let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
}; 
//转成数组
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组

let arrayLike = { 
     "0": 1,
     "1": 2,
     "length": 2
 }
 let newAry = Array.from(arrayLike, item => item *2)//[2,4]
​

注意:如果是对象,那么属性需要写对应的索引

实例方法:find()

用于找出第一个符合条件的数组成员,如果没有找到返回undefined

let ary = [{
     id: 1,
     name: '张三'
 }, { 
     id: 2,
     name: '李四'
 }]; 
 let target = ary.find((item, index) => item.id == 2);//找数组里面符合条件的值,当数组中元素id等于2的查找出来,注意,只会匹配第一个

实例方法:findIndex()

用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1

let ary = [1, 5, 10, 15];
let index = ary.findIndex((value, index) => value > 9); 
console.log(index); // 2

实例方法:includes()

判断某个数组是否包含给定的值,返回布尔值。

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

String 的扩展方法

模板字符串

ES6新增的创建字符串的方式,使用反引号定义

let name = `Lee`;
​

模板字符串中可以解析变量

let name = 'Lee'; 
let sayHello = `hello,my name is ${name}`; // hello, my name is Lee

模板字符串中可以换行

 let result = { 
     name: 'Lee', 
     age: 28,
     sex: '男' 
 } 
 let html = ` <div>
     <span>${result.name}</span>
     <span>${result.age}</span>
     <span>${result.sex}</span>
 </div> `;

在模板字符串中可以调用函数

const sayHello = function () { 
    return 'web学习';
 }; 
 let greet = `${sayHello()} 需要努力`;
 console.log(greet); // web学习 需要努力

实例方法:startsWith() 和 endsWith()

  • startsWith():表示参数字符串是否在原字符串的头部,返回布尔值

  • endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值

let str = 'Hello world!';
str.startsWith('Hello') // true 
str.endsWith('!')       // true
​

实例方法:repeat()

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

'x'.repeat(3)      // "xxx" 
'hello'.repeat(2)  // "hellohello"

Set 数据结构(★★)

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set本身是一个构造函数,用来生成 Set 数据结构

const s = new Set();

Set函数可以接受一个数组作为参数,用来初始化。

const set = new Set([1, 2, 3, 4, 4]);//{1, 2, 3, 4}
​

实例方法

  • add(value):添加某个值,返回 Set 结构本身

  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功

  • has(value):返回一个布尔值,表示该值是否为 Set 的成员

  • clear():清除所有成员,没有返回值

 const s = new Set();
 s.add(1).add(2).add(3); // 向 set 结构中添加值 
 s.delete(2)             // 删除 set 结构中的2值   
 s.has(1)                // 表示 set 结构中是否有1这个值 返回布尔值 
 s.clear()               // 清除 set 结构中的所有值
 //注意:删除的是元素的值,不是代表的索引

遍历

Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。

s.forEach(value => console.log(value))
​
​
​
​

HTML DOM classList 属性

add(class1, class2, ...)在元素中添加一个或多个类名。 如果指定的类名已存在,则不会添加
contains(class)返回布尔值,判断指定的类名是否存在。可能值:true - 元素包已经包含了该类名false - 元素中不存在该类名
item(index)返回元素中索引值对应的类名。索引值从 0 开始。 如果索引值在区间范围外则返回 null
remove(class1, class2, ...)移除元素中一个或多个类名。 注意: 移除不存在的类名,不会报错。
toggle(class, true|false)在元素中切换类名。 第一个参数为要在元素中移除的类名,并返回 false。 如果该类名不存在则会在元素中添加类名,并返回 true。 第二个是可选参数,是个布尔值用于设置元素是否强制添加或移除类,不管该类名是否存在。例如: 移除一个 class: element.classList.toggle("classToRemove", false); 添加一个 class: element.classList.toggle("classToAdd", true); 注意: Internet Explorer 或 Opera 12 及其更早版本不支持第二个参数。

JavaScript Array map() 方法

定义和用法

map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

map() 方法按照原始数组元素顺序依次处理元素。

注意: map() 不会对空数组进行检测。

注意: map() 不会改变原始数组。

参数描述
function(currentValue, index,arr)必须。函数,数组中的每个元素都会执行这个函数 函数参数: 参数描述currentValue必须。当前元素的值index可选。当前元素的索引值arr可选。当前元素属于的数组对象
thisValue可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。 如果省略了 thisValue,或者传入 null、undefined,那么回调函数的 this 为全局对象。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>web</title>
</head>
<body>
​
<p>点击按钮获取数组元素的平方根。</p>
<button onclick="myFunction()">点我</button>
<p id="demo"></p>
<script>
var numbers = [4, 9, 16, 25];
function myFunction() {
    x = document.getElementById("demo")
    x.innerHTML = numbers.map(Math.sqrt);
}
</script>
​
</body>
</html>
​

JavaScript Array filter() 方法

定义和用法

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

注意: filter() 不会对空数组进行检测。

注意: filter() 不会改变原始数组。

参数描述
function(currentValue, index,arr)必须。函数,数组中的每个元素都会执行这个函数 函数参数: 参数描述currentValue必须。当前元素的值index可选。当前元素的索引值arr可选。当前元素属于的数组对象
thisValue可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。 如果省略了 thisValue ,"this" 的值为 "undefined"
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>web</title>
</head>
<body>
​
<p>点击按钮获取数组中大于 18 的所有元素。</p>
<button onclick="myFunction()">点我</button>
<p id="demo"></p>
<script>
var ages = [32, 33, 16, 40];
function checkAdult(age) {
    return age >= 18;
}
function myFunction() {
    document.getElementById("demo").innerHTML = ages.filter(checkAdult);
}
</script>
​
</body>
</html>

map()和filter()的区别:

map主要用于遍历数组后进行计算 而filter主要用于遍历数组后进行检测和过滤 如果是map用于检测则map返回boolean。

JavaScript reduce() 方法

定义和用法

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

reduce() 可以作为一个高阶函数,用于函数的 compose。

注意: reduce() 对于空数组是不会执行回调函数的。

参数描述
function(total,currentValue, index,arr)必需。用于执行每个数组元素的函数。 函数参数:参数描述total必需。初始值, 或者计算结束后的返回值。currentValue必需。当前元素currentIndex可选。当前元素的索引arr可选。当前元素所属的数组对象。
initialValue可选。传递给函数的初始值

例子:

var arr = [1, 2, 3, 4]; 
var sum = arr.reduce(function(total, currentValue, index, arr) { 
console.log(total, currentValue); 
return total + currentValue; },10) 
console.log(arr, sum);

Array.of()方法:

它负责把一堆文本或者变量转换成数组

例:

let arr =Array.of(3,4,5,6);

console.log(arr);

let arr = Array.of('李','Lee','web前端');
console.log(arr);

fill( )实例方法:

fill()也是一个实例方法,它的作用是把数组进行填充,它接收三个参数,第一个参数是填充的变量,第二个是开始填充的位置,第三个是填充到的位置。

let arr=[0,1,2,3,4,5,6,7,8,9];

arr.fill('Lee',2,5);

console.log(arr); //[0, 1, "Lee", "Lee", "Lee", 5, 6, 7, 8, 9]

for…of循环:

例:

let arr=['Lee','李','王五']

for (let item of arr){

console.log(item);

}

for of中 item代表数组中的某一项

for…in循环:

例:

let arr=['Lee','李','王五']

for (let i in arr){

console.log(i);

}

for in中 i代表索引

for in可以遍历对象

forEach()

forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。

参数描述
function(currentValue, index, arr)必需。 数组中每个元素需要调用的函数。 函数参数:参数描述currentValue必需。当前元素index可选。当前元素的索引值。arr可选。当前元素所属的数组对象。
thisValue可选。传递给函数的值一般用 "this" 值。 如果这个参数为空, "undefined" 会传递给 "this" 值
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>web</title>
</head>
<body>
​
<p>点击按钮列出数组的每个元素。</p>
<button onclick="numbers.forEach(myFunction)">点我</button>
<p id="demo"></p>
​
<script>
demoP = document.getElementById("demo");
var numbers = [4, 9, 16, 25];
​
function myFunction(item, index) {
    demoP.innerHTML = demoP.innerHTML + "index[" + index + "]: " + item + "<br>"; 
}
</script>
​
</body>
</html>

in的用法

in是用来判断对象或者数组中是否存在某个值的。我们先来看一下用in如何判断对象里是否有某个值。

对象判断

let obj={

a:'Lee',

b:'李'

}console.log('a' in obj); //true

数组的判断

let arr=[,,,,,];console.log(0 in arr); //false

let arr1=['Lee','李'];console.log(0 in arr1); // true

注意:这里的0指的是数组下标位置是否为空。

some的用法

定义和用法

some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。

some() 方法会依次执行数组的每个元素:

  • 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。

  • 如果没有满足条件的元素,则返回false。

注意: some() 不会对空数组进行检测。

注意: some() 不会改变原始数组。

参数描述
function(currentValue, index,arr)必须。函数,数组中的每个元素都会执行这个函数 函数参数: 参数描述currentValue必须。当前元素的值index可选。当前元素的索引值arr可选。当前元素属于的数组对象
thisValue可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。 如果省略了 thisValue ,"this" 的值为 "undefined"

例:

var ages = [3, 10, 18, 20];

function checkAdult(age) { return age >= 18; }

function myFunction() { document.getElementById("demo").innerHTML = ages.some(checkAdult); }

join()方法

join() 方法用于把数组中的所有元素放入一个字符串。

例:
var arr = new Array(3)
arr[0] = "George"
arr[1] = "John"
arr[2] = "Thomas"
​
document.write(arr.join())
let arrq = arr.join(',')
console.log(typeof arrq)
​

ES6对象

ES6允许把声明的变量直接赋值给对象,我们看下面的例子。

let name="Lee";

let skill= 'web';

var obj= {name,skill};

console.log(obj); //Object {name: "Lee", skill: "web"}

对象Key值构建

有时候我们会在后台取出key值,而不是我们前台定义好的,这时候我们如何构建我们的key值那。比如我们在后台取了一个key值,然后可以用[ ] 的形式,进行对象的构建。

let key='skill';

var obj={

[key]:'web' 

}

console.log(obj.skill);

自定义对象方法

对象方法就是把对象中的属性,用匿名函数的形式编程方法。这个在以前就有应用,我们这里只是简单的复习一下。

var obj={

add:function(a,b){

return a+b;

}

}console.log(obj.add(1,2)); //3

Object.keys()和Object.values()

// 用于获取对象自身所有的属性	

var obj = {

id: 1,

user: '李',

age: 30,

height: 175

};

var arr = Object.keys(obj);

console.log(arr);

arr.forEach(function(value) {

console.log(value);



})
| var arr = Object.values(obj); | | :---------------------------- | | | console.log(arr); | |

Object.assign( )合并对象

操作数组时我们经常使用数组合并,那对象也有合并方法,那就是assgin( )。看一下啊具体的用法。

var a={a:'Lee'};var b={b:'李'};var c={c:'web'};

let d=Object.assign(a,b,c)

console.log(d);

let q = {...a,...b,...c}//这种也可以

console.log(q);

Object.defineProperty方法

  Object.defineProperty方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象

  • value: 设置属性的值
  • writable: 值是否可以重写。true | false
  • enumerable: 目标属性是否可以被枚举。true | false
  • configurable: 目标属性是否可以被删除或是否可以再次修改特性 true | false
  • set: 目标属性设置值的方法
  • get:目标属性获取值的方法 

 假设我们有个对象 user ; 我们要给它增加一个属性 name , 我们会这么做  

var user = {};
user.name="青阳子";
console.log(user);//{name: "青阳子"}

  如果想要增加一个sayHi方法呢?  

user.sayHi=function () { console.log("Hi !") };
console.log(user);//{name: "青阳子", sayHi: ƒn}

Object.defineProperty 就是做这个的

Object.defineProperty 需要三个参数(object , propName , descriptor)

  1 object 对象 => 给谁加
  2 propName 属性名 => 要加的属性的名字 【类型:String】
  3 descriptor 属性描述 => 加的这个属性有什么样的特性【类型:Object】

  那么descriptor这个是个对象 ,他有那些属性呢 ? 别着急我们一个一个说;

  既然可以给一个对象增加属性,那么我们用它来做一下给 user添加 name属性,代码是这样的  

var user = {};
Object.defineProperty(user,"name",{
 value:"青阳子"
})
 console.log(user);//{name: "青阳子"}

 还是那个经典的value属性,他就是设置属性值的。

等等,属性值只能为字符串吗?我们的 number function Object boolean 等呢?

var user = {};
Object.defineProperty(user,"name",{
 value:"青阳子"
})
Object.defineProperty(user,"isSlow",{
 value:true
})
Object.defineProperty(user,"sayHi",{
 value:function () { console.log("Hi !") }
})
Object.defineProperty(user,"age",{
 value:12
})
Object.defineProperty(user,"birth",{
 value:{
  date:"2021-09-29",
  hour:"15:30"
 }
})
console.log(user);

说明 事实证明任何类型的数据都是可以的哦~

问题又来了,如果 user对象已经有了name属性,我们可以通过Object.defineProperty改变这个值吗?

我们来试试

var user = {};
Object.defineProperty(user,"name",{
 value:"青阳子"
})
console.log(user);
user.name="新=>青阳子"
console.log(user);

为什么我改了没作用呢?

  原因:上边说了descriptor有很多属性,除了value属性还有个 writable【顾名思义属性是否可以被重新赋值】接受数据类型为 boolean(默认为false) true => 支持被重新赋值 false=>只读

  哦哦,原来如果我没设置writable值的时候就默认只读啊,所以才改不掉

  那我们看看,设置为true,是不是就可以改掉了。

var user = {};
Object.defineProperty(user,"name",{
 value:"青阳子",
 writable:true
})
console.log(user);
user.name="新=>青阳子"
console.log(user);

这个descriptor还有其他的属性吗?enumerable【顾名思义属性是否可以被枚举】接受数据类型为 boolean(默认为false) true => 支持被枚举     false=>不支持

枚举??什....什么意思?

假设我们想知道这个 user对象有哪些属性我们一般会这么做  

var user ={
 name:"青阳子",
 age:35
} ;

//es6
var keys=Object.keys(user)
console.log(keys);// ['name','age']
//es5
var keys=[];
for(key in user){
 keys.push(key);
}
console.log(keys);// ['name','age']

 如果我们使用 Object.的方式定义属性会发生什么呢?我们来看下输出

var user ={
 name:"青阳子",
 age:35
} ;
//定义一个性别 可以被枚举
Object.defineProperty(user,"gender",{
 value:"男",
 enumerable:true
})

//定义一个出生日期 不可以被枚举
Object.defineProperty(user,"birth",{
 value:"1986-05-03",
 enumerable:false
})

//es6
var keys=Object.keys(user)
console.log(keys);
// ["name", "age", "gender"]

console.log(user);
// {name: "青阳子", age: 35, gender: "男", birth: "1986-05-03"}
console.log(user.birth);
// 1986-05-03

 说明 很明显,我们定义为 enumerable=falsebirth属性并没有被遍历出来,遍历 => 其实就是枚举(个人理解啦,不喜勿喷哦~)

总结 enumerable 属性取值为 布尔类型 true | false 默认值为 false,为true时属性可以被枚举;为false则不能。此设置不影响属性的调用和 查看对象的值。

  configurable 是接下来我们要讲的一个属性,这个属性有两个作用:

  1 属性是否可以被删除
  2 属性的特性在第一次设置之后可否被重新定义特性

var user ={
 name:"青阳子",
 age:35
} ;
//定义一个性别 不可以被删除和重新定义特性
Object.defineProperty(user,"gender",{
 value:"男",
 enumerable:true,
 configurable:false
})

//删除一下
delete user.gender;
console.log(user);//{name: "狂奔的蜗牛", age: 35, gender: "男"}

//重新定义特性
Object.defineProperty(user,"gender",{
 value:"男",
 enumerable:true,
 configurable:true
})
// Uncaught TypeError: Cannot redefine property: gender
//会报错

 设置为 true

var user ={
 name:"青阳子",
 age:35
} ;
//定义一个性别 可以被删除和重新定义特性
Object.defineProperty(user,"gender",{
 value:"男",
 enumerable:true,
 configurable:true
})

//删除前
console.log(user);
// {name: "青阳子", age: 35, gender: "男"}

//删除一下
delete user.gender;
console.log(user);
// {name: "青阳子", age: 35}

//重新定义特性
Object.defineProperty(user,"gender",{
 value:"男",
 enumerable:true,
 configurable:false
})

//删除前
console.log(user);
// {name: "青阳子", age: 35, gender: "男"}
//删除一下 删除失败
delete user.gender;
console.log(user);
// {name: "青阳子", age: 35, gender: "男"}

总结 configurable设置为 true 则该属性可以被删除和重新定义特性;反之属性是不可以被删除和重新定义特性的,默认值为false(Ps.除了可以给新定义的属性设置特性,也可以给已有的属性设置特性哈

  最后我们来说说,最重要的两个属性 setget(即存取器描述:定义属性如何被存取),这两个属性是做什么用的呢?我们通过代码来看看

var user ={
 name:"青阳子"
} ;
var count = 12;
//定义一个age 获取值时返回定义好的变量count
Object.defineProperty(user,"age",{
 get:function(){
  return count;
 }
})
console.log(user.age);//12

//如果我每次获取的时候返回count+1呢
var user ={
 name:"青阳子"
} ;
var count = 12;
//定义一个age 获取值时返回定义好的变量count
Object.defineProperty(user,"age",{
 get:function(){
  return count+1;
 }
})
console.log(user.age);//13

下来我不用解释了吧,你想在获取该属性的时候对值做什么随你咯~

我们看看 set,不多说上代码

var user ={
 name:"青阳子"
} ;
var count = 12;
//定义一个age 获取值时返回定义好的变量count
Object.defineProperty(user,"age",{
 get:function(){
  return count;
 },
 set:function(newVal){
  count=newVal;
 }
})
console.log(user.age);//12
user.age=145;
console.log(user.age);//145
console.log(count);//145

//等等,如果我想设置的时候是 自动加1呢?我设置145 实际上设置是146

var user ={
 name:"青阳子"
} ;
var count = 12;
//定义一个age 获取值时返回定义好的变量count
Object.defineProperty(user,"age",{
 get:function(){
  return count;
 },
 set:function(newVal){
  count=newVal+1;
 }
})
console.log(user.age);//12
user.age=145;
console.log(user.age);//146
console.log(count);//146

  说明 注意:当使用了getter或setter方法,不允许使用writable和value这两个属性(如果使用,会直接报错滴)

  get 是获取值的时候的方法,类型为 function ,获取值的时候会被调用,不设置时为 undefined

  set 是设置值的时候的方法,类型为 function ,设置值的时候会被调用,undefined

  get或set不是必须成对出现,任写其一就可以

var user ={
 name:"青阳子"
} ;
var count = 12;
//定义一个age 获取值时返回定义好的变量count
Object.defineProperty(user,"age",{
 get:function(){
  console.log("这个人来获取值了!!");
  return count;
 },
 set:function(newVal){
  console.log("这个人来设置值了!!");
  count=newVal+1;
 }
})
console.log(user.age);//12
user.age=145;
console.log(user.age);//146

Object.defineProperty方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象

  • value: 设置属性的值
  • writable: 值是否可以重写。true | false
  • enumerable: 目标属性是否可以被枚举。true | false
  • configurable: 目标属性是否可以被删除或是否可以再次修改特性 true | false
  • set: 目标属性设置值的方法
  • get:目标属性获取值的方法 
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
// Object.defineProperty() 定义新属性或修改原有的属性
var obj = {
id: 1,
user: '李',
age: 30
};
// 1. 以前的对象添加和修改属性的方式
// obj.num = 100;
// obj.age =80;
// console.log(obj);
// 2. Object.defineProperty() 定义新属性或修改原有的属性
Object.defineProperty(obj, 'num', {
value: 100,
enumerable: true
});
console.log(obj);
Object.defineProperty(obj, 'age', {
value: 99
});
console.log(obj);
Object.defineProperty(obj, 'id', {
// 如果值为false 不允许修改这个属性值 默认值也是false
writable: false,
});
obj.id = 2;
console.log(obj);
Object.defineProperty(obj, 'home', {
value: '安徽省合肥市小区',
// 如果只为false 不允许修改这个属性值 默认值也是false
writable: false,
// enumerable 如果值为false 则不允许遍历, 默认的值是 false
enumerable: false,
// configurable 如果为false 则不允许删除这个属性 不允许在修改第三个参数里面的特性 默认为false
configurable: false
});
console.log(obj);
console.log(Object.keys(obj));
delete obj.address;
console.log(obj);
delete obj.pname;
console.log(obj);
Object.defineProperty(obj, 'home', {
value: '安徽省合肥市小区',
// 如果值为false 不允许修改这个属性值 默认值也是false
writable: true,
// enumerable 如果值为false 则不允许遍历, 默认的值是 false
enumerable: true,
// configurable 如果为false 则不允许删除这个属性 默认为false
configurable: true
});
console.log(obj.address);
</script>
</body>
</html>

ES6 Symbol

表示独一无二的值
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第7种数据类型,是一个类似字符串的数据类型

Symbol特点:
Symbol的值是唯一的,用来解决命名冲突的问题
Symbol值不能与其他数据进行运算,也不能与自己进行运算,譬如+、-、*、/、比较运算
Symbol定义的对象属性不能使用for…in遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
创建Symbol:
1.通过let s2 = Symbol('张三') 的方式创建Symbol,'张三’作为Symbol描述,作用相当于注释,这种方式创建的Symbol,即使传入的描述一致,但实际返回的值是不同的
 

        // 创建Symbol
        let s = Symbol()
        console.log(s,typeof s) // Symbol() "symbol"

        let s2 = Symbol('张三') // '张三'作为Symbol描述,作用相当于注释
        let s3 = Symbol('张三') // 即使传入的描述一致,但实际返回的值是不同的
        console.log(s2 === s3) // false

2.通过Symbol.for()创建Symbol,这种方式创建Symbol,传入的描述一致,实际返回的值也一致,可以得到唯一的Symbol值

        // Symbol.for创建Symbol
        let s4 = Symbol.for('张三')
        let s5 = Symbol.for('张三')
        console.log(s4 === s5) // true

Symbol使用场景

给对象添加属性和方法。由于Symbol值具有唯一性,所以可以很安全地把属性和方法加入对象中,如下所示

        let game = {
            up: 'upp',
            down: 'doown'
        }

        let methods = {
            up: Symbol(),
            down: Symbol(),
        }

        // 添加方法
        game[methods.up] = function(){
            console.log('up up up')
        }
        game[methods.down] = function(){
            console.log('down down down')
        }

        console.log('game----', game)
        // 调用
        game[methods.up]()
        let youxi = {
            name: '狼人杀',
            [Symbol('say')]: function(){ // 此处不能直接写 Symbol('say'): function(){...},因为Symbol('say')是动态的,和上面固定的'name'不一样
                console.log('发言')
            }
        }
       const langrensha = Object.getOwnPropertySymbols(youxi);
       console.log(langrensha)
       youxi[langrensha[0]]();

Symbol内置值

ES6除了定义自己使用的Symbol值以外,还提供了11个内置的Symbol值,指向语言内部使用的方法,比如

  1. Symbol.hasInstance
    当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法
            class Person{
                static [Symbol.hasInstance](param){
                    console.log('param----', param)
                    console.log('检测类型')
                }
            }
    
            let o = {}
    
            console.log(o instanceof Person)
            // param---- {}
            // 检测类型
            // false
    

  2. Symbol.isConcatSpreadable
    对象的Symbol.isConcatSpreadable属性等于一个bool值,表示该对象用于Array.prototype()时,是否可以展开
            const arr1 = [1,2,3]
            const arr2 = [4,5,6]
            arr2[Symbol.isConcatSpreadable] = false // arr2不可展开
            const arr = arr1.concat(arr2)
            console.log(arr) // [1,2,3,[4,5,6]]
    

  3. Symbol.unscopables
    该对象指定了使用with关键字时,哪些属性会被with环境排除

  4. Symbol.match
    当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值

  5. Symbol.replace
    当该对象被str.replace(myObject)方法调用时,会返回该方法的返回值

  6. Symbol.search
    当该对象被str.search(myObject)方法调用时,会返回该方法的返回值

  7. Symbol.split
    当该对象被str.split(myObject)方法调用时,会返回该方法的返回值

  8. Symbol.iterator
    对象进行for ... of循环时,会调用Symbol.iterator方法,返回该对象的默认遍历器

  9. Symbol.toPrimitive
    该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值

  10. Symbol.toStringTag
    在该对象上调用toString方法时,返回该方法的返回值

  11. Symbol.species
    创建衍生对象时,会使用该属性

    迭代器
    迭代器(iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署iterator接口,就可以完成遍历操作

    ES6创造了一种新的遍历命令for...of循环,iterator接口主要供for...of消费
    注:for...of遍历的是键值,for...in遍历的是键名
    for...of不能对属性值进行修改,forEach()可以

    原生具备iterator接口的数据(可用for...of遍历)

    Array
    Arguments
    Set
    Map
    String
    TypedArray
    NodeList
    工作原理:
    创建一个指针对象,指向当前数据结构的起始位置
    第一次调用对象的next方法,指针自动指向数据结构的第一个成员
    接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
    每调用next方法返回一个包含value和done属性的对象,done属性表示遍历是否结束
     

            const food = ['鱼香肉丝','糖醋里脊','酸菜鱼']
            for(let item of food){
                console.log(item)
            }
    
            let iterator = food[Symbol.iterator]()
    
            console.log(iterator.next()) // {value: "鱼香肉丝", done: false}
            console.log(iterator.next()) // {value: "糖醋里脊", done: false}
            console.log(iterator.next()) // {value: "酸菜鱼", done: false}
            console.log(iterator.next()) // {value: undefined, done: true} true 表示遍历已经结束
    

    注:需要自定义遍历数据的时候,要想到迭代器

 迭代器应用-自定义遍历数据(即自己手动实现一个迭代器)

        // 声明一个对象
        const school = {
            name: '三中',
            students: [
                'LiMing',
                'HanMeimei',
                'WangFang',
            ],
            [Symbol.iterator](){
                // 声明一个索引变量
                let index = 0
                return {
                    next: ()=>{
                        if(index < this.students.length){
                        // if(index < 3){
                            const result = {value: this.students[index], done: false}
                            // 下标自增
                            index++
                            // 返回结果
                            return result
                        }else{
                            return {value: undefined, done: true}
                        }
                    }
                }
            }
        }

        // 遍历这个对象
        for(let item of school){
            console.log(item)
        }

生成器

生成器本身是一个特殊的函数,生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数不同

  • 执行生成器函数,返回的是一个迭代器对象,通过iterator.next()调用执行函数内语句
        function * gen(){
            console.log('hello generator')
        }
        let iterator = gen() // 返回的是一个迭代器对象
        // console.log(iterator)
        // 通过.next()调用执行函数内语句
        iterator.next() // hello generator
  • yield是函数代码的分隔符,结合调用iterator.next()方法,实现函数gen1的语句的分段执行
            function * gen1(){
                console.log('--- 1 ---')
                yield '耳朵' // 函数代码的分隔符
                console.log('--- 2 ---')
                yield '尾巴'
                console.log('--- 3 ---')
            }
    
            let iterator1 = gen1()
            iterator1.next() // --- 1 ---
            iterator1.next() // --- 2 ---
            iterator1.next() // --- 3 ---
            // 通过调用.next()方法,实现函数gen1的语句的分段执行
    
  • 使用for...of遍历函数执行后返回的迭代器对象,每一次遍历的item为yield后的表达式或者自变量的值
            function * gen1(){
                yield '耳朵' // 函数代码的分隔符
                yield '尾巴'
            }
    
            // 遍历,每一次遍历的item为yield后的表达式或者自变量的值
            for(let item of gen1()){
                console.log(item)
            }
            // 执行结果:
            // 耳朵
            // 尾巴
    
            // 注:next调用和for...of调用同时存在,只会支持最先的一个
    
  • 生成器函数的参数传递
            function * gen(args){
                console.log(args) // 'aaa'
                let one = yield 111
                console.log(one) // 'bbb'
    
                let two = yield 222
                console.log(two) // 'ccc'
                
                let three = yield 333
                console.log(three)
            }
    
            // 执行生成器函数获取迭代器对象
            let iterator = gen('aaa')
    
            console.log(iterator.next()) // {value: 111, done: false}
            // next方法可以传入实参,传入的实参会作为上一个yield后返回的结果
            console.log(iterator.next('bbb')) // {value: 222, done: false}
            console.log(iterator.next('ccc')) // {value: 333, done: false}
            console.log(iterator.next('ddd')) // {value: undefined, done: true}
    
  • 生成器函数实例1:
    1s后控制台输出111 --> 2s后控制台输出222 --> 3s后控制台输出333 ==> 总计耗时6s
           // 异步编程,如文件操作、网络请求、数据库操作
            // 1s后控制台输出111 --> 2s后控制台输出222 --> 3s后控制台输出333  ==> 总计耗时6s
    
            // 用生成器函数实现
            function one (){
                setTimeout(()=>{
                    console.log(111)
                    iterator.next()
                }, 1000)
            }
    
            function two (){
                setTimeout(()=>{
                    console.log(222)
                    iterator.next()
                }, 2000)
            }
    
            function three (){
                setTimeout(()=>{
                    console.log(333)
                }, 3000)
            }
    
            function * gen(){
                yield one()
                yield two()
                yield three()
            }
    
            let iterator = gen()
            iterator.next()
            
    
            // 以下为回调地域做法
            // setTimeout(()=>{
            //     console.log(111)
            //     setTimeout(()=>{
            //         console.log(222)
            //         setTimeout(()=>{
            //             console.log(333)
            //         }, 3000)
            //     }, 2000)
            // }, 1000)
    
  • 生成器函数实例2:
    模拟获取 用户数据 --> 订单数据 --> 商品数据
            // 模拟获取 用户数据 --> 订单数据 --> 商品数据
            function getUsers(){
                setTimeout(()=>{
                    let data = '用户数据'
                    iterator.next(data) // 相当于把得到的数据,传回users
                }, 1000)
            }
    
            function getOrders(){
                setTimeout(()=>{
                    let data = '订单数据'
                    iterator.next(data)
                }, 2000)
            }
    
            function getGoods(){
                setTimeout(()=>{
                    let data = '商品数据'
                    iterator.next(data)
                },3000)
            }
    
            // 定义生成器函数
            function * gen (){
                let users = yield getUsers()
                console.log(users) // 用户数据
    
                let orders = yield getOrders()
                console.log(orders) // 订单数据
    
                let goods = yield getGoods()
                console.log(goods) // 商品数据
            }
    
            // 调用生成器函数,获取迭代器对象
            let iterator = gen()
            iterator.next()
    

import和export

export和export default的区别

export输出后 import导入接收必须加{}而且名字不能改变

export default输出后 import导入接收不需要{}名字可以自定义

ES7

介绍

  1. Array.prototype.includes:用来检测数组中是否包含某个元素,返回布尔类型值

  2. 在ES7中引入指数操作符**,用来实现幂运算,功能与Math.pow结果相同

应用

<script> //include const mingzhu = ['西游记','红楼梦','水浒传','三国演义'] console.log(mingzhu.includes('西游记')) //true console.log(mingzhu.includes('七侠五义')) //false

//**
console.log(2**10) // 1024

</script>

ES8

async和await

async函数

介绍

async和await两种语法结合可以让异步代码像同步代码一样

async函数:

  • async函数的返回值为promise对象

  • async返回的promise对象的结果值由async函数执行的返回值决定

特性

<script>
async function fn(){ 
//1.如果返回的是一个非Promise的对象,则fn()返回的结果就是成功状态的Promise对象,值为返回值 
//2.如果返回的是一个Promise对象,则fn()返回的结果与内部Promise对象的结果一致 
//3.如果返回的是抛出错误,则fn()返回的就是失败状态的Promise对象 return new Promise((resolve,reject)=>{ resolve('成功的数据'); }); } 
const result = fn(); 
result.then(value=>{ console.log(value) //成功的数据 },reason=>{ console.log(reason) })
</script>

await表达式

介绍

  1. await必须放在async函数中

  2. await右侧的表达式一般为promise对象

  3. await可以返回的是右侧promise成功的值

  4. await右侧的promise如果失败了,就会抛出异常,需要通过try…catch捕获处理

特性

<script> //创建Promise对象 const p = new Promise((resolve, reject) => { // resolve("成功的值") reject("失败了") })

//await 必须放在async函数中
async function main() {
    try {
        let res = await p;
        console.log(res);
    } catch (e) {
        console.log(e);
    }
}
​
//调用函数
main()  //失败了

</script>

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<script>
			//ajax请求返回一个promise
			function sendAjax(url) {
				return new Promise((resolve, reject) => {
					//创建对象
					const x = new XMLHttpRequest();

					//初始化
					x.open('GET', url);

					//发送
					x.send();

					//时间绑定
					x.onreadystatechange = () => {
						if (x.readyState === 4) {
							if (x.status >= 200 && x.status < 300) {
								//成功
								resolve(x.response)
							} else {
								//失败
								reject(x.status)
							}
						}
					}
				})
			}
			//async 与 await 测试
			async function main() {
				let result = await sendAjax("https://api.apiopen.top/getJoke")
				console.log(result);
			}
			main()
		</script>
	</body>
</html>

ES8对象方法扩展

<script> 
const school = { name:'课工场', cities:['北京','上海','深圳'], xueke:['前端','Java','大数据','运维'] };

//获取对象所有的键
console.log(Object.keys(school));
​
//获取对象所有的值
console.log(Object.values(school));
​
//entries,用来创建map
console.log(Object.entries(school));
console.log(new Map(Object.entries(school)))
​
//对象属性的描述对象
console.log(Object.getOwnPropertyDescriptors(school))

​Object.create()的参数理解为:第一个参数是放在新对象的原型上的,第二个参数是放在新对象的实例上的
const obj = Object.create(null,{
    name:{
        value:'课工场',
        //属性特性
        writable:true,//是否可写
        configurable:true,//是否可以删除
        enumerable:true,//是否可以枚举
    }
})

const person = {
  isHuman: false,
  printIntroduction: function() {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};

const me = Object.create(person);

me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten

me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"

</script>

ES9

运算扩展符与rest参数

<script>
			function connect({
				host,
				port,
				...user
			}) {
				console.log(host);
				console.log(port);
				console.log(user)
			}
			connect({
				host: '127.0.0.1',
				port: 3306,
				username: 'root',
				password: 'root',
				type: 'master'
			}) //127.0.0.1 3306 {username: "root", password: "root", type: "master"} 
		</script>
<script>
			const AA = {
				username: 'ran'
			}
			const BB = {
				password: 'lyyrhf'
			}
			const CC = {
				job: 'Java'
			}
			const D = {
				...AA,
				...BB,
				...CC
			};
			console.log(D) //{username: "ran", password: "lyyrhf", job: "Java"} 
		</script>

ES10

对象扩展方法

<script>
			const res = Object.fromEntries([
				['name', 'RHF'],
				['cities', '成都', '武汉']
			]) 
console.log(res) //{name: "RHF", cities: "成都"} //Map const m = new Map(); m.set('name','ranhaifeng') const result = Object.fromEntries(m) console.log(result); //{name: "ranhaifeng"} 
		</script>

字符串扩展方法

<script>
			//trim
			let str = ' asd '
			console.log(str) //asd 
            console.log(str.trimStart()) //asd 清空头空格 
            console.log(str.trimEnd()) // asd 清空尾空格 
		</script>

数组扩展方法 flat与flatMap

<script>
   //二维数组变为一维
    const arr = [1, 2, 3, 4, 5, 6, [7, 8, 9]]
    console.log(arr.flat())
	const arr = [1, 2, 3, [4, 5, 6, [7, 8, 9]]] //参数为深度,是一个数字 
    console.log(arr.flat())//三维变二维 
    console.log(arr.flat(2)) //[1,2,3,4,5,6,7,8,9]三维变一维 深度为2

    const arr2 = [1, 2, 3, 4]
	const result = arr2.map(item => item * 10); 

    //给item加上[]变为二维 可以用flatmap变为一维
    const arr2 = [1, 2, 3, 4]
	const result = arr2.map(item => [item * 10]); 

    const arr2 = [1, 2, 3, 4]
	const result = arr2.flatMap(item => [item * 10]); //如果map的结果是一个多维数组可以进行flat 是两个操作的结合
		</script>

Symbol的description

介绍

用来获取Symbol的字符串描述

实例

let s = Symbol('课工场'); console.log(s.description) //课工场

ES11

私有属性

<script> 
    class Person {
				name;
				#age;
				#weight;
				constructor(name,age,weight){
					this.name = name;
					this.#age = age;
					this.#weight = weight;
				}
				
			}
			var girl = new Person('程',18,45)
			console.log(girl.name);
			console.log(girl.#age);

//报错:Uncaught SyntaxError: Private field '#age' must be declared in an enclosing class

//使用intro()方法 放在里面可以访问到
class Person {
				name;
				#age;
				#weight;
				constructor(name,age,weight){
					this.name = name;
					this.#age = age;
					this.#weight = weight;
				}
				intro() {
					console.log(this.name);
					console.log(this.#age);
					console.log(this.#weight);
				}
			}
			var girl = new Person('程',18,45)
			console.log(girl.name);
			
			console.log(girl);
			girl.intro()
</script>

Promise

 Promise是异步编程的一种解决方案,可以替代传统的解决方案--回调函数和事件。ES6统一了用法,并原生提供了Promise对象。作为对象,Promise有一下两个特点:

* (1)对象的状态不受外界影响。

* (2)一旦状态改变了就不会在变,也就是说任何时候Promise都只有一种状态。

 Promise的状态

  Promise有三种状态,分别是:**Pending **(进行中), ** Resolved **(已完成),** Rejected ** (已失败)。Promise从Pending状态开始,如果成功就转到成功态,并执行resolve回调函数;如果失败就转到失败状态并执行reject回调函数。下面是Promise的规范图解

 基本用法

   可以通过Promise的构造函数创建Promise对象。

var promise = new Promise(function(resolve,reject)
setTimeout(function(){                              
  console.log("hello world");},2000);
});   

 Promise构造函数接收一个函数作为参数,该函数的两个参数是resolvereject,它们由JavaScript引擎提供。其中resolve函数的作用是当Promise对象转移到成功,调用resolve并将操作结果作为其参数传递出去;reject函数的作用是单Promise对象的状态变为失败时,将操作报出的错误作为其参数传递出去。如下面的代码:

function greet(){
    var promise = new Promise(function(resolve,reject){
        var greet = "hello  world";
        resolve(greet);
    });
    return promise;
    }
    greet().then(v=>{
    console.log(v);//*
    })

上面的*行的输出结果就是greet的值,也就是resolve()传递出来的参数。

注意:创建一个Promise对象会立即执行里面的代码,所以为了更好的控制代码的运行时刻,可以将其包含在一个函数中,并将这个Promise作为函数的返回值。

Promise的then方法

  promise的then方法带有以下三个参数:成功回调,失败回调,前进回调,一般情况下只需要实现第一个,后面是可选的。Promise中最为重要的是状态,通过then的状态传递可以实现回调函数链式操作的实现。先执行以下代码:

function greet(){
var promise = new Promise(function(resolve,reject){
    var greet = "hello  world";
    resolve(greet);
});
return promise;
}
var p = greet().then(v=>{
console.log(v);
})

console.log(p);

 改程序的输出为:

从中可以看出promise执行then还是一个promise,并且Promise的执行是异步的,因为hello world在最后一条输出语句的前面就打印出来,且Promise的状态为pending(进行中)。
  因为Promise执行then后还是Promise,所以就可以根据这一特性,不断的链式调用回调函数。下面是一个 例子:

function greet(){
    var promise = new Promise(function(resolve,reject){
        var greet = "hello  world";
        resolve(greet);
    });
    return promise;
}
greet().then(v=>{
    console.log(v+1);
    return v;
})
.then(v=>{
    console.log(v+2);
    return v;
})
.then(v=>{
    console.log(v+3);
})

Promise的其他方法

reject用法

  reject的作用就是把Promise的状态从pending置为rejected,这样在then中就能捕捉到reject的回调函数

function judgeNumber(num){
    var promise1 = new Promise(function(resolve,reject){
        num =5;
        if(num<5){
            resolve("num小于5,值为:"+num);
        }else{
            reject("num不小于5,值为:"+num);
        }
    });
    return promise1;
}

judgeNumber().then(
    function(message){
        console.log(message);
    },
    function(message){
        console.log(message);
    }
)

 .then后包含有两个方法,前一个执行resolve回调的参数,后一个执行reject回调的参数。

catch用法

function judgeNumber(num){
    var promise1 = new Promise(function(resolve,reject){
        num =5;
        if(num<5){
            resolve("num小于5,值为:"+num);
        }else{
            reject("num不小于5,值为:"+num);
        }
    });
    return promise1;
}

judgeNumber().then(
    function(message){
        console.log(message);
    }
)
.catch(function(message){
    console.log(message);
})

 这个时候catch执行的是和reject一样的,也就是说如果Promise的状态变为reject时,会被catch捕捉到,不过需要特别注意的是如果前面设置了reject方法的回调函数,·则catch不会捕捉到状态变为reject的情况。catch还有一点不同的是,如果在resolve或者reject发生错误的时候,会被catch捕捉到,这与java,c++的错误处理时一样的,这样就能避免程序卡死在回调函数中了。

all用法

  Promise的all方法提供了并行执行异步操作的能力,在all中所有异步操作结束后才执行回调。

function p1(){
    var promise1 = new Promise(function(resolve,reject){
        console.log("p1的第一条输出语句");
        console.log("p1的第二条输出语句");
        resolve("p1完成");
    })
    return promise1;
}

function p2(){
    var promise2 = new Promise(function(resolve,reject){
        console.log("p2的第一条输出语句");
        setTimeout(()=>{console.log("p2的第二条输出语句");resolve("p2完成")},2000);

    })
    return promise2;
}

function p3(){
    var promise3 = new Promise(function(resolve,reject){
        console.log("p3的第一条输出语句");
        console.log("p3的第二条输出语句");
        resolve("p3完成")
    });
    return  promise3;
}

Promise.all([p1(),p2(),p3()]).then(function(data){
    console.log(data);
})

 运行结果如下:

这里可以看到p2的resolve放到一个setTimeout中,最后的.then也会等到所有Promise完成状态的改变后才执行。 

race用法

  在all中的回调函数中,等到所有的Promise都执行完,再来执行回调函数,race则不同它等到第一个Promise改变状态就开始执行回调函数。将上面的`all`改为`race`,得到

Promise.race([p1(),p2(),p3()]).then(function(data){
    console.log(data);
})

红框中圈出了"p1完成"字样,说明当执行then()方法时,只有第一个promise的状态改变了。

这里还需要注意一个问题,promise的执行时异步的,比如下面这样:

var i

var promise = new Promise(function(resolve,reject){
    resolve("hello");
})

promise.then(data=>{
    i = 2;

})
console.log(i);

得到的结果是undefined,这不是因为promise不能改变外部的值,而是因为当执行console.log(i)时,then()方法还没执行完。如果你将console.log(i)延迟输出就可以得到正确的结果:

setTimeout(()=>console.log(i),1000);

 所以不要在promise后面执行一些依赖promise改变的代码,因为可能promise中的代码并未执行完,或者你可以将其延迟输出。

 //声明两个promise对象

const p1 = new Promise((resolve, reject) => {
				setTimeout(() => {
					resolve('商品数据-1')
				}, 1000)
			});

			const p2 = new Promise((resolve, reject) => {
				setTimeout(() => {
					reject('出错了!')
				}, 1000)
			});
			
			const res = Promise.allSettled([p1,p2]);
			console.log(res);
            const res2 = Promise.all([p1,p2]);
			console.log(res2);


​ //调用allsettled方法:返回的结果始终是一个成功的,并且异步任务的结果和状态都存在

const res = Promise.allSettled([p1,p2]);

console.log(res) ​ // Promise {<pending>} //     __proto__: Promise //     [[PromiseState]]: "fulfilled" //     [[PromiseResult]]: Array(2) ​

//调用all方法:返回的结果是按照p1、p2的状态来的,如果都成功,则成功,如果一个失败,则失败,失败的结果是失败的Promise的结果 const result = Promise.all([p1,p2]) console.log(result)

promise的基本用法

promise执行多步操作非常好用,那我们就来模仿一个多步操作的过程,那就以吃饭为例吧。要想在家吃顿饭,是要经过三个步骤的。

  1. 洗菜做饭。
  2. 坐下来吃饭。
  3. 收拾桌子洗碗。

这个过程是有一定的顺序的,你必须保证上一步完成,才能顺利进行下一步。我们可以在脑海里先想想这样一个简单的过程在ES5写起来就要有多层的嵌套。那我们现在用promise来实现。

let state=1;

function step1(resolve,reject){

    console.log('1.开始-洗菜做饭');

    if(state==1){

        resolve('洗菜做饭--完成');

    }else{

        reject('洗菜做饭--出错');

    }

}



function step2(resolve,reject){

    console.log('2.开始-坐下来吃饭');

    if(state==1){

        resolve('坐下来吃饭--完成');

    }else{

        reject('坐下来吃饭--出错');

    }

}



function step3(resolve,reject){

    console.log('3.开始-收拾桌子洗完');

     if(state==1){

        resolve('收拾桌子洗完--完成');

    }else{

        reject('收拾桌子洗完--出错');

    }

}

new Promise(step1).then(function(val){

    console.log(val);

    return new Promise(step2);



}).then(function(val){

     console.log(val);

    return new Promise(step3);

}).then(function(val){

    console.log(val);

    return val;

});

可选链操作符

//相当于一个判断符,如果前面的有,就进入下一层级

function main(config) {
				/* 以前 const dbHost = config&&config.db&&config.db.host
				console.log(dbHost) */
				const dbHost = config?.db?.host
				console.log(dbHost) //192.168.1.100 }
			}
			main({
				db: {
					host: '192.168.1.100',
					username: 'root'
				},
				cache: {
					host: '192.168.1.200',
					username: 'admin'
				}
			})

动态import

模块加载分为静态加载和动态加载。这里讲解如何动态加载模块,也就是在程序运行过程中,遇到某个操作或者某个事件时加载需要的模块。

1,模块代码

md.js

 export  function  add(a,b)
 {
     return a+b;
 }

2, html文件

moduldome.html

(1)静态加载

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>动态加载</title>
    </head>
    <body>
      <script type="module">
          import {add} from './md.js';
          console.log(add);
      </script>
    </body>
</html>

执行结果

(2)动态加载

设想情景:点击按钮后加载模块

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>动态加载</title>
    </head>
    <body>
    <div>
        <button onclick="buttonclick()">点击事件</button>
    </div>
    <script>
        function buttonclick()
        {
           import("./md.js").then(({add})=>{
            console.log(add);
        });
        }
 
    </script>
<!--      <script type="module">-->
<!--          import {add} from './md.js';-->
<!--          console.log(add);-->
<!--      </script>-->
    </body>
</html>

运行结果:

bigInt基本数据类型

globalThis

globalThis 是在ES2020引入的新特性之一。这个特性旨在使用全局的this。也就是在说,在浏览器中的 window,self, this 或 frames对象,在 Web Worker中是 self,在Node.js 中是 global。借助于它,不必再担心是否选择了正确的对象。

globalThis 属性提供了一个标准的方式来访问全局的 this 值。这样你可以做到一致的方式来访问全局对象,而不必知道代码运行在何种环境中。

globalThis 主要用在兼容库里。也可以用于特征检测,用于检测JavaScript某个功能在指定浏览器或者环境中是否可用。

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值