ES6 新特性
let声明的变量及声明特性
1、变量不能重复声明
2、块级作用域(es5三种作用域:全局、函数、eval)
3、不存在变量提升
4、不影响作用域链
const声明常量以及特点
1、一定要赋初始值
2、一般常量使用大写(潜规则)
3、常量的值不能修改
4、块级作用域
5、对于数组和对象的元素修改,不算做对常量的修改,不会报错
变量的解构赋值
ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
1、数组的解构
const F4 =[小沈阳","刘能","赵四","宋小宝"];
let [xiao,liu, zhao,song] = F4;
console.log(xiao); //小沈阳
console.log(liu); //刘能
console.log(zhao); //赵四
console.log(song); //宋小宝
2、对象的解构
const zhao = {
name: "赵本山",
age: "不详",
xiaopin: function(){
console.log("我可以演小品");
}
};
let {name, age, xiaopin} = zhao;
console.log(name); //赵本山
console.log(age); //不详
console.log(xiaopin); //function
xiaopin(); //我可以演小品
let {xioapin} = zhao;
xiaopin(); //我可以演小品
模板字符串
ES6引入新的声明字符串的方式:``
1、声明
let str = `我也是一个字符串!`
console.log(str, typeof str);
2、内容中可以直接出现换行符
let str = `<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>`;
3、变量拼接
let name ="同学";
let out =`${name},你好!`;
console.log(out); //同学,你好!
简化对象写法
ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁
let name = "王";
let change = function() {
console.log("哈喽哈喽!!");
}
const school = {
name,
change,
method(){
console.log("你在吗!!");
}
}
console.log(school); //{name: '王', change: ƒ, method: ƒ}
箭头函数
ES6允许使用「箭头」(=>)定义函数。
let fn = function(){
}
let fn2 = (a,b) =>{
return a + b;
}
//调用函数
let result = fn2(1, 2);
console.log(result); //3
1、this是静态的。this始终指向函数声明时所在作用域下的 this的值。
function getName(){
console.log(this.name);
}
let getName2 = () => {
console.log(this.name);
}
//设置 window对象的name属性
window.name ='王';
const school = {
name: "USC"
}
//直接调用
getName(); //王
getName2(); //王
// call 方法调用
getName.call(school); //USC
getName2.call(school); //王
2、不能作为构造函数实例化对象
let Person = (name,age) => {
this.name = name;
this.age = age;
}
let person = new Person("wang", 21);
console.log(person); //Uncaught TypeError: Person is not a constructor
3、不能使用arguments变量
let fn = () => {
console.log(arguments);
}
fn(1,2,3); //Uncaught ReferenceError: arguments is not defined
4、箭头函数简写
(1)省略小括号,当形参有且只有一个的时候。
let add = n => {
return n + n;
}
console.log(add(9)); //18
(2)省略小括号,当形参有且只有一个的时候,此时return必须省略,而且语句的执行结果就是函数的返回值。
//let pow = (n) =>{
let pow = n =>{
return n * n;
};
console.log(pow(9)); //81
//let pow = (n) => return n * n;//错误写法,return要省略
//let pow = (n) => n * n;
let pow = n => n * n;
console.log(pow(8)); //64
箭头函数适合与 this 无关的回调,定时器,数组的方法回调
箭头函数不适合与this有关的回调,事件回调,对象的方法(可以用,但不适合)
参数默认值
1、形参初始值具有默认值的参数,一般位置要靠后(潜规则)
function connect({host="127.0.0.1", username, password, port}){
console.log(host) //atguigu.com
console.log(username) //root
console.log(password) //root
console.log(port) //3306
}
connect({
host: 'atguigu.com',
username: "root",
password: "root",
port: 3306,
})
rest参数
ES6 引入rest参数,用于获取函数的实参,用来代替arguments
//ES5 获取实参的方式
function date() {
console.log(arguments,tyoeof arguments); //输出的是伪数组,是一个对象
}
date("张三", "李四", "王五")
function date(...args) {
console.log(args); //['张三', '李四', '王五']
}
date("张三", "李四", "王五")
//rest 参数必须要放到最后
function fn(a, b, ...args){
console.log(a) //1
console.log(b) //2
console.log(args) //(4) [3, 4, 5, 6]
}
fn(1,2,3,4,5,6)
扩展运算符
『…』扩展运算符能将 数组 转换为逗号分隔的 参数序列
声明一个数组 …
const arr = ['张三', '李四', '王五'];
function result() {
console.log(arguments);
}
result(arr);//Arguments [Array(3)]
result(...arr);//Arguments(3)
应用:
// 1. 数组的合并 const arr1 = ["周", "吴"]; const arr2 = ["郑", "王"]; const result = [...arr1, ...arr2]; console.log(result) //["周", "吴", "郑", "王"] // 2. 数组的克隆 const arr3 = ['2','3','4']; const arr4 = [...arr3]; //浅拷贝 console.log(arr4); //["2", "3", "4"] // 3. 将伪数组转为真正的数组 let divs = document.getElementsByTagName("div"); let divArr = [...divs]; console.log(divArr); //[div, div, div, div] //arguments也能转,但是有rest参数存在就没必要了
Symbol的介绍与创建
ES6引入了一种新的原始数据类型symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,是一种类似于字符串的数据类型。
Symbol特点:
- Symbol的值是唯一的,用来解决命名冲突的问题
- Symbol值不能与其他数据进行运算(四则运算、字符串拼接或比较 等等)
- Symbol定义的对象属性不能使用for…in循环遍历,但是可以使用Reflect.ownKeys 来获取对象的所有键名
//创建Symbol
let s = Symbol() ;
console.log(s, typeof s);
let s2 = Symbol('尚硅谷');
let s3 = Symbol('尚硅谷');
console.log(s2 === s3); //false (Symbol像是身份证不重复,他的值像是名字可以重复)
// Symbol.for创建
let s4 = Symbol.for('尚硅谷');
let s5 = Symbol.for('尚硅谷');
console.log(s4 === s5); //true
//不能与其他数据进行运算
//let result = s + 100;
//let result = s > 100;
//let result = s + '100';
//let result = s + s; //Cannot convert a Symbol value to a number
Symbol.for和Symbol()区别:
和 Symbol() 不同的是,用 Symbol.for() 方法创建的的 symbol 会被放入一个全局 symbol 注册表中。
Symbol.for() 并不是每次都会创建一个新的 symbol,它会首先检查给定的 key 是否已经在注册表中了。
假如是,则会直接返回上次存储的那个。否则,它会再新建一个。Symbol.for("foo"); // 创建一个 symbol 并放入 symbol 注册表中,键为 "foo" Symbol.for("foo"); // 从 symbol 注册表中读取键为"foo"的 symbol Symbol.for("bar") === Symbol.for("bar"); // true,证明了上面说的 Symbol("bar") === Symbol("bar"); // false,Symbol() 函数每次都会返回新的一个 symbol var sym = Symbol.for("mario"); sym.toString(); // "Symbol(mario)",mario 既是该 symbol 在 symbol 注册表中的键名,又是该 symbol 自身的描述字符串
Symbol创建对象属性
//向对象中添加方法 up down
let game = {
up: function(){},
down: function(){},
name: "俄罗斯方块"
}
//声明一个对象
let methods = {
up: Symbol(), //方法声明成symbol类型
down: Symbol(),
};
game[methods.up] = function(){ //将method里面的up方法添加到game
console.log("我可以改变形状!");
};
game[methods.down] = function(){
console.log("我可以快速下降!!");
};
console.log(game);
game[methods.up](); //我可以改变形状!
game[methods.down](); //我可以快速下降!!
//方法二
let youxi = {
name: "狼人杀",
[Symbol('say')]: function(){
console.log('我可以发言');
},
[Symbol('zibao')]: function(){
console.log('我可以自爆');
}
}
console.log(youxi)
youxi[Symbol('say')]();
Symbol内置值
除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
内置属性 | 描述 |
---|---|
Symbol.haslnstance | 当其他对象使用instanceof 运算符,判断是否为该对象的实例时,会调用这个方法 |
Symbol.isConcatSpreadable | 对象的Symbol.isConcatSpreadable属性等于的是一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。 |
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 | 创建衍生对象时,会使用该属性 |
class Person{ static [symbol.hasInstance](param){ console.log(param); //对象o console.log("我被用来检测类型了"); return true; } } let o = {}; console.log(o instanceof Person); //true const arr = [1,2,3]; const arr2 =[4,5,6]; arr2[Symbol.isConcatSpreadable] = false; console.log(arr.concat(arr2)); //[1,2,3,[4,5,6]] arr2作为整体不展开
迭代器
迭代器(lterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署lterator接口,就可以完成遍历操作。
-
ES6创造了一种新的遍历命令for…of循环,lterator接口主要供 for…of消费。
-
原生具备iterator接口的数据(可用for of遍历):
Array
Arguments
Set
Map
String
TypedArray
NodeList
工作原理:
a) 创建一个指针对象指向当前数据结构的起始位置
b) 第一次调用对象的 next方法,指针自动指向数据结构的第一个成员
c) 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
d) 每调用next方法返回一个包含value 和 done属性的对象
//声明一个数组
const arr = ['周','吴','郑','王'];
//使用for...of遍历数组
for(let v of arr){
console.log(v);
}
let iterator = arr[Symbol.iterator]();
//调用对象的next方法, 每调一次next返回新的值然后往后移动
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: '王', done: false}
console.log(iterator.next()); //{value: undefined, done: true}
注:需要自定义遍历数据的时候,要想到迭代器。
//举例:使用for...of...遍历对象里的数组 const banji = { name: "二班", stus: [ '小王', '小明', '小宁', '小张', ], [Symbol.iterator](){ //索引变量 let index = 0; //保存该作用域的this(banji) let that = this; return { next: function() { if(that.stus.length > index){ const result = {value: that.stus[index], done: false}; index ++; return result; }else{ return {value: undefined, done: true}; } }, }; }, } for(let v of banji){ console.log(v); } //小王 //小明 //小宁 //小张
生成器
生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同
生成器其实就是一个特殊的函数
作用:异步编程(以前用纯回调函数)
function * gen(){
console.log(111);
yield '一只没有要朵';
console.log(222);
yield '一只没有尾部';
console.log(333);
yield '真奇怪'; //yield为函数代码的分隔符,gen函数被其分成四块区域
console.log(444);
}
//执行获取迭代器对象
let iterator = gen();
iterator.next(); //111
iterator.next(); //222
iterator.next(); //333
iterator.next(); //444
//遍历
for(let v of gen()){
console.log(v);
}
//111
//一只没有要朵
//222
//一只没有尾部
//333
//真奇怪
//444
生成器函数参数
function* gen(arg){
console.log(arg);
let one = yield 111;
console.log(one);
let two = yield 222;
console.log(two);
let three = yield 333;
console.log(three);
}
//执行获取迭代器对象
let iterator = gen('AAA');
console.log(iterator.next());//next方法可以传入实参
console.log(iterator.next('BBB'));
console.log(iterator.next('CCC'));
console.log(iterator.next('DDD'));
//AAA
//{value: 111, done: false}
//BBB
//{value: 222, done: false}
//CCC
//{value: 333, done: false}
//DDD
//{value: undefined, done: true}
生成器函数实例
//异步编程文件操作网络操作(ajax,request)数据库操作
//1s后控制台输出 111 2s后输出 222 3s后输出 333
//“回调地狱”演示
setTimeout(()=>{
console.log(111);
setTimeout(()=>{
console.log(222);
setTimeout(()=>{
console.log(333);
},3000)
},2000)
},1000)
function one(){
setTimeout(()=>{
console.log(111);
iterator.next();
},1000)
}
function two(){
setTimeout(()=>{
console.log(222);
iterator.next();
},2000)
}
function three(){
setTimeout(()=>{
console.log(333);
iterator.next();
},3000)
}
function* gen(){
yield one();
yield two();
yield three();
}
let iterator = gen();
iterator.next();
Promise
Promise是ES6引入的异步编程的新解决方案。语法上 Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
- Promise构造函数: Promise (excutor){}
- Promise.prototype.then方法
- Promise.prototype.catch方法
//实例化 Promise 对象
const p = new Promise(function(resolve, reject){
setTimeout(function(){
//let data = "数据库中的用户数据";
//resolve(data);
let err = "数据读取失败";
reject(err);
},1000);
});
//调用promise对象的then方法
p.then(function(value){
console.log(value);
}, function(reason){
console.error(reason);
});
//通过resole和reject改变实例状态,调用resolve则Promise状态为成功便执行then方法第一个回调,调用reject状态为失败,则执行第二个回调
Promise读取文件
//1. 引入 fs 模块
const fs = require('fs');
//2. 调用方法读取文件
// fs.readFile('./resources/test.md', (err, data) => {
// //如果失败,则抛出错误
// if(err) throw err;
// //如果没有出错,则输出内容
// console.log(data.toString());
// });
//3. 使用 Promise 封装
const p = new Promise(function(resolve, reject){
fs.readFile('./resources/test1.md', (err, data) => {
//如果失败,则抛出错误
if(err) reject(err);
//如果没有出错,则输出内容
resolve(data);
});
})
p.then(function(value){
console.log(value.toString());
}, function(reason){
console.error(reason);
})
Promise封装AJAX
// const x = new XMLHttpRequest();
// x.open('GET', 'https://api.uomg.com/api/rand.qinghua?format=text');
// x.send();
// x.onreadystatechange = function() {
// if(x.readyState === 4) {
// if(x.status >= 200 && x.status < 300) {
// console.log(x.response);
// }else {
// console.error(x.status);
// }
// }
// }
//使用 Promise 封装
const p = new Promise((resolve, reject) => {
const x = new XMLHttpRequest();
x.open('GET', 'https://api.uomg.com/api/rand.qinghua?format=text');
x.send();
x.onreadystatechange = function() {
if(x.readyState === 4) {
if(x.status >= 200 && x.status < 300) {
//成功执行resolve
resolve(x.response);
}else {
//失败执行reject
reject(x.status); //5种状态 0 1 2 3 4
}
}
}
})
//指定成功或失败的回调
p.then(function(value) {
console.log(value);
}, function(reason) {
console.error(reason);
})
Promise-then方法
//创建promise对象
const p = new Promise((resolve, reject)=>{
setTimeout(()=>{
//resolve('用户数据');
reject('出错啦');
},1000)
});
//调用then方法then方法的逐回结果是 Promise 对象,对象状态由回调函数的执行结果决定
//1.如果回调函数中返回的结果是非promise类型的属性,状态为成功,返回值为对象的成功的值,没有返回值默认为undefined
const result = p.then(value => {
console.log(value);
//1.非 promise类型的属性
// return 'iloveyou';
//2.是 promise对象
//return new Promise( (resolve,reject) => {
// // resolve( 'ok');
// reject( 'error ' );
// });
//3.抛出错误
// throw new Error('出错啦!");
// throw '出错啦!';
},reason=>{
console.warn(reason);
})
//链式调用 可杜绝“回调地狱”
p.then(value=>{
}).then(value=>{
});
console.log(result);
Promise实践-读取多个文件
const fs = require('fs');
// fs.readFile('./resources/test.md', (err, data1) => {
// fs.readFile('./resources/test2.md', (err, data2) => {
// fs.readFile('./resources/test3.md', (err, data3) => {
// let result = data1 + '\r\n' + data2 +'\r\n'+ data3;
// console.log(result);
// });
// });
// });
//使用 Promise 实现
const p =new Promise((resolve, reject) => {
fs.readFile('./resources/test.md', (err, data) => {
resolve(data);
});
});
p.then( (value)=> {
return new Promise( (resolve, reject)=>{
fs.readFile('./resources/test2.md', (err, data) => {
resolve([value, data]);
});
});
}).then( (value)=> {
return new Promise( (resolve, reject)=>{
fs.readFile('./resources/test3.md', (err, data) => {
//将最后一个压入前两个
value.push(data);
resolve(value);
});
});
}).then( (value)=> {
console.log(value.join('\r\n'));
})
Promise-catch方法
const p = new Promise((resolve, reject)=>{
setTimeout(()=>{
//设置p 对象的状态为失败,并设置失败的值
reject("出错啦! ");
},1000)
});
// p.thep(function(value){}, function(reason){
// console.error( reason);
// });
p.catch(function(reason){
console.warn(reason);
});
Set
ES6提供了新的数据结构Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了iterator接口,所以可以使用 扩展运算符 和 for…of… 进行遍历,
集合的属性和方法:
- size 返回集合的元素个数
- add 增加一个新元素,返回当前集合
- delete 删除元素,返回boolean值
- has 检测集合中是否包含某个元素,返回boolean值
let s = new Set();
let s2 = new Set(['zhou','wu','zhen','wang','zhou']);
//元素个数
//console.log(s2.size); //4
//添加新的元素
//s2.add('zhang'); //Set(5) {'zhou', 'wu', 'zhen', 'wang', 'zhang'}
//删除元素
//s2.delete('wu'); //true
//console.log(s2.has('zhen')); //true
//s2.clear();
//console.log(s2); //Set(0) {size: 0}
for (let v of s2) {
console.log(v);
}
//zhou
//wu
//zhen
//wang
Set集合实践
let arr = [1,2,3,4,5,4,3,2,1];
//1. 数组去重
let result = [...new Set(arr)];
console.log(result); //(5) [1, 2, 3, 4, 5]
//2. 交集
let arr2 = [4,5,6,5,6];
let result = [...new Set(arr)].filter(item => {
let s2 = new Set(arr2);// 4 5 6
if(s2.has(item)){
return true;
}else{
return false;
}
});
//写法2
//let result = [...new Set(arr)].filter(item => new Set(arr2).has(item));
console.log(result); //(2) [4, 5]
//3. 并集
let union = [...new Set([...arr, ...arr2])]
console.log(union); //(6) [1, 2, 3, 4, 5, 6]
//4. 差集
let diff = [...new Set(arr)].filter(item => !new Set(arr2).has(item));
console.log(diff); //(3) [1, 2, 3]
Map
ES6提供了 Map数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map也实现了iterator接口,所以可以使用 扩展运算符 和 for…of… 进行遍历。
Map的属性和方法:
- size 返回Map的元素个数
- set 增加一个新元素,返回当前Map
- get 返回键名对象的键值
- has 检测Map中是否包含某个元素,返回boolean值
- clear 清空集合,返回undefined
//声明Map
let m = new Map();
//添加元素
m.set('name','王同学');
m.set('change', function(){
console.log("你好!!");
});
let key = {
school: 'USC',
};
m.set(key, ['张三','李四','王五']);
//size
console.log(m.size); //3
//删除
//m.delete('name');
//获取
//console.log(m.get('change')); //ƒ (){console.log("你好!!");}
//console.log(m.get(key)); //(3) ['张三', '李四', '王五']
//清空
//m.clear();
//遍历
for(let v of m){
console.log(v);
//(2) ['name', '王同学']
//(2) ['change', ƒ]
//(2) [{…}, Array(3)]
}
console.log(m); //Map(3) {'name' => '王同学', 'change' => ƒ, {…} => Array(3)}
class类
ES6提供了更接近传统语言的写法,引入了 Class(类〉这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
知识点:
-
class声明类
-
constructor定义构造函数初始化
-
extends继承父类
-
super调用父级构造方法
-
static定义静态方法和属性
-
父类方法可以重写
//ES5写法:
function Phone(brand, price){
this.brand = brand;
this.price = price;
}
//添加方法
Phone.prototype.call = function(){
console.log("我可以打电话!!");
}
//实例化对象
let Huawei = new Phone('华为', 5999);
Huawei.call(); //我可以打电话!!
console.log(Huawei); //Phone {brand: '华为', price: 5999}
//class写法:
class Phone{
//构造方法,名字写法固定
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
//方法必须使用该语法,不能使用 ES5 的对象完整形式
call(){
console.log("我可以打电话!!");
}
}
let onePlus = new Phone('一加', 1999);
onePlus.call(); //我可以打电话!!
console.log(onePlus);
类的静态成员
class Phone{
//静态属性
static name ='手机';
static change(){
console.log("我可以改变世界");
}
}
let nokia = new Phone();
console.log(nokia.name); //undefined
console.log(Phone.name); //手机
//static标注的属性和方法它属于类而不属于实例对象
类继承
//ES5继承
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}
Phone.prototype.call = function() {
console.log("我可以打电话");
}
function smartPhone(brand, price, color, size) {
Phone.call(this, brand, price);
this.color = color;
this.size = size;
}
//设置子级构造函数的原型并修正constructor
smartPhone.prototype = new Phone();
smartPhone.prototype.constructor = smartPhone;
smartPhone.prototype.playGame = function() {
console.log("我可以玩游戏");
}
const iphone = new smartPhone('苹果', 5999, '黑色', '6.2inch')
console.log(iphone);
//class继承
class Phone{
//构造方法
constructor(brand, price){
this.brand = brand;
this.price = price;
}
call(){
console.log("我可以打电话");
}
}
class smartPhone extends Phone {
constructor(brand, price, color, size) {
super(brand, price); //Phone.call(this, brand, price);
this.color = color;
this.size = size;
}
playGame() {
console.log("我可以玩游戏");
}
}
const iphone = new smartPhone('苹果', 5999, '黑色', '6.2inch');
console.log(iphone);
子类对父类方法重写
class Phone{
//构造方法
constructor(brand, price){
this.brand = brand;
this.price = price;
}
call(){
console.log("我可以打电话");
}
}
class smartPhone extends Phone {
constructor(brand, price, color, size) {
super(brand, price); //Phone.call(this, brand, price);
this.color = color;
this.size = size;
}
playGame() {
console.log("我可以玩游戏");
}
call(){ //子类重写父类中call方法
console.log("我可以进行视频通话");
}
}
const iphone = new smartPhone('苹果', 5999, '黑色', '6.2inch')
console.log(iphone);
iphone.call(); //我可以进行视频通话
getter和setter
class Phone {
get price() {
console.log("价格属性被读取了");
return 'price方法返回值';
}
set price(newVal) {
console.log("价格属性被修改了");
}
}
let s = new Phone();
console.log(s.price);
s.price = 'free' //price返回值被修改
只有getter定义只读属性
// 只有getter定义只读属性
class foo {
constructor(name) {
this.name = name;
}
get name() {
console.log(`getter函数`);
return this.name;
}
}
//Cannot set property name of #<foo> which has only a getter
let p3 = new foo("李四");
当一个属性只有getter没有setter的时候,我们是无法进行赋值操作的(第一次初始化也不行),这一点需要注意
当没有getter和setter时,就可以正常读写属性
数值扩展
- Number.EPSILON 是 javascript 表示的最小精度
//EPSILON 属性值接近于 2.2204460492503130808472633361816E-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)); //true
- 二进制和八进制
let b = 6b1010;
let o = 0o777;
let d = 100;
let x= 0xff;
console.log(x);
- Number .isFinite检测一个数值是否为有限数
console.log(Number.isFinite(100)); //true
console.log(Number.isFinite(100/0)); //false
console.log(Number.isFinite(Infinity)); //false
- Number.isNaN检测一个数值是否为NaN
console.log(Number.isNaN(123)); //false
- Number.parseInt Number.parseFloat字符串转整数
console.log(Number.parseInt('5211314love')); //5211314
console.log(Number.parseFloat('3.1415926神奇')); //3.1415926
- Number.isInteger判断一个数是否为整数
console.log(Number.isInteger(5)); //true
console.log(Number.isInteger(2.5)); //false
- Math.trunc将数字的小数部分抹掉
console.log(Math.trunc(3.5)); //3
- Math.sign判断一个数到底为正数负数还是零
console.log(Math.sign(100)); //1
console.log(Math.sign(3)); //1
console.log(Math.sign(0)); //0
console.log(Math.sign(-20000)); //-1
对象方法的扩展
1.Object.is 判断两个值是否完全相等
console.log(Object.is(120,120)); //true
console.log(0bject.is(NaN,NaN)); //true
console.log(NaN === NaN); //false
2.Object.assign对象的合并
const config1 = {
host: 'localhost',
port: '3306',
username: 'root',
password: 'test',
test1: 'test1',
}
const config2 = {
host: 'wlhnet.cn',
port: '80',
username: 'wlh',
password: 'wlh',
test2: 'test2',
}
console.log(Object.assign(config1, config2));
//{host: 'wlhnet.cn', port: '80', username: 'wlh', password: 'wlh', test1: 'test1', test2: 'test2',}
//有重复项则后一个覆盖前一个,没有则合并
3.Object.setPrototypeOf 设置原型对象Object.getPrototypeOf
const school = {
name: "USC"
}
const cities = {
city: ['北京','上海','深圳']
}
Object.setPrototypeOf(school, cities); // 把cities放到school原型里
console.log(Object.getPrototypeOf(school)); //获取school的原型
console.log(school);
模块化
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
模块化的优势有以下几点:
-
防止命名冲突
-
代码复用
-
高维护性
ES6之前的模块化规范有:
-
CommonJS => NodeJS、Browserify
-
AMD => requireJs
-
CMD => seaJS
ES6模块化语法
模块功能主要由两个命令构成:export和import。
-
export命令用于规定模块的对外接口
-
import命令用于输入其他模块提供的功能
通用导入方式:
<!--index.html-->
<script>
//引入 module1.js 模块内容
import * as m1 from "./src/js/module1.js";
//路径要正确,此时 module1.js 的数据都存在变量 m1 里
console.log(m1);
//引入module2.js,统一暴露
import * as m2 from "./src/js/module2.js";
console.log(m2);
//引入module3.js,默认暴露
import * as m3 from "./src/js/module3.js";
m3.default.class();
</script>
分别暴露:
//module1.js export let school = 'USC'; export function class() { sonsole.log("我是xx班的"); }
统一暴露:
//module2.js let school = 'USC'; function class() { console.log("我是xx班的"); } export {school, class};
默认暴露:
//module3.js export default { school: 'USC', class: function() { console.log("我是xx班的"); } }
解构赋值形式:
<!--index.html-->
<script>
//解构赋值形式引入 module1.js 模块内容
import {school, class} from "./src/js/module1.js";
console.log(school, class);
//解构赋值形式引入module2.js,统一暴露
import {school as usc, class} from "./src/js/module2.js";
//重名可使用 as 将 school 存入另一变量
console.log(usc, class);
//解构赋值形式引入module3.js,默认暴露
import {default as m3} from "./src/js/module3.js";
//不能直接放 default,必须用 as 存到一个变量
console.log(m3);
</script>
简便形式,针对默认暴露
<script>
import m3 from "./src/js/module1.js";
console.log(m3);
</script>
ES6模块化方式2
<!--index.html-->
<script>
console.log(m1);
console.log(m2);
m3.default.class();
</script>
<script src="./src/js/app.js" type="module"></script>
//入口文件 app.js
//模块移入
import * as m1 from "./module1.js";
import * as m2 from "./module2.js";
import * as m3 from "./module3.js";
babel对ES6模块化代码转换
1.安装工具 babel-cli babel-preset-env browserify(webpack)
(1)初始化npm init --yes
(2)npm i babel-cli babel-preset-env browserify -D
(3)局部安装需加npx npx babel src/js -d dist/js --preset-babel-preset-env
(4)打包 npx browserify dist/js/app.js -o dist/bunble.js
(5)打包完在html页面引入 bunble.js 文件
(6)更改代码需在原文件更改,改完重新转换打包(3)(4)命令。
模块化引入NPM包
import 变量名 from '模块名';
import $ from 'jquery';
ES7 新特性
Array.prototype.includes
lncludes 方法用来检测数组中是否包含某个元素,返回布尔类型值
// includes
const sdmz = ["西游记", "红楼梦", "三国演义", "水浒传"];
//判断
//console.log(sdmz.includes('西游记')); //true
//console.log(sdmz.includes('金瓶梅')); //false
指数操作符
在ES7中引入指数运算符**
,用来实现幂运算,功能与Math.pow
结果相同
// **
console.log(2 ** 10); //1024
console.log(Math.pow(2, 10)); //1024
ES8 新特性
async 和 await
async和 await两种语法结合可以让异步代码像同步代码一样。
async 函数
- async函数的返回值为promise对象。
- promise对象的结果由async函数执行的返回值决定。
//async 函数(在函数声明前加 async 就变成了async函数)
async function fn() {
//return '王同学'; //1. 即使返回的是字符串,返回的结果也是promise对象
//return ; //2. 返回的结果不是一个promise类型对象,返回的结果就是成功promise对象
//throw new Error('出错啦!!'); //3. 抛出错误,返回的结果是一个失败的Promise
return new Promise((resolve, reject) => {
//resolve('成功的数据'); //如果返回的结果为成功promise则函数返回成功的值
//resolve('失败'); //如果返回的结果为失败promise则函返回失败的值
});
}
const result = fn();
//console.log(result); //Promise {<resolved>: '王同学'}
console.log(result); //Promise {<resolved>[[PromiseResult]]:"成功的数据"}
await 表达式
- await必须写在 async函数中。
- await右侧的表达式一般为promise 对象。
- await返回的是promise成功的值。
- await的 promise失败了,就会抛出异常,需要通过 trv…catch捕获处理。
const p = new Promise((resolve, reject) => {
//resolve('用户数据');
reject('失败啦!!')
});
async function main() {
try{
let result = await p;
console.log(result);
} catch(e) {
console.log(e);
}
}
await与async结合读取文件:
const fs = require('fs');
function readTest() {
return new Promise( (resolve, reject) => {
fs.readFile('./Promise/resources/test1.md', (err,data) => {
if (err) reject(err);
resolve(data);
});
});
}
function readTest2() {
return new Promise( (resolve, reject) => {
fs.readFile('./Promise/resources/test2.md', (err,data) => {
if (err) reject(err);
resolve(data);
});
});
}
function readTest3() {
return new Promise( (resolve, reject) => {
fs.readFile('./Promise/resources/test3.md', (err,data) => {
if (err) reject(err);
resolve(data);
});
});
}
async function main() {
try{
let test = await readTest();
let test2 = await readTest2();
let test3 = await readTest3();
console.log(test.toString());
console.log(test2.toString());
console.log(test3.toString());
}catch (e) {
console.log(e);
}
}
main();
await与async封装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);
}
}
};
});
}
async function main() {
try{
let result = await sendAJAX('https://api.uomg.com/api/rand.qinghua?format=text');
console.log(result);
}catch (e) {
console.log(e);
}
}
main();
Object.values() 和 Object.entries()
- Object.values()方法返回一个给定对象的所有可枚举属性值的数组。
- Object.entries()方法返回一个给定对象自身可遍历属性 [key,value] 的数组。
const school = {
name: "USC",
classes: ['一班','二班','三班'],
course: ['前端','java','大数据','测试'],
};
//获取对象所有的键
console.log(Object.keys(school));
//获取对象所有的值
console.log(Object.values(school));
//entries
console.log(Object.entries(school));
//[['name', 'USC'], ['classes', Array(3)], ['course', Array(4)]]
//创建 Map
const m = new Map(Object.entries(school));
console.log(m.get('cities'));//['前端', 'java', '大数据', '测试']
Object.getOwnPropertyDescriptors()
该方法返回指定对象所有自身属性的描述对象
//接上面代码
console.log(Object.getOwnPropertyDescriptors(school));
//打印出来是一个对象,也有键值,键跟传入对象的键一样,值也是一个对象(包含value及属性特性)
//跟用Object.create创建的对象类似
const obj = Object.create(null, {
name: {
//设置值
value: 'USC',
//属性特性
writable: true, //是否可写
configurable: true, //是否可删除
enumerable: true //是否可枚举
}
});
ES9 新特性
扩展运算符与rest参数
对象展开:
Rest 参数与spread扩展运算符在 ES6中已经引入,不过 ES6中只针对于数组,
在ES9中为对象提供了像数组一样的rest
参数和扩展运算符
//function connect({host, port, username, password}) {
function connect({host, port, ...users}) {
console.log(host);
console.log(port);
//console.log(username);
//console.log(password);
console.log(users); //es9
}
connect({
host: '127.0.0.1',
port: '3306',
username: 'root', //之后更多参数将存到users变量中
password: 'root',
});
//127.0.0.1
//3306
//{username: 'root', password: 'root'}
const person1 = {
name1: '周',
other: '123'
}
const person2 = {
name2: '吴',
}
const person3 = {
name3: '郑',
}
const person4 = {
name4: '王',
}
const class1 = {...person1, ...person2, ...person3, ...person4};
console.log(class1);//{name1: '周',vother: '123', name2: '吴', name3: '郑', name4: '王'}
正则扩展
命名捕获分组
对分组匹配的结果进行命名方便对结果进行处理
let str = '<a href="http://www.wlhnet.cn">王同学</a>';
//提取 url 与 标签文本
const reg = /<a href="(.*)">(.*)<\/a>/;
const result = reg.exec(str);
console.log(result[0]); //<a href="http://www.wlhnet.cn">王同学</a>
console.log(result[1]); //http://www.wlhnet.cn
//groups为undefined
const reg2 = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;
const result2 = reg2.exec(str);
console.log(result2);
//此时groups有值
console.log(result2.groups.url); //http://www.wlhnet.cn
console.log(result2.groups.text); //王同学
正则断言
let str = 'WLH22311你是54654这样啊';
//正向断言
const reg = /\d+(?=这)/;
console.log(reg.exec(str));
//['54654', index: 10, input: 'WLH22311你是54654这样啊', groups: undefined]
//反向断言
const reg2 = /(?<=是)\d+/;
console.log(reg2.exec(str));
//['54654', index: 10, input: 'WLH22311你是54654这样啊', groups: undefined]
dotAll模式
dot 有 .
的意思 .
在正则中指元字符 (除换行符之外的任意单个字符)。
针对一些html内容,在进行分析和提取数据分析的时候作用非常明显。
//提取字符中电影名称以及对应上映时间存到一个对象里
let str = `
<ul>
<li>
<a>肖申克的救赎</a>
<p>上映时间:1994-09-10</p>
</li>
<li>
<a>阿甘正传</a>
<p>上映时间:1994-07-06</p>
</li>
</ul>`;
const reg = /<li>\s+<a>(.*?)<\/a>\s+<p>(.*?)<\/p>/ ;//需匹配换行
//console.log(reg.exec(str));
const reg2 = /<li>.*?<a>(.* ?)<\/a>.*?<p>(.*?)<\/p>/s;//单个匹配
//console.log(reg2.exec(str));
const reg3 = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;//全局匹配
let result;
let data = [];
while(result = reg3.exec(str)){
console.log(result);
data.push({title: result[1], time: result[2]});
}
console.log(data);
//[
// { title: '肖申克的救赎', time: '上映时间:1994-09-10' },
// { title: '阿甘正传', time: '上映时间:1994-07-06' }
//]
ES10 新特性
Object.fromEntries()
该方法用来创建一个对象 ,方法的参数接收一个二维数组或map
//二维数组
const result = Object.fromEntries([
['name', '王同学'],
['skills', 'java, js, mysql, css,vue']
]);
console.log(result); //{name: '王同学', skills: 'java, js, mysql, css,vue'}
//map
const m = new Map();
m.set('name','王同学');
const result2 = Object.fromEntries(m) //二维数组换为对象
console.log(result2); //{name: '王同学'}
//对象转为二维数组
const arr = Object.entries({
name: '王同学',
});
console.log(arr); //[['name', '王同学'], ]
//Object.fromEntries() 与 Object.entries()可看作逆运算
字符串方法扩展
String.prototype.trimStart()
String.prototype.trimEnd()
指定清除字符串左侧或右侧空白
let str = ' iloveyou '
console.log(str);
console.log(str.trimStart());
console.log(str.trimEnd());
数组方法扩展
Array.prototype.flat
将多维数组转为低维数组,flat默认值为1(深度为1 ),flat(2)表示转为1维数组,深度为2 。
const arr = [1,2,3,4,[5,6]];
console.log(arr.flat()); //(6) [1, 2, 3, 4, 5, 6]
const arr2 = [1,2,3,4,[5,6,[7,8,9]]];
//参数为深度是一个数字
console.log(arr2.flat(1)); //(7) [1, 2, 3, 4, 5, 6, [7, 8, 9]]
console.log(arr2.flat(2)); //(9) [1, 2, 3, 4, 5, 6, 7, 8, 9]
Array.prototype.flatMap
把Map的结果维度降低。
const arr = [1,2,3,4];
const result = arr.map(item => [item * 10]);
console.log(result); //(4) [[10], [20], [30], [40]]
const result2 = arr.flatMap(item => [item * 10]);
console.log(result2); //(4) [10, 20, 30, 40]
Symbol的扩展
Symbol.prototype.description
该属性用来获取Symbol字符串描述
let s = Symbol('王同学');
console.log(s.description); //王同学
ES11 新特性
私有属性
在面向对象的语法中是一个非常重要的特性,对属性的一种封装,外部无法直接对它进行操作。
class Person {
name;
//私有属性 #标识
#age;
#weight;
constructor(name, age, weight){
this.name = name;
this.#age = age;
this.#weight = weight;
}
}
const girl = new Person('小红', 18, '43kg');
console.log(girl.name); // 小红
console.log(gir1.#age); //Private field '#age' must be declared in an enclosing class
console.log(girl.#weight);
//可以在类的内部定义一个方法去访问
class Person2 {
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);
}
}
const girl2 = new Person2('小花', 20, '44kg');
girl2.intro();
//小花
//20
//44kg
Promise.allSettled()
该方法接收一个Promise数组,返回的结果也是一个Promise对象,不过返回的结果永远是成功的状态,成功的值是里面每一个Promise他们的状态和结果。
//声明两个promise对象
const p1 = new Promise( (resolve, reject) => {
setTimeout(()=>{
resolve('商品数据- 1');
},1000)
});
const p2 = new Promise( (resolve, reject) => {
setTimeout(()=>{
//resolve('商品数据- 2');
reject('出错啦')
},1000)
});
const result = Promise.allSettled([p1, p2]);
console.log(result); //result的值总为成功, 他的值为一个数组,数组中两个对象分别为p1与p2的状态与状态值
const result2 = Promise.all([p1, p2]); //Promise.all只有当全部成功才会返回成功
console.log(result2); //p1,p2有一个返回失败则result2失败,result2失败的值为里面返回失败的那个状态的值
他们都用来做批量异步任务的场景,据情况而使用
如果每个异步任务都想知道结果则用Promise.allSettled()
如果每个异步任务都要求成功才能继续往下执行则用Promise.all()
字符串扩展
String.prototype.matchAll()
这个方法用来得到正则批量匹配的结果,在正则匹配提取数据这一块是非常实用的
//提取字符中电影名称以及对应上映时间
let str = `
<ul>
<li>
<a>肖申克的救赎</a>
<p>上映时间:1994-09-10</p>
</li>
<li>
<a>阿甘正传</a>
<p>上映时间:1994-07-06</p>
</li>
</ul>`;
//声明正则
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/sg;
const result = str.matchAll(reg);
console.log(result); //RegExpStringIterator{}
//上面result返回的结果是一个可迭代对象,可用for...of遍历,也可用扩展运算符...展开
for (let v of result) {
//console.log(v);
const data = {title: v[1], time: v[2]}
console.log(data);
//{title: '肖申克的救赎', time: '上映时间:1994-09-10'}
//{title: '阿甘正传', time: '上映时间:1994-07-06'}
}
const arr2 = [...result];
console.log(arr); //[Array(3), Array(3)]
可选链操作符
?.
当在应对对象类型的参数时,且对象的深度比较深(层级高),有该操作符可以不用再做层级判断。
function main(config) {
const dbHost = config && config.db && config.db.host;//main未传参数将报错
console.log(dbHost); //192.168.1.100
const dbHost2 = config?.db?.host;//未传参数只会返回undefined,不报错,免去层层做判断的烦恼
console.log(dbHost2); //192.168.1.100
};
main({
db: {
host: '192.168.1.100',
username: 'root'
},
cache: {
host: '19.168.1.200',
username: 'admin'
},
});
动态import
ES6模块化是一个静态模块系统(在写import语句时往往是在入口文件,或者某一模块文件里面写大量import语句,导入很多模块。不能实现按需加载(懒加载)即用时加载,效率会有影响)
动态import加载,html:
<script src=="./js/app.js" type="module"></script>
app.js:
//import * as m1 from "./hello.js";
const btn = document..getElementById("btn");
btn.onclick = function() {
import('./hello.js').then(module => {
module.hello();
});
}
hello.js:
export function hello() {
alert('Hello');
}
BigInt类型
大整数,主要用来进行更大数值运算
let n = 520n;
console.log(n, typeof(n));
let n = 123;
console.log(BigInt(n)); //123n //只能传整数
//大数值运算
let max = Number.MAX_SAFE_INTEGER;
console.log(max); //9007199254740991
console.log(max + 1); //9007199254740992
console.log(max + 2); //9007199254740992 //无法更大
console.log(BigInt(max)); //9007199254740991n
//BigInt只能与BigInt进行运算
console.log(BigInt(max) + BigInt(1)); //9007199254740992n
console.log(BigInt(max) + BigInt(2)); //9007199254740993n
绝对全局对象(globalThis)
始终指向全局对象,无论执行环境是什么。如果想对全局对象进行操作,可以忽略环境用globalThis,因为它始终指向全局对象的。
console.log(globalThis);