Es6新特性详解
- -let关键字声明变量新特性
- -const关键字声明常量新特性
- -解构赋值
- -模板字符串
- -对象的简化写法
- -箭头函数 ( ) => { }
- -函数形参的初始值
- - rest参数获取函数实参
- -扩展运算符 ...
- -Symbol的基本使用
- -Symbol的内置属性
- -迭代器(Iterator)
- -自定义数据迭代
- -生成器(generator )
- -生成器函数的参数传递
- -Promise
- -Promise静态方法
- -Promise实例
- -Promise的then方法的返回值
- -then方法的链式调用
- -Set(集合)
- -Map数据结构
- -Class类
- -Class类gettr和setter运用
- -getter
- -setter
- -数值扩展
- -对象方法扩展
- -模块化
- - ES7新特性
- -ES8新特性
- - Promise的async和await
-let关键字声明变量新特性
- 变量不能重复声明
- 具有块儿级作用域(只在对应的{}块儿内有效)
- 不具备变量提升(即在let关键字声明的变量之前使用该变量会报错)
- 不影响作用域链(即在let声明变量的块儿内书写函数访问该变量时,在函数体内没找到该变量依然会随着作用域链向上找)
- 暂时性死区(用let在块儿级作用域中声明的变量与外界隔离,即使变量名相同也互不影响)
// 例如下列代码 var boxs = document.getElementsByClassName("box") for (var i = 0; i < boxs.length; i++) { boxs[i].onclick = function() { boxs[i].style.backgroundColor = "red" } }
在for循环内使用var关键字声明i时,此时i声明在全局,当代码执行时会先将for循环执行完,为boxs中的元素绑定事件,当事件实际触发时,全局的i已经变为了boxs.length+1,因此事件并不会触发
使用let关键字声明
// 使用let关键字声明 var boxs = document.getElementsByClassName("box") for (leti = 0; i < boxs.length; i++) { boxs[i].onclick = function() { boxs[i].style.backgroundColor = "red" } }
但是如果使用let关键字进行声明,for循环执行时每次i的值都只在当前的块儿级作用域有效,互不影响,因此在事件响应函数内能正确访问到当前点击事件对应的元素对象,其相当于:
{ let i = 0; boxs[i].onclick = function() { boxs[i].style.backgroundColor = "red" } } { let i = 1; boxs[i].onclick = function() { boxs[i].style.backgroundColor = "red" } }等等 ```
-const关键字声明常量新特性
- const用来声明一个常量,且在声明时必须赋值,不然会报错
- 同样具有块儿级作用域
- 同样具有暂时性死区
- 声明的变量不可更改(注意:声明的变量如果是普通的值的话其值是不可更改的,但是如果声明的是一个对象或数组,可以修改其内部的内容,这是因为const指向的地址没有发生变化,但是仍然不可以更改地址的指向)
- 潜规测–>声明的常量名大写
-解构赋值
- 针对数组[ ]、对象进行了模式匹配,简化了语法
数组-按顺序一一对应 // 左侧变量数和数组内元素个数相等 let [ a, b, c] = [1, 2, 3] console.log(a, b, c) // 1, 2, 3 // 左侧变量数和数组内元素个数不等 let [a, b] = [1, 2, 3] console.log(a, b) // 1,2 let [a ,b ,c] = [1, 2] console.log(a, b, c) // 1,2,undefined // 剩余运算符(展开运算符) ... let [a , ...b] = [1, 2, 3] console.log(a, b) // a, [2,3]
- 针对对象{ }时,接收数据的变量必须和对象的属性相同
let obj = {name: '猪八戒', age: 39, sex: '男'}; let {name ,age, sex} = obj; console.log(name, age, sex);// 猪八戒, 39, 男
-模板字符串
- 也可以用来声明一个字符串
- 模板字符串声明的字符串支持换行,不需要再用+号拼接
- 在模板字符串里可以通过${ }进行变量拼接
// 字符串拼接,十分方便,单引号和双引号需要用+号连接 let str = `<ul> <li></li> <li></li> <li></li> </ul>` // 变量拼接 ${ } let name = 猪; let str = `${name}八戒` console.log(str) // 猪八戒
-对象的简化写法
- 在ES6中支持在大括号{ } 里直接写入变量、函数来作为对象的属性、方法
let name = "猪八戒"; let changeName = function(name) { this.name = name; }; const person = { name, changeName } console.log(person.name) // 猪八戒 person.changeName("孙悟空"); console.log(person.name) // 孙悟空 }
-箭头函数 ( ) => { }
- 在ES6中支持用箭头函数来声明一个函数,简化了代码
- ( )内书写形参, { } 内书写代码体
- 箭头函数的this是静态的(即不可通过call 和 apply改变this指向),它始终指向声明时所在作用域下的this
// 函数声明方法 function getName() { console.log(this.name); } // 箭头函数方法 let getName2 = () => { console.log(this.name); } // 在Window对象下添加一个属性 window.name = "猪八戒"; // 添加一个对象 const Person = { name: "孙悟空" }; // 当直接调用时 getName(); // 猪八戒 getName2(); // 猪八戒 // 指定对象调用 getName.call(Person); // 孙悟空 getName2.call(Person);// 猪八戒(即this的指向没有改变)
- 箭头函数的简写
当( )内参数只有一个时,括号可以不写,{ }大括号里只有一条语句且该语句的执行结果是返回值时,大括号可以不写,同时return得去掉let add = (i) => { return i+i; } 等价于 let add = i => i+i; console.log(add(1)); // 2
使用箭头函数注意事项
1.不能作为构造函数实例化对象
2. 箭头函数内部不能使用 arguments伪数组获取传递进来的实参
箭头函数适用范围
1.适合与this无关的回调,例如定时器、数组的方法回调
2.不适合与this有关的回调,例如事件的回调、对象的方法-函数形参的初始值
- 在ES6中允许在函数声明时,为形参赋初始值
- 初始值一般位置要靠后,不然传参时容易将其覆盖
- 初始值可以被覆盖
// 示例代码 function add(a , b, c = 520){ return a + b + c; } console.log(add(0, 0)); // 520 console.log(add(0, 0, 996)); // 996
- 形参初始值与解构赋值联合使用
function connect({ name, age,sex }) { console.log(name, age, sex); // 孙悟空 18 男 } connect({ name: "孙悟空", age: 18, sex: "男" })
- rest参数获取函数实参
- 在ES6之前可以通过arguments获取函数传递的实参,其返回结果是一个Object
- 在ES6引入的rest参数获取函数传递的实参,其返回结果是一个数组,因此我们可以对返回结果调用一些数组的API操作
// 语法如下 function person(...args){ console.log(args); // ["孙悟空", "猪八戒"] 是一个Array } person('孙悟空','猪八戒')
- 一个函数如果有多个形参,那么rest参数必须放到最后,例如
// 例如 function person(a, ...args){ console.log(args); // ["猪八戒", "白骨精"] } person('孙悟空','猪八戒', '白骨精')
-扩展运算符 …
- 在ES6中引入了扩展运算符,它可以将一个数组转换成一个用逗号分割的参数序列
- 例如将[“猪八戒”, “白骨精” , “孙悟空”] ==> “猪八戒”, “白骨精” , “孙悟空”
- 常通过扩展运算符进行函数传参、数组合并、数组克隆、将伪数组(object)转换成真数组(Array)
// 数组合并 let person1 = ["孙悟空", "猪八戒"]; let person2 = ["唐僧", "沙僧"]; const person3 = [...person1, ...person2]; console.log(person3);// ["孙悟空", "猪八戒", "唐僧", "沙僧"] // 数组克隆 let oldArr = [1, 2, 3]; let newArr = [...oldArr]; console.log(newArr);// [1, 2, 3] // 函数传参 let person1 = ["孙悟空", "猪八戒"]; function person() { console.log(arguments); //结果(Object): Arguments(2) ["孙悟空", "猪八戒", callee: ƒ, >Symbol(Symbol.iterator): ƒ] } person(...person1); // 将伪数组转换成真数组 // 注意,因为ES6有了rest参数,所以不用对arguments进行转换 // 此处只是举例 let person1 = ["孙悟空", "猪八戒"]; function person() { console.log(arguments); // 接收到的是一个伪数组 let arr = [...arguments]; console.log(arr); // 结果(Array): ["孙悟空", "猪八戒"] } person(...person1);
-Symbol的基本使用
- Symbol是ES6新引进的一种原始数据类型, 用来解决命名冲突,表示独一无二的值,是JavaScript的第七种数据类型(前六种: String Number Boolean Null Undefined Object),Symbol类似于字符串
- Symbol的特点:
1.值唯一
2.不能与其它数据进行运算(+ - * /)
3. 通过Symbol定义的对象属性不能通过for…in来遍历,只能通过Reflect.ownKeys来获取该对象的所有键名- Symbol的创建方式(Symbol括号内相当于标识符)
1.通过Symbol( )创建 // 该方式创建的对象是唯一的 console.log(Symbol('str') === Symbol('str'));// false 2.通过Symbol.for( ) // 这种创建的对象是同一个 console.log(Symbol.for('str') === Symbol.for('str')); // true
- Symbol的应用场景
为对象添加属性时,使用Symbol(‘属性名’)可以避免该对象中存在同名属性而造成覆盖let person = { name: "孙悟空", age: 18 } // 声明一个对象 let property = { name: Symbol(), age: Symbol() } // 声明方式一 // 向person对象中添加属性 person[property.name] = "猪八戒"; person[property.age] = "20"; console.log(person); 结果: {name: "孙悟空", age: 18, Symbol(): "猪八戒", Symbol(): "20"} // 声明方式二 let xiyouji = { name: "西游记", [Symbol('say')]: function() { console.log("我是西游记"); } } console.log(xiyouji); 结果:{name: "西游记", Symbol(say): ƒ} name: "西游记" Symbol(say): ƒ () __proto__: Object
-Symbol的内置属性
- Symbol.hasInstanceof 用于判断某个对象是否为某构造器的实例对象,并可通过一个形参来接收被判断的实例对象
class Person { static [Symbol.hasInstance](param) { console.log(param); console.log("我被用于检测类型") } } console.log([] instanceof Person );
- Symbol.isConcatSpreadable 用于控制某对象使用concat( )方法时是否可展开, 它的值是一个布尔值
let arr1 = [1, 2, 3]; let arr2 = [4, 5, 6]; let newArr = arr1.concat(arr2); console.log(newArr);//[1, 2, 3, 4, 5, 6] arr2[Symbol.isConcatSpreadable] = false; // 设置arr2不可展开 newArr = arr1.concat(arr2); console.log(newArr);// [1, 2, 3, Array(3)]
更多参见下表
-迭代器(Iterator)
- 迭代器是一种接口,为各种不同的数据结构提供统一的访问机制。
- ES6创建了一种新的遍历方式 for…of循环,iterator接口主要就是供for…of循环使用
- 原生具备iterator接口的数据:
1.Array
2.Arguments
3.Set
4.Map
5.String
6.TyppedArray
7.NodeList// for...of遍历数组 遍历得到的是每一项的值 let arr = ["孙悟空", "猪八戒", "白骨精"]; for (var v of arr) { console.log(v); // 孙悟空 猪八戒 白骨精 } // for...in遍历 遍历得到的是数值下标 for (var v of arr) { console.log(v); // 0 1 2 }
- 迭代器原理:
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用next方法的时候,指针会自动指向数据结构的第一个成员
// next方法在数据结构里Symbol.iterator()的返回中 //arr[Symbol.iterator]返回的是一个函数属性,需要调用 let iterator = arr[Symbol.iterator]( ) ; console.log(iterator);// Array Iterator { }
- 接下来每调用一次next方法指针都会指向数据结构的下一个成员,直到指向最后一个成员
let iterator = arr[Symbol.iterator](); console.log(iterator.next()); // {value: "孙悟空", done: false}
- 每次调用都会返回一个对象,里面包含了value和done两个属性,value是当前指向的数据结构成员,done是表示当前遍历是否完成的一个状态,为true时结束遍历。
注意: 因此当需要自定义遍历数据结构的时候得想到使用迭代器-自定义数据迭代
// 创建一个对象 let banji = { name: "我们不一班", students: ["孙悟空", "猪八戒", "二郎神", "唐僧", "沙和尚"], // 定义Symbol.iterator方法 [Symbol.iterator]() { // 创建一个索引 let index = 0; // 保存this指向 let _this = this; return { // 在Symbol.iterator内存在一个next方法,将其返回 next: function() { if (index < _this.students.length) { // 每次迭代返回的结果都是一个包含value和done两个属性的对象,因此注意格式 const result = {value: _this.students[index], done: false }; // 同时索引自增 index++; // 返回结果 return result; } else { // 进入这里说明已经遍历完了 return { value: undefined, done: true }; } } }; } }
-生成器(generator )
生成器是一个特殊的函数,它的返回是一个迭代器,声明方法是在 function 关键字和函数名之间放一个星号( * )来表示,并能使用新的 yield 关键字,相当于函数代码的分隔符
// 定义一个生成器 function* generator() { yield "孙悟空"; yield "猪八戒"; yield "沙和尚"; yield "唐僧"; } // 执行生成器,并接收返回的迭代器 let iterator = generator() // 调用迭代器的next()方法执行生成器内语句,返回的是一个包含value和done的对象 console.log(iterator.next().value); // 孙悟空 console.log(iterator.next().value); // 猪八戒 console.log(iterator.next().value); // 沙和尚 console.log(iterator.next().value); // 唐僧 console.log(iterator.next().value); // undefined
- 由于生成器(generator)返回的是一个迭代器,因此可以使用for…of循环遍历迭代器
// 执行生成器,并接收返回的迭代器 let iterator = generator() // 使用for...of遍历 for (let v of iterator) { console.log(v);// 孙悟空 猪八戒 沙和尚 唐僧 }
-生成器函数的参数传递
- next方法可以传入实参,该参数是作为上一个yield语句的返回结果
// 定义一个生成器 function* generator(args) { console.log(args); let one = yield "孙悟空"; console.log(one); let two = yield "猪八戒"; console.log(two); let three = yield "沙和尚"; console.log(three); let four = yield "唐僧"; console.log(four); } // 执行生成器获取迭代器对象 // 生成器函数可以传入参数 let iterator = generator("111"); // 可以整体传参 111 // next方法可以传入实参,该参数是作为上一个yield语句的返回结果 console.log(iterator.next().value); // 孙悟空 222 console.log(iterator.next("222").value); // 猪八戒 333 console.log(iterator.next("333").value); // 沙和尚 444 console.log(iterator.next("444").value); // 唐僧
-Promise
- Promise是ES6新引进的解决异步编程的一种方案,Promise是一个构造函数,专门用来封装异步操作,并接收其运行成功或者失败的结果
- Promise可以避免异步操作多层嵌套的问题,即回调地狱,通过对对象调then方法拿到成功或失败的数据,然后在Promise对象外部进行接下来的操作,从而避免了多层嵌套
- Promise有三种状态, 这三种状态的切换只能是pending转换为其它两种,具有不可逆性:
1.pending: 待处理的
2.resolved: 执行完成,允许继续向下执行
3.rejected: 执行失败,其结果并不是我们想要的,拒绝向下执行- Promise的执行结果可以通过then( )方法来接收,需要传入两个回调,第一个作为Promise的状态为resolved(即成功)的回调,第二个函数作为Promise状态为rejected的回调,第二个回调一般不用,捕获异常通常使用catch( )方法来捕获
// 创建一个Promise实例对象,传入一个回调,该回调中可以接收两个参数resolved 和rejected let p = new Promise(function(resolved, rejected) { // 执行一个异步操作 setTimeout(function() { // let SucceedInfo = "得到需要的结果"; // resolved(SucceedInfo); let errorInfo = "该结果不想要"; rejected(errorInfo); }, 1000) }) // 调用Promise对象的then方法获得返回信息 // then方法传入两个回调,第一个接收Promise对象中执行得到满意结果(即>resolved)的信息,第二个接收rejected的信息 p.then(function(value) { console.log(value); // 得到需要的结果 }, function(rejected) { console.log(rejected); // 该结果不想要 }) // 通过catch( )捕获异常 p.then(function(value) { console.log(value); // 得到需要的结果 }).catch() // Uncaught (in promise) 该结果不想要
-Promise静态方法
- Promise.reject(reason)
通俗理解:创建一个立即拒绝的promise
官方解释:返回一个状态为已拒绝的 Promise 对象,并将给定的失败信息传递给对应的处理函数。- Promise.resolve(value)
通俗理解:创建一个立即完成的promise- Promise.all(iterable)
通俗理解:同时返回多个promise执行的结果,有一个错误就返回错误
官方解释:当所有的 promise 对象都成功或有任意一个 promise 失败,这个方法就会返回一个新的 promise 对象。
如果所有的 promise 都成功了,它会把一个包含 iterable 里所有 promise 返回值的数组作为成功回调的返回值。顺序跟 iterable 的顺序保持一致。
一旦有任意一个 iterable 里面的 promise 对象失败则立即以该 promise 对象失败的理由来拒绝这个新的 promise。- Promise.allSettled(iterable)
通俗理解:无论执行成功或失败,都会返回一个对象数组,其中对象对应每个promise的执行结果
官方解释:等到所有 promise 都已敲定(每个 promise 都已兑现或已拒绝)。返回一个 promise,该 promise 在所有 promise 都敲定后完成,并兑现一个对象数组,其中的对象对应每个 promise 的结果。- Promise.any(iterable)
通俗理解:返回执行最快的且执行成功的那个promise对象的值
官方解释:接收一个 promise 对象的集合,当其中的任意一个 promise 成功,就返回那个成功的 promise 的值,只有全部的promise对象都执行错误才会抛出错误。- Promise.race(iterable)
通俗理解:返回执行最快的那个promise对象,无论对错
官方解释:等到任意一个 promise 的状态变为已敲定。
当 iterable 参数里的任意一个子 promise 成功或失败后,父 promise 马上也会用子 promise 的成功返回值或失败详情作为参数调用父 promise 绑定的相应处理函数,并返回该 promise 对象。
-Promise实例
- Promise读取文件
// 引入fs模块 const fs = require('fs'); // 普通方法读取文件 // fs.readFile(('./为学.md'), (err, data) => { // // 如果读取错误则抛出错误 // if (err) throw err; // // 成功则输出文件 // console.log(data.toString()); // }); // 使用Promise封装 const p = new Promise(function(resolved, rejected) { // 读取文件,返回结果是一个Buffer(缓存数据) fs.readFile(("./为学.md"), (err, data) => { // 如果读取失败 if (err) rejected(err); // 如果读取成功 resolved(data); }) }); // 通过then方法获取Promise对象运行结果 p.then(function(value) { console.log(value.toString()); }).catch()
- Promise封装AJAX
// 接口地址: https://api.apiopen.top/api/sentences const p = new Promise(function(resolved, rejected) { // 1.创建对象 const xhr = new XMLHttpRequest(); // 2.初始化 xhr.open("GET", "https://api.apiopen.top/api/sentences"); // 3.发送 xhr.send(); // 4.绑定事件,处理响应结果 xhr.onreadystatechange = function() { // 判断 if (xhr.readyState === 4) { // 判断响应状态码 200-299 if (xhr.status >= 200 && xhr.status < 300) { // 成功 打印结果 resolved(xhr.response); } else { // 失败 rejected(xhr.status); } } } }) // 指定回调 p.then(function(value) { console.log(value); }).catch()
-Promise的then方法的返回值
- 调用then方法返回的也是一个Promise对象,里面有两个参数PromiseState(执行状态),PromiseResult(返回值)
- then方法的返回值取决与其内部回调函数的返回值
1.如果内部回调函数执行结果返回的是一个非Promise对象
则then方法返回的Promise对象中PromiseState对应的值是fulfilled,PromiseResult的值就是rejected中的值
2.如果内部回调执行结果返回的是Promise对象,则then方法的返回值取决于内部回调返回的Promise对象(假设命名为A)的执行结果
2.1 若A内部执行成功(即调用resolved),PromiseState就是fulfilled,而PromiseResult的值就是resolved内的值
2.2 若A内部调用了rejected方法,则PromiseState就是rejected,而PromiseResult的值就是rejected内的值-then方法的链式调用
- 可以通过then方法内部回调返回一个Promise对象的特性来完成then方法的链式调用
// 引入fs模块 const fs = require('fs'); // Promise读取多个文件(此处读取3个) const p = new Promise((resolved, rejected) => { // 读取文件 fs.readFile('./为学.md', (err, data) => { resolved(data); }) }) // 调用then方法获取数据 // 为了实现不断通过then方法对多个文件进行链式调用获取里层的值,应该在then方法>的回调里返回一个Promise对象 p.then(value => { // 返回一个Promise return new Promise((resolved, rejected) => { // 读取为学2文件 fs.readFile('./为学2.md', (err, data) => { // 将为学2的数据和为学的封装成数组返回 resolved([value, data]) }) }) }).then(value => { //此时then方法返回的value的值就是为学和为学2的一个数组 return new Promise((resolved, rejected) => { fs.readFile('./为学3.md', (err, data) => { // 将为学3的数据push到数组返回 value.push(data); resolved(value);// 此时返回的value就是我们想要的结果 }) }) }).then(value => {// 通过then方法接收内部返回的最终结果 console.log(value.toString()); })
-Set(集合)
- Set是ES6新增的一种数据结构,可通过new关键字来实例化一个Set对象,在Set中,成员的key值都是唯一的,因此Set内部没有重复的元素
- Set内部具有iterator接口,因此对Set类型的对象可以使用扩展运算符(…)和for…of遍历
- Set的属性
1.Set.prototype.constructor : 指向Set构造函数本身
2.Set.size: 返回Set实例对象中的成员个数- Set的操作方法
1.add( ):向Set对象中添加值
2.delete( ):删除Set对象中某个值,返回一个布尔值
3.has( ):用于判断Set中是否包含某个成员,返回一个布尔值
4.clear( ):清除所有成员- Set的遍历方法
1.keys():返回建名的遍历器;
2.values():返回建值的遍历器;
3.entries():返回建值对的遍历器;
4.forEach():使用回调函数遍历每个成员;- Set基本用法实例
// Set数组去重 let arr1 = [1, 2, 3, 4, 5, 4, 3, 2, 1]; let arr2 = [...new Set(arr1)]; console.log(arr2); // [1, 2, 3, 4, 5] // 求交集(arr1和arr3)-- 利用数组的过滤器方法 let arr3 = [1, 2, 3, 7, 8, 9]; let result = [...new Set(arr1)].filter(item => new Set(arr3).has(item)); console.log(result); // [1, 2, 3] // 求并集(arr1和arr3) let unionSet = [...new Set([...new Set(arr1), ...new Set(arr3)])]; console.log(unionSet); // [1, 2, 3, 4, 5, 7, 8, 9] // 求差集(求arr1和arr3的差集)-- 要看谁和谁求差集,谁是主体会影响最终结果 let chaSet = [...new Set(arr1)].filter(item => !new Set(arr3).has(item)); console.log(chaSet);[4, 5]
-Map数据结构
- 在ES6中新增了Map数据结构,它类似于一个对象,也是一个键值对的集合。但是Map的键没有限值,可以是任何值,包括是一个对象
- Map数据结构中也提供了iterator接口,因此它可以使用扩展运算符和for…of遍历
- Map的属性和方法
1.size : 返回map中的元素个数
2.set : 新增一个元素并返回当前map对象
3.delete: 删除指定键名的元素
4.get: 返回键名对应的值
5.has: 检测map中是否包含某个元素(包含布尔值)并返回一个布尔值
6.clear: 清空map中所有元素,返回undefined- 代码示例
// 声明一个Map实例 let m = new Map(); // 添加 m.set('键', '值'); m.set('函数', function() {}); console.log(m); // {"键" => "值", "函数" => ƒ} // 删除 m.delete('键'); console.log(m); // {"函数" => ƒ} // 获取 m.get('函数'); console.log(m); // {"函数" => ƒ} // 检测 console.log(m.has('函数')); // true // 个数 console.log(m.size); // 1 // 清空 console.log(m.clear()); // undefined
-Class类
- ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
- 构造函数使用constructor关键字来声明,构造函数可以省略,默认的构造函数会自动生成
- 静态属性和静态方法: 只在类本身,不会被实例对象继承,只有父类本身和继承该父类的子类对象才可以使用父类静态方法
- 子类可以重写父类方法,可以在子类中通过书写与父类同名的方法实现重写
- 类的继承,我们通过extends关键字来声明一个类继承自另一个类
- 可以通过super( ) 方法在子类中调用父类的构造函数,注意:如果子类有任何自己的初始化内容需要完成,它也必须先使用 super() 来调用父类的构造函数,并传递父类构造函数期望的任何参数。
- 在类中我们可以通过**#属性名**的方式定义一个私有属性或者私有方法
// 父类 class Person { // 静态属性和静态方法,只在类本身,不会被实例对象继承,只有父类>本身和继承该父类的子类对象才可以使用父类静态方法 static name = "猪"; static print() { console.log("Static"); } // 构造函数,固定写法 constructor(name, age) { this.name = name; this.age = age } // 方法 call() { console.log("hello"); } } // 创建一个实例对象 let p = new Person("fc", 21); Person.print() // 类对象本身可以使用静态方法方法 p.call() console.log(p); // 创建一个子类 class Students extends Person { constructor(name, age, id, sex) { // 调用父类的构造函数 super(name, age); // 相当于 Person.call(this,name,age) this.id = id; this.sex = sex; } } // 创建一个子类实例 let s = new Students("ly", 21, 928, "女"); Students.print() console.log(s);
-Class类gettr和setter运用
-getter
有时需要允许访问返回动态计算值的属性,或者你可能需要反映内部变量的状态,而不需要使用显式方法调用,此时就可以使用getter
- 可以通过get语法将对象属性绑定到查询该属性时将被调用的函数
- 在类中通过get关键字添加的属性是被定义在实例的原型上的
- 在该属性被查询时,会自动执行该属性绑定的函数,同时会输出该函数的返回值
- 使用get语法注意事项:
1.可以使用数值或字符串作为标识
2.必须不带参数
3.它不能与另一个 get 或具有相同属性的数据条目同时出现在一个对象字面量中(不允许使用 { get x() { }, get x() { } } 和 { x: …, get x() { } })
4.可通过delete操作符删除getter
5.可通过对象名.defineProperty( )的方式在现有对象上定义getter,但此时定义的属性将在实例自身-setter
- 当尝试设置属性时,set 语法将对象属性绑定到要调用的函数。可以在类中应用。
- 语法:set prop(val) { /* … */ }
参数说明: prop 要绑定到给定函数的属性名; val 用于保存尝试分配给prop的值的变量的一个别名- 使用set语法注意事项:
1.它的标识符可以是数字或字符串
2.它必须有一个明确的参数
3.在对象字面量中,不能为一个已有真实值的变量使用 set,也不能为一个属性设置多个 set。 ( { set x(v) { }, set x(v) { } } 和 { x: …, set x(v) { } } 是不允许的 )
4.set定义的属性名是伪属性(未定义的),单纯访问它时会返回undefined
5.可以使用delete关键字移除一个setter
6.可通过对象名.defineProperty( )的方式在现有对象上定义setter-数值扩展
// Number.EPSILON是 JavaScript的最小精度,属性的值接近于 >2.22044...E-16 function equal(a,b){ if(Math.abs(a-b) < Number.EPSILON){ return true; }else { return false; } } console.log(equal(0.1 + 0.2 === 0.3)) //false console.log(equal(0.1+0.2,0.3)) //true //二进制和八进制 let b = 0b1010; //2进制 let o = 0o777; //8进制 let d = 100; //10进制 let x = 0xff; //16进制 console.log(x) //255 //检测一个数是否为有限数 console.log(Number.isFinite(100)); //true console.log(Number.isFinite(100/0)); //false console.log(Number.isFinite(Infinity)); //false //检测一个数值是否为NaN console.log(Number.isNaN(123)) //false //字符串转整数 console.log(Number.parseInt('5213123love')); //5213123 console.log(Number.parseFloat('5.123123神器')); //5.123123 //判断是否为整数 console.log(Number.isInteger(5)); //true console.log(Number.isInteger(2.5)); //false //将小数部分抹除 console.log(Math.trunc(3.45345345345)) //3 //检测一个数到底是正数、负数、还是0 console.log(Math.sign(100)) //1 console.log(Math.sign(0)) //0 console.log(Math.sign(-123)) //-1
-对象方法扩展
- Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)
- Object.is 判断两个值是否完全相等
可以判断NaN和NaN相等,会返回true,这点全等===做不到- Object.assign 对象的合并
- Object.setPrototypeOf 设置原型对象 (不建议,Object.create()更高效)
- Object.getPrototypeof读取原型对象
//1.Object.is 判断两个值是否完全相等 console.log(Object.is(120,120)) //true console.log(Object.is(NaN,NaN)) //false //2.Object.assign 对象的合并 const a = { name:'ran', age:12 } const b = { pass:'i love you' } console.log(Object.assign(a,b)) //{name:'ran',age:'12',pass:'i love you'} //3.Object.setPrototypeOf 设置原型对象 Object.getPrototypeof const school = { name:'尚硅谷' } const cities = { xiaoqu:['北京','上海'] } Object.setPrototypeOf(school,cities) console.log(Object.getPrototypeOf(school)) //{xiaoqu: Array(2)} console.log(school) //{name: "尚硅谷"}
-模块化
- 模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来
- 模块化的好处:防止命名冲突、代码复用、高维护性
- 模块功能主要有两个命令构成:export和inport
1.export命令用于规定模块的对外接口
2.inport命令用于输入其他模块提供的功能- 暴露语法
//统一暴露 function method(){ console.log('我是统一暴露'); } export {school,findjob} //默认暴露 export default { change:function(){ console.log('我是默认暴露') } }
- 引入方法
// 通用导入 import * as m1 from "./src/js/m1.js" import * as m2 from "./src/js/m2.js" import * as m3 from "./src/js/m3.js" // 解构赋值方式导入 import {school,teach} from "./src/js/m1.js" import {school as guigu,findJob} from "./src/js/m2.js" import {default as m3 } from "./src/js/m3.js" // 针对默认暴露的简便引入写法 import m3 from "./src/js/m3.js"
- 模块引入方式二
为了避免不断在script标签中引入过多模块导致代码变得冗余时,我们可以专门创建一个js文件来引入所以模块,然后只在script标签中引入这一个js文件即可实现引入大量模块,且不会造成script中出现大量引入模块的语句
语法: src=“引入了所有模块的js文件路径” type = “module”- ES7新特性
- 在ES7中引入了Array.prototype.includes,用来检测检测数值中是否包含某个元素,返回的值是一个boolean
- 在ES5中可以使用indexof来检测,但是其返回值是如果存在则返回其对应坐标,如果不存在则返回-1
- 同时在ES7中引入了操作符**,用来实现幂运算,其功能和Math.pow( )相同
-ES8新特性
- Promise的async和await
1.async 函数是使用async关键字声明的函数。async 函数是 AsyncFunction 构造函数的实例,并且其中允许使用 await 关键字。async 和 await 关键字让我们可以用一种更简洁的方式写出基于 Promise 的异步行为,而无需刻意地链式调用 promise。
2.async函数的返回值是一个promise对象(即使在函数内部返回的不是一个promise,也会被包装成promise),这个 promise 要么会通过一个由 async 函数返回的值被解决,要么会通过一个从 async 函数中抛出的(或其中没有被捕获到的)异常被拒绝。
3.await关键字只在 async 函数内有效。如果你在 async 函数体之外使用它,就会抛出语法错误 SyntaxError 。
4.async/await的目的为了简化使用基于 promise 的 API 时所需的语法。async/await 的行为就好像搭配使用了生成器和 promise。
5.使用async和await后函数体内需要通过try{ }.catch(e){ }的方式捕获异常
6.在async的函数内当我们使用await调用函数后,当前函数后边的所有代码会在当前函数执行完毕后,被放入到微任务队列中
- 使用async函数实例
异步读取文件 // 引入fs模块 let fs = require('fs'); // 读取为学1 function read1() { return new Promise((resolve, reject) => { fs.readFile('./为学.md', (err, data) => { // 如果读取错误则抛出 if (err) throw err; // 成功 resolve(data); }) }) } // 读取为学2 function read2() { return new Promise((resolve, reject) => { fs.readFile('./为学2.md', (err, data) => { if (err) throw err; resolve(data) }) }) } // 读取为学3 function read3() { return new Promise((resolve, reject) => { fs.readFile('./为学3.md', (err, data) => { if (err) throw err; resolve(data) }) }) } async function main() { let data1 = await read1(); let data2 = await read2(); let data3 = await read3(); let arr = [data1, data2, data3]; console.log(arr.join("\r\n").toString()); } main() // 封装AJAX请求函数 function sendAJAX(url) { return new Promise((resolve, reject) => { // 创建请求实例 const xhr = new XMLHttpRequest(); // 初始化 xhr.open("GET", url); // 发送 xhr.send(); // 绑定事件,处理响应结果 xhr.onreadystatechange = function() { // 判断 if (xhr.readyState === 4) { // 判断响应状态码 if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response) } else { reject(xhr.status) } } } }) } /* // 通过then方法测试 sendAJAX("https://api.apiopen.top/api/sentences").then((value) => >{ console.log(value); }).catch() */ // async异步操作测试 async function main() { let value = await sendAJAX("https://api.apiopen.top/api/sentences") console.log(value); } main()
-ES8对象方法扩展
- 引入了kews、values、emtries、getOwnPropertyDescriptor等方法
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.getOwnPropertyDescriptor(school)) const obj = Object.create(null,{ name:{ value:'尚硅谷', //属性特性 writable:true, configurable:true, enumerable:true, } })
-ES9 rest参数和展开运算符
- 在ES9中rest参数和展开运算符已经可以在对象中使用
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"} 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"}
-ES10
- 对象扩展方法
//二维数组 const arr = [ ['name', 'FC'], ['age', '21', '22'] ] let res = Object.fromEntries(arr); console.log(res); // {name: "FC", age: "21"} //Map const m = new Map(); m.set('name', 'FC'); const result = Object.fromEntries(m); console.log(result); // {name: "FC"}
- 字符串扩展方法
let str= ' asdsadsaads ' console.log(str) // asdsadsaads console.log(str.trimStart()) //asdsadsaads 清空左侧空格 console.log(str.trimEnd()) // asd 清空右侧空格
- 数组方法扩展
const arr = [1,2,3,[4,5,6,[7,8,9]]] //参数为深度,是一个数字 console.log(arr.flat(2)) //[1,2,3,4,5,6,7,8,9] const arr2=[1,2,3,4] const result = arr2.flatmap(item => [item * 10]); //如果map的结果是一个多>维数组可以进行flat 是两个操作的结合
- Symbol.prototype.description
let s1 = Symbol('尚硅谷'); console.log(s1.description) //尚硅谷
-ES11
- 对象私有属性扩展
可以通过在属性前加一个#的方式为对象设置私有属性- Promise.allSettled方法
//声明两个promise对象 const p1 = new Promise((resolve, reject) => { setTimeout(()=>{ resolve('商品数据-1') },1000) }) const p2 = new Promise((resolve, reject) => { setTimeout(()=>{ reject('出错了!') },1000) }) //调用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)
- 可选链操作符(?.)
//相当于一个判断符,如果前面的有,就进入下一层级 function main(config){ 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的动态引入
btn.onclick = function(){ //使用之前并未引入,动态引入,返回的其实是一个Promise对象 import('./hello.js').then(module=>{ module.hello(); }) }
- BinInt类型
//大整型 let n = 521n; console.log(n,typeof(n)) // 521n n //函数 let n = 123; console.log(BigInt(n)) // 123n //不要使用浮点型,只能用int //大数值运算 let max = Number.MAX_SAFE_INTEGER; // 9007199254740991 console.log(max +1) // 9007199254740992 console.log(max +2) // 9007199254740992 出问题了 console.log(BigInt(max)+BigInt(1)) 9007199254740992n console.log(BigInt(max)+BigInt(2)) 9007199254740993n
- 绝对全局对象globalThis
//适用于复杂环境下直接操作window console.log(globalThis) //window