前端筆記之ES678&Webpack&Babel(上)初識ES678&Babel&let和const&解構&語法

1、ES版本簡介和調試運行方法

1.1 ECMAScript簡介

MDN手冊:https://developer.mozilla.org/zh-CN/docs/Web/JavaScripthtml

JavaScript 的標準是 ECMAScript。截至 2012 年,全部的現代瀏覽器都完整了支持 ECMAScript 5.1。舊式的瀏覽器至少支持 ECMAScript 3 標準。前端

2015617日,ECMA國際組織發佈了 ECMAScript 6.0,該版本正式名稱爲ECMAScript 2015,是JavaScript 語言的下一代標準。但一般被稱爲 ECMAScript 6ES6。它的目標,是使得 JavaScript 語言能夠用來編寫複雜的大型應用程序,成爲企業級開發語言。本文檔目前覆蓋了最新ECMAScript的草案,也就是ECMAScript2018vue

 

 ESECMAScript的簡稱。是JavaScript執行標準的制定者。node

  ES6 也稱爲ES2015 全稱是ECMAScript2015react

  ES7 也稱爲ES2016 全稱是ECMAScript2016git

  ES8 也稱爲ES2017 全稱是ECMAScript2017es6

  ES9 也稱爲ES2018 全稱是ECMAScript2018github

 

ES6開始ECMA組織每一年發佈一個版本,以年份做爲名稱,所以又稱ECMAScript 2017,簡稱 ES2017面試

你們基本上記不住哪一個版本的ES更新了什麼,因此統稱爲ENVECMAScript New Versionnpm


 

1.2 ECMAScriptJavaScript的關係

 一個常見的問題是,ECMAScript JavaScript 究竟是什麼關係?

講清楚這個問題,須要回顧歷史。199611月,JavaScript的創造者Netscape公司,決定將 JavaScript提交給標準化組織ECMA,但願這種語言可以成爲國際標準。次年,ECMA發佈262號標準文件(ECMA-262)的初版,規定了瀏覽器腳本語言的標準,並將這種語言稱爲ECMAScript,這個版本就是1.0版。

 

該標準從一開始就是針對JavaScript語言制定的,可是之因此不叫JavaScript,有兩個緣由。一是商標,JavaSun公司的商標,根據受權協議,只有Netscape公司能夠合法地使用 JavaScript這個名字,且 JavaScript自己也已經被 Netscape 公司註冊爲商標。二是想體現這門語言的制定者是ECMA,不是 Netscape,這樣有利於保證這門語言的開放性和中立性。

 所以,ECMAScriptJavaScript的關係是,前者是後者的標準,後者是前者的一種實現。平常場合,這兩個詞是能夠互換的。

 

關於更多ECMAScript請閱讀:http://es6.ruanyifeng.com/#docs/intro


 

1.3瀏覽器兼容性

瀏覽器和NodeJSES6新語法特性的支持,要看兼容性。各大瀏覽器的最新版本,對 ES6 的支持能夠查看:

 https://www.caniuse.com/

 http://kangax.github.io/compat-table/es6/

 

隨着時間的推移,支持度已經愈來愈高了,超過 90%ES6 語法特性都實現了。

Node JavaScript 的服務器運行環境(runtime),它對 ES6的支持度更高。

 

訪問http://ruanyf.github.io/es-checker能夠查看當前瀏覽器的支持ES6的程度

ES-Checker這個工具,能夠用來檢查你當前Node支持ES6的程度

運行下面的命令,能夠查看你在使用的Node環境對ES6的支持程度

$ npm install -g es-Checker
$ es-checker

Nodejs如今的最高版本是10,隨着版本的升級,實際上Nodejs自己好比httpfs模塊等幾乎沒什麼更新,變化就是對ES的新語法支持,Nodejs 6最高版本,幾乎所有支持了經常使用的新版ES語法。

 在瀏覽器和node環境均可以運行,可是有不少舊版瀏覽器還沒有支持。

 

高級瀏覽器開始逐步支持ES678這些新特性,好比寫一個「自動解構」語法:

