JavaScript從初級往高級走系列————ES6

ES6

原文博客地址:https://finget.github.io/2018/05/10/javascript-es6/
如今基本上開發中都在使用ES6,瀏覽器環境支持很差,能夠用babel插件來解決。

採用‘二八定律’,主要涉及ES6經常使用且重要的部分。javascript

問題:

  • ES6模塊化如何使用,開發環境如何打包
  • Class和普通構造函數有何區別
  • Promise的基本使用和原理
  • 總結一下ES6其餘經常使用功能

ES6模塊化如何使用,開發環境如何打包

模塊化的基本語法

// util1.js
export default {
  a : 100
}

const str = 'hello';
export default str;
// export default const str = 'hello'; X
// util2.js
export function fn1() {
  console.log('fn1');
}
export function fn2() {
  console.log('fn2');
}
export const fn3 = ()=> {
  console.log('fn3');
}
// index.js
import str from './util1.js';
import {fn1 , fn2} from './util2.js';
import * as fn from './util2.js';
console.log(str);
fn1();
fn2();

fn.fn1();
fn.fn2();
export default 默認輸出這個,而後在 import的時候就會拿到默認輸出的內容。例子中默認輸出的 a=100
export多個內容,在 import時須要使用 {}進行引用你須要的內容。
exportexport defaultexportsmodule.exports的區別
require: node 和 es6 都支持的引入
export / import : 只有es6 支持的導出引入
module.exports / exports: 只有 node 支持的導出
  1. module.exports 初始值爲一個空對象 {}前端

    1. exports 是指向的 module.exports 的引用
    2. require() 返回的是 module.exports 而不是 exports

Node裏面的模塊系統遵循的是CommonJS規範。java

CommonJS定義的模塊分爲: 模塊標識( module)、模塊定義( exports) 、模塊引用( require)

在nodejs,exportsmodule.exports的引用,初始化時,它們都指向同一個{}對象。node

對象在JS中屬於引用類型,意思就是exportsmodule.exports是指向同一個內存地址的。
jquery

看下面的例子:webpack

exports.fn = function(){
  console.log('fn');
}
// 這兩種狀況的效果是同樣的,上面說了exports與`module.exports初始化同一個對象,因此兩種定義方就是給這個同對象定義了一個fn的屬性,該屬性值爲一個函數。
module.exports.fn = function(){
  console.log('fn');
}
exports = function(){
  console.log('fn');
}
// 這兩種狀況就不同了。上面的exports想當於指向了另外一個內存地址。而下面這種狀況是能夠正常導出的。
module.exports = function(){
  console.log('fn');
}
exports對象是當前模塊的導出對象,用於導出模塊公有方法和屬性。別的模塊經過require函數使用當前模塊時獲得的就是當前模塊的exports對象。
  • module.exports 的使用
// sayHello.js

function sayHello() {
  console.log('hello');
}

module.exports = sayHello;

// app.js
var sayHello = require('./sayHello');

sayHello();

定義一個sayHello模塊,模塊裏定義了一個sayHello方法,經過替換當前模塊exports對象的方式將sayHello方法導出。
在app.js中加載這個模塊,獲得的是一個函數,調用該函數,控制檯打印hello。git

// sayWorld.js
module.exports = function(){
  console.log('world');
}
// app.js
var sayWorld = require('./sayWorld'); // 匿名替換
sayWorld();
  • exports 導出多個變量

當要導出多個變量怎麼辦呢?這個時候替換當前模塊對象的方法就不實用了,咱們須要用到exports對象。es6

// userExports.js
exports.a = function () {
  console.log('a exports');
}
 
exports.b = function () {
  console.log('b exports');
}

// app.js
var useExports = require('./userExports');
useExports.a();
useExports.b();
// a exports
// b exports

固然,將useExports.js改爲這樣也是能夠的:github

// userExports.js
module.exports.a = function () {
  console.log('a exports');
}
 
module.exports.b = function () {
  console.log('b exports');
}
在實際開發當中能夠只使用 module.exports避免形成沒必要要的問題。

開發環境配置

babel

Babel中文網web

  • nodejs環境 npm init
  • npm i babel-core babel-preset-es2015 babel-preset-latest --save-dev
  • 建立.babelrc文件
  • npm i --global babel-cli
