一、ES6語法 ES6中對數組新增了幾個函數:map()、filter()、reduce() ES5新增的forEach()。 都是一些語法糖。 1.1 forEach()遍曆數組 forEach()方法用來迴圈遍曆數組,方法中的function回調函數接收3個參數 參數1是遍歷的數組內容(item ...
一、ES6語法
ES6中對數組新增了幾個函數:map()、filter()、reduce()
ES5新增的forEach()。
都是一些語法糖。
1.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方法。
1.2 map()映射
map方法的作用,“映射”也就是原數組被“映射”成對應的新數組。
[].map(function(item,index,array){ ... }) var arr = ["白板","么雞","紅中","發財","八萬"]; arr.map(function(item,index,array){ console.log(item,index,array) })
案例:比如創建一個新數組,新數組的每一項是原數組的兩倍
var arr1 = [6,8,10,88,100,200]; var arr2 = arr1.map(function(item){ return item * 2; }) console.log(arr1) console.log(arr2)
map()函數本質是依次遍歷原數組中的每一項,將每一項都執行一遍函數中的語句,返回一個新的數組。
提示:
l 函數需要有return值,如果沒有,數組所有想都被映射成undefined
l map返回的數組一定和原數組長度一樣。
1.3 filter()過濾
filter為“過濾”、“篩選”之意,指從原數組中filter某些項後,返回過濾後的新數組,用法和map相似。
案例:從原數組中,挑選所有的偶數,返回新的數組。
var arr1 = [6,8,10,77,88,100,200,1,3,5]; var arr2 = arr1.filter(function(item){ return item % 2 == 0; }); console.log(arr1) console.log(arr2)
概述:arr中的每一項依次的執行函數,filter的回調函數需要返回布爾值,true則將值返回到新數組中,false則捨棄,進入下一次迴圈。
filter和map相同:都會遍曆數組每一項。
filter和map不相同:map返回數組不會少項,filter可能會少項。
1.4取整運算符
console.log(~~11.5); console.log(~~"11.5"); console.log(~~"10px"); console.log(~~true); console.log(~~false);
二、underscore.js
jQuery是DOM之王,那麼underscore就是數學之王(擅長計算)。
Underscore一個JavaScript實用庫,提供了一整套函數式編程的實用功能,但是沒有擴展任何JavaScript內置對象。
Underscore提供了100多個函數,包括常用的: map, filter, invoke 當然還有更多專業的輔助函數,如:函數綁定, JavaScript模板功能,創建快速索引, 強類型相等測試, 等等.
Underscore不依賴環境,不限制使用場景,可以載入到HTML中在瀏覽器運行,也可以中Nodejs伺服器環境中使用。封裝了一堆實用函數,這些函數基本都是針對:數組、對象、函數的。
中文文檔:http://www.css88.com/archives/5443
CDN公共資源庫:http://cdn.code.baidu.com/
生成0~100的隨機數: _.random(0,100); //生成0~100的隨機數
創建一個範圍整數數組: _.range(1,10) //[1, 2, 3, 4, 5, 6, 7, 8, 9]
取數組中的最大和最小值: var num = [10, 5, 100, 2, 1000]; console.log(_.min(num)); console.log(_.max(num));
把數組轉成對象: _.object(['a', 'b', 'c'], [10, 20, 30]); //{ a: 10, b: 20, c: 30 }
each()遍歷方法,對集合迴圈操作,可以遍曆數組、類數組元素,arguments _.each(['小王','大王','鬼王'],function(item, index){ console.log(item,index) });
JSON遍歷: _.each({'小王':'100','大王':'200','鬼王':'300'},function(item, index){ console.log(item,index) });
map(): 對集合以map方式遍歷,產生一個新數組 var arr3 = _.map({a: 1, b: 2, c: 3}, function(item, key){ return item * 3; }); console.log(arr3); //[3, 6, 9]
filter(): 過濾集合中符合條件的元素 var arr4 = _.filter([1, 2, 3, 4, 5, 6], function(item){ return item % 2 == 0; }); console.log(arr4) //[ 2, 4, 6 ]
sortBy() 自定義比較方法 var sort = _.sortBy([3, 4, 2, 1 , 6 ,88], function(item){ return Math.max(item); }) console.log(sort)
三、模板引擎
3.1 underscore模板引擎
template()方法可接受三個參數:
參數1:是必須的參數是模版字元串,你可以通過<%= %> 來插入變數,還可以通過<% %>來插入js代碼,也可以通過<%- %>來進行html轉義,如果變數很多,可以使用<% print() %>來簡化。
參數2:是傳入模版的數據,如果不傳第二個參數,那麼這個方法會返回一個模版函數,這個模版函數可以傳入數據返回完成的模版,如果傳入data參數則會直接返回一個已完成的模版。
參數3:是設置,比如這個方法預設是尋找<% %>來進行替換,可以通過設置它來改變具體的方法。
_.template 支持以下三種模板: <% %> 執行一些代碼 <%= %> 在模板中列印或者說成輸出一些值 <%- %> 列印一些HTML轉義的值
解釋:
<% %> 里包裹的是一些可執行的 JavaScript 語句,比如 if-else 語句,for 迴圈語句等等。
<%= %> 會列印傳入數據相應的 key 的值,
<%- %> 和前者相比,多了步 HTML 實體編碼的過程,可以有效防止 XSS 攻擊。
//模板 var str = "我很<%= xinqing %>啊!買了一個<%= dongxi%>,花了<%= price%>元"; //通過move字元串生成一個數據綁定函數 var compile = _.template(str); //數據 var obj = { xinqing:"高興", dongxi:"iPhone手機", price:8888 } //字元串和數據進行綁定生成 str = compile(obj); console.log(str)
還可以將HTML作為模板,將JS的數據,註入進去,將模板中“寫死”的內容都用模板的標記代替。
<head> <meta charset="UTF-8" /> <title>Document</title> <style type="text/css"> .bgColor{ background: red; } </style> </head> <body> <table id="table"> <tr> <td>學號</td> <td>姓名</td> <td>年齡</td> <td>性別</td> </tr> </table> </body> <!-- 我們使用一個故意寫錯的type的標簽存放模板 --> <script type="text/template" id="template"> <tr class="<%= leiming %>"> <td><%= id %></td> <td><%= name %></td> <td><%= age %></td> <td><%= sex %></td> </tr> </script> <script type="text/javascript" src="js/jquery-3.2.1.min.js"></script> <script type="text/javascript" src="js/underscore-min.js"></script> <script type="text/javascript"> //通過模板字元串生成一個數據綁定函數 var compile = _.template($("#template").html()); //Ajax讀取數據 $.get("data/student.json", function(data){ //遍曆數據 _.each(data.result, function(obj){ obj.leiming = obj.age >= 18 ? "" : "bgColor"; //數據綁定,得到DOM字元串 var str = compile(obj); // 上樹 $("table").append(str); }) }) </script>演示代碼
3.2模板引擎原理(JS)
拼接字元串很不爽,容易出錯。
所以就有工程師在大量的實戰中,提出模板引擎的概念,就是在一個完整的字元串中,把未定的量用特殊的語法來表示
@xinqing@
然後把這些數據替換成標記,這個操作叫數據綁定。
<script type="text/javascript"> //模板 var str = "我很@xinqing@啊!買了一個@dongxi@,花了@price@元"; //數據 var obj = { xinqing:"高興", dongxi:"iPhone手機", price:8888 } //封裝數據綁定方法 function complie(tplStr,tplObj){ tplStr = tplStr.replace(/\@([a-zA-Z]+)\@/g, function(match,$1){ return tplObj[$1]; }) return tplStr; } //調用數據綁定函數 str = complie(str, obj) console.log(str) </script>數據綁定
使用Ajax
<body> <table id="table"> <tr> <td>學號</td> <td>姓名</td> <td>年齡</td> <td>性別</td> </tr> </table> </body> <!-- 我們使用一個故意寫錯的type的標簽存放模板 --> <script type="text/template" id="template"> <tr class="@leiming@"> <td>@id@</td> <td>@name@</td> <td>@age@</td> <td>@sex@</td> </tr> </script> <script type="text/javascript" src="js/jquery-3.2.1.min.js"></script> <script type="text/javascript"> //通過模板字元串生成一個數據綁定函數 var str = $("#template").html(); //Ajax讀取數據 $.get("data/student.json", function(data){ data.result.forEach(function(item){ var domStr = complie(str, item); $("#table").append(domStr) }) }) //封裝數據綁定方法 function complie(tplStr,tplObj){ tplStr = tplStr.replace(/\@([a-zA-Z]+)\@/g, function(match,$1){ return tplObj[$1]; }) return tplStr; } </script>使用ajax
四、EChart.js(前端數據可視化)
API配置項:http://echarts.baidu.com/option.html#title
第一步:引入JS文件: <script type="text/javascript" src="js/echarts.min.js"></script> 第二步:準備一個放圖表的容器 <div id="main" style="width:600px;height:400px;"></div>
第三步:設置參數,初始化圖表
註意:這裡案例是最基礎,但在使用echarts時一定要配置xAxis,yAxis,series這三個參數。如果不想設置也要初始化,將它設置為空JSON即可,要不然會報錯。同時要保證在echarts.init之前的對象是有寬高的,要不然也會報錯。
// 基於準備好的dom,初始化echarts實例 var myChart = echarts.init(document.getElementById('main')); // 指定圖表的配置項和數據 var option = { title: { text: '數據統計' }, tooltip: {}, //懸浮提示 legend: { data:['訪問量'] }, xAxis: { data: ["Android","IOS","PC","Ohter"] }, yAxis: {}, series: [{ name: '訪問量', //nam == legend.data的時候才能顯示圖例 type: 'bar', //這裡可以改成line或pie data: [500, 200, 360, 100] }] }; // 使用剛指定的配置項和數據顯示圖表。 myChart.setOption(option);
簡單的統計圖表就出來了,官網使用的是柱狀圖,可以改成其他形狀
//基於準備好的dom,初始化echarts實例 var myChart = echarts.init(document.getElementById('main')); // 指定圖表的配置項和數據 var option = { title: { text: 'ECharts數據統計' }, tooltip: {}, //懸浮提示 legend: { data:['占有率'] }, xAxis: { data: ["Android","IOS","PC","Other"] }, yAxis: {}, series: [{ name: '占有率', //name==legend.data相等的時候才能顯示圖例 type: 'line', data: [50, 120, 36, 100] }] }; // 使用剛指定的配置項和數據顯示圖表。 myChart.setOption(option);
餅狀圖和折線圖、柱狀圖有一點區別,主要是在參數和數據綁定上。餅狀圖沒有X和Y軸坐標,數據綁定也是採用value和name對應的形式。
var option = { title:{ text:'周銷量統計', subtext:'虛擬數據' }, tooltip:{ formatter:'系列名:{a}<br />類目:{b}<br />數值:{c}' }, legend:{ data:['購買金額','銷售金額'] }, xAxis:{ data:["周一","周二","周三","周四","周五","周六","周日"] }, yAxis:{}, series:[{ name:'購買金額', type:'bar', data:[200,312,431,241,175,275,369], markPoint: { data: [ {type: 'max', name: '最大值'}, {type: 'min', name: '最小值'} ] }, markLine:{ data:[ {type:'average',name:'平均值',itemStyle:{ normal:{color:'green'} }} ] } },{ name:'銷售金額', type:'line', data:[321,432,543,376,286,298,400], markPoint: { data: [ {type: 'max', name: '最大值'}, {type: 'min', name: '最小值'} ] }, markLine:{ data:[ {type:'average',name:'平均值',itemStyle:{ normal:{color:'blue'} }} ] } }] };演示代碼
五、JS設計模式
到目前為止,我們每個案例都是一個構造函數:Ballon類、Girl類等等,單打獨鬥。此時要學習多個類之間一起配合工作,最重要的就是信息的傳遞(就是類和類之間的數據的傳遞)。
什麼是設計模式?
在大的項目中,一定有很多個類一起協作完成一個事,工程師們經過多年的經驗,寫了很多類和類之間的配合和協調工作的一些套路,這些套路我們叫“設計模式”。
設計模式的定義就是在面向對象的開發、設計過程中,針對特定的問題的簡潔而又優雅的解決方案,通俗的就是說給程式的思想起了一個名字 “設計模式”。
設計模式一共23種:
圖所示23種設計模式,實際上被分為了三個大種類。現在要學習的兩種設計模式,是最著名的設計模式。
比如現在有兩個類:老師類和學生類,老師類有留作業方法(liuzuoye),要求調用這個方法後,每個學生都擁有zuoye這個屬性,初學者往往寫出這樣的代碼:
//老師類 function Teacher(){ } Teacher.prototype.liuzuoye = function(content){ alert("老師留了作業:" + content); xiaoming.zuoye = content; //動用了其他類的實例的名字在此,耦合性高 xiaohong.zuoye = content; } //學生類 function Student(){ this.zuoye = ''; } var zhulaoshi = new Teacher(); var xiaoming = new Student(); var xiaohong = new Student(); zhulaoshi.liuzuoye("完成貪吃蛇!"); alert(xiaoming.zuoye) alert(xiaohong.zuoye)
而軟體設計(尤其是面向對象開發時)講究的是“高內聚、低耦合”。耦合性就是類儘量不要用到其他類的實例,如果其他類改名了,你的類就錯了。
為了降低耦合性,經過大量實踐,總結了很多設計模式,降低耦合性。
5.1觀察者模式
觀察者模式(observer)也叫發佈-訂閱模式(publish-subscribe)。它定義了對象間的一種1對n的依賴關係。當一個對象的狀態發生改變時,所有“訂閱”了它的對象都將得到通知。
剛剛老師發佈作業的案例,老師就是發佈者(publisher),學生就是訂閱者、觀察者(subscriber)。老師是1,學生是n。發佈者(老師)要維護自己的訂閱者(學生)列表,自己有一個屬性students存放著所有訂閱自己的列表(實例數組),當發佈作業時,用for迴圈數組清單,分別調用每個訂閱者相應的方法即可。
精髓:發佈者維持一個訂閱自己的數組,當自己要發佈信息的時候,迴圈遍歷自己的數組調用訂閱者的方法。
//老師類 function Teacher(){ //維護自己訂閱者的列表 this.students = []; } //提供一個註冊(關註訂閱)方法 Teacher.prototype.regist = function(obj){ this.students.push(obj) } Teacher.prototype.liuzuoye = function(content){ alert("老師留了作業:" + content ); //遍歷所有學生,分別調用它們的listen監聽作業方法,把信息通過實參傳遞過去 for(var i = 0;i < this.students.length;i++){ this.students[i].listen(content) } } //學生類 function Student(teacher){ // 去註冊成為指定老師的學生 teacher.regist(this) } Student.prototype.listen = function(zuoye){ this.zuoye = zuoye; } //實例化 var zhulaoshi = new Teacher(); //發佈者要先實例化 var kaola = new Teacher(); //發佈者要先實例化 var xiaoming = new Student(zhulaoshi); //註冊成為zhulaoshi的學生 var xiaohong = new Student(zhulaoshi); //註冊成為zhulaoshi的學生 var xiaogang = new Student(kaola); //註冊成為kaola的學生 var xiaobai = new Student(kaola); //註冊成為kaola的學生 //老師留作業 zhulaoshi.liuzuoye("完成貪吃蛇"); kaola.liuzuoye("寫代碼"); alert(xiaoming.zuoye) alert(xiaohong.zuoye) alert(xiaogang.zuoye) alert(xiaobai.zuoye)演示代碼
設計模式的好處在於程式足夠大的時候使用。
案例:匯率轉換小程式
人民幣換美元、歐元、日元、英鎊、泰銖
當用戶輸入人民幣的時候,需要實時顯示對應的外幣的數值。
人民幣類(RMB)就是發佈者,外幣類waibi就是訂閱者
//人民幣類 function RMB(){ //維護自己訂閱者的列表 this.listen = []; this.init(); this.bindEvent(); } RMB.prototype.init = function(){ this.p = document.createElement('p'); this.p.innerHTML = "人民幣:"; this.input = document.createElement('input'); this.p.appendChild(this.input) document.body.appendChild(this.p); } //提供一個註冊(關註訂閱)方法,可以將某一個幣種添加到自己的數組中 RMB.prototype.regist = function(obj){ this.listen.push(obj) } //監聽發佈者改變時的狀態 RMB.prototype.bindEvent = function(){ //告訴用戶輸入人民幣金額時,遍歷自己所有的訂閱者,調用他們的監聽方法,將數值告訴他們 //“告訴”是通過調用他們的方法實現,通過實參把數據傳遞給他們 var self = this; this.input.oninput = function(){ for(var i = 0;i < self.listen.length;i++){ self.listen[i].listen(this.value); } } } //訂閱者,外幣類 function Waibi(name, huilv){ this.name = name; this.huilv = huilv; //觀察者模式要求,就是去發佈者哪裡註冊自己 //訂閱人民幣類,訂閱者就是要訂閱發佈者 rmb.regist(this); this.init(); } Waibi.prototype.init = function(){ this.p = document.createElement('p'); this.p.innerHTML = this.name + ":"; this.input = document.createElement('input'); this.input.disabled = true; this.p.appendChild(this.input) document.body.appendChild(this.p); } //收聽人民幣的最新數值,此時改變自己dom中的數據 //訂閱者有一個listen監聽發佈者的方法,用來接收響應發佈者的最新消息 Waibi.prototype.listen = function(content){ this.input.value = content / this.huilv; } var rmb = new RMB(); new Waibi("美元", 6.8894); new Waibi("韓元", 0.0061); new Waibi("港幣", 0.8799); new Waibi("英鎊", 8.9542); new Waibi("日元", 0.0609);匯率轉換小程式
5.2中介者模式
觀察者模式的精髓在於“主動通知”,當老師的狀態改變的時候,能夠實時通知學生,通過調用學生的方法來實現的。中介者模式簡單一點,不能主動通知。
老師要發佈作業,此時發佈到QQ群里;學生看作業去QQ群看就行了!QQ群就是中介者,相當於全局變數。
後面製作一些複雜的DOM程式,中介者模式是使用最多的。99%的DOM效果都是中介者模式來製作的。
//********中介者QQ群類********* function QQQun(){ this.zuoye = ''; } //老師類 function Teacher(){ } Teacher.prototype.liuzuoye = function(content){ qqqun.zuoye = content; //發佈作業到qq群 } //學生類 function Student(){ } Student.prototype.xiezuoye = function(){ alert("我要寫作業啦!"+ qqqun.zuoye) } var qqqun = new QQQun(); //先實例化中介者 //實例化老師 var zhulaoshi = new Teacher(); zhulaoshi.liuzuoye("完成貪吃蛇!"); //實例化學生 var xiaoming = new Student(); var xiaohong = new Student(); xiaoming.xiezuoye() xiaohong.xiezuoye()
兩個模式的區別:
觀察者模式能主動推送消息,每個收聽者能夠實時得到發佈者的信息;
中介者模式不主動推送消息,當學生要寫作業,需要作業信息時,主動去找中介者拿,適合時效性不強的信息。
六、貪吃蛇游戲-中介者模式
有的時候,項目中的類很多,沒有所謂的1:n(1對n)的關係,它們感覺“互為信息源”,此時中介者模式是最簡單的。比如蛇需要食物,食物也要蛇的信息。
用貪吃蛇來舉例:
游戲有三個類:游戲類(Game)、蛇類(Snake)、食物類(Food)
游戲類其實就是中介者,蛇和食物都需要通過Game來交換獲取信息。
Game類是中介者類,Snake、Food類是普通類,被Game管理。
註意:
l 中介者必須有唯一的實例化對象,這個對象的名字不能更改,比如群號碼不能更改。在貪吃蛇游戲中,我們就要:
var game = new Game(); 此時game變數名一旦確定就不要改了。
除了中介者之外,其他所有的對象,都需要由中介者來實例化,在貪吃蛇游戲中,Snake蛇、Food食物類由Game類來實例化,它們都是Game類的子屬性。
function Game(){ this.snake = new Snake(); this.food = new Food(); }
Food、Snake之間如果互相要用信息,比如蛇要知道食物的位置,用中介者:
function Snake(){ console.log(game.food.x) }
這是我們第一次將一個類單獨寫在js中,每個js文件就是一個類
第一步:創建index文件,創建Game.js
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>貪吃蛇</title> </head> <body> <div id="app"> </div> </body> <script type="text/javascript" src="js/Game.js"></script> <script type="text/javascript" src="js/Food.js"></script> <script type="text/javascript" src="js/Snake.js"></script> <script type="text/javascript"> var game = new Game(); //唯一的中介者 </script> </html>第一步
第二步:創建表格,在游戲中,表格是Game類的DOM屬性,寫Game類的init方法,創建行和列的個數屬性。
註意:所有的代碼都寫在閉包中,利用window對象暴露唯一的Game游戲類即可。
(function(){ window.Game = function(){ console.log(this) } })();
(function(){ // 註意:所有的代碼都寫在閉包中,利用window對象暴露唯一的Game游戲類即可。 window.Game = function(){ this.rowAmount = 16; //行數 this.colAmount = 20; //列數 this.init(); //初始化UI界面,創建DOM表格 } //初始化UI界面,創建DOM表格 Game.prototype.init = function(){ this.dom = document.createElement('table'); document.getElementById("app").appendChild(this.dom); var tr,td; for(var i = 0; i < this.rowAmount;i++){ tr = document.createElement('tr'); //遍歷插入行 this.dom.appendChild(tr); //tr上樹 for(var j = 0; j < this.colAmount;j++){ td = document.createElement('td');//遍歷插入列 tr.appendChild(td); //td上樹 } } } })();第二步
第三步:創建蛇類,創建Snake.js文件(每一個類都是單獨一個js文件)
蛇類有自己的身體屬性,有render渲染方法。
(function(){ window.Snake = function(){ //蛇的身體 this.body = [ {"row" : 4, "col":7}, {"row" : 4, "col":6}, {"row" : 4, "col":5}, {"row" : 4, "col":4}, {"row" : 4, "col":3} ];