1). 做用域是块级做用域(在ES6以前,js只存在函数做用域以及全局做用域)javascript
if(1){ let a=1; console.log(a) }
2). 不存在变量声明提早;css
console.log(b); //ReferenceError: b is not defined let b=2;
3). 不能重复定义html
let a=1; let a=2; console.log(a);//Identifier 'a' has already been declared
4). 存在暂时性死区:能够这样来理解前端
var a=1; if(1){ console.log(a); let a=2; }
① 在一个块级做用域中,变量惟一存在,一旦在块级做用域中用let声明了一个变量,那么这个变量就惟一属于这个块级做用域,不受外部变量的影响;vue
② 不管在块中的任何地方声明了一个变量,那么在这个块级做用域中,任何使用这个名字的变量都是指这个变量,不管外部是否有其余同名的全局变量;java
③ 暂时性死区的本质就是,只要一进入当前做用域,所要使用的变量就已经存在了,可是不可获取,只有等到声明变量的那一行代码出现,才能够获取和使用该变量。node
④ 暂时性死区的意义:让咱们标准化代码。将全部的变量的声明放在做用域的最开始。webpack
2. constes6
const通常用来声明常量,且声明的常量是不容许改变的,只读属性,所以就要在声明的同时赋值。const与let同样,都是块级做用域,存在暂时性死区,不存在变量声明提早,不容许重复定义web
const A=1;//从新给常量A赋值会报错 A=3;// Uncaught TypeError: Assignment to constant variable. //错误:赋值给常量
//解构赋值,两边格式要一致 let [a,b,c] = [1,2,3];let [a,[b,c]] = [1,[2,3]]; //交互数据 let a = 10; let b = 20; [a,b] = [b,a];
用class关键字定义对象类型,用extends关键字实现继承
const private2 = Symbol('I am symbol value') class A { a1 = '1' // ES7 实例属性,须要new实例来访问, ES6规定class没有静态属性,只有静态方法因此只能在constructor中定义属性 static a2 = '2' // ES7的静态属性,直接 A.a2 访问,不须要new实例 getA1() { return this.a1 // this指向new实例 } static getA2() { return ‘2’ // 静态方法 } constructor(name) { //必定要有构造方法,若是没有默认生成空构造方法 this.a3 = '3' // 这里定义实例属性 this.name = name } // 私有方法写法 publicMethod() { private1() // 私有方法1,能够写在class体外 private2() // 利用Symbol值来定义 } [private2]() { // 这里是私有方法 } } const private1 = function() { // 这里也是私有方法,但别export出去} // 最后export class export default A // 经过extends继承 class B extends A{ constructor() { // 必定要在构造函数的第一句调用super super() // 这是调用父类的构造方法 this.b1 = '11' this.b2 = super.a1 // super直接调用时指向父类构造方法,范围属性时,指向父类实例,或调用父类静态方法 } }
Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一。
const request = url => { return new Promise((resolve, reject) => { $.get(url, data => { resolve(data) }); }) }; // 请求data1 request(url).then(data1 => { return request(data1.url); }).then(data2 => { return request(data2.url); }).then(data3 => { console.log(data3); }).catch(err => throw new Error(err));
const promise = new Promise((resolve, reject) => { // 异步处理 // 处理结束后、调用resolve 或 reject });
promise的三种状态
①promise 对象初始化状态为 pending
②当调用resolve(成功),会由pending => fulfilled
③当调用reject(失败),会由pending => rejected
注意:promsie状态 只能由 pending => fulfilled/rejected, 一旦修改就不能再变
1)then方法注册 当resolve(成功)/reject(失败)的回调函数,then方法是异步执行的
// onFulfilled 是用来接收promise成功的值 // onRejected 是用来接收promise失败的缘由 promise.then(onFulfilled, onRejected);
2)resolve(成功) onFulfilled会被调用
const promise = new Promise((resolve, reject) => { resolve('fulfilled'); // 状态由 pending => fulfilled }); promise.then(result => { // onFulfilled console.log(result); // 'fulfilled' }, reason => { // onRejected 不会被调用 })
3)reject(失败) onRejected会被调用
const promise = new Promise((resolve, reject) => { reject('rejected'); // 状态由 pending => rejected }); promise.then(result => { // onFulfilled 不会被调用 }, reason => { // onRejected console.log(rejected); // 'rejected' })
4)promise.catch
在链式写法中能够捕获前面then中发送的异常
promise.catch(onRejected) 至关于 promise.then(null, onRrejected); // 注意 // onRejected 不能捕获当前onFulfilled中的异常 promise.then(onFulfilled, onRrejected); // 能够写成: promise.then(onFulfilled) .catch(onRrejected);
promise.then方法每次调用 都返回一个新的promise对象 因此能够链式写法
function taskA() { console.log("Task A"); } function taskB() { console.log("Task B"); } function onRejected(error) { console.log("Catch Error: A or B", error); } var promise = Promise.resolve(); promise .then(taskA) .then(taskB) .catch(onRejected) // 捕获前面then方法中的异常
1)Promise.resolve 返回一个fulfilled状态的promise对象
Promise.resolve('hello').then(function(value){ console.log(value); }); Promise.resolve('hello'); // 至关于 const promise = new Promise(resolve => { resolve('hello'); });
2)Promise.reject 返回一个rejected状态的promise对象
Promise.reject(24); new Promise((resolve, reject) => { reject(24); });
3)Promise.all 接收一个promise对象数组为参数
只有所有为resolve才会调用 一般会用来处理 多个并行异步操做
const p1 = new Promise((resolve, reject) => { resolve(1); }); const p2 = new Promise((resolve, reject) => { resolve(2); }); const p3 = new Promise((resolve, reject) => { reject(3); }); Promise.all([p1, p2, p3]).then(data => { console.log(data); // [1, 2, 3] 结果顺序和promise实例数组顺序是一致的 }, err => { console.log(err); });
4)Promise.race 接收一个promise对象数组为参数
Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理
function timerPromisefy(delay) { return new Promise(function (resolve, reject) { setTimeout(function () { resolve(delay); }, delay); }); } var startDate = Date.now(); Promise.race([ timerPromisefy(10), timerPromisefy(20), timerPromisefy(30) ]).then(function (values) { console.log(values); // 10 });
/** * 摘自https://www.cnblogs.com/minigrasshopper/p/9141307.html * Promise类实现原理 * 构造函数传入一个function,有两个参数,resolve:成功回调; reject:失败回调 * state: 状态存储 [PENDING-进行中 RESOLVED-成功 REJECTED-失败] * doneList: 成功处理函数列表 * failList: 失败处理函数列表 * done: 注册成功处理函数 * fail: 注册失败处理函数 * then: 同时注册成功和失败处理函数 * always: 一个处理函数注册到成功和失败 * resolve: 更新state为:RESOLVED,而且执行成功处理队列 * reject: 更新state为:REJECTED,而且执行失败处理队列 **/ class PromiseNew { constructor(fn) { this.state = 'PENDING'; this.doneList = []; this.failList = []; fn(this.resolve.bind(this), this.reject.bind(this)); } // 注册成功处理函数 done(handle) { if (typeof handle === 'function') { this.doneList.push(handle); } else { throw new Error('缺乏回调函数'); } return this; } // 注册失败处理函数 fail(handle) { if (typeof handle === 'function') { this.failList.push(handle); } else { throw new Error('缺乏回调函数'); } return this; } // 同时注册成功和失败处理函数 then(success, fail) { this.done(success || function () { }).fail(fail || function () { }); return this; } // 一个处理函数注册到成功和失败 always(handle) { this.done(handle || function () { }).fail(handle || function () { }); return this; } // 更新state为:RESOLVED,而且执行成功处理队列 resolve() { this.state = 'RESOLVED'; let args = Array.prototype.slice.call(arguments); setTimeout(function () { this.doneList.forEach((item, key, arr) => { item.apply(null, args); arr.shift(); }); }.bind(this), 200); } // 更新state为:REJECTED,而且执行失败处理队列 reject() { this.state = 'REJECTED'; let args = Array.prototype.slice.call(arguments); setTimeout(function () { this.failList.forEach((item, key, arr) => { item.apply(null, args); arr.shift(); }); }.bind(this), 200); } } // 下面一波骚操做 new PromiseNew((resolve, reject) => { resolve('hello world'); // reject('you are err'); }).done((res) => { console.log(res); }).fail((res) => { console.log(res); })
使用Generator
能够很方便的帮助咱们创建一个处理Promise
的解释器;
async
/await
这样的语法,可让咱们以接近编写同步代码的方式来编写异步代码(无需使用.then()
或者回调函数)
Generator
是一个函数,能够在函数内部经过yield
返回一个值(此时,Generator
函数的执行会暂定,直到下次触发.next()
) 建立一个Generator
函数的方法是在function
关键字后添加*
标识。
在调用一个Generator
函数后,并不会当即执行其中的代码,函数会返回一个Generator
对象,经过调用对象的next
函数,能够得到yield
/return
的返回值。 不管是触发了yield
仍是return
,next()
函数总会返回一个带有value
和done
属性的对象。 value
为返回值,done
则是一个Boolean
对象,用来标识Generator
是否还能继续提供返回值。 P.S. Generator
函数的执行时惰性的,yield
后的代码只在触发next
时才会执行。
function * oddGenerator () { yield 1 yield 3 return 5 } let iterator = oddGenerator() let first = iterator.next() // { value: 1, done: false } let second = iterator.next() // { value: 3, done: false } let third = iterator.next() // { value: 5, done: true }
function getRandom () { return new Promise(resolve => { setTimeout(_ => resolve(Math.random() * 10 | 0), 1000) }) } async function main () { let num1 = await getRandom() let num2 = await getRandom() return num1 + num2 } console.log(`got data: ${await main()}`)
Async函数始终返回一个Promise
async function throwError () { throw new Error() } async function returnNumber () { return 1 } console.log(returnNumber() instanceof Promise) // true console.log(throwError() instanceof Promise) // true
Await是按照顺序执行的,并不能并行执行,JavaScript
是单线程的,这就意味着await
一只能一次处理一个,若是你有多个Promise
须要处理,则就意味着,你要等到前一个Promise
处理完成才能进行下一个的处理,这就意味着,若是咱们同时发送大量的请求,这样处理就会很是慢。
function delay () { return new Promise(resolve => setTimeout(resolve, 1000)) } let tasks = [1, 2, 3, 4] //要4s才能执行完 async function runner (tasks) { for (let task of tasks) { await delay() } } //优化,缩短执行时间 async function runner (tasks) { tasks = tasks.map(delay) await Promise.all(tasks) } console.time('runner') await runner(tasks) console.timeEnd('runner')
Generator
与async function
都是返回一个特定类型的对象:
Generator
: 一个相似{ value: XXX, done: true }
这样结构的Object
Async
: 始终返回一个Promise
,使用await
或者.then()
来获取返回值Generator
是属于生成器,一种特殊的迭代器,用来解决异步回调问题感受有些游手好闲了。。 而async
则是为了更简洁的使用Promise
而提出的语法,相比Generator + co
这种的实现方式,更为专一,生来就是为了处理异步编程。
// 两个参数: (x, y) => x * x + y * y // 无参数: () => 3.14 // 可变参数: (x, y, ...rest) => { var i, sum = x + y; for (i=0; i<rest.length; i++) { sum += rest[i]; } return sum; }
var obj = { age: 1, say: function() { setTimeout(function() { console.log(this, this.age); // window undefined }, 0); }, } var obj1 = { age: 1, say: function() { setTimeout(() => { console.log(this, this.age); // obj1 1 }, 0); } };
这里能够看出箭头函数中访问的this其实是其父级做用域中的this,箭头函数自己的this是不存在的,这样就至关于箭头函数的this是在声明的时候就肯定了(即this
老是指向词法做用域,也就是外层调用者handler),这个特性是颇有用的,因此,用call()
或者apply()
调用箭头函数时,没法对this
进行绑定,即传入的第一个参数被忽略。
var handler = { id: '111', doSomething: function(e) { console.log(e); }, init: function() { document.addEventListener('click', (event) => { // 这里绑定事件,函数this就能够访问到handler的方法doSomething this.doSomething(event); }, false); } } handler.init();
var Person = (name) => { // Uncaught TypeError: Person is not a constructor this.name = name; } var person = new Person('Jack');
var foo = (val) => { console.log(arguments); // Uncaught ReferenceError: arguments is not defined }; foo(); //这个特性也很好测试,可是实在要使用arguments对象要怎么办呢?咱们可使用es6的另外一个新特性rest参数,完美替代 var foo = (...args) => { console.log(args); // [1, 2, 3] }; foo(1, 2, 3);
JavaScript的默认对象表示方式{}
能够视为其余语言中的Map
或Dictionary
的数据结构,即一组键值对。可是JavaScript的对象有个小问题,就是键必须是字符串。但实际上Number或者其余数据类型做为键也是很是合理的。为了解决这个问题,最新的ES6规范引入了新的数据类型Map。
Map的遍历:
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]); m.get('Michael'); // 95 //初始化Map须要一个二维数组,或者直接初始化一个空Map。Map具备如下方法: var m = new Map(); // 空Map m.set('Adam', 67); // 添加新的key-value m.set('Bob', 59); m.has('Adam'); // 是否存在key 'Adam': true m.get('Adam'); // 67 m.delete('Adam'); // 删除key 'Adam' m.get('Adam'); // undefined //因为一个key只能对应一个value,因此,屡次对一个key放入value,后面的值会把前面的值冲掉 //遍历Map var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]); m.forEach(function (value, key, map) { console.log(value); });
Set
和Map
相似,也是一组key
的集合,但不存储value
。因为key
不能重复,因此,在Set
中,没有重复的key。
//要建立一个Set,须要提供一个Array做为输入,或者直接建立一个空Set: var s1 = new Set(); // 空Set var s2 = new Set([1, 2, 3]); // 含1, 2, 3 //重复元素在Set中自动被过滤: var s = new Set([1, 2, 3, 3, '3']); s; // Set {1, 2, 3, "3"} //经过add(key)方法能够添加元素到Set中,能够重复添加,但不会有效果: s.add(4); s; // Set {1, 2, 3, 4} s.add(4); s; // 仍然是 Set {1, 2, 3, 4} //经过delete(key)方法能够删除元素: var s = new Set([1, 2, 3]); s; // Set {1, 2, 3} s.delete(3); s; // Set {1, 2} //遍历Set var s = new Set(['A', 'B', 'C']); s.forEach(function (element, sameElement, set) { console.log(element); });
数组去重
//1.for循环嵌套,利用splice去重 function newArr(arr){ for(var i=0;i<arr.length;i++){ for(var j=i+1;j<arr.length;j++) if(arr[i]==arr[j]){ //若是第一个等于第二个,splice方法删除第二个 arr.splice(j,1); j--; } } } return arr; } var arr = [1,1,2,5,6,3,5,5,6,8,9,8]; console.log(newArr(arr)) //2.建新数组,利用indexOf去重 function newArr(array){ //一个新的数组 var arrs = []; //遍历当前数组 for(var i = 0; i < array.length; i++){ //若是临时数组里没有当前数组的当前值,则把当前值push到新数组里面 if (arrs.indexOf(array[i]) == -1){ arrs.push(array[i]) }; } return arrs; } var arr = [1,1,2,5,5,6,8,9,8]; console.log(newArr(arr)) //3.ES6中利用Set去重 function newArr(arr){ return Array.from(new Set(arr)) } var arr = [1,1,2,9,6,9,6,3,1,4,5]; console.log(newArr(arr))
如今的Chrome浏览器已经支持ES6了,可是有些低版本的浏览器仍是不支持ES6的语法,这就须要咱们把ES6的语法自动的转变成ES5的语法。Webpack是有自动编译转换能力的,除了Webpack自动编译,还可使用用Babel来完成。
{ "name": "es6", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
能够根据本身的须要进行修改,好比咱们修改name的值为es6。
{ "presets":[ "es2015" ], "plugins":[] }
这个文件咱们创建完成后,如今能够在终端输入的转换命令了,此次ES6成功转化为ES5的语法。 babel dist/index.js -o src/index.js
{ "name": "es6", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "babel src/index.js -o dist/index.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "babel-cli": "^6.24.1", "babel-preset-es2015": "^6.24.1" } }
修改好后,之后咱们就可使用 npm run build 来进行转换了。
webpack的loaders是一块很重要的组成部分。咱们都知道webpack是用于资源打包的,里面的全部资源都是“模块”,内部实现了对模块资源进行加载的机制。可是Webpack自己只能处理 js模块,若是要处理其余类型的文件,就须要使用 loader 进行转换。
Loader 能够理解为是模块和资源的转换器,它自己是一个函数,接受源文件做为参数,返回转换的结果,例如可使用loader加载器能够快速编译预处理器(less,sass,coffeeScript)。 Loader 能够在require()引用模块的时候添加,也能够在 webpack 全局配置中进行绑定,还能够经过命令行的方式使用。
loader的特性是:
ES6以前已经出现了js模块加载的方案,最主要的是CommonJS和AMD规范。commonjs主要应用于服务器,实现同步加载,如nodejs。AMD规范应用于浏览器,如requirejs,为异步加载。同时还有CMD规范,为同步加载方案如seaJS。
ES6在语言规格的层面上,实现了模块功能,并且实现得至关简单,彻底能够取代现有的CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。
ES6模块主要有两个功能:export和import
export用于对外输出本模块(一个文件能够理解为一个模块)变量的接口
import用于在一个模块中加载另外一个含有export接口的模块。
CommonJS模块的重要特性是加载时执行,即脚本代码在require的时候,就会所有执行。一旦出现某个模块被“循环加载”就只输出已经执行的部分,尚未执行的部分是不输出的。
ES6模块是动态引用,若是使用import从一个模块加载变量,那些变量不会缓存,而是成为一个指向被加载模块的引用,须要开发者本身保证,真正取值的时候可以取到值
impor/export(都是语法糖啦)最终都是编译为require/exports来执行的
CommonJS规范规定,每一个模块内部,module变量表明当前模块,这个变量是一个对象,他的exports属性是对外的接口,加载某个模块,实际上是加载该模块module.exports属性。export命令规定的是对外的接口,必须与模块内部的变量创建一一对应的关系
AMD是RequireJS在推广过程当中对模块定义的规范化产出,它是一个概念,RequireJS是对这个概念的实现,就比如JavaScript语言是对ECMAScript规范的实现。AMD是一个组织,RequireJS是在这个组织下自定义的一套脚本语言
CMD---是SeaJS在推广过程当中对模块定义的规范化产出,是一个同步模块定义,是SeaJS的一个标准,SeaJS是CMD概念的一个实现,SeaJS是淘宝团队提供的一个模块开发的js框架
CommonJS规范---是经过module.exports定义的,在前端浏览器里面并不支持module.exports,经过node.js后端使用的。Nodejs端是使用CommonJS规范的,前端浏览器通常使用AMD、CMD、ES6等定义模块化开发的
bable.js
babel是一个转译器,感受相对于编译器compiler,叫转译器transpiler更准确,由于它只是把同种语言的高版本规则翻译成低版本规则,而不像编译器那样,输出的是另外一种更低级的语言代码。
可是和编译器相似,babel的转译过程也分为三个阶段:parsing、transforming、generating,以ES6代码转译为ES5代码为例,babel转译的具体过程以下:
ES6代码输入 ==》 babylon进行解析 ==》 获得AST
==》 plugin用babel-traverse对AST树进行遍历转译 ==》 获得新的AST树
==》 用babel-generator经过AST树生成ES5代码
此外,还要注意很重要的一点就是,babel只是转译新标准引入的语法,好比ES6的箭头函数转译成ES5的函数;而新标准引入的新的原生对象,部分原生对象新增的原型方法,新增的API等(如Proxy、Set等),这些babel是不会转译的。须要用户自行引入polyfill来解决
//摘自博客:https://blog.csdn.net/qq_42149830/article/details/88295747 function _defineProperties(target,prop){ prop.forEach(ele => { //可能会传入多个属性 Object.defineProperty(target,ele.key,{ value:ele.value, writable:true, configurable:true, }) });//设置所设置的属性是否可写,可枚举 } function _createClass(_constructor,_prototypeProperties,_staticProperties){ //这里传入的三个参数分别是构造函数,原型上的属性,静态属性 if(_prototypeProperties){ //设置公有属性 _defineProperties(_constructor.prototype,_prototypeProperties) } if(_staticProperties){ //设置静态属性 _defineProperties(_constructor,_staticProperties) } } function _classCallCheck(_this,_constructor){ if(!(_this instanceof _constructor)){ //判断是不是经过new(建立实例)来调用_constructor throw "TypeError: Class constructor AirPlane cannot be invoked without 'new'" } } var FatherPlane=(function(){ function FatherPlane(name,color){ _classCallCheck(this,FatherPlane) this.name=name||'liu'; this.color=color||'red' } _createClass(FatherPlane,[ { key:'fly', value:function(){ console.log('fly') } } ],[ { key:'static', value:function(){ console.log('static') } } ]) return FatherPlane; })() var airplane=new FatherPlane()