es6
es6是ECMAScript的第六次修订,于2015年完成,也叫ES2015。
特点:语法上更加简洁,提高开发效率。
(当要求动态上下文的时候就不能够用箭头函数)
特点:
1、箭头函数中,this的指向是定义时所在的作用域对象,而非运行时;
2、不能够用作构造函数,即不能使用new命令;
2、不能够用arguments对象;
3、不能使用yield命令;
附:超时调用的代码都是在全局作用域中执行的,即setTimeOut中的this指向window,但是使用箭头函数可以改变this的指向.
二、var 、let 、const区别
var 有变量提升、没有块级作用域
let和const没有变量提升,一定要先声明再使用,有块级作用域,
let定义变量;const定义常量;
const定义后,不能修改其指向的目标,若是对象、数组,可以修改里面的属性值。我们可以在引用第三方库的时,把需要声明的变量,用const来声明,这样可以避免未来不小心重命名而导致出现bug .
const xxx = require('xxxxx');
var oLi=document.querySelectorAll('li') var arr=[]; for(var i=0;i<oLi.length;i++){ oLi[i].onClick(function(index){ console.log(index) //打印出来的是3 }); } , //var定义的i为全局变量,每循环一次,新值都会覆盖旧值 //let 定义的i为局部变量,没循环一次,都为i创建新的绑定代码 //闭包 function show(i){ return function(){ console.log(i) //0、1、2 } } var arr=[]; for(var i=0;i<3;i++){ oLi[i].onClick(show(index)); } //立即执行函数 for(var i=0;i<3;i++){ (oLi[i].onClick(function(index){ console.log(index) }))(i); } //0/1/2 //立即执行函数给每个 li 创造一个独立作用域
Set是一个构造函数,类似数组,里面的值是唯一的;Set没有length属性值,遍历只能用for of
//数组去重: let set=new Set([1,2,3,3]); console.log([...set]) //[1,2,3] console.log(Array.from(set)) //[1,2,3] 类数组转换为数组: Array.prototype.slice.call(likeArray)//利用slice的返回新数组以及call改变this指向而形成一个新数组
类数组:(类似于一个数组的对象):是一个对象,里面有数组的值以及相应的属性(length).
类数组是指在写法上跟数组一样,比如argumens,函数的第一个参数是argument[0],写法上跟数组一样,但是不是数组,他的原型是Object。
常见的类数组有:
1、函数的参数 arguments,
2、DOM 对象列表(比如通过 document.querySelectorAll 得到的列表)
3、 jQuery 对象 (比如 $(“div”)).
Map与对象:
1、对象的key只能是字符串,Map的key可以是任意数据类型;
2、Map有自己的操作方法:
//不传参数的情况下,type为'cat' function animal(type = 'cat'){ console.log(type) } animal()
rest参数(形式为“…变量名”),用于获取函数的多余参数
//es5 function fn(){ for(var i = 0; i<arguments.length ; i++){ console.log(arguments[i]); } } fn(1,2,3);//1,2,3 //es6 function newFn(...args){ args.filter((obj)=>{console.log(obj)}) } newFn(1,2,3);//1 2 3
四、模板字符串
用反引号来拼接
`hello ${name}`
五、class、extend、super
Class是定义类的方法,es5用构造函数定义类,es6用class,让对象原型的写法更清晰、更面向对象编程的语法、更通俗易懂。
用class关键字定义一个类对象,这个类的构造器(constructor)和原型方法都放在这个对象里。
//es5 构造函数 function Person(name,age){ this.name=name; this.age=age; } //原型方法 Person.prototype.getName=function(){ rturn this.name; } //es6 class Person{ constructor(name,age){ //构造函数,私有的 this.name=name; this.age=age; } getName(){ //原型方法,共享的 rturn this.name; } static getGender(){ //静态(私有方法) return this.gender; } } //继承 class Student extends Person{ constructor(name,age){ super(name,age); //相当于 Person.call(this),初始化this,子类的this继承父类的this //super就是父类的constructor,执行super()即调用父类构造函数,之后才会有this this.gender=gender; this.sex=sex; } getName(){ //原型方法,共享的 super.getName(); //super指向父类的原型 } static getGender(){ //静态(私有方法)也可以继承 super.getGender(); //super指向父类本身 } }
class定义一个类时,不存在变量提升,为了方便继承,先声明子类,再声明父类。
//ES5可以先使用再定义,存在变量提升 new A(); function A(){ } //ES6不能先使用再定义,不存在变量提升 会报错 new B();//B is not defined class B{ }
五、promise
1、什么是promise?
回调地狱:由于javascript是单线程的,通常我们实现异步编程时就会用到回调函数,但是在某些场景下,会使用一次又一次的回调,最后会发现代码会变成金字塔状,这就称为回调地狱。
回调地狱的特点:可读性差、难以维护、难以捕获异常。
异步编程的另一种解决方法:promise
promise构造函数是同步执行的,then方法是异步执行的
名称:
承诺,表达了将来会执行的操作,代表异步操作
状态:
pending:进行中
fulfilled:已成功
rejected:已失败
过程:pending=>fulfilled,pending=>rejected(此时进入catch)
特点:
1、只有异步操作可以决定当前处于的状态
2、一旦状态改变就不会再变
const promise=new Promise(resolve,reject)=>{ if(success){ resolve(value) }else{ reject(error) } } promise.then(()=>{ //success },()=>{ //failed ,这个函数不是必须的 });
reject和catch的区别:
reject用来抛出异常,catch用来处理异常。
reject后的结果会进入then的第二个回调,如果没有第二个回调,则会进入catch
封装一个ajax的promise的简易版本
let ajax=function(url,data,type='post'){ return new Promise((resolve,reject)=>{ $.ajax({ url:url, data:data, type:type, success(res){ resolve(res) }, fail(res.statusText){ reject(res.statusText) } }); }); } ajax('http:.....',{name:'',phone:1223352}).then((res)=>{ //success }).catch((error)=>{ }); //链式操作 ajax.then(success,fail).then(success,fail)
方法:
Promise方法:
1、Promise.prototype.then(()=>{console.log('resolve')},()=>{console.log('reject')}) // Promise实例状态改变时的回调
3、Promise.all() //只有等数组所有的状态都变成fulfilled
,Promise的状态才会变成fulfilled
,此时会返回一个数组,传递给Promise的回调函数。若有一个为rejected,则进入reject或者catch回调
// 生成一个Promise对象的数组 const promises = [2, 3, 5, 7, 11, 13].map(function (id) { return getJSON('/post/' + id + ".json"); });当数组所有 Promise.all(promises).then(function (posts) { // ... }).catch(function(reason){ // ... });
4、Promise.race() //谁先执行完成就先执行回调。先执行完的不管是进行了race的成功回调还是失败回调,其余的将不会再进入race的任何回调。
const pro1 = new Promise((resolve,reject) => { setTimeout(resolve,100,'1'); }); const pro2 = new Promise((resolve,reject) => { setTimeout(resolve,200,'2'); }); const pro3 = new Promise((resolve,reject) => { setTimeout(resolve,300,'3'); }); const pro4 = new Promise((resolve,reject) => { setTimeout(resolve,10,'4'); }); Promise.race([pro4,pro1,pro2,pro3]).then(data => { console.log(data); // 1 输出最快的那个 }).catch(err => { console.log(err); })
5、Promise.resolve() | Promise.reject() //将现有对象转为 Promise 对象,并手动改变状态
const p_1 = Promise.resolve('success'); p_1.then(data => { console.log(data); // success }) const p_2 = Promise.reject('err'); p_2.then(data => { console.log(data); }).catch(err => { console.log(err); // err })
六、模块化
1、函数封装、立即执行函数等,解决全局变量污染,防止模块内部的属性和方法被外部修改。
2、commonJS:通用模块规范,一个单独的文件就是一个模块,模块内部的变量无法被外部读取,但是commonJS是同步的,主要用于服务器。
3、AMD(预加载)/CMD(懒加载)异步模块加载,require方式。
4、js原生支持的模块加载方式:import(引入)、export(导出)
七、setTimeout、Promise、Async/Await 的区别
进程和线程:
进程:cpu资源分配的最小单位(系统给它分配内存)
线程:一个进程包含一个或多个线程,分别处理不同的事务;
浏览器是多进程的:Browser进程、第三方插件进程、GPU进程、渲染进程(浏览器内核)
其中浏览器内核(渲染进程)是多线程的:
1、GUI渲染线程
2、js引擎线程
3、事件触发线程
4、定时触发器线程
5、异步http请求线程
同步和异步:
同步:
异步:setTimeout、setInterVal、ajax、onClick事件
因为js引擎是单线程的,同一时间只能做一件事。为了提高效率,js把所有任务分为同步任务和异步任务,同步任务可以直接进入主线程执行,对于一些需要等待一段时间才会有结果的操作,把它放到任务队列当中,当它有结果之后就会通知主线程,然而只有等主线程有空闲的时候,才会把它放入主线程执行。
事件循环机制:
宏任务:script 、setTimeout、setInterval 、setImmediate 、I/O 、UI rendering
微任务:Promise.then()或reject()、Promise为基础开发的其它技术,比如fetch API、V8的垃圾回收过程、Node独有的process.nextTick。
js执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列。它每次只能执行一个任务,先执行宏任务,在执行过程中遇到微任务,就会把微任务放到微任务的事件队列当中,当一个宏任务执行完毕后,就会依次执行当前的微任务,微任务执行完毕,GUI线程开始渲染,渲染完毕,js引擎线程继续接管,执行下一个宏任务。
注意⚠️:在所有任务开始的时候,由于宏任务中包括了script,所以浏览器会先执行一个宏任务,在这个过程中你看到的延迟任务(例如setTimeout)将被放到下一轮宏任务中来执行。
async function async1() { console.log('async1 start'); await async2(); console.log('asnyc1 end'); } async function async2() { console.log('async2'); } console.log('script start'); setTimeout(() => { console.log('setTimeOut'); }, 0); async1(); new Promise(function (reslove) { console.log('promise1'); reslove(); }).then(function () { console.log('promise2'); }) console.log(script end) script start=>async1 start=>async2=>promise1=>script end=>promise2=>setTimeOut //1、首先,new Promise是同步的任务,会被放到主进程中去立即执行。而.then()函数是异步任务会放到异步队列中去,那什么时候放到异步队列中去呢?当你的promise状态结束的时候,就会立即放进异步队列中去了。 //2、带async关键字的函数会返回一个promise对象,如果里面没有await,执行起来等同于普通函数;如果没有await,async函数并没有很厉害是不是 //3、await 关键字要在 async 关键字函数的内部,await 写在外面会报错;await如同他的语意,就是在等待,等待右侧的表达式完成。此时的await会让出线程,阻塞async内后续的代码,先去执行async外的代码。等外面的同步代码执行完毕,才会执行里面的后续代码。就算await的不是promise对象,是一个同步函数,也会等这样操作
10、forEach() forEach():对数组进行遍历循环,对数组中的每一项运行给定函数。这个方法没有返回值。参数都是function类型,默认有传参,参数分别为:遍历的数组内容;第对应的数组索引,数组本身。 var arr = [1, 2, 3, 4, 5]; var a=arr.forEach(function(x, index, a){ console.log(x + '|' + index + '|' + (a === arr)); }); console.log(a) // 输出为: // 1|0|true // 2|1|true // 3|2|true // 4|3|true // 5|4|true // undefined 11、map() map():指“映射”,对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。 下面代码利用map方法实现数组中每个数求平方。 var arr = [1, 2, 3, 4, 5]; var arr2 = arr.map(function(item){ return item*item; }); console.log(arr2); //[1, 4, 9, 16, 25] 12、filter() filter():“过滤”功能,数组中的每一项运行给定函数,返回满足过滤条件组成的数组。 var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var arr2 = arr.filter(function(x, index) { return index % 3 === 0 || x >= 8; }); console.log(arr2); //[1, 4, 7, 8, 9, 10] 13、every() every():判断数组中每一项都是否满足条件,只有所有项都满足条件,才会返回true。 var arr = [1, 2, 3, 4, 5]; var arr2 = arr.every(function(x) { return x < 10; }); console.log(arr2); //true var arr3 = arr.every(function(x) { return x < 3; }); console.log(arr3); // false 14、some() some():判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回true。 var arr = [1, 2, 3, 4, 5]; var arr2 = arr.some(function(x) { return x < 3; }); console.log(arr2); //true var arr3 = arr.some(function(x) { return x < 1; }); console.log(arr3); // false