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;
上面代码中,变量foo
用var
命令声明,会发生变量提升,即脚本开始运行时,变量foo
已经存在了,但是没有值,所以会输出undefined
。变量bar
用let
命令声明,不会发生变量提升。这表示在声明它之前,变量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=false
的birth
属性并没有被遍历出来,遍历 => 其实就是枚举(个人理解啦,不喜勿喷哦~)
总结 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.除了可以给新定义的属性设置特性,也可以给已有的属性设置特性哈
)
最后我们来说说,最重要的两个属性 set
和get
(即存取器描述:定义属性如何被存取),这两个属性是做什么用的呢?我们通过代码来看看
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值,指向语言内部使用的方法,比如
- Symbol.hasInstance
当其他对象使用instanceof
运算符,判断是否为该对象的实例时,会调用这个方法class Person{ static [Symbol.hasInstance](param){ console.log('param----', param) console.log('检测类型') } } let o = {} console.log(o instanceof Person) // param---- {} // 检测类型 // false
- 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]]
-
Symbol.unscopables
该对象指定了使用with关键字时,哪些属性会被with环境排除 -
Symbol.match
当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值 -
Symbol.replace
当该对象被str.replace(myObject)方法调用时,会返回该方法的返回值 -
Symbol.search
当该对象被str.search(myObject)方法调用时,会返回该方法的返回值 -
Symbol.split
当该对象被str.split(myObject)方法调用时,会返回该方法的返回值 -
Symbol.iterator
对象进行for ... of循环时,会调用Symbol.iterator方法,返回该对象的默认遍历器 -
Symbol.toPrimitive
该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值 -
Symbol.toStringTag
在该对象上调用toString方法时,返回该方法的返回值 -
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
介绍
-
Array.prototype.includes:用来检测数组中是否包含某个元素,返回布尔类型值
-
在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表达式
介绍
-
await必须放在async函数中
-
await右侧的表达式一般为promise对象
-
await可以返回的是右侧promise成功的值
-
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构造函数接收一个函数作为参数,该函数的两个参数是resolve
,reject
,它们由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执行多步操作非常好用,那我们就来模仿一个多步操作的过程,那就以吃饭为例吧。要想在家吃顿饭,是要经过三个步骤的。
- 洗菜做饭。
- 坐下来吃饭。
- 收拾桌子洗碗。
这个过程是有一定的顺序的,你必须保证上一步完成,才能顺利进行下一步。我们可以在脑海里先想想这样一个简单的过程在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某个功能在指定浏览器或者环境中是否可用。