// .babelrc
{
  "presets": ["es2015","latest"],
  "plugins": []
}
webpack

四大維度解鎖webpack3筆記

rollup.js

Rollup.js官網

  • npm init
  • 安裝 rollup.js須要的一些插件

npm i rollup rollup-plugin-node-resolve rollup-plugin-babel babel-core babel-plugin-external-helpers babel-preset-latest --save-dev

  • 配置 .babelrc
  • 配置 rollup.config.js
rollup 功能單一(打包js模塊化), webpack功能強大
工具儘可能功能單一,可繼承,可擴展
// .babelrc
{
  "presets":[
    ["latest", {
      "es2015":{
        "modules": false
      }
    }]
  ],
  "plugins":["external-helpers"]
}
// rollup.config.js
import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';

export default {
  entry: 'src/index.js',
  format: 'umd',
  plugins: [
    resolve(),
    babel({
      exclude: 'node_modules/**'
    })
  ],
  dest: 'build/bundle.js'
}
// package.json
...
"scripts":{
  "start": "rollup -c rollup.config.js"
}
...

npm run start

關於JS衆多模塊化標準

  • 沒有模塊化
  • AMD成爲標準,require.js
  • 前端打包工具,使得nodejs模塊化(CommonJS)能夠被使用
  • ES6出現,想統一如今全部模塊化標準
  • nodejs積極支持,瀏覽器還沒有統一
  • 你能夠自造lib,可是不要自造標準!!!

Class和普通構造函數有何區別

JS構造函數

// 構造函數
function MathHandle(x, y){
  this.x = x;
  this.y = y;
}
// 原型擴展
MathHandle.prototype.add = function(){
  return this.x + this.y;
}

// 建立實例
var m = new ManthHandle(1,2);
console.log(m.add()); // 3

Class基本語法

class MathHandle { // 直接跟大括號
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  
  add() {
    return this.x + this.y;
  }
}
const m = new ManthHandle(1,2);
console.log(m.add()); // 3

語法糖

typeof MathHandle = 'function'
MathHandle實際上是個 function,‘構造函數’
MathHandle === MathHandle.prototype.constructor

繼承

原生js繼承

// 動物
function Animal() {
  this.eat = function() {
    console.log('animal eat');
  }
}
// 狗
function Dog() {
  this.bark = function(){
    console.log('dog bark');
  }
}
// 綁定原型,實現繼承
Dog.prototype = new Animal();
// 實例化一隻狗
var hashiqi = new Dog();

// hashiqi就有了eat方法
hashiqi.eat(); // animal eat
廖雪峯老師的原型繼承: 點這裏

ES6繼承

class Animal {
  constructor(name){
    this.name = name;
  }
  eat() {
    console.log(`${this.name} eat`);
  }
}

class Dog extends Animal { // extends 繼承
  constructor(name){
    super(name); // 必須*  記得用super調用父類的構造方法!
    this.name = name;
  }
  say() {
    console.log(`${this.name} say`);
  }
}

const dog = new Dog('hashiqi');
dog.eat(); // hashiqi eat

Promise 的基礎使用

解決回調地獄(Callback Hell)
詳細點的Promise: 點這裏

Promise 基礎語法

new Promise((resolve, reject) => {
  // 一段耗時很長的異步操做
    .....
  resolve(); // 數據處理完成
  reject(); // 數據處理出錯
}).then(function A() {
  // 成功,下一步
}, function B(){
  // 失敗,作相應處理
})
我最開始接觸到 Promise的時候,一直傻了吧唧的在想 resolve()reject()在何時調用。
resolve()reject()就是爲後面 then()中的兩個函數服務的。

resolve和reject

new Promise((resolve, reject) => {
  setTimeout(()=>{
    resolve('good,我要傳給then裏的一個函數');
  },2000);
  setTimeout(()=>{
    reject('錯了,把我給我then裏的第二個函數');
  },2000);
}).then(value => {
  console.log(value); // good,我要傳給then裏的一個函數
},value => {
  console.log(value); // 錯了,把我給我then裏的第二個函數
});

來個實際的例子

