手把手教es6,ecma2015,力求作通俗易懂的es6教程

变量声明符let

1.1 :严格界定范围和使用顺序,做用域消失变量消失,必须先声明再使用

console.log(test)//wrong
let test='1';

1.2 :重复定义

  • 禁止代码区域中重复
let test='1'
     let test='1'//wrong
  • 思考是否能够在switch中使用?:答案在本章最后
  • 禁止在循环体中与同名var变量重复
var test='1'
     for(let test=1;test++;test<10)
{
         alert(test)//wrong
         }

1.3 :let应用:闭包问题的优化

  • 问题1:请思考代码执行结果 function A(){ for( a=0;a<10;a++) { ClassB=function B(){ alert(a)
    } } } A() ClassB()
  • 结果1:弹出10次 从0到9 ? 代码执行函数定义,执行了10次定义过程,
    前1次覆盖后1次的定义过程,每次循环均覆盖
    内存方法区里被写入函数定义,发现同名函数会替换函数方法体
    在函数调用的时候 只会执行1次alert,结果1确定是错的
  • 结果2:弹出1次 9
    结果1是错的,那么结果2中只弹出1次是对的
    到底弹出几呢?循环9次最后9应该是9
    难道是弹出9?
  • 结果3:undefined
    得出这个答案的朋友基础知识很是棒,对函数调用和内存的问题掌握的很好:
    在函数A中声明了另外的内部函数B 内部B使用了外部函数A中的变量a 咱们先思考下函数调用的问题吧 变量a位于函数A中, A函数发生调用之后 A函数出栈
    A中定义的全部变量会被清空 一样变量a消失 但是当咱们调用函数B的时候须要用到a 那么a消失了因此是undefined
  • 正确答案:alert(10) 根据结果3得知:函数内定义了另外函数,另外函数使用了外部函数的变量
    熟悉java的同窗:是否是和java的内部类和外部类很像? 在发生函数定义的时候 javaScript引擎发现内部函数在使用这个变量
    该变量须要共享 ,不能存到栈中,由于栈中会被清空数据 对应于JAVA程序中 该变量会被存储到内存的方法区
    和全部static变量位于同1个区域,该区域内的变量要加final关键字
    变量共享之后内部函数和外部函数均操做该变量的引用
    外部函数在代码执行完毕时候注意最后还有个a++ 此刻a=10;
    此刻内部函数也在使用这个a(共享的a对象引用) 那么内部函数在使用a(共享的a)的时候,a(共享的a)已经被修改为10了 因此内部函数再调用,访问a的时候 弹出alert(10)
  • 闭包解决方案1:使用函数封装闭包代码 形式参数接受共享变量 //使用函数封装闭包代码 //那么在函数内使用的是形式参数i接受共享变量a //在发生闭包调用的时候 直接调用这个函数把共享变量传递进来i=a; //方法中的形式参数但是每次都出栈的,咱们操做的都是形式参数 //第一次循环操做的是形式参数i 此刻i=a,a是0那么i=0,方法写入方法区 //第2次循环i=a,a是1 弹出alert(i)弹出1 方法写入方法区替换 //第10次循环i=a,a是9 弹出alert(i)是9 方法写入方法区替换 //A执行完毕修改了a,a++此刻a=10 //可是方法区里的方法操做的是alert(i) 再也不是a了,写入方法区那一时刻i=9 //因此结果是alert(9) function A() { var _loop = function _loop(i) { ClassB = function B() { alert(i); }; }; for (var a = 0; a < 10; a++) { _loop(a); } } A(); ClassB();
  • 闭包解决方案优化: //每次都写个函数定义形式参数好麻烦,使用let可完成上述操做 function A(){ for(let a=0;a<10;a++) { ClassB=function B(){ alert(a)
    } } } A() ClassB()

变量自动析构

1.4 普通赋值析构

[对象属性.赋值的变量]=对象  
 [a.A,b.B,c]=对象  
 获取对象的属性a 赋值给变量A
 b同a赋值
 获取对象的属性c 没有发现要赋值的变量  
 自动生成同名变量c 则c=对象c属性对应的数值

1.5 参数传递析构

var func= function({a,b,c}){
  console.log(a)//获取实际参数对象中的属性a赋值 没有赋值变量 生成同名变量a
  console.log(b)
  console.log(c)
 }  
 func({a:1,b:2,c:3})

对象展开符和动态参数:

1.6函数调统阶段使用。。。展开

//参数展开
function sum (x, y, z) {return x+y+z }
  args=[0,1,2]
  sum(..args)//自动获取数组args中的每1个对象而且赋值

1.7 函数定义阶段使用。。。动态传参 做为数组接受全部参数

function sum(..x)
{
  console.log(x)
}      
  sum(1,2,3)

1.8 赋值阶段使用:拷贝数据

var arr = [1,2,3];
var arr2 = [...arr];//展开[1,2,3]而且拷贝返回新的对象1,2,3

箭头函数

1.9 在箭头函数中实现代码使用{},大括号表示代码执行