const [a,b,c] = [1,2,3];
console.log(a)
console.log(b)
console.log(c)


2、Babel翻譯器

     因爲瀏覽器的版本和兼容性問題,不少JavaScript的新的方法都不能使用,等到能夠大膽使用的時候,可能已通過去了好幾年。Babel就所以而生,它可讓你放心使用大部分的JavaScript的新的標準的方法,而後編譯成兼容絕大多數的主流瀏覽器的代碼。

Babel是一個普遍使用的ES6轉碼器,能夠將ES6代碼轉爲ES5代碼,從而在現有環境執行。這意味着,你能夠用 ES6 的方式編寫程序,又不用擔憂現有環境是否支持。

 

中文官網:https://babeljs.cn

 

2.1 babel安裝

Babel是咱們學習的第一個前端工做流工具,它是一個JavaScript編譯器,能將ES678的代碼翻譯成IE8可以識別的ES5語法。

什麼是前端工做流工具?

Nodejs編寫的,用npm發佈的,一些開發人員在開發的時候可以運行的便利工具。

 

安裝babel工做流工具:

npm install -g babel-cli

babel提供babel-cli工具,用於命令行轉換,-g安裝以後的程序並非屬於某一個單獨的項目,而是操做系統的全局。CLI表示Command Line Interface,命令行接口,就是用CMD可以調用這個命令。

 

 安裝完畢後,用命令查看版本是否安裝成功

babel --version

 

在項目文件夾中打開CMD命令行窗口,輸入:

babel 01.js -o a.js

-o表示output輸出,這條語句能將01.js翻譯爲a.js

 

可是發現,並無將ES6語法翻譯成ES5語法:

 

 

 

 

 

由於咱們沒有告訴babel,讓它去幫咱們翻譯ES6語法,怎麼指導它翻譯?

接下來須要配置對應的.babelrc文件才能夠發揮完整的做用。


2.2 .babelrc文件preset

必須使用.babelrc文件去指導babel工做。

windows系統不容許用戶建立「.」開頭的文件

 

方法1:用sublime能夠新建。

 

方法2:打開cmd窗口,輸入一下命令將a.txt文件重命名爲.babelrc