/**
 * 基於jquery封裝一個promise ajax請求
 * @param  {[type]} param [選項]
 * @return {[type]}       [description]
 */
request(param){
  return new Promise((resolve,reject) => {
    $.ajax({
      type : param.type || 'get',
      url : param.url || '',
      dataType : param.dataType || 'json',
      data : param.data || null,
      success:(res)=>{ // 用箭頭函數避免this指向問題
        if (0 === res.status) {
           typeof resolve === 'function'&&resolve(res.data, res.msg); // 成功就把請求到的數據用resolve返回,這樣就能夠在then的第一個函數裏拿到值了
        } else {
           typeof reject === 'function'&&reject(res.msg || res.data); // 失敗就返回錯誤信息
        }

      },
      error:(err)=>{ // 這個失敗是請求失敗,上面那個失敗是請求成功發送了,可是沒有拿到數據失敗了
         typeof reject === 'function'&&reject(err.statusText);
      }
    })
  })
}

ES6經常使用其餘功能

let/const

let constvar都是用來定義變量的,不一樣的是 let自帶做用域, const不能重複賦值。
let name = 'FinGet'
while (true) {
    let name = 'GetFin'
    console.log(name)  //GetFin
    break
}
console.log(name)  //FinGet
let定義的變量只在包含它的代碼塊內有用
const PI = 3.1415926;
PI = 3.14; // 錯誤

多行字符串/模板變量

let name = 'FinGet';
let age = 22;
// js
var str = '我是'+ name+',今年'+age+'歲'; // 很麻煩

let str1 = `我是${name},今年${age}歲`; // 簡單多了
模板字符串就是用 `(Tab鍵上面那個)包含,變量就是用${}`表示

解構賦值

let obj = {
  name: 'FinGet',
  age: 22,
  job: '前端',
  addr: '成都'
}
let {name,age} = obj;
console.log(name); // FinGet
console.log(age); // 22

還能夠反過來:

let  name = 'FinGet';
let  age = 22;
let  job = '前端';
let  addr = '成都';

let obj = {name,age,job,addr};
//obj = {name: 'FinGet',age: 22,job: '前端',addr: '成都'}

塊級做用域

另一個var帶來的不合理場景就是用來計數的循環變量泄露爲全局變量,看下面的例子:

// js
var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10
let 自帶塊級做用域
// ES6
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

原生js想實現這種效果,須要用到閉包:

var a = [];
for (var i = 0; i < 10; i++) {
  (function(j){ // 當即執行函數
    a[j] = function() {
      console.log(j);
    }
  }(i))
}
a[6](); // 6
當即執行函數造成了一個塊級做用域,將參數j保存了下來,並不會被‘污染’,原生js沒有塊級做用域, varfor中定義的變量是個全局變量,能夠在外部訪問,也就能夠被改變,因此每次 for循環都是重置修改 i的值,致使最後只能輸出10。

函數默認參數與rest

default很簡單,意思就是默認值。你們能夠看下面的例子,調用animal()方法時忘了傳參數,傳統的作法就是加上這一句 type = type || 'cat' 來指定默認值。
function animal(type){
    type = type || 'cat'  
    console.log(type)
}
animal()

若是用ES6咱們而已直接這麼寫:

function animal(type = 'cat'){
    console.log(type)
}
animal(); // cat

最後一個rest語法也很簡單,直接看例子:

function animals(...types){
    console.log(types)
}
animals('cat', 'dog', 'fish') //["cat", "dog", "fish"]
而若是不用ES6的話,咱們則得使用ES5的arguments。

箭頭函數

// js函數
function (a,b){
  console.log(a+b);
}
// es6箭頭函數
(a,b) => {
  console.log(a+b);
}
function去掉,在 (){}之間加上 =>

當咱們使用箭頭函數時,函數體內的 this對象,就是定義時所在的對象,而不是使用時所在的對象。
並非由於箭頭函數內部有綁定this的機制,實際緣由是箭頭函數根本沒有本身的 this,它的 this是繼承外面的,所以內部的 this就是外層代碼塊的 this

最後

建立了一個前端學習交流羣,感興趣的朋友,一塊兒來嗨呀!

相關文章
相關標籤/搜索