(a)={alert(a)}    ==== function(a){alert(a)}

1.10 箭头函数返回对象(),小括号表示对象返回

(a)={return a}    ==== (a)=>(a) ===function(a) {return a}

1.11 操做this

var func=(a)={alert(this)}
 this是什么?区别于普通函数this再也不是函数调用这
p=new Person()
  p.func();//func中的this再也不是Person对象
  箭头函数中的this是箭头函数声明所在类,或者所在function的对象
  箭头函数在哪里声明的this就是谁  
  优势:节省了绑定this操做  
  以往在jsx中操做点击事件 要处理当前组件对象数据  
  须要手动bind(this)这样才能够访问到当前组件  
  如今只须要用箭头函数 自动绑定

1.12 链式调用

var func=(a)=>(b)=>{alert(b)}
b是什么?  
   b是执行一次调用:func()  
   之后再次执行调用传递的参数  
   func()('this is b')  
   即b是用来接收第2次调用的形式参数  
   在发生调用的时候 第2次调用的参数赋值给b
  • 优势: 使用高阶函数实现柯里化操做 再调用函数时 业务更加清晰
    未柯里化: func(a,b,c) { 分别使用a,b,c处理业务 }
    func(1,2,3) 柯里化 : func(a) { a处理业务 return func(b) { b处理业务 return func(c) { c处理业务 } } } func(1)(2)(3) 即把之前须要传递多个参数的函数操做
    定义成只须要传递1个参数
    每次操做结束后再次调用 接受下1个参数
    完成之前多个参数的需求
  • 缺点: 自动生成了大量闭包,变量过多被共享到内存方法区里注意内存泄漏

1.13 链式调用的应用

  • 实现异步操做:(也叫作thunk封装)
    需求:获取数据之后再更新HTML页面
    使用指定api:UpdateHtml(getData())
    getData为异步ajax获取数据 UpdateHtml为更新HTML
    ajax响应成功之后才会更新 如何在调用UpdateHtml此函数的时候 不更新页面
    让getData再ajax获取数据成功之后在更新页面呢? //定义异步操做
    Update=function(){ document.getElementById('root').innerHTML="数据更新成功" } //该UpdateHtml函数此刻已经成为异步函数 //调用它自己不会去更新dom //咱们在使用getData的时候 经过链式函数能够获取到真正的更新DOM的Update方法 //此类封装叫作thunk var UpdateHtml=(getdata)=>{ getdata()(Update)}//调用传递过来的getdata函数而且执行高阶调用,把更新dom的函数做为高阶函数的参数传递过去 //函数声明完毕开始函数调用 //如何在getData函数里获取到Update方法呢? var getData=()=>assignByUpdate=>{ //getData函数发送ajax请求更新dom //经过链式调用 声明assignByUpdate //该变量assignByUpdate被UpdateHtml第2次调用的时候传递的参数赋值 //var UpdateHtml=(getData)(Update) //assignByUpdate==Update //true ajax( success:function(){ assignByUpdate } ) } UpdateHtml(getData)
  • Redux里使用异步action会大量使用上述的thunk操做

yield :代码执行流程可控

1.14 定义:

给函数A标记*   
  函数被*标记之后表示该函数会被while(true)无限循环监控  
  在标记有yield的代码的地方会暂停下来
  由于有while监控 因此当使用特殊指令的时候 代码才会继续执行
  该函数会返回1个对象用于控制代码执行流程   使用返回对象.netx()代码才会继续执行
  //发现星号标记 开启无限循环while(1)监控代码执行步骤
  var work=function* A()
  {
 yield  console.log(1) //发现yield标记代码会在此处中止
  console.log(2)
  console.log(3)
 }
 work.next();//从yield出继续执行下面的代码

1.15 对比上面的thunk封装,yield更加简洁

//首先在使用yiled上的函数上加*号
     //异步ajax获取数据
     //标记yield的代码行被javaScript引擎解析
     //代码在此处暂停,等待接受指令
     var result=*function UpdateHTML(){
     yiled getData();
     document.getElementById('root').innerHTML="数据更新成功"
     }
     function getData(){
     if('数据查询成功')
     {
     result.next();//发出指令 容许返回result标记*号的方法继续从yield处执行
     //即UpdateHTML()从yield标记开始继续执行了
     }
     }

1.16 yield应用:redux saga

如下代码为redux saga示例
 经过*和yield来控制代码执行流程
 缺点:while无限循环影响性能
 import { call, put } from 'redux-saga/effects'
 export function* fetchData(action) {
 try {
 const data = yield call(Api.fetchUser, action.payload.url);
 yield put({type: "FETCH_SUCCEEDED", data});
 } catch (error) {
 yield put({type: "FETCH_FAILED", error});
 }
 }

import指令和export指令 :

1.17 export:变量导出 不一样位置js文件变量交互

