转载请注明出处原文连接 http://blog.huanghanlian.com/article/5c7aa6c7bf3acc0864870f9des6 是什么首先弄明白ECMA和js的关系。ECMA是标准,Javascript是ECMA的实现。因为js也是一种语言,但凡语言都有一套标准,而ECMA就是javascript的标准。 在2015年正式发布了ECMAscript6.0,简称ES6,又称为ECMAscript2015。历史ECMAScript和JavascriptECMA是标准,JS是实现类似于HTML5是标准,IE10,Chrome,FF都是实现换句话说,将来也能有其他XXXScript来实现ECMAECMAScript简称ECMA或ES目前版本低级浏览器主要支持ES 3.1高级浏览器正在从ES 5过度ES 6历史版本时间ECMAJS解释1996.11EC 1.0JS稳定Netscript将js提交给ECMA组织,ES正式出现1998.06ES 2.0 ES2正式发布1999.12ES 3.0 ES3被广泛接受2007.10ES 4.0 ES4过于激进,被废了2008.07ES 3.1 4.0退化为严重缩水版的3.1<br/>因为吵得太厉害,所以ES3.1代号为Harmony(和谐)2009.12ES 5.0 ES5正式发布<br/>同时公布了JavaScript.next也就是后来的ES6.02011.06ES 5.1 ES5.1成为了ISO国际标准2013.03ES 6.0 ES6.0草案定稿2013.12ES 6.0 ES6.0草案发布2015.06ES 6.0 ES6.0预计发布正式版<br/>JavaScript.next开始指向ES 7.0ES6兼容性和新特性es5兼容性http://kangax.github.io/compat-table/es5/es6兼容性http://kangax.github.io/compat-table/es6/ES6(ES2015)– IE10+,Chrome,FireFox,移动端,NodeJS。这些环境基本上都是认得,都能兼容但是有需求兼容ie怎么办有两种办法比方说在移动端或者是混合开发当中,多去用用ES6,在老的版本中不用。惹不起咋躲得起。编译,转换在线转换简单来说就是写好了ES6了然后引用一个js库进来。我什么也不用做了。它替我去做了各种各样的事情缺点,用户每次打开页面都要重新转换一遍,性能体验不是很好。提前编译ES6的到底有什么样的东西?变量(对原有的变量做了修改)函数(对原有的函数也做了修改)数组(对数组做了一些改进)字符串(改进)面向对象Promise(串行化的异步请求方式)yield && generator(generator是专门把同步操作拆成异步操作,generator是对Promise的一个封装)模块化变量-let和const回顾ES5是怎么生明变量的,有什么样的缺点var的缺点可以重复声明无法限制修改没有块级作用域可以重复声明最大的问题var a=12;var a=5;alert(a);//弹窗5会发现5能出来,没有报错,没有警告,什么都没有这在其他语言是不可出现的。无法限制修改在程序中,有些东西是永远不变的。比方说常量PI=3.1415926是不会发生改变在很多语言中都有常量的概念。在js中没有至少var不是一个常量。换句话说,要不要改,能不能让别人别动这个值,不要改这个值。全凭自觉。为什么java是全世界最流行的一门语言原因很简单,因为他非常的严谨,他非常的死板。相信一件事,越是容易的语言,越是简单的语言。实际上是不严谨。就没法去开发大型项目反过来他可能让你觉得很难受的语言java,对你限制很严格。但是你掌握了呀之后,开发起大型应用会非常的得心应手。没有块级作用域es5 只在函数中支持块级作用域{ //这就是语法块}if(){ 变量=xxxx}//变量出来就用不了了,这就是块作用域for(){}体现块级作用域作用if(true){ var a=12;}alert(a);//在块级作用域内声明变量。在外部依然能够访问在ES6中有了两种新的定义变量的方式let和constlet不能重复声明,let是变量,可以修改,块级作用域块级作用域可修改let变量的值const不可重复声明,const常量,不能修改,块级作用域块级作用域不可修改const变量的值let 不能重复声明例子let a=12;let a=5;console.log(a);//报错//Uncaught SyntaxError: Identifier ‘a’ has already been declared//不能重复声明const 不能重复声明例子const a=12;const a=5;console.log(a);//报错//Uncaught SyntaxError: Identifier ‘a’ has already been declared//不能重复声明在大型项目中,重复声明这件事,指不定你定义了什么东西别人也定义了。还不报错,到时候定位bug很难找。变量和常量变量let a=12;//声明变量赋值a=5;//给变量a赋值console.log(a);//你会明确的发现它变成了5常量const a=12;a=5;console.log(a);报错,不能对常量赋值块级作用域var 块级作用域只在函数中体现,也就是说在函数中var声明的变量不会在全局作用域中体现function aa(){ var a=1; console.log(a)}aa();console.log(a)let和const只在块级作用域,或者在语法块之内起作用if(true){ let a=12;}console.log(a);//Uncaught ReferenceError: a is not definedif(true){ const a=12;}console.log(a);//Uncaught ReferenceError: a is not defined语言推出一个新的版本,一个更好的版本,他一定是要解决一些原来有的问题,ES6也不例外。块级作用域有什么<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>Document</title></head><body> <input type=“button” value=“按钮1”> <input type=“button” value=“按钮2”> <input type=“button” value=“按钮3”></body><script type=“text/javascript”> window.onload=function(){ var aBtn=document.getElementsByTagName(‘input’); for (var i = 0; i < aBtn.length; i++) { aBtn[i].onclick=function(){ alert(i) }; } };</script></html>以上代码执行,不管按哪个按钮弹出都是3由于var声明变量只在函数作用域中扩散到全局在for或者if快级作用域中声明的变量会在局部或全局生效当for循环执行完毕,i这个变量暴露到全局了,等于3所以for循环中执行的事件绑定,是将点击事件回调函数执行。当点击按钮时候,会出发绑定回调函数,此时当前作用域中,i等于3,所以无论点击哪个按钮弹出都是3以前我们是通过闭包解决这个问题<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>Document</title></head><body> <input type=“button” value=“按钮1”> <input type=“button” value=“按钮2”> <input type=“button” value=“按钮3”></body><script type=“text/javascript”> window.onload=function(){ var aBtn=document.getElementsByTagName(‘input’); for (var i = 0; i < aBtn.length; i++) { ! function(i) { aBtn[i].onclick=function(){ alert(i) }; }(i); } console.log(i) };</script></html>在每一层循环的时候,用一个匿名函数而且是立即执行的匿名函数给他包装起来,然后将每一次遍历的1.2.3分别的值去传到这个匿名函数里,然后匿名函数接到这个参数i再放到点击事件中去引用i当我们每次点击事件输出的值i就会取每一个闭包环境下的i。所以这样就能达到效果。使用let来实现<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>Document</title></head><body> <input type=“button” value=“按钮1”> <input type=“button” value=“按钮2”> <input type=“button” value=“按钮3”></body><script type=“text/javascript”> window.onload=function(){ var aBtn=document.getElementsByTagName(‘input’); for (let i = 0; i < aBtn.length; i++) { aBtn[i].onclick=function(){ alert(i) }; } console.log(i) };</script></html>for循环本身就是一个语法块,自身就是一个块由于var只把函数作为作用域所以以上需要通过立即执行函数来包一层,来实现效果。而let本身是支持块级作用域的,所以电脑按钮执行回掉函数,打印i,是当前块级作用域下的i这个i在非for块作用域下是未定义的。函数-箭头函数箭头函数在写法上对es5做了一些修整,代码看起来更显得简洁如果只有一个参数,圆括号"()“可以省略函数体如果只有一句return语句,花括号也可以省略// 定义一个箭头函数let a = (arg)=>{ // 这里=>符号就相当于function关键字 return arg+=1}// 也可以简写为let a = arg => arg+=1箭头函数的作用跟以前接触的函数没什么本质的区别,更多的是一种写法上的变化。function show() {}同等于let show =()=>{}function () {}同等于()=>{}<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>Document</title></head><body></body><script type=“text/javascript”> window.onload=function(){ alert(‘123’) };</script></html>同等于<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>Document</title></head><body></body><script type=“text/javascript”> window.onload=()=>{ alert(‘123’) };</script></html>箭头函数也对this的指向做了修整 es6之前的函数的this指向调用函数时所在的对象,而箭头函数的this指向函数定义时所在的对象//普通函数var obj = { say: function () { setTimeout(function() { console.log(this) }); }}obj.say();//Window object// 箭头函数var obj = { say: function () { setTimeout(() => { console.log(this) }); }, test:123}obj.say(); // obj函数-参数参数扩展/数组展开默认参数参数扩展收集剩余参数ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。function show (a,b,…args){ console.log(a);//1 console.log(b);//2 console.log(args);//[3,4,5,6]}show(1,2,3,4,5,6);下面是一个 rest 参数代替arguments变量的例子。// arguments变量的写法function sortNumbers() { return Array.prototype.slice.call(arguments).sort();}// rest参数的写法const sortNumbers = (…numbers) => numbers.sort();展开数组展开后的效果,跟直接把数组内容写在这一样let arr=[1,2,3];console.log(1,2,3);console.log(…arr);//1,2,3同等于…arrfunction show(a,b,c){ console.log(a) console.log(b) console.log(c)}let arr=[1,2,3];show(1,2,3);show(…arr);//1,2,3同等于…arrlet arr1=[1,2,3];let arr2=[4,5,6];let arr=[…arr1,…arr2];let arrS=[1,2,3,4,5,6];//…arr1,写法,相当于将数组内容掏出来内容默认参数function show(a,b=5,c=6){ //我希望b,默认是5 不传的时候 //我希望c,默认是6 不传的时候 console.log(a,b,c);//1,2,6}show(1,2);解构赋值允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。比如:var [a,b] = [1,2]// a=1 b=2左右两边结构必须一样右边必须是个合法的数据声明和赋值必须一句话完成,不能把声明与赋值分开let [a, b] = [1, 2] // 左右都是数组,可以解构赋值let {a, b} = {a:1, b:2} // 左右都是对象,可以解构赋值let [obj, arr] = [{a:1}, [1, 2]] // 左右都是对象,可以解构赋值let [a, b] = {a:1, b:2} // err 左右结构不一样,不可以解构赋值let {a,b} = {1, 2} // err 右边不是一个合法的数据,不能解构赋值let [a, b];[a, b] = [1, 2] // err 声明与赋值分开,不能解构赋值数组数组扩展了4个方法:map、reduce、filter、forEachmap 映射通过指定函数处理数组的每个元素,并返回处理后的数组。一个对一个[12,58,99,86,45,91][不及格,不及格,及格,及格,不及格,及格]//将数组映射成另一个数组[45,57,135,28]//将用户id映射成对象[ {name:‘huang’,role:1}, {name:‘huang1’,role:2}, {name:‘huang2’,role:3}, {name:‘huang4’,role:1}]map例子let arr=[12,5,8];//我想要将数组内容乘与2的结果let result=arr.map(function(item){ console.log(item); //需要将你要的内容返回出来 return item2;});console.log(arr);//[12, 5, 8]console.log(result);//[24, 10, 16]简写1let arr=[12,5,8];//我想要将数组内容乘与2的结果let result=arr.map(item=>{ console.log(item); //需要将你要的内容返回出来 return item2;});console.log(arr);//[12, 5, 8]console.log(result);//[24, 10, 16]简写2let arr=[12,5,8];//我想要将数组内容乘与2的结果let result=arr.map(item=>item2);console.log(arr);//[12, 5, 8]console.log(result);//[24, 10, 16]let arr=[12,58,99,86,45,91]let result=arr.map(item=>item>=60?‘及格’:‘不及格’);console.log(arr);//[12, 58, 99, 86, 45, 91]console.log(result);//[“不及格”, “不及格”, “及格”, “及格”, “不及格”, “及格”]reduce 汇总将数组元素计算为一个值(从左到右)。一堆出一个算个总数let arr=[12,58,99,86,45,91]/** * [description] * @param {[type]} (total,currentValue,index,arr [ * 初始值, 或者计算结束后的返回值。 * 当前元素 * 当前元素的索引 * 当前元素所属的数组对象。 * ] * @return {[type]} [返回计算结果] /let result=arr.reduce((total,currentValue,index,arr)=>{ return total+currentValue;});console.log(result)//391算个平均数let arr=[12,58,99,86,45,91]/* * [description] * @param {[type]} (total,currentValue,index,arr [ * 初始值, 或者计算结束后的返回值。 * 当前元素 * 当前元素的索引 * 当前元素所属的数组对象。 * ] * @return {[type]} [返回计算结果] /let result=arr.reduce((total,currentValue,index,arr)=>{ if(index!=arr.length-1){ //如果不是最后一次 return total+currentValue; //求和 }else{ //最后一次 return (total+currentValue)/arr.length; //求和再除于长度个数 }});console.log(result)//65.16666666666667filter 过滤器检测数值元素,并返回符合条件所有元素的数组。定义和用法filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。注意: filter() 不会对空数组进行检测。注意: filter() 不会改变原始数组。需求,能被3整除的留下,不能的去除let arr=[12,58,99,86,45,91]//需求,能被3整除的留下,不能的去除/* * [description] * @param {[type]} (currentValue,index,arr [ * 当前元素的值 * 当前元素的索引值 * 当前元素属于的数组对象 * ] * @return {[type]} [返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组。] /let result=arr.filter((currentValue,index,arr)=>{ if(currentValue%3==0){ return true; }else{ return false; }});console.log(result)//[12, 99, 45]简写let arr=[12,58,99,86,45,91]//需求,能被3整除的留下,不能的去除/* * [description] * @param {[type]} (currentValue,index,arr [ * 当前元素的值 * 当前元素的索引值 * 当前元素属于的数组对象 * ] * @return {[type]} [返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组。] /let result=arr.filter((currentValue,index,arr)=>{ return currentValue%3==0;});console.log(result)//[12, 99, 45]再次简写let arr=[12,58,99,86,45,91]let result=arr.filter(currentValue=>currentValue%3==0);console.log(result)//[12, 99, 45]forEach 循环(迭代)数组每个元素都执行一次回调函数。forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。注意: forEach() 对于空数组是不会执行回调函数的。字符串多了两个新方法字符串模板多了两个新方法startsWith() 表示参数字符串是否在原字符串的头部,返回布尔值startsWith应用let str=‘http://blog.huanghanlian.com’if(str.startsWith(‘http://’)){ console.log(‘普通网址’);}else if(str.startsWith(‘https://’)){ console.log(‘加密网址’);}else if(str.startsWith(‘git://’)){ console.log(‘git网址’);}else if(str.startsWith(‘svn://’)){ console.log(‘svn网址’);}else{ console.log(‘其他’)}endsWith() 表示参数字符串是否在原字符串的尾部,返回布尔值let str=‘http://blog.huanghanlian.com/sitemap.xml'if(str.endsWith('.xml')){ console.log(‘网站地图’);}else if(str.endsWith(’.jpg’)){ console.log(‘图片’);}else if(str.endsWith(’.txt’)){ console.log(‘文本文件’);}else{ console.log(‘其他’)}//网站地图includes() 表示是否在原字符串找到了参数字符串,返回布尔值字符串模板模板字符串有两个能力能直接把变量塞到字符串中去。可以折行平常写字符串有两种写法,let str=“abc”;let str2=‘efg’;一种是单引号,一种是双引号。在js里都能用。没什么区别现在出来一种新的字符串let str=abc;这种符号叫反单引号简单使用例子let a=12;let str=a${a}bc;console.log(str);//a12bc反单引号中的美元符号带上花括号他的作用就是把变量直接塞进字符串里面去。例子以前字符串拼接let title=‘我是标题’;let content=‘我是内容’;let str=’<div class=“wrap”>\ <h1>’+title+’</h1>\ <p>’+content+’</p>\ </div>’;document.body.innerHTML=str;console.log(str);有了字符串模板后的写法let title=‘我是标题’;let content=‘我是内容’;let str=’<div class=“wrap”>\ <h1>’+title+’</h1>\ <p>’+content+’</p>\ </div>’;let str2=&lt;div class="wrap"&gt; &lt;h1&gt;${title}&lt;/h1&gt; &lt;p&gt;${content}&lt;/p&gt; &lt;/div&gt;;document.body.innerHTML=str2;console.log(str2);面向对象-基础面向对象es5面向对象function User(name,pass){ this.name=name; this.pass=pass;}//给这个类加原型方法/* * [showName 获取用户名] * @return {[type]} [返回用户名] /User.prototype.showName=function(){ console.log(this.name); return this.name;};/* * [showPass 获取用户密码] * @return {[type]} [返回用户密码] /User.prototype.showPass=function(){ console.log(this.pass); return this.pass;};var ul=new User(‘黄继鹏’,‘abc’);//调用类方法ul.showName();//黄继鹏这样写的缺点类和构造函数不分,类散开了,先声明一个构造函数,然后对函数原型添加方法ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。 先看如何定义一个class类:class User { constructor(name) { // 构造器,相当于es5中的构造函数 this.name = name // 实例属性 } showName(){ // 定义类的方法,不能使用function关键字,不能使用逗号分隔 console.log(this.name) }}var foo = new User(‘黄继鹏’)foo.showName();//黄继鹏(1)constructores6中class类专用的构造器,相当于之前定义的构造函数,每个类都必须有constructor,如果没有则自动添加一个空的constructor构造器。创建实例的时候自动执行constructor函数constructor中的this指向实例,并且默认返回this(实例)(2)class类的prototype其实class的基本类型就是函数(typeof User = “function”),既然是函数,那么就会有prototype属性。类的所有方法都是定义在prototype上class User { constructor() { // … } toString() { // … } toValue() { // … }}User.toValue() // err User.toValue is not a functionUser.prototype.toValue() // 可以调用toValue方法// 等同于User.prototype = { constructor() {}, toString() {}, toValue() {},};(3)类的实例类的实例只能通过new来创建除了静态方法,定义在类上的所有的方法都会被实例继承除非定义在类的this对象上才是实例属性,否则都是定义在类的原型(prototype)上//定义类class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return ‘(’ + this.x + ‘, ’ + this.y + ‘)’; }}var point = new Point(2, 3);point.toString() // (2, 3)point.hasOwnProperty(‘x’) // truepoint.hasOwnProperty(‘y’) // truepoint.hasOwnProperty(’toString’) // falsepoint.proto.hasOwnProperty(’toString’) // true(4)静态方法如果在类中定义一个方法的前面加上static关键字,就表示定义一个静态方法,静态方法不会被实例继承,但会被子类继承,所以不能通过实例使用静态方法,而是通过类直接调用class User { constructor(name){ this.name = name } static show(){ console.log(‘123’) }}class VipUser extends User{}VipUser.show() // 123User.show() // 123var foo = new User(‘foo’)foo.show() // foo.show is not a function(5)静态属性class的静态属性指的是 Class 本身的属性,目前只能通过Class.propName定义静态属性静态属性可以被子类继承,不会被实例继承class User{}User.name = ‘foo’ // 为class定义一个静态属性class VipUser extends User{}console.log(VipUser.name) // foovar foo = new User()console.log(foo.name) // undefined(6)私有属性和私有方法es6是不支持私有属性和私有方法,但是日常需求可能会用到私有属性和私有方法,所以目前有一些提案,不过只是提案,尚未支持。类的继承ES5写法function User(name,pass){ this.name=name; this.pass=pass;}User.prototype.showName=function(){ console.log(this.name);};/* * [showPass 获取用户密码] * @return {[type]} [返回用户密码] /User.prototype.showPass=function(){ console.log(this.pass);};//继承user类function aUser(name, pass, type) { User.call(this, name, pass); this.type = type;};aUser.prototype.showType = function() { console.log(this.type);};var ul=new User(‘黄继鹏’,‘abc’);ul.showName()//黄继鹏var ull=new aUser(‘继小鹏’,‘ccc’,‘男’);ul.showName();//继小鹏ull.showType();//男//aUser继承类User类,并且有自己的方法class通过extends关键字实现继承:class User { constructor(name){ this.name = name } show(){…}}class VipUser extends User{ constructor(vipName){ // 子类的构造器 super(vipName) // 调用父类的constructor。相当于User.prototype.constructor.call(this,vipName) } showVip(){…}}var v = new VipUser(‘foo’) // 创建实例v instanceof VipUser // v是子类VipUser的实例v instanceof User // v还是父类User的实例(1)supersuper可以当做函数使用,也可以当做对象使用。当做函数使用super作为函数调用时,代表父类的构造函数,就是在子类的构造器中执行父类的constructor函数以获取父类的this对象,因为子类没有自己的this对象,所以ES6规定子类必须在constructor中执行一次super函数。super()函数只能在子类的constructor中执行,不能在其他地方执行。虽然super代表父类的构造器,但是super()在执行时内部的this指向子类,所以super()就相当于User.prototype.constructor.call(this)。当做对象使用super可以作为对象调用父类的属性和方法,在子类的普通方法中,指向父类的原型对象(即User.prototype);在子类的静态方法中,指向父类。class User { constructor(){ this.x = ‘hello’ } show() { return 2; }}class VipUser extends User { constructor() { super(); console.log(super.show()); // 2 此时super指向User.prototype,相当于User.prototype.show() console.log(super.x) // undefined 无法访问实例属性 }}let vip = new VipUser();console.log(vip.x);//hello由于super对象在普通函数中使用super指向User.prototype,所以super只能访问父类的原型上的方法,没法访问父类的实例属性和实例方法。ES6规定如果在子类中使用super对象调用父类的方法时,方法内部的this指向子类class User { constructor() { this.x = 1 } show() { return this.x; }}class VipUser extends User { constructor() { super(); this.x = 2 console.log(super.show()) // 2 此时show()方法内部的this指向子类,所以输出2,而不是1 }}let vip = new VipUser();上述代码中虽然super.show()调用的是User.prototype.show(),但是由于通过super对象调用父类方法时,方法内部的this指向子类,所以super.show()相当于 super.show().call(this),也就是User.prototype.show().call(this)在子类的静态方法中super对象指向父类,而不是父类的原型(User.prototype)。class User { constructor() { this.x = 1 } static fn() { console.log(‘父类静态方法’) }}class VipUser extends User { constructor() { super(); this.x = 2 } static childFn() { super.fn() // 相当于User.fn() }}VipUser.childFn()(2)类的prototype和proto属性在es5中每一个对象都有proto属性,指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖,同时有prototype属性和proto属性,因此同时存在两条继承链。子类的proto属性,表示构造函数的继承,总是指向父类。子类prototype属性的proto属性,表示方法的继承,总是指向父类的prototype属性。class User {}class VipUser extends User {}VipUser.proto === User // trueVipUser.prototype.proto === User.prototype // true(3)实例的proto属性子类实例的proto属性指向子类的原型(子类的prototype),子类实例的proto属性的proto属性指向父类的原型(父类的prototype)class User {}class VipUser extends User {}var vip = new VipUser()console.log(vip.proto === VipUser.prototype) // trueconsole.log(vip.proto.proto === User.prototype) // true面向对象-应用面向对象应用—reactreact介绍:组件化在react里一个组件就是一个class,依赖于JSXjsx==babel==browser.js<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title></title> <script src=“https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src=“https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src=“https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> <script type=“text/babel”> let oDiv=document.getElementById(‘div1’); ReactDOM.render( <span>123</span>, oDiv ); </script></head><body> <div id=“div1”></div></body></html>ReactDOM.render( <span>123</span>, oDiv );这种语法为什么会支持呢?这个就是jsx和普通js最大的差别。你可以认为jsx是普通js的扩展版本既然是扩展版本,那肯定会多出一些功能来。如果不写引号,不是字符串同时长得像html,他就是可以要创建一个标签切换搭配重点react是一个基于组件组件与class形式存在我想写一个组件,作为一个组件是不是应该有一些基本的功能,比如我能被渲染,我有一些状态,我有生命周期,换句话说我现在不是从零开始写一个class,我需要很多基础的类的集成。class Test extends React.Component{ }类继承最大的意义在于一切不用从零开始一个类需要有构造函数constructor.作为继承类,在构造函数中需要继承父级的属性<!DOCTYPE html><html lang=“en”><head><meta charset=“UTF-8”><title></title><script src=“https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script><script src=“https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script><script src=“https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script><script type=“text/babel”>class Test extends React.Component{ //构造函数接到任何参数,都给继承父级类 constructor(…args){ super(…args); } render(){ return <div>hello world</div> }}let oDiv=document.getElementById(‘div1’);ReactDOM.render( <Test>123</Test>, oDiv);</script></head><body> <div id=“div1”></div></body></html>组件套组件方法例子<!DOCTYPE html><html lang=“en”><head><meta charset=“UTF-8”><title></title><script src=“https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script><script src=“https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script><script src=“https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script><script type=“text/babel”>class Item extends React.Component{ //构造函数接到任何参数,都给继承父级类 constructor(…args){ super(…args); } render(){ return <div>{this.props.str}</div> }}class List extends React.Component{ //构造函数接到任何参数,都给继承父级类 constructor(…args){ super(…args); } render(){ ///写法1 let aItem=[]; for(let i=0;i<this.props.arr.length;i++){ aItem.push(<Item key={i} str={this.props.arr[i]}></Item>); }/ /// 写法2 let aItem=this.props.arr.map((str,index)=>{ return <Item key={index} str={str}></Item> }) return <div> {aItem} </div>/ // 写法3 return <div> { this.props.arr.map((str,index)=>{ return <Item key={index} str={str}></Item> }) } </div> }}let oDiv=document.getElementById(‘div1’);ReactDOM.render( <List arr={[‘abc’,’efg’,‘hij’]}></List>, oDiv);</script></head><body> <div id=“div1”></div></body></html>PromisePromise的中文含义是承诺了解Promise之前。先来了解下同步异步异步:操作之间没啥管系,同时进行多个操作同步:同时只能做一件事同步异步的优缺点异步:代码更复杂同步:代码简单一个页面可能会有多个请求比如淘宝网页,banner区域,侧边栏,导航栏,右侧栏,信息商品等都是由镀铬接口异步请求组成这就回造成代码逻辑复杂按照以往前端ajax请求写法。一个请求成功后继续请求嵌套。逻辑会变得异常费劲异步$.ajax({ type: ‘post’, url: ‘/api/banner’, success:function(result){ console.log(‘成功’); $.ajax({ type: ‘post’, url: ‘/api/1’, success:function(result){ console.log(‘成功’); $.ajax({ type: ‘post’, url: ‘/api/banner’, success:function(result){ console.log(‘成功’); $.ajax({ type: ‘post’, url: ‘/api/banner’, success:function(result){ console.log(‘成功’) }, error:function(error){ console.log(‘失败’) }, }) }, error:function(error){ console.log(‘失败’) }, }) }, error:function(error){ console.log(‘失败’) }, }) }, error:function(error){ console.log(‘失败’) },})同步let banner_data=ajax_async(’/banner’);let banner_data1=ajax_async(’/banner1’);let banner_data2=ajax_async(’/banner2’);let banner_data3=ajax_async(’/banner3’);let banner_data4=ajax_async(’/banner4’);你会发现异步处理性能好,用户体验好,但实际代码复杂要是同步方式页面用户体验不好这个时候幻想一下,我能不能像同步方式来写代码。也像异步一样请求数据。Promise就能做到这个工作Promise–消除异步操作用同步书写方式,来书写异步方法Promise如何使用需要使用promise的时候,你需要new一个promise对象。这个对象接收一个参数,是一个函数。将异步的代码写在函数里这个函数两个参数resolve决心reject拒绝//封装Promise ajaxlet p=new Promise(function(resolve,reject){ //异步代码块 //resolve–成功了 //reject–失败了 $.ajax({ type: ‘post’, dataType:‘json’, url: ‘/api/banner’, success:function(result){ resolve(result); }, error:function(error){ reject(error); }, })});//使用Promise ajax封装//当Promise调用有结果了就会调用then//then有两个参数,都是函数,第一个是resolve,第二个是rejectp.then((result)=>{ console.log(result);},(error)=>{ console.log(error);})function createPromise(url){ return new Promise(function(resolve,reject){ //异步代码块 //resolve–成功了 //reject–失败了 $.ajax({ type: ‘post’, dataType:‘json’, url, success:function(result){ resolve(result); }, error:function(error){ reject(error); }, }) });}createPromise(’./aa’).then((res)=>{ console.log(res)},(err)=>{ console.log(err)})function createPromise(url){ return new Promise(function(resolve,reject){ //异步代码块 //resolve–成功了 //reject–失败了 $.ajax({ type: ‘post’, dataType:‘json’, url, success:function(result){ resolve(result); }, error:function(error){ reject(error); }, }) });}Promise.all([ createPromise(’./aa’), createPromise(’./bb’)]).then((res)=>{ let [arr1,arr2]=res},(err)=>{ console.log(err)})generator-认识生成器函数generator的作用generator-生成器生成器是程序里面的一个概念,可以依靠它生成一堆东西Generator可以理解为生成器,和普通函数没多大区别,普通函数是只要开始执行,就一直执行到底,而Generator函数是中间可以停,搭配使用next函数继续执行。生动的比喻普通函数好比坐飞机,飞机起飞,不到目的地中途是不会降落的Generator好比于出租车。可以随叫随停。停了再走,走了再停(1)定义一个Generator函数function * fn(){ alert(‘a’) yield alert(‘b’)}var f = fn()f.next() // af.next() // b直接调用Generator函数,是什么都不执行的,调用第一个next()才开始执行,一直执行到第一个yield停止,第二次调用next(),从第一个yield执行到第二个yield停止,依次类推现在疑惑,在真实场景中,我为什么要让一个函数停呢?刚才举个了出租车的例子,说白了,你为什么要让出租车司机停车,肯定是你有事情,你要去忙,或者要求拿什么东西,或者见什么朋友。等你事情办完了,还再回来。所以Generator特别适合一个场景。比如说你要请求数据。请求数据不是瞬间就能回来的,这个时候就需要暂停,来等他结果过来。再继续执行下面的操作。/** * 普通函数在执行过程中需要请求得到结果再执行对应代码,就会出现代码嵌套再嵌套 /function 函数(){ 代码… ajax({ 代码… })}/* * Generator函数可以让代码在那一步暂时暂停 拿到数据后再继续往下走 */function *函数(){ 代码… yiels ajax(xxx) 代码…}Generator是怎么做到走走停停的?其实本质是用Generator函数生成了一堆小函数比方说fn函数function * fn(){ alert(‘a’) yield alert(‘b’)}var f = fn()f.next() // af.next() // b其实他在背后生成了两个小函数function fn_1(){ alert(‘a’)}function fn_2(){ alert(‘b’)}当然这个过程我们是看不见的相当于把一个大函数切分成了两个小函数第一次next的时候他走的是fn_1第二次next的时候走的是fn_2generator-yieldyield和nextyield代表暂时暂停执行,next代表继续执行。yield和next可以传参数,也可以有返回值yield可以传参function *show(){ console.log(‘a’) let a=yield; console.log(‘b’) console.log(a)}let gen=show();gen.next(12)gen.next(5)//a//b//5第一次执行next的时候执行黄色框代码第二次执行红色框的代码传参的时候通过yield来传参的时候,第一个next是无效的,如果想给第一个过程传参需要使用传统方法,在使用函数时传参function *show(num1,num2){ console.log(${num1},${num2}) console.log(‘a’) let a=yield; console.log(‘b’) console.log(a)}let gen=show(11,12);gen.next(12);//没法给yield传参gen.next(5)//11,12//a//b//5yield返回function *show(){ console.log(‘a’) let a=yield 12; console.log(‘b’)}let gen=show(11,12);let res1=gen.next();console.log(res1)let res2=gen.next()console.log(res2)//a//{value: 12, done: false}//b//{value: undefined, done: true}value是yield 返回的参数done代码函数是否走完为什么第二次执行完value是空因为第二次next是执行的最后一道程序,最后一道程序就没有yield 了,如果想返回东西需要使用returnfunction *show(){ console.log(‘a’) let a=yield 12; console.log(‘b’) return 111;}let gen=show(11,12);let res1=gen.next();console.log(res1)let res2=gen.next()console.log(res2)//a//{value: 12, done: false}//b//{value: 111, done: true}yield 到底是个啥generator-实例:runner这种Generator函数适用多个异步请求之间有逻辑分析的情况,比如有一个需求,先请求用户数据,根据用户数据的类型判断用户是普通用户还是VIP用户,然后再根据判断结果请求普通商品数据或者VIP商品数据// 借助runner脚本,runner脚本规定Generator函数执行完一个next之后自动执行下一个nextrunner(function() * (){ let userData = yield $.ajax(…) // 请求用户数据 if(userData.type === ‘vip’) { let goods = yield $.ajax(…) // 请求vip商品数据 } else { let goods = yield $.ajax(…) // 请求普通商品数据 }})第一次yield ajax其实是Promise对象,将Promise对象yield 出去。yield 给了runner对象将数据请求完成给data1这个函数暂停了使用Generator函数使得代码看起来更像同步代码,其实使用Promise同样可以实现这种效果,只不过得需要在then()函数中嵌套请求。异步请求的几种方式回调写法$.ajax({ type: ‘post’, url: ‘/api/banner’, success:function(result){ console.log(‘成功’); $.ajax({ type: ‘post’, url: ‘/api/1’, success:function(result){ console.log(‘成功’); $.ajax({ type: ‘post’, url: ‘/api/banner’, success:function(result){ console.log(‘成功’); $.ajax({ type: ‘post’, url: ‘/api/banner’, success:function(result){ console.log(‘成功’) }, error:function(error){ console.log(‘失败’) }, }) }, error:function(error){ console.log(‘失败’) }, }) }, error:function(error){ console.log(‘失败’) }, }) }, error:function(error){ console.log(‘失败’) },})Promise写法function createPromise(url){ return new Promise(function(resolve,reject){ //异步代码块 //resolve–成功了 //reject–失败了 $.ajax({ type: ‘post’, dataType:‘json’, url, success:function(result){ resolve(result); }, error:function(error){ reject(error); }, }) });}Promise.all([ createPromise(’./aa’), createPromise(’./bb’)]).then((res)=>{ let [arr1,arr2]=res},(err)=>{ console.log(err)})Generator写法runner(function() * (){ let userData = yield $.ajax(…) // 请求用户数据 if(userData.type === ‘vip’) { let goods = yield $.ajax(…) // 请求vip商品数据 } else { let goods = yield $.ajax(…) // 请求普通商品数据 }})Promise和Generator相比,Generator并没有特别的省事Promise也有它不适用的地方。我如果是写死要请求接口。那么Promise和Generator确实没太大区别,Generator他的优点在于适合参杂一些逻辑比方说在请求一个接口拿到用户信息,根据信息判断他该去请求哪些不同的接口感觉比普通嵌套还麻烦带逻辑-Generator// 借助runner脚本,runner脚本规定Generator函数执行完一个next之后自动执行下一个nextrunner(function() * (){ let userData = yield $.ajax(…) // 请求用户数据 if(userData.type === ‘vip’) { let goods = yield $.ajax(…) // 请求vip商品数据 } else { let goods = yield $.ajax(…) // 请求普通商品数据 }})Promise适合一次请求一堆场景Generator适合逻辑性请求处理generator-实例:KOAKOA是nodejs的框架async awaitasync其实就是对Generator的封装,只不过async可以自动执行next()。async function read () { let data1= await new Promise(resolve => { resolve(‘100’) }) let data2 = await 200return 300}async 返回值async默认返回一个Promise,如果return不是一个Promise对象,就会被转为立即resolve的Promise,可以在then函数中获取返回值。async必须等到里面所有的await执行完,async才开始return,返回的Promise状态才改变。除非遇到return和错误。async function fn () { await 100 await 200 return 300}fn().then(res => { console.log9(res) // 300})awaitawait也是默认返回Promise对象,如果await后面不是一个Promise对象,就会转为立即resolve的Promise如果一个await后面的Promise如果为reject,那么整个async都会中断执行,后面的awiat都不会执行,并且抛出错误,可以在async的catch中捕获错误async function f() { await Promise.reject(’error’); await Promise.resolve(‘hello world’); // 不会执行}f().then(res =>{}).catch(err=>{ console.log(err) // error})如果希望一个await失败,后面的继续执行,可以使用try…catch或者在await后面的Promise跟一个catch方法:// try…catchasync function f() { try { await Promise.reject(‘出错了’); } catch(e) { } return await Promise.resolve(‘hello world’);}f().then(v => console.log(v)) // hello world// catchasync function f() { await Promise.reject(‘出错了’) .catch(e => console.log(e)); // 出错了 return await Promise.resolve(‘hello world’);}f().then(v => console.log(v)) // hello world