ES6
防抖和节流
防抖
function debounce(delay,fun){
let timmer=null;
return function(){
if(timmer){
clearTimeout(timmer);
}
timmer = setTimeout(function(){
fun && fun();
},delay);
}
};
节流
function throttle(delay,fun){
let startTimmer = 0;//设置开始时间
return function(){
let currTime = new Date();
//计算时间差
if(currTime - startTimmer >= delay){
startTimmer = currTime;
fun && fun();
}
};
}
扩展运算符
概念
在对象中使用扩展运算符(…),用来取出参数对象的所有可以遍历的属性,复制到当前对象中
扩展运算符的功能
1.它可以将数组转化为逗号分隔的参数序列
2.可以复制数组,也是一个深拷贝
3.可以把一个字符串,转化为数组
4.扩展运算符和结构赋值结合在一起,用来生成数组
扩展运算符使用在函数中,一定是最后一个参数使用,表示把剩余的参数存放到一个数组中(此刻也叫rest参数)
例
function sum3(str1,str2,…arg){
console.log( str1 );
console.log( str2 );
let total = 0;
for(let i=0;i<arg.length;i++){
total += arg[i];
}
console.log( total );
}
sum3(‘hello’,‘world’,1,2,3,4,5,56);
解构赋值
概念
模式匹配:等号两边的数据模式相同,那么左侧的变量名,会被对应的右侧内容赋值
例
let [v1,v2,v3] = ['朝鲜','韩国','日本'];
console.log( v1,v2,v3 );
结合扩展运算符使用
let [s5,s6,...s7] =['土耳其','巴黎','迈阿密','泰国','新加坡'];
console.log( s5,s6,s7 );
对对象进行解构赋值,只和对象的属性名有关,和次序无关
let { age,name,job } = { name:'甲乙丙',job:'村支书',age:40 };
console.log( age,name,job );
实际上上面的写法,可以理解为下面写法的简写形式,也就是说,对象形式的解构赋值的内部机制是,先找到同名属性,然后再赋值给对应的变量,真正被赋值的是左侧属性名所对应的属性值,此时属性值是一个变量
用途
//交换变量
let num1 = 20;
let num2 = 30;
// let temp = num1;
// num1 = num2;
// num2 = temp;
[num2,num1] = [num1,num2];
console.log( num1,num2 );
同时接收函数返回的多个值
function test(){
return ['孙悟空',230,'男'];
}
let [name2,age2,sex] = test();
console.log( name2,age2,sex );
//json解析
let json = {
name3:name2,
age3:age2,
sex1:sex
};
let {name3,age3,sex1} = json;
console.log( name3,age3,sex1 );
拷贝对象
在对象中使用扩展运算符,用来取出参数对象中所有的可遍历属性和方法,复制到当前对象中,这是一个深拷贝
let obj = {
name:'许仙',
age:20,
};
let obj1 = { ...obj,son:'许士林' };
obj1.job = '医生'
console.log( obj,obj1 );//不会影响obj
Object.assign(参数1,参数2,参数3) 把从参数2开始的所有对象的属性,都复制到参数1中,如果右相同属性,则覆盖,得出的参数1,是合并后的结果
返回值也是合并后的结果·
let abc = Object.assign(obj,obj1,obj2);
console.log( obj );
console.log( abc );
//常用放法
let obj3 = Object.assign({},obj,obj1,obj2);
字符串操作
includes(‘目标字符串’):返回一个布尔值,表示当前字符串中是否含有目标字符串
stratsWith(‘目标字符串’):返回一个布尔值,表示当前字符串是否以目标字符串开头
endsWith(‘目标字符串’):返回一个布尔值,表示当前字符串是否以目标字符串结尾
let str1 = '民国万税,天下太贫';
console.log( str1.indexOf('万税') );
console.log( str1.includes('万税') );
console.log( str1.startsWith('民国') );
console.log( str1.endsWith('天下') );
数组操作
1.Array.from() 用来将两类对象转化为真正的数组,第一是类似数组的对象第二是可遍历的对象(包含 ES6新增的 Set 和 Map)
2.Array.from() 还可以接受第二个参数,作用和map类似,可以对可遍历的对象中的数据加以操作,返回到数组中对应的位置
3.Array.of() 它可以将一组数据,转化为数组
数组实例的方法
find() 它的参数是一个回调函数,该回调函数的参数是每一次查找的数组元素,该回调该函数返回一个判断条件。find()方法返回的是满足该判断条件的第一个数组元素。如果没有符合条件的元素,返回一个undefined。
findIndex 使用方式和find()类似,返回第一个满足条件的数组元素的下标,如果没有符合条件的元素,返回-1。
fill(填充内容) 用来填充数组的值,返回填充后的数组(原数组)
fill(填充内容) 用来初始化空数组,他会把原数组中的所有内容清空
fill(填充内容) 还可以接受第二个和第三个参数,用来指定填充的起始位置和结束位置
includes() 方法返回的是一个布尔值,表示是否含有指定的数组元素,和字符串的includes()方法一致,都是属于ES7中的方法
该方法还有第二个参数,第二个参数代表开始查找的位置,如果为负数,则表示倒着数,如果负数的绝对值大于数组的长度,则从0开始查找
indexOf() 的两个缺点,第一是不够语义化,他的真正用途是用来查找某一个数据值在数组中的下标,并且每一次比较都是数据和-1的比较表达不够直观。第二,它的内部是严格使用全等运算符(===)进行判断,这样会导致NaN的误判,
includes使用的是不同的判断方法,不会有这个问腿
函数操作
设置函数参数的默认值
设置的方式是参数=默认值,如果在函数调用的时候,传入了实参,该参数的值就是实参值,如果没有传入实参,则该参数的值就是默认值
函数作用域:一旦设置了参数的默认值,函数就会进行声明初始化的时候,参数会形成一个单独的作用域,如果初始化结束,则这个作用域消失。这个作用域在不设置默认参数值的时候是没有的
箭头函数
如果箭头函数体中,只有一个返回语句,则可以把 {} 和 return 都省略
给箭头函数传参,
如果箭头函数只有一个参数,则可以省略包裹参数的()
由于{} 括号会被解析为代码块,所以如果箭头函数返回一个对象的话,必须在对象外面包裹一个(),否则会报错
箭头函数注意
使用箭头函数需要注意的问题
1.箭头函数中的this,指的是箭头函数创建时候所在对象的this,不是使用时候所在对象的this(其实箭头函数没有this)
2.箭头函数不能当作构造函数,也就是说箭头函数不能使用new操作符,否则报错
3.箭头函数没有arguments,如果需要使用类似功能,则可以使用rest参数
4.箭头函数不能作构造器函数
由于箭头函数中没有自己的this,因此就不能使用 call() apply() bind() 等方法改变this的指向
//箭头函数不能用作构造函数
// let My = ()=>{
// this.name=‘蚩尤’;
// this.age=200;
// };
// let my = new My();
// console.log( my );
//箭头函数没有arguments,因此以下箭头函数中的arguments使sum的arguments
对象操作
1.对象的简写,如果属性名和属性值的外形一致的时候,可以简写为一个
对象方法简写,直接写如下形式
obj = {
msg(){
}
}
2.Object.is(obj1,obj2) 用来比较两个对象是否相等
返回true的情况
两个参数都是 undefined,或者一个是undefined,一个为空
两个参数都是 null
两个参数都是 NaN
两个参数都是 相同的字符串
两个参数都是 指向同一个对象
两个参数都是 相同的布尔值(同true或者同false)
两个参数都是 +0 或者都是 -0
3.Object.defineProperty(属性所在的对象,属性名,属性的配置项)
用来修改或者添加一个对象的属性,如果该属性存在,则是修改,否则为添加属性
属性配置项 是一个对象,用来给属性配置更加完善的信息
configurable:表示设置是否通过 delete 关键字删除该属性,默认为false
writable:表示设置是否可以 修改 该属性的值,默认是 false
enumerable:表示设置是否可以 通过 for-in 循环访问该属性,默认是 false
value:设置该属性的值,默认是 undefined
get方法:读取该属性的时候,执行的方法,默认是 undefined
set方法:外部设置(修改)该属性值的时候,执行的方法,默认是 undefined
注意,设置 get 和 set 方法的时候,不能同时设置 value 和 writable 属性
Set数据
概念
Es6提供了新的数据结构 Set ,它类似于数组,但是它里面的元素都是唯一的,没有重复的值,Set 是一个构造函数,用来实例化Set 数据对象
通过 add方法添加set数据
let arr1 = [1,2,3,4,1,2,3,4];
arr1.forEach(item=>s1.add(item));
把 set 转化为 数组
let arr2 = Array.from(s1);
console.log( [...s1] );
console.log( arr2 );
Set 可以接收一个数组或者类似数组的对象,用来初始化实例对象
let s2 = new Set([1,2,3,1,2,3,'刘备','刘备']);
console.log( [...s2] );
Set 数据结构的实例具有以下属性 和方法
size属性,返回实例的数据总数
操作方法
add(添加的内容) 添加某个值,返回的是 实例本身
delete(删除的内容) 删除某个内容,返回的是一个布尔值,表示删除是否成功
has(查询的内容) 返回一个布尔值,表示实例中是否含有 查询的内容
clear() 清空所有内容
遍历方法
keys() 返回所有数据的键名
values() 返回所有数据的键值
entries() 返回所有数据的键值对
forEach() 使用该方法遍历实例中所有的数据
Promise
概念
Promise 他是异步编程的一个优化方案,他比传统的方法更加合理,更加强大
Promise 对象,可以把异步操作 以 同步操作的形式展示出来,避免了回调地狱(指的是回调函数相互嵌套多层)的出现。Promise 对象提供了很多方法,使其控制异步操作更加方便
Promise 是一个构造函数,用来生成一个Promise 实例对象
一个Promise是一个异步操作,它具有三种状态:Pending(进行中),Resolve(操作已完成,也叫fulfilled) 和Rejected(已失败)。只有在异步操作结束的时候,才会知道当前是哪一个状态,任何操作都无法改变操作结果的状态。
创建一个Promise实例,在构造函数的回调中具有两个参数,第一个参数叫resolve,他是 异步操作成功执行后 执行的方法,可以把成功的数据传递出去,第二个参数叫reject,他是异步操作执行失败后 执行的方法,用来提示操作失败。这两个参数是由js引擎提供的,所以不需要开发者自己绑定
Promise实例
resolve 函数的作用是,将 promise对象的状态从pendig(进行中) 变成 fulfilled(已完成),并且会将异步操作的结果通过参数的形似,传递出去
reject 函数的作用是,将 promise对象的状态从pendig(进行中) 变成 rejected(已失败),并且会将异步操作失败的结果,通过参数的形式,传递出去
通过promise实例传递出去的数据,可以使用promise实例的then方法来接收
then方法可以接受两个参数,每一个参数都是一个回调函数。第一个回调函数是promise对象的状态变化为fulfilled的时候调用。第二个回调函数是 promise对象的状态变化为rejected时候调用。可以简单理解为,promise对象中的 异步操作执行成功,调用第一个参数,否则调用第二个参数
第二个参数是可以省略的。
两个回调函数都接受来自promise对象传递的值作为参数。
then的理解
then方法中的回调函数,返回的是一个新的promise实例。不是原来的那个实例
then方法也可以写成链式操作。
then的回调函数一般返回三种情况
- 函数中返回的是一个 非 promise实例,默认通过then之后会变成一个promise实例,并且实例的状态是fulfilled,因此通过下一个下一个then() 直接获取返回的数据
- 函数直接返回一个promise实例
- 抛出一个错误 throw new Error(‘失败是成功之母’)
Promise的方法
通过 catch() 可以直接接收 rejected 状态的信息,简单理解为专门处理异步操作错误时候的方法,用法和then一样
promise原型对象具有的方法,用来直接处理promise
all() 他是promise的静态方法,并且它返回一个新的promise,当前参数数组中的所有Promise都是成功状态的时候,返回的是一个所有数组成员的返回值 组成的数组。如果数组中有一个是 rejected状态,则返回一个rejected,并且带有错误信息。特点是按次序执行数组中所有的 Promise实例
race() 他是promise的静态方法,并且它返回一个新的promise,参数也是一个Promise实例数组,他只返回一个最先完成异步操作的Promise。如果数组中有一个是 rejected状态,则返回一个rejected,并且带有错误信息。(有一个错误就返回错误信息)
any() 特点和race一致,不同的是,只有当 数组中和所有的 Promise都是rejected 状态的时候,才返回rejected。
resolve() 该方法直接返回一个状态时 fullfilled Promise实例
具有4种参数
1.无参数
2.参数时普通数据
3.参数是一个 thenable对象
thenable = {
then:()=>{}
}
4.参数是一个 Pomise 实例
reject()
类的继承
es6中创建类的关键字时 class
class 类名 {
constructor(){
类的构造函数
}
类的属性
类的方法
} 类名首字母大写
constructor() 构造方法,他是类的默认方法,在类实例化对象的时候,会自动调用该方法。一个类必须有 constructor 方法,如果constructor方法没有被显式声明,那么他会在执行的时候隐式声明。constructor() 方法的返回值是实例对象.
类也具有原型对象,可以在原型属性上设置方法
在类的实例上调用方法,其实就是在调用原型上的方法,因为所有的类的方法都会被定义在原型上
同ES5不同的是,类没有变量提升的概念,必须在声明之后才能使用类
class Animal {
//类的属性,实例化的时候无法传入
sex='母';
//构造函数
constructor(type,name,color,sex=this.sex){
//构造函数中的this代表的是 实例对象
//类的属性
this.type = type;
this.name = name;
this.color = color;
this.sex = sex;
}
//创建类的方法
show(){
console.log( this.type+':这是一个动物'+this.name );
}
run(){
console.log( '动物都会跑' );
}
}
//实例 Animal 类 的 对象
let ani = new Animal('狗','旺财','黑色','其他');
ani.show();
ani.run();
console.log( ani.sex );
//类也具有原型属性
class Food{
name='胡辣汤';
price = 3;
show(){
console.log( this.name+'真好喝' );
}
}
Food.prototype.love = function(){
console.log( this.name+'很爱喝' );
}
let f = new Food();
f.love();
f.show();
console.log( f.love === Food.prototype.love );
console.log( f.show === Food.prototype.show );
可以使用声明变量的形式声明类
const MyClass = class My{
getName(){
// console.log( this.name );undefined 因为实例没有name属性
console.log( My.name );// My只能在类内部使用
}
}
let m1 = new MyClass();
m1.getName();
console.log( m1.name );
console.log( My.name );//My只能在类内部使用
console.log( MyClass.name );//结果是 My
类的继承
例子
/*
两个class之间可以通过 extends 关键字来显示继承
*/
//父类
class Animal {
food = '动物吃什么';
constructor(type,age){
this.type = type;
this.age = age
}
show(){
console.log( `${this.type}的寿命是${this.age}岁` );
}
}
//子类
class Dog extends Animal {
constructor(type,age,food){
super(type,age);
this.food = food;
}
msg(){
console.log( this.food );
super.show();
}
show(){
console.log( '狗是人类的好朋友' );
}
}
let dog1 = new Dog('狗',20,'肉狗粮');
dog1.show();
dog1.msg();
在子类的constructor中,有一个super()方法,他在此处表示父类的构造函数,用来新建父类的this对象
子类有显式的constructor()方法,必须在constructor中执行super(),否则会报错。因为子类没有自己的this对象,是继承父类的this对象。
两一个需要注意的地方是,在子类的constructor()中,必须先调用super(),然后再使用this关键字,否则报错。因为子类实例的构建,是基于父类实例的加工,只有先super(),才能得到父类的this对象。
类的存值和取值
/*
在类的内部可以使用get和set关键字,对某个属性设置值和存储值,或者拦截该属性的设置或者存储值的方法
*/
class Run{
constructor(){
this._time = '3:40';
}
//设置 获取time属性值的方法
get time(){
return this._time;
}
//设置time属性的方法,参数是val是设置的值
set time(val){
console.log( `设置的值是${val}` );
return this._time=val;
}
}
let r = new Run();
console.log( r.time );//3:40
r.time = '3:20';
console.log( r.time );
类的静态方法
类可以简单理解为实例的原型,所以在类中定义的方法,都会被实例继承。
如果在一个类中的方法前添加 static 关键字,就代表该方法不会被实例继承,而是直接通过类来调用,那么这个方法就称为静态方法
实例中不能调用静态方法,只有类可以直接调用
子类 可以调用父类的静态方法 ,也可以调用父类的静态属性
static 修饰符,修饰的方法称为静态方法,修饰的属性,称为静态属性
静态属性 和静态方法一致,不能通过实例调用静态属性,只能通过类调用
class Dog {
//设置静态属性
static eat = '鱼';
student = '老虎';
//创建静态方法
static show(){
console.log( '柴门闻犬吠' );
}
run(){
console.log( '竹杖芒鞋轻胜马' );
}
}
let dog = new Dog();
// dog.show(); 报错
dog.run();
//直接使用类来调用静态方法
Dog.show();
//创建子类
class Cat extends Dog {
}
let cat = new Cat();
cat.run();
Cat.show();
console.log( Cat.eat );
console.log( cat.student );
类的私有属性
//ES6中的私有属性和方法,可以直接使用 # 修饰符,私有属性和方法只能在当前类中使用,不能再类的实例或者子类中使用
// 不能在子类中使用父类的私有属性和私有方法
class Person {
name='丁不三';
//私有属性
#msg='武松';
//设置私有方法
#show(){
console.log( this.name );
}
showMsg(){
console.log( '山东'+this.#msg );
}
}
let p = new Person();
console.log( p.name );
console.log( p.msg );
p.showMsg();
// p.show();
// Person.#show();
class Son extends Person {
constructor(){
super();
}
info(){
//不能在子类中使用父类的私有属性和私有方法
// console.log( this.#msg );
}
}
let s = new Son();
console.log( s.msg );
s.showMsg();
模块
如果引入的js文件体积很大,下载和执行时间会很长,因此会造成浏览器阻塞,就是用户会感到浏览器’卡死’,因为在加载js的过程中,浏览器没有任何响应,因此浏览器允许js脚本异步加载,异步加载的方式十给script标签,添加defer 或者 async 属性
使用 defer 或者 async 属性,浏览器会立即引入外部的js,并且不会等到js全部引入才执行,而是引入的同时直接执行js代码
defer 和 async 的qubie
defer:等到整个页面正常渲染结束之后才执行代码。
async:把js代码下载完以后,会终止页面的渲染,立即执行js代码,当js代码执行完毕以后,页面继续渲染
如果多个script标签具有defer属性,会按照顺序一次执行。
如果多个script标签具有defer属性,谁先加载完谁先执行,不按照顺序
代理
Prosy 代理,他可以读取目标对象,进而对目标对象中的函数调用进行拦截。不可以直接操作对象,而是使用代理模式对对象进行操作
new Proxy(目标对象,{
get(){
},
set(){
}-});
set和get的3个参数
target:指的是目标对象
prop:当前操作的目标对象
val:设置的新值
//对对象obj设置代理
let p = new Proxy(obj,{
//get 方法,再获取对象的某一个属性值的时候,触发的函数,此函数中可以在获取值的同时,进行拦截或者其他操作
get(target,prop){
return target[prop]+'葬花';
},
set(target,prop,val){
if(val=='孙悟空'){
target[prop] = val+'孙悟空不能倒拔林黛玉'
}else{
target[prop] = '设置成:'+val;
}
}
});