在1.js中定义了2个变量
 var name='李雷'
 var age="15"
 在2.js中如何使用这2个定义好的变量呢?  
 咱们只须要在1.js中
 var name='李雷'
 var age="12"
 //添加以下代码
 export {name,age}  
 在2.js中使用就可使用了

1.18 import:使用导出的变量

import {name,age} from "./1.js"    
console.log(name) //'李雷'
console.log(age)//12

1.19 变量导出导入的方案:

普通导出export {name,age}
-- 使用的时候必须指定name和age:即import {name,age} from "./1.js"
-- 若是在2.js中使用的时候不想使用name和age,想用your1_name或your_age2来接受  
    1.js中的2个变量怎么办?
-- 使用默认导出
默认导出 export default 只支持一个变量导出  
export default name
-- 使用的时候 import any from "./1.js"  
-- any能够任意起名  
-- console.log(any)//李雷
批量导出1.js  
-- export const name = '李雷'
-- export const age = '12'
批量导入2.js  
-- import * as types from '1.js'
使用types对象封装1.js中全部export的变量  
export中变量的定义名字是types对象的属性  
export中改变量的对应的数值是types对象的改属性的数值
console.log(type)//{name:'李雷',age:'12' }

promise :异步控制:

1.20 变量导出导入的方案:

在数据状态变化的时候,去执行预先定义好的方法
 //定义好了异步对象 分别封装了2种状态
 //异步函数须要传递个回调函数做为状态切换方法
 //该回调函数中2个参数resolve, reject
 //resolve是resolve(data)用于切换resolve状态
 //reject是reject(data)用于切换reject状态
 //data为两种状态切换时传递的参数
 var notify=new Promise(function (resolve, reject) {
 if (ready) {
  resolve("请求成功");
 } else {
 reject("网络出错");
 }
 //若是对象状态发生切换,即手动调用了resolve(data),reject(data)
 //会触发promis对象的then中定义的对应的回调方法,即参数1函数和参数2函数
 nofity.then(func1(data){alert(data)//会显示"请求成功"},func2(data)//会显示"请求失败")

代理模式(有的浏览器不支持Proxy):

1.21 代理模式概述

代理对象调用本身的一切方法时候均会被拦截从而实现业务操做
静态代理:被代理的原始类方法发生变化须要手动修改地代理里方法  
动态代理:根据被代理的原始类自动生成代理类对象,  
原始类方法变化 代理对象按照原始类生成也随着变化  
不用手动修改,java中spring aop中用的基本都是asm动态代理

1.22 代理模式应用

代码中有10个service类 ,之前的业务是不涉及到事务操做  
如今需求是在service中查询数据库的时候开启事务和关闭事务  
那么若是没有代理模式 须要操做10个类文件一步一步改  
为了不在每一个service对象中均开启事务 关闭事务  
咱们能够把原始的service对象修改为代理对象,  
那么在代理对象中查询数据库 等相关操做均会被拦截   
从而开启事务 关闭事务  
咱们仅仅在1个类中修改   
就能够完成之前在全部service对象中修改的效果

1.23 代理模式ES6应用

const target = {
name: '李雷',
       age: 15
       };
     let handler = {  
     //获取被代理对象的属性会调用
     get(target, key, proxy) {
     //容许方法继续执行下去
     return Reflect.get(target, key, proxy);
     }
}
//构建代理对象
const proxy = new Proxy(target, handler);

新增长的数据结构和数组api

1.24 arrayObject.map(function(i){})

//迭代处理数组中的每一个元素i是迭代器,即每次迭代的对象

1.25 arrayObject.reduce(回调函数(p,c){},初始数值)

//若是指定了初始数值 则第一次迭代的p返回数值就是这个初始数值
  //若是没有指定初始数值,则第一次的带返回的p默认是0
  //c是当前指针位置元素
  //[1,2,3].reduce(function(p,c){return p+c},0)
  // 第一次迭代函数返回0,因此p=0,指针指向0号位置数值是1因此c=1
  // 第二次迭代p=上次的返回数值0+1 c是2
  //依次类推0+1+2+3是结果
  arrayObject.reduce(回调函数(p,c){},初始数值)  
  //find,filter api相似map

#类和属性和方法定义java

1.26 使用Class定义类

class Person
     { }

1.27 定义属性:经过构造器定义属性,区别于java的成员变量

class Person
     {
     constructor(name) {
       this.name = name;//定义属性name
       this.age=12 ;//定义属性age 这点和java有所区分 不须要成员变量
      }
     }

1.28 定义类方法:方法函数不要加function

class Person
     {
       constructor(name) {
       this.name = name;//定义属性name
       this.age=12 ;//定义属性age 这点和java有所区分 不须要成员变量
      }
      say(){
        console.log("hello");//类中方法千万不要加function
     }
     }

本章思考题答案

switch (i) {
 case 0:
 let a=0;
 break;
 case 1:
 let a=1; // TypeError for redeclaration.
 break;
 }
 由于switch做用域内禁止出现同名let变量
 即便break,在代码编译时会失效

版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)ajax

相关文章
相关标签/搜索