1、ES6
ES6是什么?
ECMAScript6.0(ES6)是JavaScript语言下一代的标准,已经在2015年6月正式发布。它的目标是使得JavaScript语言可以编写复杂的大型应用。
ECMAScript与JavaScript区别?
ECMAScript是一种标准规范,JavaScript是ECMAScript的具体实现。
ES6与ECMAScript2015的关系?
ES6在2015年6月份正式发布,正式名称就是《ECMAScript2015标准》简称ES2015。
ES6 == ECMAScript2015
以后在每年的6月份将会发布一个新版本,将会已年份命名为版本号。(ES2016 ES2017)
总结:
ES6是一个历史名词,也是一个泛指,含义是5.1版本之后的JavaScript的下一代标准,涵盖了ES2015、
ES2016、ES2017等。而ES2015则是正式名称,特指该年发布的正式版本的语言标准。
2、Babel
Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,
以便能够运行在当前和旧版本的浏览器或其他环境中。
babel无法转义一些API
babel默认只转义新标准引入的语法,比如ES6的箭头函数,不转移新的API,比如:iterator、Set、Map、Promise等全局对象。
解决方法: 使用babel-polyfill 为当前环境提供一个垫片.
npm install --save babel-polyfill
3、let const var
let const 是ES6中提出的变量声明
let与const的特点一样。只不过let是用来声明变量的而const是用来声明常量的。
var存在的问题:
1.全局作用域
2.存在变量提升
3.可重复声明
let const特点:
1.局部作用域
2.不可重复声明(同一作用域)
3.没有变量提升
const特性:
1.使用const必须赋值。
2.如果值是基本数据类型,则值不能改动。
3.如果const声明的是对象类型,其保存的是对象的地址,const只能保证地址不发生改变,
却不能保证该对象的值不发生改变。所以使用const时需要注意数值的类型。
onConst() {
//const a; //报错
const a = 10;
a = 100; //值不可改动
}
问题代码案列:
//使用var 定义的变量是全局变量,预期我们想要的是局部声明
for (var i = 0; i < 10; i++) {}
console.log(i); //10
//重复声明 变量提升问题
onStatement() {
/*
使用var定义 此处只会抛出undefined 而不是我们预期的报错
JS实际上将代码声明提到了前面
var b;
console.log(b);
b = 5;
*/
console.log(b); //undefined
var b = 5;
var b = 6; //重复声明不报错
},
//使用let解决重复声明 变量提升问题
onStatementLet() {
/** 无bable引入便会抛出异常
Cannot access 'c' before initialization
否则你将会看到和var等同的效果,bable会将es6的语法转换低级语法。
let 没有变量提升
let 无法重复声明变量
*/
console.log(c);
let c = 5;
//let c = 6; 报错
}
4、Symbol数据类型
ES6之前JS存在的数据类型:
Number
String
Boolean
Object
Null
Undefined
转载:https://www.jianshu.com/p/f40a77bbd74e
5、解构赋值
//解构赋值
let a, b, c;
[a, b, c] = [1, 2];
console.log(a, b, c); //1 2 undefined 未赋值将会赋值undefined
[a, b, c] = [1, 2, 3];
console.log(a, b, c); //1 2 3
let d, arrys;
[d, ...arrys] = [1, 2, 3, 4, 5];
console.log(d, arrys); //1 [2, 3, 4, 5](...后将会已数组形式赋值)
let q, w;
[q, , w] = [1, 2, 3]; // ,占位符
console.log(q, w); //1 3
//解构赋值对象
let t, y;
({ t, y } = { t: 123, y: 456 });
console.log({ t, y }); //{t: 123, y: 456}
let num, total;
({ k: num, j: total } = { k: 123, j: 456 });
console.log({ num, total }); //{num: 123, total: 456}
//模拟后台JSON数据 使用解构赋值
simulationJson() {
return {
name: "司大屌",
age: 23,
hobby: ["篮球", "lol", "跑步"],
computer: [{ name: "华硕" }, { name: "小米" }, { name: "苹果" }]
};
},
let dataJson = this.simulationJson();
console.log(dataJson);
let {
name: JsonName,
age: JsonAge,
hobby: JsonHobby,
computer: JsonComputer
} = dataJson;
//司大屌 23 ["篮球", "lol", "跑步"] [{…}, {…}, {…}]
console.log(JsonName, JsonAge, JsonHobby, JsonComputer);
let {
name: JName,
age: JAge,
hobby: [JHobby], //只取出hobby数组第一个值
computer: [{ name: JComputerName }] //只取出computer数组第一个对象的name值
} = dataJson;
//司大屌 23 篮球 华硕
console.log(JName, JAge, JHobby, JComputerName);
6、ES6加强Unicode的缺陷
在ES5中JavaScript允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的unicode码点。
这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出必须用双字节形式表示,但是ES5无法正确识别双字节组成的字符,ES6可以。
let a = "\u20bb7";
console.log(a); //₻7 乱码
let b = "\u{20bb7}";
console.log(b); //𠮷 ES6中码点用{}包起来
补充知识点:for-of
console.log(b.length); //2
//传统for会将unicode字符识别乱码打印 � �
for (let i = 0; i < b.length; i++) {
console.log(b[i]);
}
//使用for of 会正确打印出数值 𠮷
for (let w of b) {
console.log(w);
}
//普通字符串 传统for 与 for-of 效果相同
let bba = "1234567";
for (let i = 0; i < bba.length; i++) {
console.log("传统", bba[i]);
}
for (let w of bba) {
console.log("for-of", w);
}
7、ES6中新增字符串API和模板字符串
//ES6字符串API
let str = "123Ldd321";
//判断字符串是否包含指定字符串
console.log("includes字符串包含", str.includes("Ldd")); //true
//判断字符串的开头是否包含指定字符串,返回值是布尔值
console.log("startsWith", str.startsWith("123")); //true
//判断字符串的结尾是否包含指定字符串,返回值是布尔值
console.log("endsWith", str.endsWith("123")); //false
//repeat 将一个字符串重复n次
str = str.repeat(3);
console.log(str); //123Ldd321123Ldd321123Ldd321
let b = "大屌";
//padStart 在字符串开头已指定的字符补全 3代表补全后字符串的长度 "司"指定字符串
b = b.padStart(3, "司");
console.log(b); //司大屌
//如果补全后的字符串长度小于补全前的 将不会执行
b = b.padStart(1, "司");
console.log(b); //司大屌
//padEnd 在字符串后面补全
b = b.padEnd(5, "游戏");
console.log(b); //司大屌游戏
let name = "司大屌";
let str = `我叫${name},今年${this.onReturnAge()} 反引号使用转义符 \``;
console.log(str);//我叫司大屌,今年23 反引号使用转义符 `
onReturnAge() {
return 23;
},
注意:
1.在模板字符串中使用反引号需要用到转义符
2.在模板字符串中所有的空格和缩进符将会被保存输出
3.在模板字符串中使用变量,必须${变量名}方式引入
4.在模板字符串中${...}大括号中可以放入任意的JavaScript表达式
8、ES6数组操作
//数组扩展运算符使用
//1.浅拷贝(复制数组)
let list = [1, 2, 3, 4];
let list2 = [...list];
console.log(list2); //[1, 2, 3, 4]
//2.分割数组
let baseList = [1, 2, 3, 4];
let [, ...newList] = baseList; //,占位符跳过第一个数据
console.log(newList); //[2, 3, 4]
//3.方法传参
let addList = [1, 2];
console.log(this.addTwoData(...addList)); //3
addTwoData(x, y) {
return x + y;
},
//fill 填充数据 数组是引用类型
let fillList = [1, 2, 3, 4, 5];
let fillList2 = fillList.fill(3); //将数组中所有的元素替换成3
console.log(fillList == fillList2); //true fill改变原数组的值 只不过将地址复制。可参考下面浅拷贝
console.log(fillList2); // [3, 3, 3, 3, 3]
let fillListT = [1, 2, 3, 4, 5];
let fillList3 = fillListT.fill(3, 1, 4); // 3 需要替换的值 1起始下标(包含) 4结束下标(不包含)
console.log(fillList3); //[1, 3, 3, 3, 5]
//浅拷贝使用fill 不会改变baseListFill
let baseListFill = [1, 2, 3, 4];
let baseListFill2 = [...baseListFill].fill(2);
console.log(baseListFill); //[1, 2, 3, 4]
console.log(baseListFill2); //[2, 2, 2, 2]
//find findIndex
const list = [
{ name: "张三", id: 1 },
{ name: "李四", id: 2 },
{ name: "王五", id: 3 },
{ name: "张三", id: 4 }
];
//注意find 会找到首先找到第一个
let listF = list.find(function(item) {
return item.name === "张三";
});
console.log(listF); //{name: "张三", id: 1}
let listFIndex = list.findIndex(function(item) {
return item.name === "张三";
});
console.log(listFIndex); //0 返回下标
//includes只能检测基本数据类型返回boolean 比indexOf 方便
const bList = [1, 2, 3, 4, 5];
let iB = bList.includes(2);
console.log(iB); //true
//flat 展开内嵌数据
let flatList = [1, 2, 3, ["司大屌", 4, 5, ["欧文", 5, 6]]];
//flat 默认展开1层 可以输入参数 指定展开层数
let nList = flatList.flat(2);
console.log(nList); // [1, 2, 3, "司大屌", 4, 5, "欧文", 5, 6]
//Array.from() 将字符串转换为数组
let str = "我家住在黄土高坡";
let strList = Array.from(str);
//["我", "家", "住", "在", "黄", "土", "高", "坡"]
console.log(strList);
console.log(typeof strList); //object
//数组 map函数使用
let itemList = [
{
name: "vue",
status: 1
},
{
name: "java",
status: 0
},
{
name: "Spring",
status: 1
}
];
//map函数可以处理数组特定对象
let newItemList = itemList.map(function(item) {
let obj = {};
//浅复制
Object.assign(obj, item);
obj.status = obj.status ? "已开课" : "未开课";
return obj;
});
console.log(itemList); //0: {name: "vue", status: 1} 1: {name: "java", status: 0} 2: {name: "Spring", status: 1}
console.log(newItemList); //0: {name: "vue", status: "已开课"} 1: {name: "java", status: "未开课"} 2: {name: "Spring", status: "已开课"}
//数组 reduce使用
//array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
/* total 必需初始值 initialValue若为空 total 为数组第一个元素
currentValue 必需当前元素
currentIndex 可选。当前元素的索引
arr 可选。当前元素所属的数组对象。
initialValue 可选。传递给函数的初始值
*/
let str = "abdhsdancsaa";
let strList = Array.from(str);
let obj = strList.reduce(function(total, value) {
total[value] ? total[value]++ : (total[value] = 1);
return total;
}, {});
console.log(obj);
9、ES6 和ES8对象新特性和API
//ES6和ES8对象新特性及新增方法
//1.复制对象
//注意:...复制对象是浅拷贝
let obj = { name: "李某人", age: 23, hobby: { name: "hobby" } };
let obj2 = { ...obj };
obj.name = "司某人";
obj.hobby.name = "篮球";
console.log(obj == obj2); //false
//注意 如果拷贝对象中存在对象,那么该字段将会存储的该对象的指针
console.log(obj2); //{name: "李某人",age: 23,hobby: {name: "篮球"}}
//2.设置默认值
let sObj = { name: "李某人", age: 23 };
let snObj = { ...sObj, name: "詹姆斯" };
console.log(snObj); //{name: "詹姆斯", age: 23}
//3.合并对象
let sobj1 = { name: "欧文" };
let sobj2 = { name: "科温顿", age: 23 };
let sobj3 = { ...sobj1, ...sobj2 };
let sobj4 = { ...sobj2, ...sobj1 };
console.log(sobj3); //{name: "科温顿", age: 23}
console.log(sobj4); //{name: "欧文", age: 23}
//es5 书写对象 与 es6书写对象
let name = "张三";
let age = 23;
let es5Obj = {
name: name,
age: age,
sayHello: function() {
console.log("es5--->Hello");
}
};
//ES6更加直观
let es6Obj = {
name,
age,
sayHello() {
console.log("es6--->Hello");
}
};
es5Obj.sayHello();
es6Obj.sayHello();
//可计算的属性名
let wName = "userName";
//ES5写法
let es5O = {};
es5O[wName] = "王尼玛";
//ES6初始对象写法
let es6O = {
[wName]: "司大雕"
};
console.log(es5O); //{userName: "王尼玛"}
console.log(es6O); //{userName: "司大雕"}
// Object.is 判断两个值相等
console.log(Object.is(NaN, NaN)); //true
console.log(NaN === NaN); //false
console.log(+0 === -0); //true
console.log(Object.is(+0, -0)); //false
console.log({} === {}); //false
console.log(Object.is({}, {})); //false
// Object.assign 拷贝对象 也是浅拷贝 与 ... 存在同样问题 拷贝对象中内嵌对象属性
let obj = { name: "张三", age: 23 };
let obj2 = {};
//obj2 目标对象 obj 来源对象
Object.assign(obj2, obj);
console.log(Object.is(obj, obj2)); //false
console.log(obj2); //{name: "张三", age: 23}
//获取对象key value entrie
//Object.keys() Object.values() Object.entries()
let objK = { name: "王尼玛", age: 23, hobby: "睡觉" };
console.log(Object.keys(objK)); // ["name", "age", "hobby"]
console.log(Object.values(objK)); // ["王尼玛", 23, "睡觉"]
console.log(Object.entries(objK)); //[Array(2), Array(2), Array(2)]
//使用keys 循环复制对象
let nObjK = {};
for (let item of Object.keys(objK)) {
nObjK[item] = objK[item];
}
console.log(nObjK); //{name: "王尼玛", age: 23, hobby: "睡觉"}
10、Map与WeakMap结构的特点
JavaScript对象实质就是键值对的集合(Hash结构),但在对象里却只能用字符串作为
键名。在一些特殊的场景中满足不了业务需求,因此, Map这一数据对象提出。
Map对象:
用于保存键值对,任何值。(对象或原始值都可以作为一个键名或一个值)
WeakMap:
1、只接受引用对象作为键名。
2、没有clear 没有size 无法遍历
//Map对象使用
//1
let mapObj = new Map();
mapObj.set([1, 2, 3], "科比");
console.log(mapObj);
//2new对象是初始化值
//new Map([[key,value],[key,value],[key,value].....])
let mapObj2 = new Map([
["name", "司大屌"],
["age", 23]
]);
console.log(mapObj2);
console.log(mapObj2.get("name")); //司大屌
//has 检测是否存在key
console.log(mapObj2.has("name")); //true
//delete
mapObj2.delete("name");
console.log(mapObj2);
//mapObj2.keys() mapObj2.values() mapObj2.entries()
//默认遍历 mapObj2.entries()
for (let item of mapObj2) {
console.log(item);
}
11、Set与WeakSet结构特点
Set是ES6提供的一种类似数组的数据结构,可以理解为集合。它和数组最大的区别就是:它的值不会重复。
WeakSet:
1.数组成员必须是对象。
2.无法遍历 不存在keys() values() entries() forEach()等方式和size属性
let setObj = new Set();
setObj.add(1).add(2);
console.log(setObj);
let setObj2 = new Set([1, 2, 3, 4, 5]);
console.log(setObj2);
setObj2.delete(2);
console.log(setObj2);
//setObj2.keys() setObj2.values() setObj2.entries()
//setObj2.keys() setObj2.values () 遍历的值相同
//默认遍历的是 setObj2.values()
for (let key of setObj2.keys()) {
console.log(key);
}
12、Map、Set与Array及Object区别
// 判断对象中是否包含某个属性
let obj = {
name
};
obj["name"] = "李某人";
//判断某个属性是否存在
let checkIs = "name" in obj;
let checkIs2 = "age" in obj;
console.log("check", checkIs, checkIs2); //check true false
//删除对象中某个属性
delete obj.name;
console.log(obj); //{}
//类型转换
//1 对象转换map
let obj1 = {
name: "李某人",
age: 23
};
let map = new Map(Object.entries(obj1));
console.log(map);
//2 map转换对象
let obj2 = Object.fromEntries(map);
console.log(obj2);
//3 数组转换set
let array = [1, 2, 3, 4];
let set = new Set(array);
console.log(set);
//4 set转换数组
let arrsy2 = Array.from(set);
console.log(arrsy2);
13、ES6中Proxy与Reflect
//Proxy ES6引入操作对象API 代理对象的读取 设置等操作
let obj = {
name: "李明明",
age: 23,
phone: "18700185678"
};
//第一个参数 用于代理的目标对象
//第二个参数 一个对象,对代理对象进行拦截操作的函数
let objProxy = new Proxy(obj, {
//target 当前目标对象
//key 目标对象属性名
get: function(target, key) {
switch (key) {
case "phone":
return target[key].substr(0, 3) + "****" + target[key].substr(7);
default:
return target[key];
}
},
set: function(target, key, value) {
switch (key) {
case "name":
return (target[key] = value + "代理名称");
default:
return target[key];
}
},
// in操作
has: function(target, key) {
if (key in target) {
console.log("属性存在");
} else {
console.log("属性不存在");
}
},
//delete
deleteProperty: function(target, key) {
if (Object.is(key, "name")) {
console.log("name属性无法删除");
} else {
delete target[key];
}
}
});
console.log(objProxy.phone); //187****5678
objProxy.name = "司大屌";
console.log(objProxy.name); //司大屌代理名称
"name" in objProxy; //属性存在
//delete objProxy["name"]; //name属性无法删除
/**
* Reflect
* ES6引入Reflect也是用来操作对象的,它将对象里一些明显属于语言
* 内部方法移植到Reflect对象中,它对某些方法返回结果进行了修改,
* 使其更合理,并且使用函数方法实现了Object命令式操作
*
* */
let objR = {
name: "李某人",
age: 23
};
//以后操作对象尽量都使用Reflect对象
//获取属性值
console.log(Reflect.get(objR, "name")); //李某人
//修改属性值
Reflect.set(objR, "name", "詹姆斯");
console.log(objR.name); //詹姆斯
//检测属性名是否存在
console.log(Reflect.has(objR, "name")); //true
14、ES6函数扩展
//ES6函数扩展
//1 函数默认参数
function seyHello(x, y = "world") {
console.log(x + y);
}
seyHello("hello"); //helloworld
seyHello("你好", "世界"); //你好世界
//2 可变参数
function sumAdd(...v) {
let num = 0;
for (let p of v) {
num += p;
}
console.log("num", num);
}
sumAdd(1, 2, 3, 4, 5);
15、箭头函数
//箭头函数
//声明函数
const arrow = (x) => {
console.log("我是箭头函数", x);
};
//调用
arrow(100); //我是箭头函数 100
//如果箭头函数参数只有一个可以省略() 如果返回只有一条语句可以省略{}
const arrow2 = x => x * 2;
console.log(arrow2(2)); //4
16、ES6类
//ES5 使用构造函数来实现类的功能
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = () => {
console.log(`我叫${this.name},今年${age}`);
};
let p = new Person("张三", 23);
console.log(p); //Person {name: "张三", age: 23}
//ES6实现类的方法
class PersonES6 {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`我叫${this.name},今年${this.age} 再也不给xx借钱`);
}
}
let p1 = new PersonES6("李某人", 22);
console.log(p1);
p1.sayHello(); //我叫李某人,今年22 再也不给xx借钱
//ES6类继承
class A {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
class B extends A {}
class C extends A {
constructor(name, age) {
//子类构造函数中super放到第一行
super(name, age);
this.name = name;
this.age = age;
}
// get set 方法
get getName() {
return this.name;
}
set setName(value) {
this.name = value;
}
//静态方法
static sayHelloS() {
console.log("我是静态方法");
}
}
let cP = new C("李默", 22);
console.log(cP); // {name: "李默", age: 22}
console.log(cP.name); //李默
console.log(cP.getName); //李默
cP.setName = "张三";
console.log(cP); // {name: "张三", age: 22}
console.log(cP.name); //张三
console.log(cP.getName); //张三
//调用类的静态方法
C.sayHelloS(); //我是静态方法
17、import export
方式1
//JSFile1.js
export let a = 10;
export function sayHello() {
console.log("hello");
}
//方式1
import { a, sayHello } from "../utils/jsFile1";
//引入全部 取个别名
import * as oneAll from "../utils/jsFile1";
方式2 推荐这种方式
//jsFile2
let a = 10;
function sayHello() {
console.log("hello");
}
export default {
a,
sayHello
}
import two from "../utils/jsFile2";
console.log(two.a);
two.sayHello();
18、Promise
//promise
//1.主要用于异步计算
//2.可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
//3.可以在对象之间传递和操作promise,帮助我们处理队列
//then() 调用的就是resolve()
//catch() 调用的就是reject()
function ajax(num) {
//resolve 成功时执行下一步操作
//reject 失败时执行的下一步操作
return new Promise((resolve, reject) => {
if (Object.is(num, 200)) {
resolve();
} else {
reject();
}
});
}
ajax(200)
.then(() => {
console.log("成功");
})
.catch(() => {
console.log("失败");
});
//模拟多层异步回调使用
function ajaxAsyn() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), 1000);
});
}
ajaxAsyn()
.then(() => {
console.log("任务进度1执行完成");
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), 1000);
});
})
.then(() => {
console.log("任务进度2执行完成");
});
19、iterator
/**
* iterator遍历器是一种接口,目的是为了给不同的数据结构提供统一的循环方式,
* 任何数结构如果部署了iterator接口,就能够实现遍历的操作。
*
* 原生具有iterator接口
*
* Array
* String
* Set
* Map
* 函数的argument对象
*
* */
let arrsy = [1, 2, 3, 4, 5];
let iterator = arrsy[Symbol.iterator]();
// 使用for of
// for (const it of iterator) {
// console.log(it); // 1 2 3 4 5
// }
//使用 iterator.next() 迭代
let s = iterator.next();
while (!s.done) {
console.log(s.value);
s = iterator.next();
}
20、async
async:
async是异步简写,用于声明一个函数是异步函数.
awati:
等待的是一个表达式,这个表达式的计算结果是Promise对象或者其他值.
function fn1() {
setTimeout(() => console.log("任务1"), 1000);
}
function fn2() {
setTimeout(() => console.log("任务2"), 1000);
}
function fn3() {
setTimeout(() => console.log("任务3"), 1000);
}
function init() {
fn1();
fn2();
fn3();
}
/**
* 任务1 任务2 任务3 在等待1秒后同时输出了
* 若想实现 任务1 输出后 等待1秒输出任务2 任务2输出后等待1秒输出任务3
* 使用async 结合 Promise
*
*/
init();
//使用async Promise改进
function pn1() {
return new Promise(resolve => {
setTimeout(() => {
console.log("异步任务1");
resolve();
}, 1000);
});
}
function pn2() {
return new Promise(resolve => {
setTimeout(() => {
console.log("异步任务2");
resolve();
}, 1000);
});
}
function pn3() {
return new Promise(resolve => {
setTimeout(() => {
console.log("异步任务3");
resolve();
}, 1000);
});
}
async function initP() {
//await 会等待 Promise 中 resolve() 或 reject() 调用后执行
//new Promise 中若没有调用.then() 或者.catch() 默认是传入的空函数,方法体中代码在返回对象时执行
await pn1().then(() => {
console.log("结束");
});
await pn2();
await pn3();
}
initP();