MDN手冊:https://developer.mozilla.org/zh-CN/docs/Web/JavaScripthtml
JavaScript 的標準是 ECMAScript。截至 2012 年,全部的現代瀏覽器都完整了支持 ECMAScript 5.1。舊式的瀏覽器至少支持 ECMAScript 3 標準。前端
2015年6月17日,ECMA國際組織發佈了 ECMAScript 6.0,該版本正式名稱爲ECMAScript 2015,是「JavaScript 語言的下一代標準」。但一般被稱爲 ECMAScript 6或ES6。它的目標,是使得 JavaScript 語言能夠用來編寫複雜的大型應用程序,成爲企業級開發語言。本文檔目前覆蓋了最新ECMAScript的草案,也就是ECMAScript2018。vue
ES是ECMAScript的簡稱。是JavaScript執行標準的制定者。node
ES6 也稱爲ES2015 全稱是ECMAScript2015react
ES7 也稱爲ES2016 全稱是ECMAScript2016git
ES8 也稱爲ES2017 全稱是ECMAScript2017es6
ES9 也稱爲ES2018 全稱是ECMAScript2018github
從 ES6開始ECMA組織每一年發佈一個版本,以年份做爲名稱,所以又稱ECMAScript 2017,簡稱 ES2017。面試
你們基本上記不住哪一個版本的ES更新了什麼,因此統稱爲ENV(ECMAScript New Version)。npm
一個常見的問題是,ECMAScript 和 JavaScript 究竟是什麼關係?
講清楚這個問題,須要回顧歷史。1996年11月,JavaScript的創造者Netscape公司,決定將 JavaScript提交給標準化組織ECMA,但願這種語言可以成爲國際標準。次年,ECMA發佈262號標準文件(ECMA-262)的初版,規定了瀏覽器腳本語言的標準,並將這種語言稱爲ECMAScript,這個版本就是1.0版。
該標準從一開始就是針對JavaScript語言制定的,可是之因此不叫JavaScript,有兩個緣由。一是商標,Java是Sun公司的商標,根據受權協議,只有Netscape公司能夠合法地使用 JavaScript這個名字,且 JavaScript自己也已經被 Netscape 公司註冊爲商標。二是想體現這門語言的制定者是ECMA,不是 Netscape,這樣有利於保證這門語言的開放性和中立性。
所以,ECMAScript和JavaScript的關係是,前者是後者的標準,後者是前者的一種實現。平常場合,這兩個詞是能夠互換的。
關於更多ECMAScript請閱讀:http://es6.ruanyifeng.com/#docs/intro
瀏覽器和NodeJS對ES6新語法特性的支持,要看兼容性。各大瀏覽器的最新版本,對 ES6 的支持能夠查看:
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自己好比http、fs模塊等幾乎沒什麼更新,變化就是對ES的新語法支持,Nodejs 6最高版本,幾乎所有支持了經常使用的新版ES語法。
在瀏覽器和node環境均可以運行,可是有不少舊版瀏覽器還沒有支持。
高級瀏覽器開始逐步支持ES6、7、8這些新特性,好比寫一個「自動解構」語法:
const [a,b,c] = [1,2,3];
console.log(a)
console.log(b)
console.log(c)
因爲瀏覽器的版本和兼容性問題,不少JavaScript的新的方法都不能使用,等到能夠大膽使用的時候,可能已通過去了好幾年。Babel就所以而生,它可讓你放心使用大部分的JavaScript的新的標準的方法,而後編譯成兼容絕大多數的主流瀏覽器的代碼。
Babel是一個普遍使用的ES6轉碼器,能夠將ES6代碼轉爲ES5代碼,從而在現有環境執行。這意味着,你能夠用 ES6 的方式編寫程序,又不用擔憂現有環境是否支持。
中文官網:https://babeljs.cn
Babel是咱們學習的第一個前端工做流工具,它是一個JavaScript編譯器,能將ES6、7、8的代碼翻譯成IE8可以識別的ES5語法。
l 什麼是前端工做流工具?
用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文件才能夠發揮完整的做用。
必須使用.babelrc文件去指導babel工做。
windows系統不容許用戶建立「.」開頭的文件
方法1:用sublime能夠新建。
方法2:打開cmd窗口,輸入一下命令將a.txt文件重命名爲.babelrc
rename a.txt .babelrc
預設(presets)
該文件用來設置轉碼規則和插件,使用的時候須要安裝對應的插件,而且寫一些配置:
{ "presets":["es2015","es2016"] }
用presets設定轉碼規則,就是說babel要翻譯哪些語法,咱們通常翻譯es2015、es2016寫在數組中。
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
開始學習ES6、7、8的語法精粹。
更多的去看:http://es6.ruanyifeng.com/
const用來定義常量,所謂的常量就是值一旦給定後就不變,一次定義終身不變的量。
const a = 10;
a = 20;
上面的a就是常量,若是給a從新賦值,會報錯。
const一般會定義兩種值
l 定義函數
const sum = function(a,b){ return a + b; } console.log(sum(3,4))
l 定義一些特定的字符串和常數
const PI = 3.14;
const HELLO_WORLD = "世界,你好";
常量名通常全是大寫,若是由多個單詞組成,用下劃線隔開,暗示這個是常量。
let用來定義塊級做用域的變量,只要有「{}」包裹,就表示塊級做用域。
注意:不是JSON,if語句體、函數、while、for是塊,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]();
let和for是絕配,let平時沒啥用。
總結:
l const用來定義常量,一般定義函數和常數
l let用來定義{}塊級的做用域的變量,一般用來定義for循環的變量
常量是塊級做用域,很像使用let,語句定義的變量,常量的值不能經過從新賦值來改變,而且不能從新聲明。
let和const的疑點等面試題再加,好比:
console.log(m); let m = 10; //報錯,let沒有變量聲明提高
對於const。babel會翻譯var,可是翻譯的時候,會驗證const是否從新賦值了,是就報錯。
翻譯前:
|
翻譯後:
|
for循環的翻譯:
翻譯前:
|
翻譯後:
|
ES6 容許按照必定模式,從數組和對象中提取值,給變量進行賦值,這被稱爲解構。
解構的做用是能夠快速取得數組或對象中的元素或屬性,而無需使用arr[x]或obj[key]等傳統方式去賦值。
之前爲變量賦值,只能直接指定值:
var a = 1; var b = 2; var c = 3;
所謂的解構就是一個快速給數組或者對象中的值,快速傳入變量的方法。
數組能夠解構,當等號右側是數組的時候,等於左側能夠將變量裝入[]中接收,一一對應接收。
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
對象的結構和數組有個不一樣點,數組的元素是按照順序排列,變量的取值由它的位置決定。
而對象的屬性沒有順序,可是變量必須與屬性同名,才能取到正確的值。
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。也就是說,對象解構賦值的內部機制,是先找到同名屬性,而後再賦值給對應的變量,真正被賦值的是後者。
解構賦值容許指定默認值。
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"
擴展運算符(spread)是三個點(...)。將一個數組轉爲用逗號分隔的參數序列,還能強制展開一個對象,一般用於對象的賦值,使用靈活普遍。
l 第一個做用:稱爲「展開運算符」,做用和字母意思用於,就是把東西展開,能夠用在數組和對象上。
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
第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中當一個對象的k和v一致時,能夠省略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)
擴展運算符和剩餘操做符的區別
簡單的說,在某種程度上,剩餘操做符和擴展運算符相反,擴展運算符會「展開」數組變成多個元素,剩餘操做符會收集多個零散元素合併成一個元素。
注意:babel會將「[]」和「{}」解構變爲一個個的var。
數組解構翻譯前 |
數組解構翻譯後 |
|
![]()
|
可是babel不能翻譯「對象解構」,只能翻譯數組解構:
緣由:object-rest-spread 還處於stage階段(屬於實驗性屬性)。
解決方法:安裝babel插件transform-rest-spread,放到.babelrc文件配置上,便可使用。
{ "presets":["es2015","es2016"], "plugins":["transform-object-rest-spread"] }
.babelrc的presets是用來定義預設,plugins定義插件。
安裝依賴:
npm install babel-plugin-transform-object-rest-spread --save-dev
此時,babel就能夠翻譯對象解構了。
對象翻譯前 |
對象翻譯後 |
|
![]()
|
變量的解構賦值用途不少。
ES6中對數組新增了幾個函數:map()、filter()、reduce()
ES5新增的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方法。
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函數的本質是依次遍歷原數組的每一項,將每一項都執行一遍函數中的語句,返回一個新的數組。
注意:
l 函數須要有return值,若是沒有,數組全部項都被映射成undefined。
l 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(","))
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中的每一項會依次的執行函數,filter的callback函數須要返回布爾值true或false。true則將值返回到新數組中,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)
filter和map相同點:都會遍歷數組的每一項
filter和map不一樣點:map返回數組不會少項,filter可能少項。
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值,這一次的遍歷是cur,prev會有累加的感受。
案例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)
上面的四大數組方法:map、filter、reduce特別有用,作「函數式」編程。
什麼是「函數式(蘭姆達式編程)」編程?
全部的函數都是純函數的時候,此時就是函數式編程。
什麼是純函數?
純函數是指不依賴於且不改變它做用域以外的變量狀態的函數。
l 這個函數內部不改變傳入的參數。
l 傳入這個函數必定有參數,必定會返回某一個肯定的值。
l 函數中的語句,不能有隨機性,好比Math.random()、new Date(),傳入的參數相同,返回的值一定相同;
l 這個函數裏面不能有任何異步語句,好比$.get()、fs.readFile()、setInterval()
必定要好好學純函數,由於react、vue都是純函數(蘭姆達)式編程。
【例子1】請寫一個純函數addItem(),接收arr、n當作參數,可以在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(),接收arr、n當作參數,可以在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(),接收arr、n當作參數,可以將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(),接收arr、n、a當參數,能將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)
上面代碼,咱們只計算了做用域內的局部變量,沒有任何的做用域外的變量被改變了。
更改指定id的name屬性
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
l 總結
爲何要煞費苦心地構建純函數?由於純函數很是「靠譜」,執行一個純函數你不用擔憂它會幹什麼壞事,它不會產生不可預料的行爲,也不會對外部產生影響。無論什麼時候何地,你給它什麼它就會乖乖地吐出什麼。若是你的應用程序大多數函數都是由純函數組成,那麼你的程序測試、調試起來會很是方便。
l 使用純函數的好處
最主要的好處是沒有反作用。純函數不會修改做用域以外的狀態,作到這一點,代碼就變得足夠簡單和清晰:當你調用一個純函數,你只要關注它的返回值,而不用擔憂由於別處的問題致使錯誤。
純函數是健壯的,改變執行次序不會對系統形成影響,所以純函數的操做能夠並行執行。
純函數很是容易進行單元測試,由於不須要考慮上下文環境,只須要考慮輸入和輸出。
函數是接受一些輸入,併產生一些輸出的過程。這些輸入稱爲參數,輸出稱爲返回值。
純函數的返回值只由它調用時的參數決定,它的執行不依賴於系統的狀態(好比:什麼時候、何處調用它)。
l 純函數的條件:
l 一個函數的返回結果只依賴於它的參數
l 不依賴外部狀態
l 執行過程當中沒有反作用
什麼叫函數執行過程沒有反作用?
一個函數執行過程當中對外部產生了變化,那麼就說這個函數是有反作用的。