美文网首页React
React快速上手2-ES6语法

React快速上手2-ES6语法

作者: xinyiyake | 来源:发表于2019-02-19 10:09 被阅读0次

    在进一步学习react之前,你需要对js的新语法有一定的了解,因为有可能在react项目中我们会一遍又一遍的使用,如果你对js新语法有足够的了解,你可以跳过这一节。

    变量

    b变量是一种标识符号,你可以在接下来的程序中使用它,变量在使用前必须要声明。我们有三种方式来声明变量:var,let和const,这三种方式在声明之后的使用中会有所不同。

    1. var

    在ES2015之前,var是声明变量的唯一方式,它有下面这些特点:

    如果你给一个未声明的变量赋值,结构可能有所不同。在现代环境中,如果严格模式开启,你会得到一个错误,而在旧的环境中(或者严格模式被禁止)你将会得到一个全局变量。

    如果你声明了一个变量但是没给它赋值,这个变量将是undefined,直到你给其赋值为止。

    var a  //typeof a ==='undefined'
    

    你可以重复声明一个同一个变量,后面的值会覆盖掉前面的值。

    var a = 1
    var a = 2
    

    你也可以使用var同时声明多个变量并赋值。

    var a =1, b=2
    

    在函数外用var声明的变量都是全局变量,在任何地方都能访问到它,而在函数里面用var声明变量,就只有局部作用域,这个变量只能在函数内部访问,其它地方都不能访问。重要的一点是var声明的变量没有块级作用域,只有函数作用域。

    在函数内部,不管var声明的变量定义在函数的什么位置,哪怕是最后,它仍然能够在代码开始处获取到,因为javascript执行前会把所有的变量声明移动到最顶部(变量提升)。为了避免这种结果,所以每次都在函数最开始声明变量。

    2.使用let

    let是ES6提出的新特性,它比var多了一个快作用域,它的作用域限制在定义它的块,语句或表达式。
    现代js开发者可能只选择使用let,而完全丢弃var,在函数外使用let定义的变量不会变成一个全局变量,这点适合var声明的变量完全不一致的。

    3.使用const

    使用let或者var声明的变量在程序后面可能被更改,或者重新定义,但是如果用const声明的值,它将不能再被更改。

    const a = 'hello'
    

    但是如果使用const声明了一个常量,它的值是一个对象,那么还是可以去更改这个常量的属性值的。

    const a ={a:1}
    a.a =2 //a.a的值会被改成2
    

    const和let一样,也拥有块级作用域,我们会用const去声明一个在后面的代码中不会改变的值。

    箭头函数

    箭头函数是ES6中最具影响力的改变,并且在今天得到广泛的使用,它们和普通函数还是有一些区别的。从代码书写角度来看,箭头函数能够让我们以一种更简短的语法来定义一个函数:

    const oldFunction = function(){
      //...
    }
    

    变成:

    const arrowFunction = () => {
      //...
    }
    

    如果这个函数体只有一行表达式,你可以省略花括号并且只写成一行:

    const myFunction = () => doSomething()
    

    也可以通过括弧来传递参数:

    const myFunction = (param1,param2) => doSomething(param1,param2)
    

    如果只有一个参数,括弧也可以被省略掉

    const myFunction = param => doSomething(param)
    

    箭头函数对于一些简单的函数定义非常有用。

    1.箭头函数的return

    箭头函数有一个隐藏的return,如果箭头函数的函数体只是一个值,你可以不用return就能获得一个返回值。注意,花括号需省略掉:

    const myFunction = () => 'test'
    myFunction() //test
    

    如果返回值是一个对象,那么就要注意需要用括弧包括这个对象,否则会认为是箭头函数的函数体,报语法错误:

    const myFunction = () => ({a:1})
    myFunction() //{a:1}
    

    2.箭头函数的this

    this在js中是一个复杂的概念,不同的函数上下文或者不同的javascript模式(严格或不严格)都会影响this的指向。对于我们来说,掌握好this的概念非常重要,因为箭头函数在这点上和普通函数有着完全不一样的区别。
    在一个对象中,对象有一个普通函数定义的方法,在这里this指向是这个对象本身:

    const obj = {
      name: 'Tom',
      age: 16,
      writeName: function() {
        console.log(this.name)
      }
    }
    obj.writeName() //Tom
    

    调用obj.writeName()将会打印“Tom”

    而箭头函数的this继承的是当前执行上下文,箭头函数至始至终都没有绑定this,所以this值将会在调用堆栈中查找,如果使用箭头函数来定义上面的对象,结果将是打印“undefined”

    const obj = {
      name: 'Tom',
      age: 16,
      writeName: () => {
        console.log(this.name)
      }
    }
    obj.writeName() //undefined
    

    因为这一点,箭头函数不适合在对象方法中使用。
    箭头函数也不能作为构造函数使用,否则在创建一个对象时会抛出一个TypeError错误。
    在DOM绑定事件的时候,如果使用箭头函数作为事件的回调,里面的this指向为window,而如果是普通函数作为事件回调,this指向的则是该DOM:

    const ele = document.querySelector('#ele')
    ele.addEventListener('click',() => {
      // this === window
    })
    
    const ele = document.querySelector('#ele')
    ele.addEventListener('click',function() {
      // this === ele
    })
    

    使用扩展运算符(...)操作数组和对象

    扩展运算符...在现代javascript中是一种非常有用的操作方式。我们可以从操作数组开始熟悉这一操作:

    cosnt a = [1, 2, 3]
    

    然后你可以这样创建一个新数组

    cosnt b = [...a, 4, 5, 6] // [1, 2, 3, 4, 5, 6]
    

    你也可以复制一个数组

    cosnt c = [...a] // [1, 2, 3]
    

    同样的,你也可以这里来复制一个对象

    const newObj = { ...oldObj }
    

    如果是一个字符串使用...,我们会用字符串中的每个字符创建一个数组

    const str = 'Hello'
    const arr = [...str] // [H, e, l, l, o]
    

    ...运算符还可以用来很方便的传递函数参数

    cosnt func = (param1,param2) => {}
    cosnt paramArr = [1, 2]
    func(...paramArr)
    //在ES6之前你可能使用f.apply(null,a)来传递参数,但是这样不美观,可读性也很差
    

    扩展运算符在数组解构中运用:

    const arr = [1, 2, 3, 4, 5]
    [a1, a2, ...a3] = arr
    /**
    *a1 = 1
    *a2 = 2
    *a3 = [3, 4, 5]
    **/
    

    扩展运算符在对象解构中运用:

    const {p1, p2, ...p3} = {
        p1: 1,
        p2: 2,
        p3: 3,
        p4: 4
    }
    p1 // 1
    p2 // 2
    p3 // {p3: 3, p4: 4}
    

    对象和数组的解构赋值

    第一个例子,我们使用解构语法定义一些变量:

    const person = {
      firstName: 'Tom',
      age: 18,
      gender: 'boy'
    }
    const {firstName: name, age} = person
    name // 'Tom'
    age // 18
    

    在这个例子中我们定义了两个变量:name和age,name的值是person.firstName,age的值是person.age,如果变量名和对象的属性名一致的话,可以省略写,也就是说:

    const {firstName: name, age} = person
    // 等同于const {firstName: name, age: age} = person
    

    同样的,这样的写法在数组中也起作用:

    const arr = [1, 2, 3, 4, 5]
    const [a1, a2] = arr
    a1 // 1
    a2 // 2
    

    如果我们想创建第三个变量,这个变量是数组arr中的第5个值:

    const arr = [1, 2, 3, 4, 5]
    const [a1, a2, , , a3 ] = arr
    a1 // 1
    a2 // 2
    a3 // 5
    

    模板字符串

    在ES6中模板字符串是一种新的声明字符串的方式,非常有用。第一眼看上去时,它的语法很简单,只是在声明字符串时使用反引号(`)替换单引号(‘’)或双引号(“”):

    const str1 = `my test string`
    

    但是实际上他们很不一样,因为他们提供了不少比用‘’或“”建立的普通字符串没有的特性:

    • 方便定义多行字符串
    • 方便在字符串中使用变量或者表达式

    多行字符串

    ES6之前,定义多行字符串是比较麻烦的事:

    const str = 
    'first line\n \
    second line'
    

    或者

    const str = 'first line\n' + 'second line'
    

    使用模板字符串会非常简单和美观:

    const str = `
    first line
          second line
    `
    

    并且定义时输入的空白字符也会得到保留。

    插值语法

    我们可以使用${...}语法在模板字符串中插入变量或者表达式:

    const name = 'Tom'
    const str = `name is ${name}`
    str // 'name is Tom'
    
    const str1 = `age is ${10+8}`
    str1 // `age is 18
    
    const str2 = `gender is ${false?'male':'female'}`
    str2 // gender is female
    

    Classes(类)

    JavaScript有一种非常罕见的实现继承的方式:原型继承,但与大多数其他流行的编程语言的继承实现不同,后者是基于类的。来自其他语言的人很难理解原型继承的复杂性,因此ECMAScript委员会决定在原型继承之上撒上语法糖,这样它就像基于类的继承在其他流行实现中的工作方式。
    这很重要:底层下的JavaScript仍然相同,您可以通常的方式访问对象原型。

    1.一个类的定义

    下面就是一个简单的类定义:

    class People {
      constructor(name) {
        this.name = name
      }
    
      sayHello() {
        return 'Hello, I am ' + this.name + '.'
      }
    }
    let people_tom = new People('Tom')
    people_tom.sayHello()
    // "Hello, I am Tom."
    

    当一个对象初始化后,consturctor方法将会被调用,并且传递参数,这个对象也可以调用类中声明的方法。

    2.类继承

    一个类可以从其它类进行扩展,通过这个扩展类建立的对象,将会继承所有类的方法。如果继承的类具有与层次结构中较高的类之一具有相同名称的方法,则最接近的方法优先:

    class Student extends People {
      sayHello() {
        return super.sayHello() + ' I am a student.'
      }
    }
    const  student_tom = new Student('Tom')
    student_tom.sayHello()
    // "Hello, I am Tom. I am a student."
    

    类没有显式的类变量声明,但您必须初始化构造函数中的所有变量。在类中,您可以引用调用super()的父类。

    3.静态方法

    通常,方法是在实例上定义的,而不是在类上定义的,现在静态方法在类上执行:

    class People {
      static genericHello() {
        return 'Hello'
      }
    }
    People.genericHello() 
    //Hello
    

    4.取值函数(getter)和存值函数(setter)

    您可以添加以get或set为前缀的方法来创建getter和setter,它们是根据您正在执行的操作执行的两个不同的代码:访问变量或修改其值。
    对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

    class People {
      constructor(name) {
        this._name = name
      }
      set name(newName) {
        this._name = newName
      }
      get name() {
        return this._name.toUpperCase();
      }
    }
    let p1 = new People('Tom')
    p1._name // Tom
    p1.name // TOM
    p1.name = 'Jack'
    p1._name // 'Jack'
    p1.name  // 'JACK'
    

    如果您只有一个getter,则无法设置该属性,并且任何尝试这样做的操作都会被忽略:

    class People {
      constructor(name) {
        this._name = name
      }
      get name() {
        return this._name
      }
    }
    let p1 = new People('Tom')
    p1._name // Tom
    p1.name // Tom
    p1.name = 'Jack'
    p1._name // 'Tom
    p1.name  // 'Tom'
    

    如果您只有一个setter,则可以更改该值,但不能从外部访问它:

    class People {
      constructor(name) {
        this._name = name
      }
      set name(newName) {
        this._name = newName
      }
    }
    let p1 = new People('Tom')
    p1._name // Tom
    p1.name //undefined
    p1.name = 'Jack'
    p1._name // 'Jack'
    p1.name  //undefined
    

    Promises

    Promise是在JavaScript中处理异步代码的一种方法,避免了在代码中写太多回调的问题。
    所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
    Async函数使用promises API作为基础,因此理解它们是很基本的,即便在较新的代码中,您可能会使用异步函数而不是promises。

    1.创建一个promise

    我们使用new Promise()来初始化一个promise对象:

    let isDone = true
    const testPromise = new Promise((resolve, reject) => {
      if (isDone) {
        const result = 'success!'
        resolve(result)
      } else {
        const result = 'failed!'
        reject(result)
      }
    })
    

    正如您所看到的,promise会检查已完成的全局常量,如果值为true,我们使用resolve传回一个值,否则使用reject传回一个值。在上面的例子中我们只返回一个字符串,但它也可以是一个对象。

    2.使用promise

    我们使用上面创建的promise对象作为示例:

    testPromise.then(res =>{
      console.log(res)
    }).catch(err => {
      console.log(err)
    })
    

    promise并使用then回调等待它解析,如果有错误,它将在catch回调中处理它。

    3.链式写法

    一个promise对象可以返回另一个promise,因此可以使用链式的写法:

    const statusFunc = response => {
      if (response.status >= 200 && response.status < 300) {
        return Promise.resolve(response)
      }
      return Promise.reject(new Error(response.statusText))
    }
    const toJson = response => response.json()
    fetch('/todos.json')
      .then(statusFunc)
      .then(toJson)
      .then(data => {
        console.log('Success!', data)
      })
      .catch(error => {
        console.log('Failed', error)
      })
    

    在这个例子中,我们调用fetch()去读取todos.json文件,然后创建一个promises链。
    运行fetch()会返回一个响应,该响应具有许多属性:

    • status,表示HTTP状态代码的数值
    • statustext,状态消息,如果请求成功,则为OK

    statusFunc方法读取JSON文件数据,返回是一个promise;
    toJson方法把上一步骤中json数据通过json()方法转为json对象,它也是返回一个promise;
    这一长串的方法会发生什么样的事情呢?链中的第一个promise是我们定义的方法statusFunc,它检查响应状态,如果实在200到300之间,就是reslove状态,否则就是失败的reject状态,如果是reject状态,就会跳出后面所有的promise,直接被catch()所捕获到,记录失败信息。
    如果是成功的状态,下一个promise会把上一步promise的返回值当作输入值来做处理。

    4.错误处理

    我们使用catch方法来处理promise中的错误,当promise链中的任何内容失败并引发错误或reject时,代码执行都调转到链中最近的catch()语句中,此时的catch方法的输入会是代码执行的异常或者是reject方法的输入值。

    5.Promise.all()

    如果你定义了一个promises列表,需要等待所有promise都有执行结果后再进行下一步处理,Promise.all()是一个方便的处理方法:

    const p1 = fetch('/test1.json')
    const p2 = fetch('/test2.json')
    
    Promise.all([p1, p2])
      .then(res => {
        console.log('results: ', res)
      })
      .catch(err => {
        console.error(err)
      })
    

    而ES6的解构赋值语法也可以这样来写:

    Promise.all([p1, p2]).then(([res1, res2]) => {
      console.log('Results', res1, res2)
    })
    

    6.Promise.race()

    Promise.race()会在您传递给它的一个promise有执行结果后立即运行,并且只处理一次后面回调,执行的是第一个执行完成的promise的结果:

    const p1 = new Promise((resolve, reject) => {
      setTimeout(resolve, 500, 'first')
    })
    const p2 = new Promise((resolve, reject) => {
      setTimeout(resolve, 100, 'second')
    })
    Promise.race([p1,p2]).then(result => {
      console.log(result) // 'second'
    })
    

    ES6 modules简介

    ES Modules是用于处理模块的ECMAScript标准。虽然Node.js多年来一直使用CommonJS标准,但是在浏览器中从未有过模块系统,直到ES6中modules标准的制定,浏览器才开始实施这个标准,现在ES模块在现代浏览器Chrome,Safari,Edge和Firefox中都得到了支持(从60版开始)。
    模块功能非常有用,您可以封装各种功能,并将此功能公开给其他JS文件使用。

    1.ES modules语法

    导入一个模块的语法很简单:

    import package from 'module-name'
    

    模块是一个使用export关键字导出一个或多个值(对象,函数或变量)的js文件。 下面即为一个简单的模块:

    // test.js
    export default str => str.toUpperCase()
    

    在例子中,模块定义了单个的default export,因此它可以是匿名函数,否则,它需要一个名称来区别于其它导出。然后任何其他js模块都可以通过导入test.js来导入它提供的功能:

    import toUpperCase from './uppercase.js'
    

    之后我们就可以在js代码中使用:

    toUpperCase('test') //'TEST'
    

    2.其它import/export选项

    在前面的例子中,我们创建了一个默认的导出,但是有时候我们可能需要在一个js文件中导出多个内容:

    const a = 1
    const b = 2
    const c = 3
    export { a, b, c }
    

    在其它js中的引用可以有多种写法:

    • 引入所有的export
    import * from 'module'
    
    • 使用结构赋值引入一部分的export
    import { a, b } from 'module'
    
    • 方便起见,你可以使用as重命名任何export
    import { a, b as test } from 'module'
    
    • 您可以按名称导入默认export和任何非默认export,这种方式在react中比较常见
    import React, { Component } from 'react'
    

    持续更新中

    上一篇:React快速上手1-react安装
    下一篇:React快速上手3-JSX快速入门

    相关文章

      网友评论

        本文标题:React快速上手2-ES6语法

        本文链接:https://www.haomeiwen.com/subject/wmneeqtx.html