rename a.txt .babelrc

 

 預設(presets

該文件用來設置轉碼規則和插件,使用的時候須要安裝對應的插件,而且寫一些配置:

{
    "presets":["es2015","es2016"]
}

presets設定轉碼規則,就是說babel要翻譯哪些語法,咱們通常翻譯es2015es2016寫在數組中。

 

env表示ECMAScript New Version,就是新的版本ES語法:

{
    "presets":["env"]
}

 

接下須要安裝語法依賴:

npm install --save-dev babel-preset-es2015
npm install --save-dev babel-preset-es2016

--save-dev表示添加「開發依賴」,--save表示添加「運行依賴」,開發時須要babel翻譯,運行時babel已經翻譯好了,運行就不須要babel了,因此--save-dev是開發才須要的。

 

安裝完插件後,就能夠再次使用babel翻譯

babel 01.js -o a.js

翻譯前:

 

 

翻譯後:

 

 

 

Babel的配置和使用總流程:

 

presets字段設定轉碼規則,官方提供如下的規則集,你能夠根據須要安裝。

 

最新轉碼規則:

$ npm install --save-dev babel-preset-latest

 

不一樣階段語法提案的轉碼規則(共有4個階段),選裝一個

$ npm install --save-dev babel-preset-stage-0
$ npm install --save-dev babel-preset-stage-1
$ npm install --save-dev babel-preset-stage-2
$ npm install --save-dev babel-preset-stage-3

 

而後,將這些規則加入.babelrc文件。

{
    "presets": [
      "latest",
      "react",
      "stage-2"
    ],
    "plugins": []
  }

上面這些代碼是在全局環境下,進行Babel轉碼。這意味着,若是項目要運行,全局環境必須有安裝Babel,也就是說項目產生了對環境的依賴。另外一方面,這樣作也沒法支持不一樣項目使用不一樣版本的Babel

 

因此,雖然在你的機器上全局安裝 Babel CLI, 但根據單個項目進行本地安裝會更好一些。

這樣作有兩個主要的緣由:

同一機器上的不一樣項目能夠依賴不一樣版本的 Babel, 這容許你一次更新一個項目。

這意味着在你的工做環境中沒有隱含的依賴項。它將使你的項目更方便移植、更易於安裝。

 

解決辦法是將babel-cli安裝在項目中,經過如下命令本地安裝Babel CLI到項目依賴:

npm install --save-dev babel-cli

更多請閱讀:

http://www.ruanyifeng.com/blog/2016/01/babel.html


3、constlet

開始學習ES678的語法精粹。

更多的去看:http://es6.ruanyifeng.com/

 

3.1 const

const用來定義常量,所謂的常量就是值一旦給定後就不變,一次定義終身不變的量。

const a = 10;
a = 20;

上面的a就是常量,若是給a從新賦值,會報錯。

 

 

const一般會定義兩種值

定義函數

const sum = function(a,b){
    return a + b;
}
console.log(sum(3,4))

 

定義一些特定的字符串和常數

const PI = 3.14;
const HELLO_WORLD = "世界,你好";

常量名通常全是大寫,若是由多個單詞組成,用下劃線隔開,暗示這個是常量。


3.2 let

let用來定義塊級做用域的變量,只要有{}」包裹,就表示塊級做用域。

注意:不是JSONif語句體、函數、whilefor是塊,for循環的()部分也是塊級。

{
    var a = 10;
}
console.log(a); //10

 

var這個定義的變量是函數級做用域,不是塊級做用域。

{
    let a = 10;
}
console.log(a); //報錯 a is not defined

 

注意:一般在for循環使用let,此時循環體中自動加了閉包:

for(let i = 0;i < 10;i++){

}
console.log(i);//報錯

 

看一個面試題:

var arr = [];
for(var i = 0; i < 10;i++){
    arr.push(function(){
        console.log(i)
    })
}
console.log(arr)
arr[4]();

數組中存儲了10個函數,當咱們任意調用時,好比arr[4]()函數,會輸出10.

 

由於函數調用時,i已經變成10了,i實際上是一根全局變量,要使用IIFE執行函數:

var arr = [];
for(var i = 0; i < 10;i++){
    (function(a){
        arr.push(function(){
            console.log(a)
        })
    })(i);
}
console.log(arr)
arr[4]();

 

如今用let,很是簡單:

var arr = [];
for(let i = 0; i < 10;i++){
    arr.push(function(){
        console.log(i)
    })
}
console.log(arr)
arr[4]();

letfor是絕配,let平時沒啥用。

 

總結:

l const用來定義常量,一般定義函數和常數

l let用來定義{}塊級的做用域的變量,一般用來定義for循環的變量

常量是塊級做用域,很像使用let,語句定義的變量,常量的值不能經過從新賦值來改變,而且不能從新聲明。

 

letconst的疑點等面試題再加,好比:

console.log(m);
let m = 10; //報錯,let沒有變量聲明提高

3.3 Babel的翻譯結果

對於constbabel會翻譯var,可是翻譯的時候,會驗證const是否從新賦值了,是就報錯。

翻譯前:

 

 

翻譯後:

 

 

 

for循環的翻譯:

翻譯前:

 

 

翻譯後:

 

 

 


4、變量的解構賦值

ES6 容許按照必定模式,從數組和對象中提取值,給變量進行賦值,這被稱爲解構。

解構的做用是能夠快速取得數組或對象中的元素或屬性,而無需使用arr[x]obj[key]等傳統方式去賦值。

之前爲變量賦值,只能直接指定值:

var a = 1;
var b = 2;
var c = 3;

4.1數組解構

所謂的解構就是一個快速給數組或者對象中的值,快速傳入變量的方法。

數組能夠解構,當等號右側是數組的時候,等於左側能夠將變量裝入[]中接收,一一對應接收。

var [a,b,c] = [1,2,3];
console.log(a); //1
console.log(b); //2
console.log(c); //3

上面代碼表示,能夠從數組中提取值,按照對應位置,變量賦值。

本質上,這種寫法屬於「模式匹配」,只要等號兩邊的模式相同,左邊的變量就會被賦予對應的值。

 

若是數組較爲複雜,此時左側結構的那項變量,也有相同的結構

var [a,b,[c,d]] = [1,2, [3,4]];
console.log(a); //1
console.log(b); //2
console.log(c); //3
console.log(d); //4
var [a,b,c] = [1,2, [3,4]];
console.log(a); //1
console.log(b); //2
console.log(c); //[3,4]

 

下面是一些嵌套數組進行解構的例子:

let [x, ,y] = [1, 2, 3];
console.log(x) // 1
console.log(y) // 3

若是解構不成功,變量的值等於undefined

let [a] = [];
let [b,c] = [1];
console.log(a); //undefined
console.log(b); //1
console.log(c); //undefined

 

另外一種狀況是不徹底解構,即等號左邊的模式,只匹配一部分的等於右邊的數組,這種狀況:

let [a,[b],c] = [1,[2,3],4];
console.log(a); //1
console.log(b); //2
console.log(c); //4

4.2對象解構

對象的結構和數組有個不一樣點,數組的元素是按照順序排列,變量的取值由它的位置決定。

而對象的屬性沒有順序,可是變量必須與屬性同名,才能取到正確的值。

var {name,id,sex} = {"id":1001, "name":"小明", "sex":"男"};
console.log(id)
console.log(name)
console.log(sex)

上面的例子,等號左邊的變量順序和等號右邊3個同名屬性的順序不同,但取值沒有影響。

 

若是變量名和屬性名不一致,必須寫成下面這樣:

var {name:names, id:ids, sex: sexs} = {id:1001, name:"小明", sex:"男"};
console.log(ids)
console.log(names)
console.log(sexs)

實際上說明,對象的解構賦值是上面形式的簡寫。

上面代碼中name是匹配模式,names纔是變量,真正被賦值的是names,而不是names。也就是說,對象解構賦值的內部機制,是先找到同名屬性,而後再賦值給對應的變量,真正被賦值的是後者。


4.3默認值

解構賦值容許指定默認值。

let [a = 100] = [];
let [x, y = 200] = [100]
console.log(a); //100
console.log(x); //100
console.log(y); //200

注意:ES6內部使用嚴格相等運算符(===),判斷一個位置是否有值,因此只有當一個數組成員嚴格等於undefined,默認值纔會生效。

 

let [a = 88] = [undefined]
let [b = 88] = [null]
console.log(a); //88
console.log(b); //null

上面代碼中有一個數組成員是null,默認值就不生效,由於null不嚴格等於undefined

更多解構的知識點閱讀:http://es6.ruanyifeng.com/#docs/destructuring

 

字符串也能夠解構賦值。這是由於此時,字符串被轉換成了一個相似數組的對象。

const [a, b, c, d, e] = 'hello';
console.log(a) // "h"
console.log(b) // "e"
console.log(c) // "l"
console.log(d) // "l"
console.log(e) // "o"

4.4擴展運算符(spread operator

擴展運算符(spread)是三個點(...)。將一個數組轉爲用逗號分隔的參數序列,還能強制展開一個對象,一般用於對象的賦值,使用靈活普遍。

 

第一個做用:稱爲「展開運算符」,做用和字母意思用於,就是把東西展開,能夠用在數組和對象上。

var obj1 = {
    a : 100,
    b : 200,
    c : 300
}

var obj2 = {
   ...obj1,
    d : 888
}

console.log(obj1)
console.log(obj2)
console.log(obj1 === obj2);

 

數組也能夠強制展開,一般數組的賦值,好比有兩個數組合併成一個數組:

var arr1 = ["小白","小黑","白無常","黑無常"];
var arr2 = ["小明","小紅","小花","小剛"];

var arr = [...arr1, ...arr2];
console.log(arr);

 

 原來想把一個數組打散傳入到函數中做爲參數,一般用apply

function sum(a,b,c,d){
    return a+b+c+d;
}

var arr = [1,2,3,4];
console.log(sum.apply(null, arr)); //10

 

能夠用...運算符,將數組強制展開爲一個散列的形式

function sum(a,b,c,d){
    return a+b+c+d;
}

var arr = [1,2,3,4];
console.log(sum(...arr)); //10

4.5剩餘操做符(rest operator

第2、三個做用:叫「剩餘操做符」是解構的一種,意思是把剩餘的參數放到一個數組或對象中賦值給它。通常針對數組或對象。

注意「...」只能出現最後一個參數,而且經過這個例子發現...能將零散的值收納爲數組。

var [a,b, ...c] = [1,2,3,4,5,6];
console.log(a);
console.log(b);
console.log(c);

 

邏輯上「...」是一個運算符

console.log(...[1,2,3,4])

經過這個實驗發現,...」能將數組打散爲零散值

 

應用場景1:當寫Ajax時,參數是JSON,此時能夠直接將JSON解構

JSON

{"a":100,"b":200,"c":300}

 

傳統方法接收:

$.get("data/1.json", function(data){
   console.log(data.a)
   console.log(data.b)
   console.log(data.c)
})

 

ES6的解構語法接收數據:

$.get("data/1.json", function({a,b,c}){
   console.log(a)
   console.log(b)
   console.log(c)
})

 

補充個知識點,在ES6中當一個對象的kv一致時,能夠省略v

var a = 10;
var obj = {
    a,
    b : 20,
    c : 30,
}
console.log(obj); //{ a: 10, b: 20, c: 30 }

 

應用場景2

之後函數大機率都是接收一個JSON當參數,而且用ES6解構語法寫形參

調用函數的時候傳的參數,通常都是k:v,一直省略v

var name = "小明";
var height = 170;
var weight = 100;
function buy({name,weight,height}){
    console.log(name)
    console.log(height)
    console.log(weight)
}

// buy({name:"小明", height:170, weight:100});
// buy({name:name, height:height, weight:weight});
buy({name, height, weight});

紅色部分的語句是在建立JSON,綠色的部分是在進行結構。

調用函數時參數順序打亂也不影響結構,由於解構,會自動匹配key

 

應用場景3

有一個數組arr1,在不改變arr1數組狀況下,想往arr1數組前面加一項做爲arr2新數組。

var arr1 = [8,9,10,11,12];
var arr2 = [...arr1, 13];
var arr3 = [7, ...arr1];
console.log(arr1)
console.log(arr2)
console.log(arr3)

 

擴展運算符和剩餘操做符的區別

簡單的說,在某種程度上,剩餘操做符和擴展運算符相反,擴展運算符會「展開」數組變成多個元素,剩餘操做符會收集多個零散元素合併成一個元素。


4.6對象解構的翻譯問題

注意:babel會將「[]」和「{}」解構變爲一個個的var

數組解構翻譯前

數組解構翻譯後

 

 

 

 

 

可是babel不能翻譯「對象解構」,只能翻譯數組解構:

 

緣由:object-rest-spread 還處於stage階段(屬於實驗性屬性)。

 

解決方法:安裝babel插件transform-rest-spread,放到.babelrc文件配置上,便可使用。

{
    "presets":["es2015","es2016"],
    "plugins":["transform-object-rest-spread"]
}

.babelrcpresets是用來定義預設,plugins定義插件。

 

安裝依賴:

npm install babel-plugin-transform-object-rest-spread --save-dev

此時,babel就能夠翻譯對象解構了。

對象翻譯前

對象翻譯後

 

 

 

 

 

變量的解構賦值用途不少。


5、ES6語法

ES6中對數組新增了幾個函數:map()filter()reduce()

ES5新增的forEach()

都是一些語法糖。

5.1 forEach()遍歷數組

forEach()方法用來循環遍歷數組,方法中的function回調函數接收3個參數

參數1是遍歷的數組內容(item);參數2是對應的數組索引(index),參數3是是數組自己(array)

[].forEach(function(item,index,array){
...
})
var arr = ["白板","幺雞","紅中","發財","八萬"];
arr.forEach(function(item,index,array){
   console.log(item,index,array)
})

forEach函數沒有返回值

for從此是建立數組,遍歷或操做數組能夠交給forEach方法。


5.2 map()映射

map方法的做用,「映射」也就是原數組被「映射」成對應的新數組。

[].map(function(item,index,array){
...
})
var arr = ["白板","幺雞","紅中","發財","八萬"];
arr.map(function(item,index,array){
   console.log(item,index,array)
})

 

寫一個案例:好比建立一個新數組,每一項都是原數組值的兩倍

var arr = [10,20,30,40,50,99];
var newArr = arr.map(function(item,index,array){
    return item * 2; //返回一個新的結果,給變量接收,原數組不變
});
console.log(arr)
console.log(newArr);

 

 map函數的本質是依次遍歷原數組的每一項,將每一項都執行一遍函數中的語句,返回一個新的數組。

注意:

函數須要有return值,若是沒有,數組全部項都被映射成undefined

map返回的數組必定和原數組的長度同樣。

 

在實際使用時,能夠利用map()方便獲取對象數組中的特定屬性值們,例以下面例子:

var user = [
    {name:"小明","email":"xiaoming@qq.com"},
    {name:"小紅","email":"xiaohong@qq.com"},
    {name:"小剛","email":"xiaogang@qq.com"}
]

var emails = user.map(function(item,index,array){
    return item.email;
})
console.log(emails.join(","))


5.3 filter()過濾

filter爲「過濾、篩選」之意,指原數組中filter某些項後,返回過濾後的新數組,用法和map類似。

好比想從原數組中,挑選全部的偶數,返回新的數組。

var arr = [312,55,77,11,13,15,18,26,30,40,50,99];
var newArr = arr.filter(function(item,index,array){
    return item % 2 == 0;
});
console.log(arr)
console.log(newArr)

描述:arr中的每一項會依次的執行函數,filtercallback函數須要返回布爾值truefalsetrue則將值返回到新數組中,false無情地將你拋棄…

 

再好比,從數組中選擇及格的學生:

var arr1 = [
    {name:"小明","chengji":50},
    {name:"小紅","chengji":70},
    {name:"小黑","chengji":56},
    {name:"小剛","chengji":88}
]

// var arr2 = arr1.filter(function(item){
//     return item.chengji >= 60;
// });

// var arr2 = arr1.filter((item)=>{
//     return item.chengji >= 60;
// });

// var arr2 = arr1.filter(item=>{
//     return item.chengji >= 60;
// });

var arr2 = arr1.filter(item => item.chengji >= 60;);

console.log(arr1)
console.log(arr2)

filtermap相同點:都會遍歷數組的每一項

filtermap不一樣點:map返回數組不會少項,filter可能少項。


5.4 reduce()迭代

reduce中文意思是「減小」、「約簡」,不過從功能來看,是沒法與「減小」這種含義聯繫起來的,反而更接近於「迭代」、「遞歸(recursion)」

arr.reduce(callback,[initialValue])

第一個參數的callback回調函數有四個參數,第二個爲設定的初始值(可選)。

callback函數有四個參數:

previous :上一次疊加的結果值或初始值

current 當前會參與疊加的項

index :當前值的下標

array :原數組自己

 

var arr = ["白板","幺雞","紅中","發財","三餅"];
arr.reduce(function(prev,cur,index,array){
    console.log(prev,cur)
})

reduce的原理:從下標爲1的項開始遍歷,每次return的值將做爲下一項的prev值,這一次的遍歷是curprev會有累加的感受。


案例1:求數組的總和:

var arr = [3,4,5,6];
var sum = arr.reduce(function(prev, cur){
    console.log(prev); //三、七、十二、18
    // console.log(cur); //4,5,6
    return prev + cur;
})

console.log(sum);//18

 

求數組的最大值【經典面試題】:

var arr = [43,5,4,6,888,78,554,5,6];
// var max = Math.max.apply(null,arr);
var max = arr.reduce(function(a, b){
    console.log(a); //4三、4三、4三、4三、88八、88八、888...
    // console.log(b);
    return a > b ? a : b;
})

console.log(max);

reduce能夠設置初始參數(參數自定義),下標從0開始遍歷。

 

求文科狀元是誰?

var arr = [
    {id:1,name:"小明",sex:"男",yuwen:30},
    {id:2,name:"小紅",sex:"女",yuwen:60},
    {id:3,name:"小白",sex:"男",yuwen:130},
    {id:4,name:"小黑",sex:"男",yuwen:90},
    {id:5,name:"小花",sex:"男",yuwen:120}
]

var newArr = arr.reduce(function(a,b){
    return a.yuwen > b.yuwen ? a : b;
})
console.log(newArr)

 

要求最高分:

reduce能夠設置初始參數(參數自定義),當reduce有第二個參數時,此時reduce遍歷將從第0項開始遍歷,而不是第1項開始。

var newArr = arr.reduce(function(a,b){
    console.log(a); //0
    return b.yuwen > a  ? b.yuwen : a;
}, 0); //初始值是0
console.log(newArr)

5.5用途-寫純函數

上面的四大數組方法:mapfilterreduce特別有用,作「函數式」編程。

什麼是「函數式(蘭姆達式編程)」編程?

    全部的函數都是純函數的時候,此時就是函數式編程。

什麼是純函數?

純函數是指不依賴於且不改變它做用域以外的變量狀態的函數。

l 這個函數內部不改變傳入的參數。

l 傳入這個函數必定有參數,必定會返回某一個肯定的值。

函數中的語句,不能有隨機性,好比Math.random()new Date(),傳入的參數相同,返回的值一定相同;

這個函數裏面不能有任何異步語句,好比$.get()fs.readFile()setInterval()

必定要好好學純函數,由於reactvue都是純函數(蘭姆達)式編程。

 

【例子1】請寫一個純函數addItem(),接收arrn當作參數,可以在arr的尾部增長n

錯誤的例子:

var arr = [99,88,77];
function addItem(arr,n){
    arr.push(n);
}
addItem(arr, 66)
console.log(arr);//arr被改變了,addItem不是純函數

咱們絕對不能改變傳入的參數的值。

 

正確寫法:

var arr = [99,88,77];
function addItem(arr,n){
    return [...arr, n];
}
var arr2 = addItem(arr, 66); //返回一個新數組
console.log(arr); //原數組不變
console.log(arr2);

 

【例子2】請寫一個純函數addItem(),接收arrn當作參數,可以在arr的頭部增長n

var arr = [99,88,77];
function addItem(arr,n){
    return [n,...arr];
}
var arr2 = addItem(arr, 66); //返回一個新數組
console.log(arr); //原數組不變
console.log(arr2);

 

【例子3】請寫一個純函數removeItem(),接收arrn當作參數,可以將arr第下標爲n的那項刪除。

var arr = ["白板","幺雞","二條","三萬"];
function removeItem(arr,n){
    // return arr.filter(function(item,index){
    //     return index != n;
    // })
    return arr.filter((item,index)=> index != n)
}
var arr2 = removeItem(arr, 2)
console.log(arr);
console.log(arr2);

 

【例子4】請寫一個純函數changeItem(),接收arrna當參數,能將arr第下標爲n的那項改變值爲a

var arr = ["白板","幺雞","二條","三萬"];
function changeItem(arr,n,a){
    return arr.map(function(item,index){
        return index === n ? a : item;
    })
}
var arr2 = changeItem(arr, 2 ,"九條");
console.log(arr)
console.log(arr2)

 

好比,寫一個函數,能夠刪除數組指定id那項,傳統寫法:

var arr = [
    {id:1,name:"小明",sex:"男",yuwen:30},
    {id:2,name:"小紅",sex:"女",yuwen:60},
    {id:3,name:"小白",sex:"男",yuwen:130},
    {id:4,name:"小黑",sex:"男",yuwen:90},
    {id:5,name:"小花",sex:"男",yuwen:120}
]
//刪除某一個id學生

function delStudent(arr,id){
    for(var i = 0;i < arr.length;i++){
        if(arr[i].id == id){
            arr.splice(i, 1)
        }
    }
}
delStudent(arr, 2)
console.log(arr); //arr被改變了,不是純函數

這個數組雖然好用,但不是純函數,由於它返回的結果依賴於外部變量arr,而且這個函數改變了原來的數組,它會把原來的數組弄的一團糟,咱們絕對不改變傳入的參數的值

 

 

如下都是純函數寫】

var arr = [
    ...
]
//刪除某一個id學生

function delStudent(arr,id){
     return arr.filter(function(item){
        //若是item.id不等於傳入的id則爲真會被返回,不然不返回
        return item.id != id;
     })
}
var arr2 = delStudent(arr, 2); //返回一個新數組,原數組不變
console.log(arr)
console.log(arr2)

上面代碼,咱們只計算了做用域內的局部變量,沒有任何的做用域外的變量被改變了。

 

更改指定idname屬性

var arr = [
    ...
]
//改變某一個id學生的name屬性
function changeName(arr, id, name){
    return arr.map(function(item){
        // if(item.id == id){
        //     return {...item, name}
        // }
        // return item;
        item.id == id ? {...item, name} : item;
    })
}

 

增長學生:

var arr = [
    {id:1,name:"小明",sex:"男",yuwen:30},
    {id:2,name:"小紅",sex:"女",yuwen:60},
    {id:3,name:"小白",sex:"男",yuwen:130},
    {id:4,name:"小黑",sex:"男",yuwen:90},
    {id:5,name:"小花",sex:"男",yuwen:120}
]
function addStudent(arr,{id,name,sex,yuwen}){
    return [...arr,{id,name,sex,yuwen}];
}
var s = {id:8,name:"鋼炮",sex:"未知",yuwen:100}
var newArr = addStudent(arr, s)
console.log(newArr)

 

查找全部男同窗

var arr = [
    {id:1,name:"小明",sex:"男",yuwen:30},
    {id:2,name:"小紅",sex:"女",yuwen:60},
    {id:3,name:"小白",sex:"男",yuwen:130},
    {id:4,name:"小黑",sex:"男",yuwen:90},
    {id:5,name:"小花",sex:"男",yuwen:120}
]

function findSex(arr, sex){
    return arr.filter(function(item){
        return item.sex == sex;
    })
}
var newArr = findSex(arr, "男");

console.log(newArr)

刪除用filter、修改用map、增長用...map

 

總結

爲何要煞費苦心地構建純函數?由於純函數很是靠譜,執行一個純函數你不用擔憂它會幹什麼壞事,它不會產生不可預料的行爲,也不會對外部產生影響。無論什麼時候何地,你給它什麼它就會乖乖地吐出什麼。若是你的應用程序大多數函數都是由純函數組成,那麼你的程序測試、調試起來會很是方便。

 

使用純函數的好處

最主要的好處是沒有反作用。純函數不會修改做用域以外的狀態,作到這一點,代碼就變得足夠簡單和清晰:當你調用一個純函數,你只要關注它的返回值,而不用擔憂由於別處的問題致使錯誤。

純函數是健壯的,改變執行次序不會對系統形成影響,所以純函數的操做能夠並行執行。

純函數很是容易進行單元測試,由於不須要考慮上下文環境,只須要考慮輸入和輸出。

 

函數是接受一些輸入,併產生一些輸出的過程。這些輸入稱爲參數,輸出稱爲返回值。

 

純函數的返回值只由它調用時的參數決定,它的執行不依賴於系統的狀態(好比:什麼時候、何處調用它)。

 

純函數的條件:

一個函數的返回結果只依賴於它的參數

不依賴外部狀態

執行過程當中沒有反作用

 

什麼叫函數執行過程沒有反作用?

一個函數執行過程當中對外部產生了變化,那麼就說這個函數是有反作用的。

相關文章
相關標籤/搜索