ES6新特性速览
文章目录
ECMAScript,是由ECMA国际化标准组织制定的一项脚本语言的标准化规范,而平时经常编写的JavaScript,是ECMAScript的一种实现,所以ES新特性其实指的就是 JavaScript的新特性
ES6是一个泛指,2015及后续的版本
年份 | 版本 |
---|---|
2015年6月 | ES2015 |
2016年6月 | ES2016 |
2017年6月 | ES2017 |
2018年6月 | ES2018 |
2019年6月 | ES2019 |
2020年6月 | ES2020 |
2021年6月 | ES2021 |
新特性:
- 语法简洁
- 框架开发应用
什么是ECMA?
Ecma International(正式名称是 European Computer Manufacturers Association,欧洲计算机制造商协会)是一个开发计算机硬件、通信和程序语言标准的非盈利组织。
在网络上该组织因维护 JavaScript 语言的核心规范 the ECMA-262 specification即 ECMAScript而为人所知。
ECMA的第39号技术专家委员会(Technical Committee 39,简称TC39)负责制订ECMAScript标准,成员包括Microsoft、Mozilla、Google等大公司。TC39的总体考虑是,ES5与ES3基本保持兼容,较大的语法修正和新功能加入,将由JavaScript.next完成。
ES6是继ES5之后的一次主要改进,语言规范由ES5.1时代的245页扩充至600页。具有里程碑意义。ES6增添了许多必要的特性,例如:模块和类,以及一些实用特性,例如Map、Set、Promise、生成器(Generator)等。尽管ES6做了大量的更新,但是它依旧完全向后兼容以前的版本。 其目标是将 JavaScript打造为企业级开发语言,使他能够开发复杂的大型应用程序。
let
特点
-
使用let声明的变量具有块级作用域
//简单的说就是{}产生的作用域 if(true){ let a = 10; console.log(a);//10 } console.log(a);// a is not defined //防止循环变量变成全局变量 for(var i=0;i<2;i++){ } console.log(i);//2 for(let j=0;j<2;j++){ } console.log(j);//j is not defined
-
变量不能重复声明
-
不存在变量提升
console.log(a);//Cannot access 'a' before initialization再初始化之前无法访问a let a = 100; console.log(b);//undefined var b = 100;
-
不影响作用域链
-
具有暂时性死区
var num = 10;
if(true){
//不会向上一级作用域查找num,因为本级作用域中用let声明了num
console.log(num);//Cannot access 'num' before initialization
let num = 20;
}
const
- 用于声明常量(存储数据对应的内存地址不可更改)
- 具有块级作用域
- 一定要赋初值
- 一般常量大写
- 对数组和对象的修改不算对常量的修改,不会报错
const PI = Math.PI;
console.log(PI);
// 对数组和对象元素的修改不算对常量的修改,不会报错
const arr = [1,2,3,4,5,6];
arr.push(7);
console.log(arr)
let、const、var的区别
var | let | const |
---|---|---|
函数作用域 | 块级作用域 | 块级作用域 |
变量提升 | 不存在变量提升 | 不存在变量提升 |
值可以更改 | 值可以更改 | 值不可以更改 |
函数的定义、数学公式中一些恒定的值、 |
变量解构赋值
解构:
- 分解数据结构
负值:
- 为变量赋值
ES6允许按照一一对应的方式从数组或者对象中提取值,并对变量进行赋值
这被称为解构赋值,可以更加简便的从数组或者对象中提取值。
1.数组的解构
//数组结构允许按照一一对应的关系从数组中提取值,然后赋值给变量
let [a,b,c] = [1,2,3];
console.log(a);//1
console.log(b);//2
console.log(c);//3
//如果不是一一对应的话
let [a,b,c,d] = [1,2,3];
console.log(a);//1
console.log(b);//2
console.log(c);//3
console.log(d);//undefined
2.对象的解构
对象结构实际上是属性匹配,变量的名字匹配对象中属性的名字
如果匹配成功就将对象中属性的值赋给变量
//对象结构允许使用变量的名字匹配对象的属性,匹配成功就将对象的属性赋值给变量
let people = {name:"yxj",age:18,say:function(){console.log("i can");}};
let {name,age,say} = people;
console.log(name);//yxj
console.log(age);//18
say();//i can
//可以使用别名
let {name:myName,age:myAge,say:MySay} = people;
console.log(myName);//yxj
console.log(myAge);//18
MySay();//i can
模板字符串
- 内容中可以直接换行
- 直接进行变量拼接${}
let str = `
<ul>
<li>li1</li>
<li>li2</li>
<li>li3</li>
</ul>`;
console.log(`我输出的内容是:${str}`);
简化对象写法
ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样写更简洁
let name = '尚硅谷';
let change = function(){
console.log("我可以改变!");
}
const school ={
// 加入name 和 change属性
name,//等效于name:name,change:change
change,
// improve:function (){
// console.log('我们可以提高你的技能');
// },
// 简化写法
improve(){
console.log('我的技能得到提高了!');
}
}
console.log(school);
箭头函数
在ES6里允许使用()=>{}来定义函数
特性:
1.this是静态的(它不绑定this),始终是指向函数声明时所在作用域下的this的值,无法使用call、apply等函数方法改变
const obj = {name:"yxj"}
function fn(){
console.log(this);//谁调用fn this就是谁
return ()=>{
console.log(this);//应该指向定义它的区域的this 谁调用fn this就是谁
}
}
fn.call();//window
const newFn = fn.call(obj);
newFn();//obj
2.不能作为构造函数实例化对象
3.不能使用 arguments
4.形参有且只有一个的时候 可以简写
let add = a=> a+a;
console.log(add(2));//4
5.允许将一个不定数量的参数表示为一个数组
function sum(first,...second){
console.log(first);//1
console.log(second);//他是一个数组,可以使用数组的方法[2, 3, 4]
}
sum(1,2,3,4);
测试this指向
//var age = 100;
var obj = {
let:20,
//obj是一个对象,不能产生作用域
say:()=>{
//箭头函数没有自己的this它指向的是定义他的区域的this
console.log(this.age);
}
}
// obj是一个对象,不能产生作用域,相当于调用了window.age
obj.say();//undefined
var obj2 ={
age:16,
say:function(){
console.log(this.age);
},
}
obj2.say();//16
函数参数的默认值设置
ES6允许给函数参数设置初始值
- 形参初始值
// 一般将有默认值的参数放后面
function add(a,b,c=10){
//
return a+b+c;
}
let result = add(1,2);
console.log(result);
//
function connect(option){
// 每次都要写option
let host = option.host;
let username = option.username;
}
// 与解构赋值结合简化写法
function connect2(host='http://127.0.0.1',username,password ,port){
console.log(host);
console.log(username);
console.log(password);
console.log(port);
}
connect({
host:'localhost',
username:'root',
password:'123456',
port:3306
});
rest(剩余)参数
ES6引入了rest参数,用于获取函数的实参,用来代替arguments获取实参
当函数实参个数大于形参个数时,可以将剩余的实参放到一个数组(是真正的数组,不是数伪组)中
function sum(first,...arg){
console.log(first);//10
console.log(arg);//[20,30]
console.log(arg instanceof Array);//true
}
sum(10,20,30);
扩展运算符
. . .
拆分数组
扩展运算符能将数组转换为逗号分割的参数序列后,将参数序列又放在了console.log方法中,参数序列中的逗号会被当成方法的参数分隔符
let array = [1,2,3];
// 为什么输出结果没有逗号?
console.log(...array);//1 2 3
// 因为参数序列中的逗号会被当成方法的参数分隔符
console.log(1,2,3);//1 2 3
扩展运算符的应用
- 合并数组
//方法1
let arr1 = [1,2,3];
let arr2 = [4,5,6];
let arr3 = [...arr1,...arr2];// [ 1, 2, 3, 4, 5, 6 ]
//方法2
arr1.push(...arr2);
console.log(arr1);//[ 1, 2, 3, 4, 5, 6 ]
- 将伪数组转换为真正的数组
let oDivs = document.querySelectorAll("div");
console.log( oDivs instanceof Array);//false
oDivs = [...oDivs];
console.log( oDivs instanceof Array);//true
- 实现数组的浅拷贝
补充:
Array.from()
方法也可以将伪数组或可遍历的对象转为真正的数组
该方法可以接受两个参数
-
要转换的对象(伪数组)
let arryLike = { "0":"a", "1":"b", "2":"c", length:3//必须要有length属性 } let arr = Array.from(arryLike); console.log(arr);//[ "a", "b", "c" ]
-
一个函数,对数组中的元素进行加工处理,数组中有多少个元素,函数就会执行多少次,函数有一个形参,代表要处理的值,只需要将要处理的结果返回即可
let arryLike = {
"0":"1",
"1":"2",
"2":"c",
length:3
}
let newArr = Array.from(arryLike,(item)=>{
return item*2;
});
console.log(newArr);//[ 2, 4, NaN ]
Symbol
ES6引入了一种新的原始(基本)数据类型,表示独一无二的值。他是JavaScript语言的第七种数据类型,是一种类似于字符串的数据类型。
Symbol的特点
- Symbol的值是唯一的,用来解决命名冲突的问题
- Symbol的值不能与其他数据进行运算
- Symbol定义的对象属性不能使用for…in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
创建一个 Symbol
-
方法1
let s = Symbol(); console.log(s,typeof s);//Symbol() symbol
-
方法2
// yxj 是一个描述字符串,描述它是为谁设置的,作用相当于一个注释 //此时s1,s2是一个函数 let s1 = Symbol("yxj"); let s2 = Symbol("yxj"); console.log(s1 === s2);//false
-
方法3
//此时s3是一个对象 let s3 = Symbol.for("yxj"); let s4 = Symbol.for("yxj"); console.log(s3,typeof s3);//Symbol("yxj") symbol console.log(s3 === s4);//true
Symbol的作用
-
给对象和属性添加方法
//向game对象添加up、down方法 let game ={ up:function(){ console.log("我是原生的up"); }, down:function(){ console.log("我是原生的down"); }, // 原生的添加方式 [Symbol("say")]:function(){ console.log("我是原生的Symbol"); } } //先声明一个对象 let methods= { up:Symbol(), down:Symbol() } //通过该方法向原来的对象里添加方法,不会破环对象原有的属性 game[methods.up] = function(){ console.log("我是新添加的up"); } game[methods.down] = function(){ console.log("我是新添加down"); } console.log(game); /** * down: function down() up: function up() Symbol(say): function say() Symbol(): function methods.up() Symbol(): function methods.down() */
Symbol的内置属性
-
除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用方法。
-
其实就是Symbol的属性
-
它用来做对象的属性
-
通过对这些属性的设置来改变对象在特定场景下的表现结果
Symbol的内置值 | 描述 |
---|---|
Symbol.hashInstance(这个整体将作为对象的属性) | 当其他对象使用instanceof运算符判断该对象的实例时,会调用这个方法 |
Symbol.isConcatSpreadable | 对象的Symbol.isConcatSpreadable属性等于的是一个布尔值,表示该对象用于用于Array.prototype.concat()时,是否可以展开 |
Symbol.unscopables | 指定了使用with关键字时,哪些属性会被with环境排除 |
Symbol.match | 当执行Symbol.match(Object)时如果属性存在会调用它,返回该方法的返回值 |
Symbol.repalce | 当该对象被Symbol.repalce(Object)方法调用时,会返回该方法的返回值 |
Symbol.search | 当该对象被Symbol.search(Object)方法调用时,会返回该方法的返回值 |
Symbol.spilt | 当该对象被Symbol.spilt(Object)方法调用时,会返回该方法的返回值 |
Symbol.iterator | 对象进行for…of循环遍历,会调用Symbol.iterator方法,返回该对象的默认遍历器 |
Symbol.toPrimitive | 该对象转为原始类型的值时,会调用这个方法,返回该方法对应的原始类型值 |
Symbol.species | 创建衍生对象时,会使用该属性 |
- Symbol.hashInstance
- 可以自己控制类型检测
class Person{
// 该方法和其他方法不一样,有自动执行的场景,
static [Symbol.hasInstance](param){//可以把o这个对象传递进来
console.log(param);//{}
console.log("我被用来检测类型了");
// 可以自动控制类型检测
return true;
}
}
let o ={};
console.log(o instanceof Person);//true
-
Symbol.isConcatSpreadable
当使用concat方法对数组进行合并的时候,可以通过Symbol.isConcatSpreadable属性,设置合并数组的值能不能展开
const arr = [1,2,3];
const arr2 =[4,5,6];
console.log(arr.concat(arr2));//[ 1, 2, 3, 4, 5, 6 ]
arr2[Symbol.isConcatSpreadable] = false;//用于控制值是否可以展开
console.log(arr.concat(arr2));//[ 1, 2, 3, [4,5,6] ]
迭代器
迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作。
-
再js里Iterator接口指的是对象里的一个属性,该属性的名字是Symbol.Iterator
-
ES6创造了一种新的遍历命令for…of循环,Iterator接口主要提供for…of消费
-
原生具备Iterator接口的数据结构(可用for…of遍历)
- Array
- arguments
- Set
- Map
- String
- TypedArray
- NodeList
-
工作原理(为什么Symbol.Iterator能够实现循环遍历)
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的next方法(返回对象身上的一个方法),指针会自动指向数据结构的第一个成员,将结果返回给我们
- 接下来不断调用next方法(每次调用都会返回新的值),指针一直往后移动,直到指向最 后一个成员
- 每次返回时都会返回一个对象,该对象包含了value和done属性,done是一个是否完成的状态(done:)done为真时返回undefined,并结束循环
const name =["yxj","lqy","son","girl"];
// 使用for...of遍历,保存的是键值
for(let v of name){
// console.log(v);//yxj lqy son girl
}
//使用for...of遍历,保存的是键名
for(let n in name){
console.log(n);//0 1 2 3
}
// 为什么数组可以使用for...of遍历
//因为数组的原型上有Symbol(Symbol.iterator)这个属性: function values()
console.log(name);//Symbol(Symbol.iterator): function values()
迭代器的应用
-
需要自定义遍历数据时,就要用到迭代器
const obj = { name:"yxj", game:[ "a", "b", "c", "d" ], // 使用迭代器自定义遍历数据 [Symbol.iterator](){ // 定义索引变量 let index = 0; let that = this; return{ next:function(){ if(index<that.game.length){ const result = {value:that.game[index],done:false}; // 下标自增 index++; // 返回结果 return result; }else{ return {value:undefined,done:true}; } } } } } //遍历出game数组的值 obj.game.forEach((item)=>{//但是不符合面向对象的思想 // console.log(item);//a b c d }) // 要使用for of遍历,且返回值是数组的成员 for(let v of obj){ console.log(v);//a b c d }
生成器
生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同
-
之前的异步编程(node fs、ajax、mongodb)的实现:纯回调函数
-
生成器函数的声明和执行方式比较特殊
function * generator(){
console.log("我是生成器函数");
}
let iterator = generator();
console.log(iterator);//Generator { }
//是一个迭代器对象,可以使用next方法
iterator.next();
-
生成器函数的yield语句
算是函数代码的分隔符
function * generator(){
console.log("代码块1");
yield '我是函数代码的分隔符1';
console.log("代码块2");
yield '我是函数代码的分隔符2';
console.log("代码块3");
yield '我是函数代码的分隔符3';
console.log("代码块4");
}
- yield语句,由next方法控制执行
//三个分隔符产生四块代码
function * generator(){
console.log("代码块1");
yield '我是函数代码的分隔符1';
console.log("代码块2");
yield '我是函数代码的分隔符2';
console.log("代码块3");
yield '我是函数代码的分隔符3';
console.log("代码块4");
}
let iterator = generator();
// iterator.next();//代码块1
// iterator.next();//代码块2
// iterator.next();//代码块3
生成器函数的参数
- 执行获取迭代器对象
function * gen(){
yield 111;
yield 222;
yield 333;
}
// 执行获取迭代器对象
let iterator = gen();
console.log(iterator.next());//Object { value: 111, done: false }
console.log(iterator.next());//Object { value: 222, done: false }
console.log(iterator.next());//Object { value: 333, done: false }
console.log(iterator.next());//Object { value: undefined, done: true }
- next方法可以传入实参
function * gen(arg){
console.log(arg);
yield 111;
yield 222;
yield 333;
}
// 执行获取迭代器对象,并传入实参
let iterator = gen("yxj");
// 第一次调用next方法输出的是yxj
console.log(iterator.next());// yxj { value: 111, done: false }
- 该实参就是yield语句整个的返回结果
function * gen(arg){
console.log(arg);
let one = yield 111;
console.log(one);
yield 222;
yield 333;
}
let iterator = gen("yxj");
console.log(iterator.next());//yxj tmp.html:19 {value: 111, done: false}
//第二次调用next方法,传入BBB实参,将作为第一个yield语句的整体返回结果
console.log(iterator.next('BBB'));//BBB tmp.html:21 {value: 222, done: false}
生成器函数的实例
js是异步单线程的,很多操作都是异步的:文件操作(读取)、网络操作(ajax、request)、数据库操作。
- 回调地狱的例子(一秒钟输出一个语句)定时器实现异步任务
setTimeout(()=>{
console.log("1s");
setTimeout(()=>{
console.log("2s");
setTimeout(()=>{
console.log("3s");
},1000)
},1000)
},1000)
//如果异步任务特别多的话....
- 生成器函数在异步编程的实现(异步任务)
function one(){
setTimeout(()=>{
console.log(1);
interator.next();
},1000)
}
function two(){
setTimeout(()=>{
console.log(2);
interator.next();
},1000)
}
function three(){
setTimeout(()=>{
console.log(3);
interator.next();
},1000)
}
// 把异步函数的调用放在了生成器函数里
function * gen(){
yield one();
yield two();
yield three();
}
//调用生成器函数
let interator = gen();
interator.next();//1 2 3
生成器函数模拟数据获取
// 用户数据获取 订单数据 商品数据
function getUsers(){
setTimeout(()=>{
let data = '用户数据';
// 调用next方法,并将数据传入
iterator.next(data);
},1000)
}
function getOders(){
setTimeout(()=>{
let data = '订单数据';
iterator.next(data);
},1000)
}
function getGoods(){
setTimeout(()=>{
let data = '商品数据';
iterator.next(data);
},1000)
}
// 按照用户数据、 订单数据、 商品数据、顺序依次获取
function * gen(){
// 看上去跟同步代码一样,实际上运行时是异步的
let users = yield getUsers();
console.log(users);
let orders = yield getOders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
//调用生成器函数
let iterator = gen();
iterator.next();//间隔1s输出 用户数据 订单数据 商品数据
Promise
- Promise是ES6引入的异步编程的新解决方案(主要是解决回调地狱的问题)。语法上Promise是一个构造函数,用来封装异步操作并可以获取其成功或者失败的回调结果。
- 没有生成器、Promise之前处理异步编程时都是层层回调嵌套的,行成回调地狱的局面,不好看也很难维护。
- 异步编程主要是指的是一些IO的代码、文件IO、数据库IO、网络请求
- Promise的构造函数:Promise(excutor){}
- Promise:prototype.then方法
- Promise.prototype.catch方法
- Promise对象有三个状态
- 初始化
- 成功(通过resolve方法改变)
- 失败(通过reject方法该改变)
实例化Promise对象
//该对象接受一个参数,参数类型为函数,该函数有两个参数
const p = new Promise(function(resolve,reject){
//封装一个异步操作
setTimeout(()=>{
//假设请求到了数据
let data = "用户数据";
},1000)
});
- 请求到数据以后可以调用resolve、reject函数来改变Promise对象的状态
const p = new Promise(function(resolve,reject){
setTimeout(()=>{
let data = '数据库中的数据';
resolve(data);//当调用resolve函数后Promise对象的状态就变成了成功
},1000)
});
-
成功或者失败都可以调用Promise对象的then方法
then接收两个参数,两个参数都是函数类型
每个函数都有一个形参
成功的形参为value,失败的形参为reason
p.then(function(value){
//成功时才会调用
console.log(value);
},function(reason){
//失败时才会调用该方法
console.error(reason);
})
- 数据读取错误时
const p = new Promise(function(resolve,reject){
setTimeout(()=>{
let data = '数据库中的数据';
let err = "数据读取错误"
reject(err);//此时状态为失败
},1000)
});
p.then(function(value){
console.log(value);
},function(reason){
//此时会调用该方法
console.error(reason);
})
通过以上方式可以将异步任务封装在Promise对象里
Promise读取文件内容
//1. 引入fs模块
const fs = require('fs');
//2. 调用方法读取文件
// fs.readFile('./为学.md',(err,data)=>{
// // 如果失败则抛出错误
// if(err) throw err;
// // 如果没有出错,则输出内容
// console.log(data.toString());
// })
// 使用promise封装
const p = new Promise(function(resole,reject){
fs.readFile('./为学.md',(err,data)=>{
if(err){
reject(err);
}else if(data){
resole(data);
}
})
});
p.then(function(value){
console.log(value.toString());
},function(reason){
console.log("读取失败");
})
//node 文件名 运行
Promise封装AJAX
- 原生的Ajax
const xhr = new XMLHttpRequest();
// 初始化
xhr.open("GET","https://api.apiopen.top/getJoke");
//发送请求
xhr.send();
//处理结果
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300){
console.log(xhr.response);
}else{
console.error(xhr.status);
}
}
}
- Promise封装的AJAX
const p = new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest();
// 初始化
xhr.open("GET","https://api.apiopen.top/getJoke");
// 发送请求
xhr.send();
//根据返回结果判断Promise状态
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300){
// 表示成功
resolve(xhr.response);
// console.log(xhr.response);
}else{
// 成功
reject(xhr.status);
console.error(xhr.status);
}
}
}
});
// 区别在于处理成功和失败的方式不一样了,原生Ajax是在回调函数里执行成功或者失败的结果
// 现在是在异步任务的后边通过then方法来指定回调,不会产生对调地狱的问题
p.then(function(value){
console.log(value);
},function(reason){
console.error(reason);
})
then方法的返回结果
//创建promise对象
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('用户数据');
},1000)
})
const result = p.then(value=>{
console.log(value);
// 1.返回非promise类型的属性
return 123;//<state>: "fulfilled" <value>: 123
},reason=>{
console.log(reason);
})
//查看then方法的返回结果
//也是一个promise对象,他的状态由回调函数的执行结果来决定,
console.log(result);// Promise { <state>: "pending" }
then方法的链式调用
//因为then方法可以返回一个promise对象,所以他是可以链式调用的
p.then(value=>{
},reason=>{
}).then(value=>{
}).then(value=>{
}).then(value=>{
})
Promise.catch方法
catch方法是then的语法糖,可以只指定一个回调
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject("出错了");
},1000)
})
p.catch(function(reason){
console.error(reason);
})
Set
ES6提供了新的数据结构Set(集合),它类似数组,但成员的值是唯一的,集合实现了iterator接口,所以可以使用[扩展运算符]和for…of 进行遍历,
集合的属性和方法:
- size 返回集合的元素个数
- add 增加一个新元素,返回当前集合
- delete 删除元素,返回boolean值
- has 检测集合中是否包含某个元素,返回boolean值
声明Set
let s = new Set();
console.log(s);//Set []
console.log(typeof s);//object
操作Set
let s = new Set([1,2,3,4,5,6]);
// 查看元素个数
console.log(s.size);//6
//添加新元素
s.add(7);
console.log(s);//Set(7) [ 1, 2, 3, 4, 5, 6, 7 ]
// 删除元素
s.delete(2);
console.log(s);//[ 1, 3, 4, 5, 6, 7 ]
//检测元素
console.log( s.has(3));//true
// 遍历
for(let a of s){
console.log(a);//1 3 4 5 6 7
}
// 清空集合
s.clear();
console.log(s);//set []
Set实现数组去重
let arr = [2,3,5,4,1,5,2,3,6,9,1,5,2];
let result = [...new Set(arr)];
console.log(result);//[ 2, 3, 5, 4, 1, 6, 9 ]
Set获取交集
let arr =[14,2,1,3,2,1,3,2,8];
let arr2 = [3,14,5,6,9,11];
// 求交集之前先去重
let result = [...new Set(arr)];
let result2 = new Set(arr2);
result = result.filter((item)=>{
if(result2.has(item)){
return true;
}else{
return false;
}
})
console.log(result);//14 ,3
let result = [...new Set(arr)].filter((item)=>{
return new Set(arr2).has(item)
});
console.log(result);//[14,3]
Set获取并集
let arr =[14,2,1,3,2];
let arr2 = [3,14,5,6,9];
let union = [...new Set([...arr,...arr2])];
console.log(union);//[ 14, 2, 1, 3, 5, 6, 9 ]
Set获取的差集
// 差集其实就是交集取反
let A = [1,2,3];
let B = [3,4,5,6];
// A与B的差集(A-B),A里有但是B没有
let diff = [...new Set(A)].filter((item)=>{
return !(new Set(B).has(item));
})
console.log(diff);//[1,2]
// B与A的差集(B-A)
let diff2 = [...new Set(B)].filter((item)=>{
return !(new Set(A).has(item));
})
console.log(diff2);//[1,2]
Map
ES6提供了Map数据结构。他类似对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map也实现了iterator 接口,所以可以使用 扩展运算符 和 for…of 进行遍历。
Map的属性和方法
- size 返回Map的元素个数
- Set 增加一个新元素,返回当前Map
- get 返回键名对象的键值
- has 检测Map中是否包含某个元素,返回Boolean值
- clear 清空集合,返回undefined
声明Map
let m = new Map();
console.log(m);//Map(0)
- 添加元素
let m = new Map();
m.set('name','yxj');
console.log(m);//Map { name → "yxj" }
m.set('change',function(){
console.log("change");
});
let key = {
address:'xian'
}
m.set(key,['王道村','马道村','禄道村']);
console.log(m);
/*
size: 3
<entries>
0: name → "yxj"
1: change → function ()
2: Object { address: "xian" } → Array(3) [ "王道村", "马道村", "禄道村" ]
*/
- 删除元素
let m = new Map();
m.set('name','yxj');
console.log(m);//Map(0)
- 获取
let m = new Map();
m.set('name','yxj');
m.set('age',18);
console.log(m.get('name'));//yxj
- 清空
let m = new Map();
m.set('name','yxj');
m.set('age',18);
m.clear();
console.log(m);//Map(0)
- for…of遍历
let m = new Map();
m.set('name','yxj');
m.set('age',18);
for(let v of m){
console.log(v);
}
/*
结果是一个数组,第一项是键,第二项是值
Array [ "name", "yxj" ]
Array [ "age", 18 ]
*/
class 类
ES6提供了更接近传统语言的写法,引入了Class(类)的概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作是一个语法糖,他的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更清晰、更像面向对象编程。
class声明类
class People{
//构造方法
//函数只要new生成实例时,就会自动调用这个方法;
constructor(unamae,age){
//
this.unamae = unamae;
this.age = age;
}
say(){
console.log("666");
}
}
var ldh = new People("刘德华",18);
console.log(ldh.age);//18
构造函数的静态成员
- 在构造函数本身上添加的成员
- 不能通过实例成员来访问
function Star(name,age){}
//静态成员,
Star.sex = '男';
console.log(yxj.name);//undefined
console.log(Star.name);//Star
static(类的静态成员)
- 对于static标识的属性,属于类;不属于对象
- 不能通过实例对象访问静态属性
class Phone{
// 静态属性
static name = 'Nova6';
static price(){
console.log("¥2500");
}
}
let huawei = new Phone();
console.log(huawei.name);//undefined
console.log(Phone.name);//Nova6
构造函数的实例成员
function Star(name,age){
//实例成员
this.name = name;
this.age = age;
}
//通过构造函数实例化的对象
var ldh = new Star('刘德华',18);
//通过实例化的对象来访问实例成员
console.log(ldh.name);//刘德华
//不可以通过构造函数来访问实例成员
console.log(Star.name);//Star
类的继承
- extends 用于实现类与类之间的继承
- super 用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数
class Phone{
constructor(name,price){
this.name = name;
this.price = price;
}
// 父类的方法
play(){
console.log("我可以玩游戏");
}
}
class SmartPhone extends Phone{
constructor(name,price,color){
//super必须放在this前
// super就是父类的constructor方法
super(name,price);
this.color = color;
}
photo(){
console.log("拍照");
}
}
const oppo = new SmartPhone('Find x5','¥4799','紫色');
console.log(oppo);//Object { name: "Find x5", price: "¥4799", color: "紫色" }
子类对父类方法的重写
- 当子类的方法名与父类的方法名一样时就会重写父类的方法
- 子类不能直接调用父类的同名方法(在普通的成员方法里不能出现super)
class Phone{
constructor(name,price){
this.name = name;
this.price = price;
}
// 父类的方法
play(){
console.log("我可以玩游戏");
}
}
class SmartPhone extends Phone{
constructor(name,price,color){
// super就是父类的constructor方法
super(name,price);
this.color = color;
}
photo(){
console.log("拍照");
}
play(){
console.log("我可以玩大型游戏");
}
}
const oppo = new SmartPhone('Find x5','¥4799','紫色');
oppo.play();//我可以玩大型游戏
get和set
用于对对象的属性进行方法的绑定
可以添加一个get方法,当对某一个属性进行获取时,会执行get对应的函数
当对某一个属性进行设置时会执行set对应的函数
- get 通常用于对对象的动态属性进行封装,比如求整数、平均数时
- set 通常用于控制和判断 例如判断值的类型是否合法
class Phone{
constructor(price){
this.price = price;
}
//对price属性读取时就会调用
get price(){
console.log("价格属性被读取了");
return '新价格'
}
//对price属性修改时就会调用
//他必须接收一个参数
set price(newVal){
console.log("价格被修改了");
}
}
//实例化一个对象
let s = new Phone("2999");
console.log(s.price);
/*
价格被修改了
价格属性被读取了
新价格
*/
数值扩展
-
Number.EPSION 是JS表示的最小精度
主要用于用于浮点数运算
let m =Number.EPSILON; console.log(m);//2.220446049250313e-16 console.log(0.1+0.2 === 0.3);//false
function equal(a,b){ //误差精度小于Number.EPSILON时为真 if(Math.abs(a-b) < Number.EPSILON){ return true; }else{ return false; } } console.log(equal(0.1 + 0.2,0.3));//true
-
二进制八进制十六进制
//二进制 ob let b = 0b0010; console.log(b);//10 // 八进制 0o let o = 0o777; console.log(o);511 // 十六进制 let x = 0xff; console.log(x);//255
-
Number.isFinite()
检测一个数是否为有限数
let a = 100; console.log(Number.isFinite(a));//true
-
Number.isNaN()
-
检测一个数是否为NaN
let g = 100; console.log(Number.isNaN(g));//false
-
Number.parseInt()
-
Number.ParsFloat()
-
Number.Integer()
判断一个数是否为整数
-
Math.trunc()
去掉数值的小数部分
-
Math.sign 判断一个数到底为正数、负数、还是0
对象方法的扩展
-
Object.is判断两个值是否完全相等
console.log(Object.is(120,120));//true console.log(Object.is(NaN,NaN));//true console.log(NaN === NaN);//false
-
Object.assign对象的合并
const config1 = { host:'locahost', port:3306, name:'root', pass:'root', test:'test' }; const config2 = { host:'http://www.yxj.com', port:8080, name:'yxj', pass:'666' }; //后边的对象会把前面的对象覆盖(重名情况下才会覆盖) let obj = Object.assign(config1,config2); console.log(obj);//Object { host: "http://www.yxj.com", port: 8080, name: "yxj", pass: "666" }
-
Object.setPrototypeOf
设置原型对象
const school ={ naem:'xust' } const cities = { area:['临潼','骊山','雁塔'] } Object.setPrototypeOf(school,cities); console.log(school);//school的原型对象指向了cities对象
-
Object.getPrototypeOf
获取原型对象
const school ={ naem:'xust' } const cities = { area:['临潼','骊山','雁塔'] } console.log( Object.getPrototypeOf(school));//Object { … }
模块化
模块化是将一个大的文件拆分成很多小的文件,然后将小的文件组合起来。这些小文件就是模块
模块化的好处
- 防止命名冲突
- 代码复用
- 高维护性
模块化规范产品
es6之前的模块化规范
- commonJS => Node.js、Browserify
- AMD => requireJS
- CMD => seaJS
ES6模块化语法
模块功能主要由两个命令构成:export和import
- export 命令用于规定模块的对外接口
- import 命令用于输入其他模块提供的功能
模块暴露语法
-
分别暴露
export let obj ={ name:'yxj', age:18 } export let school = 'xust';
//通用导入my.js模块内容
import * as my from './my.js';
console.log(my);
/*
obj: Object { name: "yxj", age: 18 }
school: "xust"
Symbol(Symbol.toStringTag): "Module"
*/
-
统一暴露
let obj ={ name:'yxj', age:18 } let school = 'xust'; export{obj,school}
import * as my from './my.js';
console.log(my);
-
默认暴露
export default{ obj :{ name:'yxj', age:18 }, school:'xust' }
import * as my from './my.js'; console.log(my); //调用方法的话必须加一个default my.default.log();//666
模块引入语法
-
通用导入方法
import * as my from './my.js'; console.log(my);
-
解构赋值的方式 对分别暴露使用
import {obj} from './my.js' console.log(my);
-
使用别名引入 对统一暴露使用
import {obj as obj1} from './my.js'; console.log(obj1);
-
引入默认暴露
import {default as school} from "./my.js" console.log(school);
-
简便引入形式(只能针对默认暴露)
import obj from "./my.js"; console.log(obj);
浏览器使用ES6模块化的另一种方式
-
新建一个app.js文件。他负责引入其他模块的js文件,作为所有js代码的入口
import * as obj from "./my.js"; import * as obj2 from "./my2.js"; console.log(obj); console.log(obj2);
-
在index页面引入app.js文件
<script src="./app.js" type="module"></script>
ES6模块化在项目中的使用方式
Bable
一个JavaScript编译器,能够将ES新特性的语法转化为浏览器能够识别的语法
安装工具
-
babel-cli
babel命令行的工具
-
bababel-preset-env
是一个预设包,能够把ES最新的特性转换为ES5的语法
-
browserify(webpack)
一个打包工具
安装工具命令
npm init --yes
npm i babel-cli babel-preset-env browserify -D
对指定文件夹的文件代码进行语法转换
npx babel js -d dist/js --presets=babel-preset-env
- npx 表示局部安装,如果是全局安装的话就直接使用babel
- babel后的第一个参数表示源文件的目录
- -d后边是 指定转换完成后存储的路径
- 最后加上 --presets=babel-preset-env
生成对应的文件后还需要进一步打包
npx browserify dist/js/app.js -o dist/pack.js
这样再引入就可以了
<script src="./pack.js"></script>
ES6模块化与npm包的使用
通过jQuery包对index.html的背景颜色做一个修改
-
安装jQuery的包
npm i jquery
-
在入口文件(不是打包之后的app.js) 导入npm包
import $ from 'jquery'; //相当于 const $ = require('juety') $('body').css('background','pink');
-
然后重新打包
npx babel js -d dist/js --presets=babel-preset-env npx browserify dist/js/app.js -o dist/pack.js