ES6中對數組新增了幾個函數:map()、filter()、reduce()javascript
ES5新增的forEach()。css
都是一些語法糖。html
forEach()方法用來循環遍歷數組,方法中的function回調函數接收3個參數前端
參數1是遍歷的數組內容(item);參數2是對應的數組索引(index),參數3是是數組自己(array)。java
[].forEach(function(item,index,array){ ... }) var arr = ["白板","幺雞","紅中","發財","八萬"]; arr.forEach(function(item,index,array){ console.log(item,index,array) })
forEach函數沒有返回值jquery
for從此是建立數組,遍歷或操做數組能夠交給forEach方法。ajax
map方法的做用,「映射」也就是原數組被「映射」成對應的新數組。編程
[].map(function(item,index,array){ ... }) var arr = ["白板","幺雞","紅中","發財","八萬"]; arr.map(function(item,index,array){ console.log(item,index,array) })
案例:好比建立一個新數組,新數組的每一項是原數組的兩倍json
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返回的數組必定和原數組長度同樣。
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可能會少項。
console.log(~~11.5); console.log(~~"11.5"); console.log(~~"10px"); console.log(~~true); console.log(~~false);
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)
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>
拼接字符串很不爽,容易出錯。
因此就有工程師在大量的實戰中,提出模板引擎的概念,就是在一個完整的字符串中,把未定的量用特殊的語法來表示
@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>
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'} }} ] } }] };
到目前爲止,咱們每一個案例都是一個構造函數: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)
而軟件設計(尤爲是面向對象開發時)講究的是「高內聚、低耦合」。耦合性就是類儘可能不要用到其餘類的實例,若是其餘類更名了,你的類就錯了。
爲了下降耦合性,通過大量實踐,總結了不少設計模式,下降耦合性。
觀察者模式(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);
觀察者模式的精髓在於「主動通知」,當老師的狀態改變的時候,可以實時通知學生,經過調用學生的方法來實現的。中介者模式簡單一點,不能主動通知。
老師要發佈做業,此時發佈到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} ]; } //渲染蛇的身體方法 Snake.prototype.render = function(){ for(var i = 0;i < this.body.length; i++){ //這裏寫違反了高內聚低耦合的原則,改一改東西的屬性應該要調用人家提供的方法 //game.dom.getElementsByTagName('tr')[this.body[i].row].getElementsByTagName('td')[this.body[i].col].style.background = 'red'; game.setColor(this.body[i].row,this.body[i].col,'red'); } } })();
在Game類實例化蛇類,同時提供一個setColor方法:
(function(){ // 注意:全部的代碼都寫在閉包中,利用window對象暴露惟一的Game遊戲類便可。 window.Game = function(){ this.rowAmount = 16; //行數 this.colAmount = 20; //列數 this.init(); //初始化UI界面,建立DOM表格 //實例化蛇類 this.snake = new Snake(); //渲染蛇方法 this.snake.render(); } //初始化UI界面,建立DOM表格 Game.prototype.init = function(){ ... } //設置蛇身的顏色 Game.prototype.setColor = function(row,col,color){ this.dom.getElementsByTagName('tr')[row].getElementsByTagName('td')[col] .style.background = color; } })();
此時會發現報錯了:
緣由是在Game類的構造函數中,使用game這個實例的名字,此時是undefined,由於構造函數還沒執行完畢,還沒執行完四步走。
解決方法兩種:
方法1:在Game類中new Snake(this)傳入上下文,this表示即將返回的Game對象。
window.Game = function(){ this.rowAmount = 16; //行數 this.colAmount = 20; //列數 this.init(); //初始化UI界面,建立DOM表格 //實例化蛇類 this.snake = new Snake(this); //渲染蛇方法 this.snake.render(); }
Snake類中接收this
window.Snake = function(mediator){ //接收Game類當作子屬性(中介者) this.mediator = mediator; //蛇的身體,可讓蛇運動起來,頭增尾刪 this.body = [ {"row" : 3, "col":8}, {"row" : 3, "col":7}, {"row" : 3, "col":6}, {"row" : 3, "col":5}, {"row" : 3, "col":4} ]; } //渲染蛇的身體方法 Snake.prototype.render = function(){ this.mediator.setColor(this.body[0].row,this.body[0].col,'green'); for(var i = 1;i < this.body.length; i++){ this.mediator.setColor(this.body[i].row,this.body[i].col,'red'); } }
方法2:利用定時器解決
定時器20毫秒一幀,第一幀就發生在20毫秒以後,此時構造函數已經執行完畢,game對象就已經返回。
建立定時器,在定時器中,渲染蛇。同時加上清屏的語句,在每一幀都是清屏,從新繪製蛇。
window.Game = function(){ this.rowAmount = 16; //行數 this.colAmount = 20; //列數 this.init(); //初始化UI界面,建立DOM表格 //實例化蛇類 this.snake = new Snake(); //開啓遊戲定時器 this.start(); } //初始化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上樹 } } } //設置蛇身的顏色 Game.prototype.setColor = function(row,col,color){ this.dom.getElementsByTagName('tr')[row].getElementsByTagName('td')[col].style.background = color; } //清屏,遍歷行和列,設爲白色 Game.prototype.clear = function(){ for(var i = 0;i < this.rowAmount; i++){ for (var j = 0; j < this.colAmount; j++) { this.dom.getElementsByTagName('tr')[i].getElementsByTagName('td')[j].style.background = "#fff"; }; } } // 遊戲開始方法 Game.prototype.start = function(){ var self = this; this.f = 0; //幀編號 setInterval(function(){ self.f++; document.getElementById("info").innerHTML = "幀編號:" + self.f; //清屏 self.clear(); //每隔30幀更新一下 self.f % 30 == 0 && self.snake.update(); //渲染蛇方法 self.snake.render(); },20); }
第五步:讓蛇動起來,套路是,每一幀都清屏,而後更新蛇、渲染蛇...
this.body = [ {"row" : 4, "col" : 7}, {"row" : 4, "col" : 6}, {"row" : 4, "col" : 5}, {"row" : 4, "col" : 4}, {"row" : 4, "col" : 3} ];
變爲:
this.body = [ {"row" : 4, "col" : 8} {"row" : 4, "col" : 7}, {"row" : 4, "col" : 6}, {"row" : 4, "col" : 5}, {"row" : 4, "col" : 4}, ];
在Snake控制方向:
//更新方法,這個方法最關鍵 Snake.prototype.update = function(){ this.body.pop(); //尾刪 this.body.unshift({"row":this.body[0].row, "col":this.body[0].col+1});//頭插 }
Snake.prototype.update = function(){ this.body.pop(); //尾刪 //根據方向頭插 switch(this.direction){ case "R": var toucha = {"row": this.body[0].row, "col" : this.body[0].col + 1}; this.body.unshift(toucha); break; case "L": var toucha = {"row": this.body[0].row, "col" : this.body[0].col - 1}; this.body.unshift(toucha); break; case "U": var toucha = {"row": this.body[0].row - 1, "col" : this.body[0].col}; this.body.unshift(toucha); break; case "D": var toucha = {"row": this.body[0].row + 1, "col" : this.body[0].col}; this.body.unshift(toucha); break; } }
Snake.prototype.changeDireciton = function(str){ this.direction = str; }
//綁定鍵盤監聽,調用changeDireciton改變方向方法
Game.prototype.bindEvent = function(){ var self = this; document.onkeydown = function(e){ switch(e.keyCode){ case 37: //按左鍵,若是當前往右走,不容許掉頭 if(self.snake.direction == "R") return; self.snake.changeDirection("L"); break; case 38: if(self.snake.direction == "D") return; self.snake.changeDirection("U"); break; case 39: if(self.snake.direction == "L") return; self.snake.changeDirection("R"); break; case 40: if(self.snake.direction == "U") return; self.snake.changeDirection("D"); break; } } }
第六步:食物類
l 咱們採用每吃到一次食物,就從新new一個食物
l 食物不能隨機到蛇的身上(所在的表格中)
l 咱們採用的是每一幀要清除全部小格的html內容,而後從新渲染全部小格
window.Game = function(){ ... this.snake = new Snake(); //實例化食物 this.food = new Food(this); this.start();//開啓遊戲定時器 this.bindEvent();//監聽事件 }
在Game.js中:
Game.prototype.start = function(){ this.timer = setInterval(function(){ self.clear();//清除屏幕 //更新蛇 self.f % 30 == 0 && self.snake.update(); self.snake.render();//渲染蛇 self.food.render();//渲染食物 }, 20); }
//設置食物的方法 Game.prototype.setHTML = function(row,col,html){ this.dom.getElementsByTagName('tr')[row].getElementsByTagName('td')[col] .innerHTML = html; }
Food.js類:
(function(){ window.Food = function(mediator){ //隨機食物位置,不能在蛇的身上 var self = this; do{ this.row = ~~(Math.random() * mediator.rowAmount); this.col = ~~(Math.random() * mediator.colAmount); }while((function(){ //IIFE的執行,返回true或fasle for(var i = 0;i < mediator.snake.body.length;i++){ if(mediator.snake.body[i].row == self.row && mediator.snake.body[j].col == self.col){ return true; //食物隨機到蛇身上,從新隨機一次 } return false; //若是食物不在蛇身上,終止循環 } })()); } Food.prototype.render = function(){ game.setHTML(this.row,this.col,"♥"); } })();
如下在Snake.js寫:
第七步:吃到食物蛇身變長
// 食物判斷 if(toucha.row == game.food.row && toucha.col == game.food.col){ //當你吃到食物的時候,不用刪尾巴,並且須要從新new一個食物 game.food = new Food(game); //傳上下文,要中介者(game) game.f = 0; }else{ //當沒有吃到食物時候,刪除尾巴一項 this.body.pop(); //尾刪 }
第八步:死亡斷定,撞牆和撞本身
//撞牆判斷 if(toucha.row<0 ||toucha.col<0 || toucha.col > game.colAmount-1 || toucha.row > game.rowAmount-1){ alert("你撞牆了,長度是:" + this.body.length); this.body.shift(); //撞牆繼續頭插不合法 clearInterval(game.timer); }
// 撞本身判斷 for(var i = 1;i < this.body.length;i++){ if(toucha.row == this.body[i].row && toucha.col == this.body[i].col){ alert("撞本身啦!傻缺,長度是:" + this.body.length); this.body.shift(); //繼續頭插不合理 clearInterval(game.timer); } }
更新蛇,蛇越長速度越快
var s = self.snake.body.length < 10 ? 30 : 5; self.f % s == 0 && self.snake.update();
最後,解決bug。
Snake.prototype.changeDirection = function(str){ //更改將來的方向 this.willDirection = str; }
var Snake = window.Snake = function(){ //蛇的身體 .... //蛇當前動態的方向 this.direction = "R"; //即將設置的方向,這裏是爲了防止用戶按很快出bug。 this.willDirection = "R"; }
Snake.prototype.update = function(){ //讓當前的方向和即將設置的方向一致,這裏是爲了放置用戶按很快出bug。 this.direction = this.willDirection; }