es6常用知识总结
# 一、let 的使用
let的作用与var类似, 用于声明一个变量,主要有三个特点: 1、作用域在块内:{} 2、不能够重复声明 3、不会预处理(var会在编译时变量提升)
//console.log(age);// age is not defined
let age = 12;
//let age = 13;不能重复声明
console.log(age);
let btns = document.getElementsByTagName('button');
for(let i = 0;i<btns.length;i++){//这里如果是var时,会导致alter出来的结果都是3
btns[i].onclick = function () {
alert(i);
}
}
# 二、const的使用
const的作用是定义一个常量,过去定义常量时,用var声明,虽然全部大写,来表示常量,但是实际上是允许修改的,const声明的常量是不允许修改的。主要特点: 1、作用域在块内:{}, 2、不能够重复声明 3、不会预处理
const sex = '男';
console.log(sex);
//sex = '女';//不能修改
console.log(sex);
# 三、变量的结构和赋值
可以用一条语句给多个变量赋值: 1、对象: let {n, a} = {n:'tom', a:12} 2、数组: let [a,b] = [1, 'atguigu'];
let obj = {name : 'kobe', age : 39};
//对象的解构赋值
let {age} = obj;
console.log(age);
// let {name, age} = {name : 'kobe', age : 39};
// console.log(name, age);
//3. 数组的解构赋值 不经常用
let arr = ['abc', 23, true,4581];
let [a, b, c, d] = arr;
console.log(a, b, c, d);
let [, , , e] = arr;//找第四个位置的变量
console.log(e);
function person(p) {//不用解构赋值
console.log(p.name, p.age);
}
person(obj);
function person1({name, age}) {
console.log(name, age);
}
person1(obj);//可以直接传递对象
# 四、模板字符串的拼接
用来简化字符串的拼接,使用``内...${xxx}...
let obj = {
name : 'anverson',
age : 41
};
console.log('我叫:' + obj.name + ', 我的年龄是:' + obj.age);
console.log(`我叫:${obj.name}, 我的年龄是:${obj.age}`);
# 五、对象的简化写法
1、可以省略的对象中的同名属性值 2、省略方法的function
let x = 3;
let y = 5;
//普通写法
// let obj = {
// x : x,
// y : y,
// getPoint : function () {
// return this.x + this.y
// }
// };
//简化的写法
let obj = {
x,
y,
getPoint(){
return this.x
}
};
console.log(obj, obj.getPoint());
# 六、箭头函数
箭头函数主要的作用就是定义匿名函数,多用来定义回调函数。 基本语法: 1、没有参数: () => console.log('x') 2、一个参数: i => i+8 3、大于一个参数: (i,j) => i+j 4、函数体不用大括号: 默认返回结果,可以省略return语句,打印时可以体现。 5、 函数体如果有多个语句, 需要用{}包围,若有需要返回的内容,需要手动返回
let fun = function () {
console.log('fun()');
};
fun();
//没有形参,并且函数体只有一条语句,可以改写成
let fun1 = () => console.log('fun1()');
fun1();
console.log(fun1());
//一个形参,并且函数体只有一条语句
let fun2 = x => x;
console.log(fun2(5));
//形参是一个以上
let fun3 = (x, y) => x + y;
console.log(fun3(25, 39));//64
//函数体有多条语句
let fun4 = (x, y) => {
console.log(x, y);
return x + y;
};
console.log(fun4(34, 48));//82,如果不return的话,打印的是没有结果的
//setTimeout会将当中的this指向window对象
setTimeout(() => {
console.log(this);
},1000)
function Person() {
this.obj = {
showThis : () => {
console.log(this);
}
}
}
let fun5 = new Person();
fun5.obj.showThis();//Person函数
箭头函数中最重要的就是this的指向问题 官方定义:箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this 判断方法:箭头函数的this看外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window。
# 七、点点点运算符
arguments:主要是在函数内部获取形参的伪数组,内部并没有数组的遍历方法,提供了callee函数来指向函数本身(多用来迭代使用) 点点点运算符: 1、可变参数:用在函数内部,是真正的数组,可以调用数组相关的方法的。 2、扩展运算符:为了拼接数组更加的方便(暂时)
function fun(...values) {
console.log(arguments);
// arguments.forEach(function (item, index) {
// console.log(item, index);//是无法使用的
// });
console.log(values);//真正的数组,可以调用数组的方法
values.forEach(function (item, index) {
console.log(item, index);
})
}
fun(1,2,3);
2、
let arr = [2,3,4,5,6];
let arr1 = ['abc',...arr, '123132'];
console.log(arr1);
# 八、形参默认值
形参默认值是函数有形参的情况下,调用函数时未传参,使用的默认值
function Point(x=12, y=12) { //如果不传参的情况下,xy默认为12
this.x = x;
this.y = y;
}
let point = new Point(25, 36);
console.log(point);
let p = new Point();
console.log(p);
# 九、Promise对象
代表了未来将要发生的事,有三个状态:pending(初始化状态),fullfilled(成功状态), rejected( 失败状态) 作用:可以将异步的操作用同步的流程表达出来,避免了层层嵌套的回调(回调地狱:异步操作执行成功后继续进行回调,会产生多层的回调,出错时,很难定位错误的位置,代码的可读性也不高。)
//创建一个promise实例对象
let promise = new Promise((resolve, reject) => {
//初始化promise的状态为pending---->初始化状态
console.log('1111');//同步执行
//启动异步任务
setTimeout(function () {
console.log('3333');
//resolve('atguigu.com');
//修改promise的状态pending---->fullfilled(成功状态)
reject('xxxx');
//修改promise的状态pending----->rejected(失败状态)
},1000)
});
promise.then((data) => {
console.log('成功了。。。' + data);
}, (error) => {
console.log('失败了' + error);
});
console.log('2222');
最终的打印结果为:1111,2222,3333,失败了
主要应用可以用来封装ajax请求:
//定义一个用ajax的方法
function getNews(url) {
//创建一个promise对象
let promise = new Promise((resolve, reject) => {
//初始化promise状态为pending
//启动异步任务
let request = new XMLHttpRequest();
//设置监听
request.onreadystatechange = function () {
if(request.readyState === 4){
if(request.status === 200){
let news = request.response;
resolve(news);
}else{
reject('请求失败了。。。');
}
}
};
request.responseType = 'json';//设置返回的数据类型
request.open("GET", url);//规定请求的方法,创建链接
request.send();//发送
})
return promise;
}
返回的promise对象可以调用then():
promise.then(function(
result => console.log(result),
errorMsg => alert(errorMsg)
))
实际使用:(接上)
getNews('xxx') .then((data) => {//返回值是一个promise对象
console.log(data);
return getNews('xxx'+data.xxx);//可以继续调用
}, (error) => {
alert(error);
})
.then((data) => {
console.log(data);
}, (error) => {
alert(error);
})
# 十、Symbol数据类型
Symbol 是ES6中添加的一种原始的数据类型(String, Number, boolean, null, undefined, 对象),他具有以下特点: 1、Symbol属性对应的值是唯一的,可以解决对象的命名冲突问题 2、Symbol值不能与其他数据进行计算,包括同字符串拼串 3、for in, for of遍历时不会遍历symbol属性。
let symbol = Symbol();//产生symbol是不需要进行new,可以直接产生。
//let symbol = Symbol('one');//也可以直接参数使用
//let symbol2 = Symbol('two');
console.log(typeof symbol); //symbol
console.log(symbol); //Symbol
// 用作对象的属性(唯一)
let obj = {username: 'kobe', age: 39};
obj[symbol] = 'hello';
obj[symbol] = 'symbol';
console.log(obj);//打印的结果如下
for(let i in obj){
console.log(i); //遍历的结果中没有Symbol(),使用for of 遍历时会报错
}
# 十一、Iterator遍历器
iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制,使满足的数据成员可以按照某种次序排列,并且主要是满足了for..of 的使用。 工作原理: 1.创建一个指针对象,指向数据结构的起始位置。 2.第一次调用next方法,指针自动指向数据结构的第一个成员(第一次调用时才会指向第一个成员!!!) 3.接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员 4.每调用next方法返回的是一个包含value和done的对象,value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。当遍历结束的时候返回的value值是undefined,done值为true,其他时候的done为false。 目前可以直接使用Iterator遍历器功能的对象为: 1、Array 2、arguments 3、set容器 4、map容器 5、String等
// 原生测试 数组
let arr3 = [1, 2, 'kobe', true];
for(let i of arr3){
console.log(i);
}
// 字符串 string
let str = 'abcdefg';
for(let item of str){
console.log(item);
}
//模拟Iterator遍历器功能的函数
function mockIterator(arr) {
let nextIndex = 0;
return {
next: function () {
return nextIndex<arr.length?{value: arr[nextIndex++], done: false}:{value: undefined, done: true}
}
}
}
let arr = [1,2,3,4,5];
let iteratorObj = mockIterator(arr);
console.log(iteratorObj.next());
console.log(iteratorObj.next());
console.log(iteratorObj.next());
# 十二、Generator函数
Generator函数是ES6提供的解决异步编程方案之一,他其实是一个状态机,内部用yield来实现对不同状态的控制。可以生成遍历器对象。可以配合对象的Symbol.iterator属性来使对象具有for of 的使用方法。
主要特点: 1、function 与函数名之间有一个星号 2、内部用yield表达式来定义不同的状态
function* generatorExample(){
let result = yield 'hello'; // 状态值为hello
yield 'generator'; // 状态值为generator
}
3、generator函数返回的是指针对象(接iterator),而不会执行函数内部逻辑 4、调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true} 5、再次调用next方法会从上一次停止时的yield处开始,直到最后
function* generatorTest() {
console.log('函数开始执行');
yield 'hello';
console.log('函数暂停后再次启动');
yield 'generator';
}
// 生成遍历器对象
let Gt = generatorTest();
// 执行函数,遇到yield后即暂停
console.log(Gt); // 遍历器对象
let result = Gt.next(); // 函数执行,遇到yield暂停
console.log(result); // {value: "hello", done: false}
result = Gt.next(); // 函数再次启动
console.log(result); // {value: 'generator', done: false}
result = Gt.next();
console.log(result); // {value: undefined, done: true}表示函数内部状态已经遍历完毕
6、可以配合对象的Symbol.iterator属性来使对象具有for of 的使用方法。
// 对象的Symbol.iterator属性;
let myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 4;
};
for(let i of myIterable){
console.log(i);// 1 2 4
}
let obj = [...myIterable];
console.log(obj); //[1,2,4]
7、yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。
function getNews(url) {
$.get(url, function (data) { //使用jQuery的方式发送ajax请求
console.log(data);
let commentsUrl = data.commentsUrl;
let url = 'xxx' + commentsUrl;
// 调用next传参会作为上次暂停是yield的返回值
sx.next(url); //通过next传参的操作是要在yield的下一步进行的
})
}
function* sendXml() {
// url为next传参进来的数据
let url = yield getNews('xxx');
yield getNews(url);
}
let sx = sendXml();
sx.next();
# 十三、async函数
async函数是ES2017的内容,主要是解决异步回调的问题,用同步流程来解决异步操作,属于Generator的语法糖
async function foo(){
await 异步操作;
await 异步操作;
}
主要特点: 1、不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行 2、返回的总是Promise对象,可以用then方法进行下一步操作 3、async取代Generator函数的星号*,await取代Generator的yield
//定义一个异步函数,返回的是Promise对象
async function timeout(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
})
}
async function asyncPrint(value, ms) {
console.log('函数执行', new Date().toTimeString());
await timeout(ms);//等待异步函数执行结束,才会继续的向下执行
console.log('延时时间', new Date().toTimeString());
console.log(value);
}
asyncPrint('hello async', 2000)
# 十四、class类
ES6可以通过定义class类实现构造方法,类的继承。可以通过new来创建实例,用extends实现类的继承
class Person {
//调用类的构造方法
constructor(name, age){
this.name = name;
this.age = age;
}
//定义一般的方法
showName(){
console.log(this.name, this.age);
}
}
let person = new Person('kobe', 39);
console.log(person, person.showName());//方法在实例的原型上,可以直接调用
//定义一个子类
class StrPerson extends Person{
constructor(name, age, salary){
super(name, age);//调用父类的构造方法,需要将参数传入
this.salary = salary;
}
//在子类自身定义方法,相当于重写了父类的方法
showName(){
console.log(this.name, this.age, this.salary);
}
}
let str = new StrPerson('weide', 38, 1000000000);
console.log(str);
str.showName();
- 通过class定义类/实现类的继承
- 在类中通过constructor定义构造方法
- 通过new来创建类的实例
- 通过extends来实现类的继承
- 通过super调用父类的构造方法
- 重写从父类中继承的一般方法
<script type="text/javascript">
class Person {
//调用类的构造方法
constructor(name, age){
this.name = name;
this.age = age;
}
//定义一般的方法
showName(){
console.log(this.name, this.age);
}
}
let person = new Person('kobe', 39);
console.log(person, person.showName());
//定义一个子类
class StrPerson extends Person{
constructor(name, age, salary){
super(name, age);//调用父类的构造方法
this.salary = salary;
}
showName(){//在子类自身定义方法
console.log(this.name, this.age, this.salary);
}
}
let str = new StrPerson('weide', 38, 1000000000);
console.log(str);
str.showName();
# 十六、字符串方法的扩展
- includes(str) : 判断是否包含指定的字符串
- startsWith(str) : 判断是否以指定字符串开头
- endsWith(str) : 判断是否以指定字符串结尾
- repeat(count) : 重复指定次数
let str = 'abcdefg';
console.log(str.includes('a'));//true
console.log(str.includes('h'));//false
//startsWith(str) : 判断是否以指定字符串开头
console.log(str.startsWith('a'));//true
console.log(str.startsWith('d'));//false
//endsWith(str) : 判断是否以指定字符串结尾
console.log(str.endsWith('g'));//true
console.log(str.endsWith('d'));//false
//repeat(count) : 重复指定次数a
console.log(str.repeat(5));//abcdefgabcdefgabcdefgabcdefgabcdefg
# 十七、数值方法的拓展
- 二进制与八进制数值表示法: 二进制用0b, 八进制用0o
- Number.isFinite(i) : 判断是否是有限大的数
- Number.isNaN(i) : 判断是否是NaN
- Number.isInteger(i) : 判断是否是整数
- Number.parseInt(str) : 将字符串转换为对应的数值
- Math.trunc(i) : 直接去除小数部分
console.log(0b1010);//10
console.log(0o56);//46
//Number.isFinite(i) : 判断是否是有限大的数
console.log(Number.isFinite(NaN));//false
console.log(Number.isFinite(5));//true
//Number.isNaN(i) : 判断是否是NaN
console.log(Number.isNaN(NaN));//true
console.log(Number.isNaN(5));//falsse
//Number.isInteger(i) : 判断是否是整数
console.log(Number.isInteger(5.23));//false
console.log(Number.isInteger(5.0));//true 这也属于整数
console.log(Number.isInteger(5));//true
//Number.parseInt(str) : 将字符串转换为对应的数值
console.log(Number.parseInt('123abc'));//123
console.log(Number.parseInt('a123abc'));//NaN
// Math.trunc(i) : 直接去除小数部分
console.log(Math.trunc(13.123));//13
# 十八、数组方法的拓展
- Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
- Array.of(v1, v2, v3) : 将一系列值转换成数组
- find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素
- findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标
//Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
let btns = document.getElementsByTagName('button');//伪数组
console.log(btns.length);//3
Array.from(btns).forEach(function (item, index) {
console.log(item, index);
});
//Array.of(v1, v2, v3) : 将一系列值转换成数组
let arr = Array.of(1, 'abc', true);
console.log(arr);
//find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素
let arr1 = [1,3,5,2,6,7,3];
let result = arr1.find(function (item, index) {
return item >3
});
let result2 = arr1.find((item)=>{
return item > 6 //表达式要返回正确的值
})
console.log(result);//5
//findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标
let result1 = arr1.findIndex(function (item, index) {
return item >3
});
console.log(result1);//2
# 十九、对象方法的扩展
- Object.is(v1, v2):判断2个数据是否完全相等
- Object.assign(target, source1, source2..): 将源对象的属性复制到目标对象上
- 直接操作 proto 属性:(隐式原型)
console.log(Object.is('abc', 'abc'));//true
console.log(NaN == NaN);//false NaN与任何的数都不相等
console.log(0 == -0);//true
//这两个属于特殊情况,底层在实现的时候用了字符串进行比较
console.log(Object.is(NaN, NaN));//true
console.log(Object.is(0, -0));//false
//Object.assign(target, source1, source2..)
let obj = {name : 'kobe', age : 39, c: {d: 2}};
let obj2 = {name : 'jignisgniasjfdi', sex: "test", c: {d: 2}};
let obj1 = {};
Object.assign(obj1, obj,obj2);
console.log(obj1, obj1.name);//结果如图所示,注意属性的覆盖
//直接操作 __proto__ 属性
let obj3 = {name : 'anverson', age : 41};
let obj4 = {};
obj4.__proto__ = obj3;
console.log(obj4, obj4.name, obj4.age);//可以调用原型上面的属性和方法
# 二十、set 和 map 的常用方法
1、set:无序不可重复的多个value的集合体
- Set() :通个过new Set()生成set 对象
- Set(array) :通过数组生成set对象
- add(value)
- delete(value)
- has(value)
- clear()
- size
2、 Map容器 : 无序的 key不重复的多个key-value的集合体 - Map():通过new Map() 生成map对象
- Map(array):通过数组生成map对象
- set(key, value)
- get(key)
- delete(key)
- has(key)
- clear()
- size
let set = new Set([1,2,3,4,3,2,1,6]);
console.log(set);
set.add('abc');
console.log(set, set.size);//会将重复的自动删除
//delete(value)
set.delete(2);
console.log(set);
//has(value)
console.log(set.has(2));//false
console.log(set.has(1));//true
//clear()
set.clear();
console.log(set);
let map = new Map([['abc', 12],[25, 'age']]);//注意是二维数组,数组里面如果出现超过两个,自动去除,就要前两个
console.log(map);
map.set('男', '性别');
console.log(map);
console.log(map.get(25));//age
//delete(key)
map.delete('男');
console.log(map);
console.log(map.has('男'));//false
console.log(map.has('abc'));//true
map.clear();
console.log(map);
# 二十一、for...of...使用总结
for(let value of target) { }:循环遍历
- 遍历数组
- 遍历Set:可以用来对数组进行去重。
- 遍历Map
- 遍历字符串
- 遍历伪数组
# 二十二、深度克隆
数据类型: 数据分为基本的数据类型(String, Number, boolean, Null, Undefined)和对象数据类型。基本数据类型存储的是该对象的实际数据,对象数据类型存储的是该对象在栈中引用,真实的数据存放在堆内存里。 克隆数据的区别: 1、基础数据的复制是可以直接复制的,因为存放的就是实际的数据。 2、对象直接复制时,复制的是对象在栈内存中的引用 常用的拷贝: 1、直接复制,属于 浅拷贝 2、Object.assign() 浅拷贝 3、Array.prototype.concat() 浅拷贝 4、Array.prototype.slice() 浅拷贝 5、JSON.parse(JSON.stringify())深拷贝
参考老师的自定义深度克隆方法。
主要:
1、Object.prototype.toString.call()可以判断是对象还是数组
2、for ..in..数组是下标,对象是key,都可以用obj[item]来调用
//获取类型
function getType(target){
return Object.prototype.toString.call(target).slice(8,-1)
}
//返回结果
function getClone(target){
let type = this.getType(target);
let result;
if(type==='Object'){ //为了每次递归时数据不用覆盖
result={}
}else if(type==='Array'){
result=[]
}else{
return target;
}
for(let i in target){
if(this.getType(target[i])==='Object'||this.getType(target[i])==='Array'){
result[i]=this.getClone(target[i]) //进行递归
}else{
result[i]=target[i]
}
}
return result;
}
let a={
x:1,
y:2,
z:[232,232434]
}
b=this.getClone(a)
b.x=4748;
b.z[0]=989999999
console.log(b,b.z)
//手写深拷贝
function deepClone(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj
}
let newObj
if (Array.isArray(obj)) {
newObj = []
} else {
newObj = {}
}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key])
}
}
return newObj
}
说明:上面的代码首先判断输入的对象是否为对象或数组,如果不是则直接返回原对象。 否则,根据对象的类型创建一个新的空对象,然后递归遍历对象的每一个属性,并将该属性的值拷贝到新对象上。 注意:该代码仅适用于简单的对象,如果需要复制循环引用的对象,则需要额外处理。对于循环引用的对象,我们可以使用一个 WeakMap 来记录已经处理过的对象,以避免无限递归:
function deepClone(obj, seen = new WeakMap()) {
if (typeof obj !== 'object' || obj === null) {
return obj
}
if (seen.has(obj)) {
return seen.get(obj)
}
let newObj
if (Array.isArray(obj)) {
newObj = []
} else {
newObj = {}
}
seen.set(obj, newObj)
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key], seen)
}
}
return newObj
}