整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          《JavaScript權威指南》 原書第7版

          《JavaScript權威指南》 原書第7版

          avascript經典之作,中文版犀牛書,在很多工程師心目中有著至高無上的地位

          一、作者簡介

          David Flanagan從1995起就開始使用JavaScript并寫作本書的第1版。他擁有麻省理工學院計算機科學與工程學位,目前是VMware的一名軟件工程師。

          二、內容簡介

          JavaScript是Web編程語言,也是很多軟件開發(fā)者使用的編程語言。近25年來,這本暢銷書一直是幾十萬JavaScript程序員必讀的技術著作,本版已經更新到涵蓋JavaScript的2020版。書中令人深思、富有啟發(fā)性的示例隨處可見。

          這本“犀牛書”在很多工程師心目中有著至高無上的地位。如果你由于種種原因錯過了它之前的版本,那一定不要再錯過這一版了!

          本書介紹JavaScript語言和由瀏覽器與Node實現的JavaScript API。本書適合有一定編程經驗、想學習JavaScript讀者,也適合已經在使用JavaScript但希望更深入地理解進而真正掌握這門語言的程序員。

          本書的目標是全面地講解JavaScript語言,對JavaScript程序中可能用到的重要的客戶端API和服務器端API提供深入的介紹。本書篇幅較長,內容非常詳盡,相信認真研究本書的讀者都能獲益良多。

          三、學習目標

          太經典不用多少,學就是了,學完還的時常復習。

          四、知識導圖

          ===================================

          本人系列原創(chuàng)經典技術書單合集:


          Java系列書單合集(5):Java系列書單

          Python系列書單合集(3):Python系列書單

          算法系列書單合集(4):算法系列書單
          人工智能系列書單合集(9):人工智能系列書單
          系統(tǒng)架構系列書單合集(6):系統(tǒng)架構系列書單
          元宇宙系列書單合集(4):元宇宙系列書單
          大數據系列書單合集(6):大數據系列書單

          持續(xù)更新中。

          文轉載自:https://www.cnblogs.com/imwtr/p/9451129.html
          望大家多多支持原創(chuàng)作者!!!

          設計原則

          單一職責原則(SRP)

          一個對象或方法只做一件事情。如果一個方法承擔了過多的職責,那么在需求的變遷過程中,需要改寫這個方法的可能性就越大。

          應該把對象或方法劃分成較小的粒度

          最少知識原則(LKP)

          一個軟件實體應當 盡可能少地與其他實體發(fā)生相互作用

          應當盡量減少對象之間的交互。如果兩個對象之間不必彼此直接通信,那么這兩個對象就不要發(fā)生直接的 相互聯(lián)系,可以轉交給第三方進行處理

          開放-封閉原則(OCP)

          軟件實體(類、模塊、函數)等應該是可以 擴展的,但是不可修改

          當需要改變一個程序的功能或者給這個程序增加新功能的時候,可以使用增加代碼的方式,盡量避免改動程序的源代碼,防止影響原系統(tǒng)的穩(wěn)定


          什么是設計模式

          假設有一個空房間,我們要日復一日地往里 面放一些東西。最簡單的辦法當然是把這些東西 直接扔進去,但是時間久了,就會發(fā)現很難從這 個房子里找到自己想要的東西,要調整某幾樣東 西的位置也不容易。所以在房間里做一些柜子也 許是個更好的選擇,雖然柜子會增加我們的成 本,但它可以在維護階段為我們帶來好處。使用 這些柜子存放東西的規(guī)則,或許就是一種模式

          一、單例模式

          1. 定義

          保證一個類僅有一個實例,并提供一個訪問它的全局訪問點

          2. 核心

          確保只有一個實例,并提供全局訪問

          3. 實現

          假設要設置一個管理員,多次調用也僅設置一次,我們可以使用閉包緩存一個內部變量來實現這個單例

          function SetManager(name) {
              this.manager=name;
          }
          
          SetManager.prototype.getName=function() {
              console.log(this.manager);
          };
          
          var SingletonSetManager=(function() {
              var manager=null;
          
              return function(name) {
                  if (!manager) {
                      manager=new SetManager(name);
                  }
          
                  return manager;
              } 
          })();
          
          SingletonSetManager('a').getName(); // a
          SingletonSetManager('b').getName(); // a
          SingletonSetManager('c').getName(); // a

          這是比較簡單的做法,但是假如我們還要設置一個HR呢?就得復制一遍代碼了

          所以,可以改寫單例內部,實現地更通用一些

          // 提取出通用的單例
          function getSingleton(fn) {
              var instance=null;
          
              return function() {
                  if (!instance) {
                      instance=fn.apply(this, arguments);
                  }
          
                  return instance;
              }
          }

          再進行調用,結果還是一樣

          // 獲取單例
          var managerSingleton=getSingleton(function(name) {
              var manager=new SetManager(name);
              return manager;
          });
          
          managerSingleton('a').getName(); // a
          managerSingleton('b').getName(); // a
          managerSingleton('c').getName(); // a

          這時,我們添加HR時,就不需要更改獲取單例內部的實現了,僅需要實現添加HR所需要做的,再調用即可

          function SetHr(name) {
              this.hr=name;
          }
          
          SetHr.prototype.getName=function() {
              console.log(this.hr);
          };
          
          var hrSingleton=getSingleton(function(name) {
              var hr=new SetHr(name);
              return hr;
          });
          
          hrSingleton('aa').getName(); // aa
          hrSingleton('bb').getName(); // aa
          hrSingleton('cc').getName(); // aa

          或者,僅想要創(chuàng)建一個div層,不需要將對象實例化,直接調用函數

          結果為頁面中僅有第一個創(chuàng)建的div

          function createPopup(html) {
              var div=document.createElement('div');
              div.innerHTML=html;
              document.body.append(div);
          
              return div;
          }
          
          var popupSingleton=getSingleton(function() {
              var div=createPopup.apply(this, arguments);
              return div;
          });
          
          console.log(
              popupSingleton('aaa').innerHTML,
              popupSingleton('bbb').innerHTML,
              popupSingleton('bbb').innerHTML
          ); // aaa  aaa  aaa

          二、策略模式

          1. 定義

          定義一系列的算法,把它們一個個封裝起來,并且使它們可以相互替換。

          2. 核心

          將算法的使用和算法的實現分離開來。

          一個基于策略模式的程序至少由兩部分組成:

          第一個部分是一組策略類,策略類封裝了具體的算法,并負責具體的計算過程。

          第二個部分是環(huán)境類Context,Context接受客戶的請求,隨后把請求委托給某一個策略類。要做到這點,說明Context 中要維持對某個策略對象的引用

          3. 實現

          策略模式可以用于組合一系列算法,也可用于組合一系列業(yè)務規(guī)則

          假設需要通過成績等級來計算學生的最終得分,每個成績等級有對應的加權值。我們可以利用對象字面量的形式直接定義這個組策略

          // 加權映射關系
          var levelMap={
              S: 10,
              A: 8,
              B: 6,
              C: 4
          };
          
          // 組策略
          var scoreLevel={
              basicScore: 80,
          
              S: function() {
                  return this.basicScore + levelMap['S']; 
              },
          
              A: function() {
                  return this.basicScore + levelMap['A']; 
              },
          
              B: function() {
                  return this.basicScore + levelMap['B']; 
              },
          
              C: function() {
                  return this.basicScore + levelMap['C']; 
              }
          }
          
          // 調用
          function getScore(level) {
              return scoreLevel[level] ? scoreLevel[level]() : 0;
          }
          
          console.log(
              getScore('S'),
              getScore('A'),
              getScore('B'),
              getScore('C'),
              getScore('D')
          ); // 90 88 86 84 0

          在組合業(yè)務規(guī)則方面,比較經典的是表單的驗證方法。這里列出比較關鍵的部分

          // 錯誤提示
          var errorMsgs={
              default: '輸入數據格式不正確',
              minLength: '輸入數據長度不足',
              isNumber: '請輸入數字',
              required: '內容不為空'
          };
          
          // 規(guī)則集
          var rules={
              minLength: function(value, length, errorMsg) {
                  if (value.length < length) {
                      return errorMsg || errorMsgs['minLength']
                  }
              },
              isNumber: function(value, errorMsg) {
                  if (!/\d+/.test(value)) {
                      return errorMsg || errorMsgs['isNumber'];
                  }
              },
              required: function(value, errorMsg) {
                  if (value==='') {
                      return errorMsg || errorMsgs['required'];
                  }
              }
          };
          
          // 校驗器
          function Validator() {
              this.items=[];
          };
          
          Validator.prototype={
              constructor: Validator,
              
              // 添加校驗規(guī)則
              add: function(value, rule, errorMsg) {
                  var arg=[value];
          
                  if (rule.indexOf('minLength') !==-1) {
                      var temp=rule.split(':');
                      arg.push(temp[1]);
                      rule=temp[0];
                  }
          
                  arg.push(errorMsg);
          
                  this.items.push(function() {
                      // 進行校驗
                      return rules[rule].apply(this, arg);
                  });
              },
              
              // 開始校驗
              start: function() {
                  for (var i=0; i < this.items.length; ++i) {
                      var ret=this.items[i]();
                      
                      if (ret) {
                          console.log(ret);
                          // return ret;
                      }
                  }
              }
          };
          
          // 測試數據
          function testTel(val) {
              return val;
          }
          
          var validate=new Validator();
          
          validate.add(testTel('ccc'), 'isNumber', '只能為數字'); // 只能為數字
          validate.add(testTel(''), 'required'); // 內容不為空
          validate.add(testTel('123'), 'minLength:5', '最少5位'); // 最少5位
          validate.add(testTel('12345'), 'minLength:5', '最少5位');
          
          var ret=validate.start();
          
          console.log(ret);

          4. 優(yōu)缺點

          優(yōu)點

          可以有效地避免多重條件語句,將一系列方法封裝起來也更直觀,利于維護

          缺點

          往往策略集會比較多,我們需要事先就了解定義好所有的情況


          三、代理模式

          1. 定義

          為一個對象提供一個代用品或占位符,以便控制對它的訪問

          2. 核心

          當客戶不方便直接訪問一個 對象或者不滿足需要的時候,提供一個替身對象 來控制對這個對象的訪問,客戶實際上訪問的是 替身對象。

          替身對象對請求做出一些處理之后, 再把請求轉交給本體對象

          代理和本體的接口具有一致性,本體定義了關鍵功能,而代理是提供或拒絕對它的訪問,或者在訪問本體之前做一 些額外的事情

          3. 實現

          代理模式主要有三種:保護代理、虛擬代理、緩存代理

          保護代理主要實現了訪問主體的限制行為,以過濾字符作為簡單的例子

          // 主體,發(fā)送消息
          function sendMsg(msg) {
              console.log(msg);
          }
          
          // 代理,對消息進行過濾
          function proxySendMsg(msg) {
              // 無消息則直接返回
              if (typeof msg==='undefined') {
                  console.log('deny');
                  return;
              }
              
              // 有消息則進行過濾
              msg=('' + msg).replace(/泥\s*煤/g, '');
          
              sendMsg(msg);
          }
          
          
          sendMsg('泥煤呀泥 煤呀'); // 泥煤呀泥 煤呀
          proxySendMsg('泥煤呀泥 煤'); // 呀
          proxySendMsg(); // deny

          它的意圖很明顯,在訪問主體之前進行控制,沒有消息的時候直接在代理中返回了,拒絕訪問主體,這數據保護代理的形式

          有消息的時候對敏感字符進行了處理,這屬于虛擬代理的模式


          虛擬代理在控制對主體的訪問時,加入了一些額外的操作

          在滾動事件觸發(fā)的時候,也許不需要頻繁觸發(fā),我們可以引入函數節(jié)流,這是一種虛擬代理的實現

          // 函數防抖,頻繁操作中不處理,直到操作完成之后(再過 delay 的時間)才一次性處理
          function debounce(fn, delay) {
              delay=delay || 200;
              
              var timer=null;
          
              return function() {
                  var arg=arguments;
                    
                  // 每次操作時,清除上次的定時器
                  clearTimeout(timer);
                  timer=null;
                  
                  // 定義新的定時器,一段時間后進行操作
                  timer=setTimeout(function() {
                      fn.apply(this, arg);
                  }, delay);
              }
          };
          
          var count=0;
          
          // 主體
          function scrollHandle(e) {
              console.log(e.type, ++count); // scroll
          }
          
          // 代理
          var proxyScrollHandle=(function() {
              return debounce(scrollHandle, 500);
          })();
          
          window.onscroll=proxyScrollHandle;


          緩存代理可以為一些開銷大的運算結果提供暫時的緩存,提升效率

          來個栗子,緩存加法操作

          // 主體
          function add() {
              var arg=[].slice.call(arguments);
          
              return arg.reduce(function(a, b) {
                  return a + b;
              });
          }
          
          // 代理
          var proxyAdd=(function() {
              var cache=[];
          
              return function() {
                  var arg=[].slice.call(arguments).join(',');
                  
                  // 如果有,則直接從緩存返回
                  if (cache[arg]) {
                      return cache[arg];
                  } else {
                      var ret=add.apply(this, arguments);
                      return ret;
                  }
              };
          })();
          
          console.log(
              add(1, 2, 3, 4),
              add(1, 2, 3, 4),
          
              proxyAdd(10, 20, 30, 40),
              proxyAdd(10, 20, 30, 40)
          ); // 10 10 100 100


          四、迭代器模式

          1. 定義

          迭代器模式是指提供一種方法順序訪問一個聚合對象中的各個元素,而又不需要暴露該對象的內部表示。

          2. 核心

          在使用迭代器模式之后,即使不關心對象的內部構造,也可以按順序訪問其中的每個元素

          3. 實現

          JS中數組的map forEach 已經內置了迭代器

          [1, 2, 3].forEach(function(item, index, arr) {
              console.log(item, index, arr);
          });

          不過對于對象的遍歷,往往不能與數組一樣使用同一的遍歷代碼

          我們可以封裝一下

          function each(obj, cb) {
              var value;
          
              if (Array.isArray(obj)) {
                  for (var i=0; i < obj.length; ++i) {
                      value=cb.call(obj[i], i, obj[i]);
          
                      if (value===false) {
                          break;
                      }
                  }
              } else {
                  for (var i in obj) {
                      value=cb.call(obj[i], i, obj[i]);
          
                      if (value===false) {
                          break;
                      }
                  }
              }
          }
          
          each([1, 2, 3], function(index, value) {
              console.log(index, value);
          });
          
          each({a: 1, b: 2}, function(index, value) {
              console.log(index, value);
          });
          
          // 0 1
          // 1 2
          // 2 3
          
          // a 1
          // b 2

          再來看一個例子,強行地使用迭代器,來了解一下迭代器也可以替換頻繁的條件語句

          雖然例子不太好,但在其他負責的分支判斷情況下,也是值得考慮的

          function getManager() {
              var year=new Date().getFullYear();
          
              if (year <=2000) {
                  console.log('A');
              } else if (year >=2100) {
                  console.log('C');
              } else {
                  console.log('B');
              }
          }
          
          getManager(); // B

          將每個條件語句拆分出邏輯函數,放入迭代器中迭代

          function year2000() {
              var year=new Date().getFullYear();
          
              if (year <=2000) {
                  console.log('A');
              }
          
              return false;
          }
          
          function year2100() {
              var year=new Date().getFullYear();
          
              if (year >=2100) {
                  console.log('C');
              }
          
              return false;
          }
          
          function year() {
              var year=new Date().getFullYear();
          
              if (year > 2000 && year < 2100) {
                  console.log('B');
              }
          
              return false;
          }
          
          function iteratorYear() {
              for (var i=0; i < arguments.length; ++i) {
                  var ret=arguments[i]();
          
                  if (ret !==false) {
                      return ret;
                  }
              }
          }
          
          var manager=iteratorYear(year2000, year2100, year); // B


          五、發(fā)布-訂閱模式

          1. 定義

          也稱作觀察者模式,定義了對象間的一種一對多的依賴關系,當一個對象的狀態(tài)發(fā) 生改變時,所有依賴于它的對象都將得到通知

          2. 核心

          取代對象之間硬編碼的通知機制,一個對象不用再顯式地調用另外一個對象的某個接口。

          與傳統(tǒng)的發(fā)布-訂閱模式實現方式(將訂閱者自身當成引用傳入發(fā)布者)不同,在JS中通常使用注冊回調函數的形式來訂閱

          3. 實現

          JS中的事件就是經典的發(fā)布-訂閱模式的實現

          // 訂閱
          document.body.addEventListener('click', function() {
              console.log('click1');
          }, false);
          
          document.body.addEventListener('click', function() {
              console.log('click2');
          }, false);
          
          // 發(fā)布
          document.body.click(); // click1  click2

          自己實現一下

          小A在公司C完成了筆試及面試,小B也在公司C完成了筆試。他們焦急地等待結果,每隔半天就電話詢問公司C,導致公司C很不耐煩。

          一種解決辦法是 AB直接把聯(lián)系方式留給C,有結果的話C自然會通知AB

          這里的“詢問”屬于顯示調用,“留給”屬于訂閱,“通知”屬于發(fā)布

          // 觀察者
          var observer={
              // 訂閱集合
              subscribes: [],
          
              // 訂閱
              subscribe: function(type, fn) {
                  if (!this.subscribes[type]) {
                      this.subscribes[type]=[];
                  }
                  
                  // 收集訂閱者的處理
                  typeof fn==='function' && this.subscribes[type].push(fn);
              },
          
              // 發(fā)布  可能會攜帶一些信息發(fā)布出去
              publish: function() {
                  var type=[].shift.call(arguments),
                      fns=this.subscribes[type];
                  
                  // 不存在的訂閱類型,以及訂閱時未傳入處理回調的
                  if (!fns || !fns.length) {
                      return;
                  }
                  
                  // 挨個處理調用
                  for (var i=0; i < fns.length; ++i) {
                      fns[i].apply(this, arguments);
                  }
              },
              
              // 刪除訂閱
              remove: function(type, fn) {
                  // 刪除全部
                  if (typeof type==='undefined') {
                      this.subscribes=[];
                      return;
                  }
          
                  var fns=this.subscribes[type];
          
                  // 不存在的訂閱類型,以及訂閱時未傳入處理回調的
                  if (!fns || !fns.length) {
                      return;
                  }
          
                  if (typeof fn==='undefined') {
                      fns.length=0;
                      return;
                  }
          
                  // 挨個處理刪除
                  for (var i=0; i < fns.length; ++i) {
                      if (fns[i]===fn) {
                          fns.splice(i, 1);
                      }
                  }
              }
          };
          
          // 訂閱崗位列表
          function jobListForA(jobs) {
              console.log('A', jobs);
          }
          
          function jobListForB(jobs) {
              console.log('B', jobs);
          }
          
          // A訂閱了筆試成績
          observer.subscribe('job', jobListForA);
          // B訂閱了筆試成績
          observer.subscribe('job', jobListForB);
          
          
          // A訂閱了筆試成績
          observer.subscribe('examinationA', function(score) {
              console.log(score);
          });
          
          // B訂閱了筆試成績
          observer.subscribe('examinationB', function(score) {
              console.log(score);
          });
          
          // A訂閱了面試結果
          observer.subscribe('interviewA', function(result) {
              console.log(result);
          });
          
          observer.publish('examinationA', 100); // 100
          observer.publish('examinationB', 80); // 80
          observer.publish('interviewA', '備用'); // 備用
          
          observer.publish('job', ['前端', '后端', '測試']); // 輸出A和B的崗位
          
          
          // B取消訂閱了筆試成績
          observer.remove('examinationB');
          // A都取消訂閱了崗位
          observer.remove('job', jobListForA);
          
          observer.publish('examinationB', 80); // 沒有可匹配的訂閱,無輸出
          observer.publish('job', ['前端', '后端', '測試']); // 輸出B的崗位

          4. 優(yōu)缺點

          優(yōu)點

          一為時間上的解耦,二為對象之間的解耦。可以用在異步編程中與MV*框架中

          缺點

          創(chuàng)建訂閱者本身要消耗一定的時間和內存,訂閱的處理函數不一定會被執(zhí)行,駐留內存有性能開銷

          弱化了對象之間的聯(lián)系,復雜的情況下可能會導致程序難以跟蹤維護和理解


          六、命令模式

          1. 定義

          用一種松耦合的方式來設計程序,使得請求發(fā)送者和請求接收者能夠消除彼此之間的耦合關系

          命令(command)指的是一個執(zhí)行某些特定事情的指令

          2. 核心

          命令中帶有execute執(zhí)行、undo撤銷、redo重做等相關命令方法,建議顯示地指示這些方法名

          3. 實現

          簡單的命令模式實現可以直接使用對象字面量的形式定義一個命令

          var incrementCommand={
              execute: function() {
                  // something
              }
          };

          不過接下來的例子是一個自增命令,提供執(zhí)行、撤銷、重做功能

          采用對象創(chuàng)建處理的方式,定義這個自增

          // 自增
          function IncrementCommand() {
              // 當前值
              this.val=0;
              // 命令棧
              this.stack=[];
              // 棧指針位置
              this.stackPosition=-1;
          };
          
          IncrementCommand.prototype={
              constructor: IncrementCommand,
          
              // 執(zhí)行
              execute: function() {
                  this._clearRedo();
                  
                  // 定義執(zhí)行的處理
                  var command=function() {
                      this.val +=2;
                  }.bind(this);
                  
                  // 執(zhí)行并緩存起來
                  command();
                  
                  this.stack.push(command);
          
                  this.stackPosition++;
          
                  this.getValue();
              },
              
              canUndo: function() {
                  return this.stackPosition >=0;
              },
              
              canRedo: function() {
                  return this.stackPosition < this.stack.length - 1;
              },
          
              // 撤銷
              undo: function() {
                  if (!this.canUndo()) {
                      return;
                  }
                  
                  this.stackPosition--;
          
                  // 命令的撤銷,與執(zhí)行的處理相反
                  var command=function() {
                      this.val -=2;
                  }.bind(this);
                  
                  // 撤銷后不需要緩存
                  command();
          
                  this.getValue();
              },
              
              // 重做
              redo: function() {
                  if (!this.canRedo()) {
                      return;
                  }
                  
                  // 執(zhí)行棧頂的命令
                  this.stack[++this.stackPosition]();
          
                  this.getValue();
              },
              
              // 在執(zhí)行時,已經撤銷的部分不能再重做
              _clearRedo: function() {
                  this.stack=this.stack.slice(0, this.stackPosition + 1);
              },
              
              // 獲取當前值
              getValue: function() {
                  console.log(this.val);
              }
          };

          再實例化進行測試,模擬執(zhí)行、撤銷、重做的操作

          var incrementCommand=new IncrementCommand();
          
          // 模擬事件觸發(fā),執(zhí)行命令
          var eventTrigger={
              // 某個事件的處理中,直接調用命令的處理方法
              increment: function() {
                  incrementCommand.execute();
              },
          
              incrementUndo: function() {
                  incrementCommand.undo();
              },
          
              incrementRedo: function() {
                  incrementCommand.redo();
              }
          };
          
          
          eventTrigger['increment'](); // 2
          eventTrigger['increment'](); // 4
          
          eventTrigger['incrementUndo'](); // 2
          
          eventTrigger['increment'](); // 4
          
          eventTrigger['incrementUndo'](); // 2
          eventTrigger['incrementUndo'](); // 0
          eventTrigger['incrementUndo'](); // 無輸出
          
          eventTrigger['incrementRedo'](); // 2
          eventTrigger['incrementRedo'](); // 4
          eventTrigger['incrementRedo'](); // 無輸出
          
          eventTrigger['increment'](); // 6


          此外,還可以實現簡單的宏命令(一系列命令的集合)

          var MacroCommand={
              commands: [],
          
              add: function(command) {
                  this.commands.push(command);
          
                  return this;
              },
          
              remove: function(command) {
                  if (!command) {
                      this.commands=[];
                      return;
                  }
          
                  for (var i=0; i < this.commands.length; ++i) {
                      if (this.commands[i]===command) {
                          this.commands.splice(i, 1);
                      }
                  }
              },
          
              execute: function() {
                  for (var i=0; i < this.commands.length; ++i) {
                      this.commands[i].execute();
                  }
              }
          };
          
          var showTime={
              execute: function() {
                  console.log('time');
              }
          };
          
          var showName={
              execute: function() {
                  console.log('name');
              }
          };
          
          var showAge={
              execute: function() {
                  console.log('age');
              }
          };
          
          MacroCommand.add(showTime).add(showName).add(showAge);
          
          MacroCommand.remove(showName);
          
          MacroCommand.execute(); // time age


          七、組合模式

          1. 定義

          是用小的子對象來構建更大的 對象,而這些小的子對象本身也許是由更小 的“孫對象”構成的。

          2. 核心

          可以用樹形結構來表示這種“部分- 整體”的層次結構。

          調用組合對象 的execute方法,程序會遞歸調用組合對象 下面的葉對象的execute方法

          但要注意的是,組合模式不是父子關系,它是一種HAS-A(聚合)的關系,將請求委托給 它所包含的所有葉對象。基于這種委托,就需要保證組合對象和葉對象擁有相同的 接口

          此外,也要保證用一致的方式對待 列表中的每個葉對象,即葉對象屬于同一類,不需要過多特殊的額外操作

          3. 實現

          使用組合模式來實現掃描文件夾中的文件

          // 文件夾 組合對象
          function Folder(name) {
              this.name=name;
              this.parent=null;
              this.files=[];
          }
          
          Folder.prototype={
              constructor: Folder,
          
              add: function(file) {
                  file.parent=this;
                  this.files.push(file);
          
                  return this;
              },
          
              scan: function() {
                  // 委托給葉對象處理
                  for (var i=0; i < this.files.length; ++i) {
                      this.files[i].scan();
                  }
              },
          
              remove: function(file) {
                  if (typeof file==='undefined') {
                      this.files=[];
                      return;
                  }
          
                  for (var i=0; i < this.files.length; ++i) {
                      if (this.files[i]===file) {
                          this.files.splice(i, 1);
                      }
                  }
              }
          };
          
          // 文件 葉對象
          function File(name) {
              this.name=name;
              this.parent=null;
          }
          
          File.prototype={
              constructor: File,
          
              add: function() {
                  console.log('文件里面不能添加文件');
              },
          
              scan: function() {
                  var name=[this.name];
                  var parent=this.parent;
          
                  while (parent) {
                      name.unshift(parent.name);
                      parent=parent.parent;
                  }
          
                  console.log(name.join(' / '));
              }
          };

          構造好組合對象與葉對象的關系后,實例化,在組合對象中插入組合或葉對象

          var web=new Folder('Web');
          var fe=new Folder('前端');
          var css=new Folder('CSS');
          var js=new Folder('js');
          var rd=new Folder('后端');
          
          web.add(fe).add(rd);
          
          var file1=new File('HTML權威指南.pdf');
          var file2=new File('CSS權威指南.pdf');
          var file3=new File('JavaScript權威指南.pdf');
          var file4=new File('MySQL基礎.pdf');
          var file5=new File('Web安全.pdf');
          var file6=new File('Linux菜鳥.pdf');
          
          css.add(file2);
          fe.add(file1).add(file3).add(css).add(js);
          rd.add(file4).add(file5);
          web.add(file6);
          
          rd.remove(file4);
          
          // 掃描
          web.scan();

          掃描結果為


          4. 優(yōu)缺點

          優(yōu)點

          可 以方便地構造一棵樹來表示對象的部分-整體 結構。在樹的構造最終 完成之后,只需要通過請求樹的最頂層對 象,便能對整棵樹做統(tǒng)一一致的操作。

          缺點

          創(chuàng)建出來的對象長得都差不多,可能會使代碼不好理解,創(chuàng)建太多的對象對性能也會有一些影響


          八、模板方法模式

          1. 定義

          模板方法模式由兩部分結構組成,第一部分是抽象父類,第二部分是具體的實現子類。

          2. 核心

          在抽象父類中封裝子類的算法框架,它的 init方法可作為一個算法的模板,指導子類以何種順序去執(zhí)行哪些方法。

          由父類分離出公共部分,要求子類重寫某些父類的(易變化的)抽象方法

          3. 實現

          模板方法模式一般的實現方式為繼承

          以運動作為例子,運動有比較通用的一些處理,這部分可以抽離開來,在父類中實現。具體某項運動的特殊性則有自類來重寫實現。

          最終子類直接調用父類的模板函數來執(zhí)行

          // 體育運動
          function Sport() {
          
          }
          
          Sport.prototype={
              constructor: Sport,
              
              // 模板,按順序執(zhí)行
              init: function() {
                  this.stretch();
                  this.jog();
                  this.deepBreath();
                  this.start();
          
                  var free=this.end();
                  
                  // 運動后還有空的話,就拉伸一下
                  if (free !==false) {
                      this.stretch();
                  }
                  
              },
              
              // 拉伸
              stretch: function() {
                  console.log('拉伸');
              },
              
              // 慢跑
              jog: function() {
                  console.log('慢跑');
              },
              
              // 深呼吸
              deepBreath: function() {
                  console.log('深呼吸');
              },
          
              // 開始運動
              start: function() {
                  throw new Error('子類必須重寫此方法');
              },
          
              // 結束運動
              end: function() {
                  console.log('運動結束');
              }
          };
          
          // 籃球
          function Basketball() {
          
          }
          
          Basketball.prototype=new Sport();
          
          // 重寫相關的方法
          Basketball.prototype.start=function() {
              console.log('先投上幾個三分');
          };
          
          Basketball.prototype.end=function() {
              console.log('運動結束了,有事先走一步');
              return false;
          };
          
          
          // 馬拉松
          function Marathon() {
          
          }
          
          Marathon.prototype=new Sport();
          
          var basketball=new Basketball();
          var marathon=new Marathon();
          
          // 子類調用,最終會按照父類定義的順序執(zhí)行
          basketball.init();
          marathon.init();


          九、享元模式

          1. 定義

          享元(flyweight)模式是一種用于性能優(yōu)化的模式,它的目標是盡量減少共享對象的數量

          2. 核心

          運用共享技術來有效支持大量細粒度的對象。

          強調將對象的屬性劃分為內部狀態(tài)(屬性)與外部狀態(tài)(屬性)。內部狀態(tài)用于對象的共享,通常不變;而外部狀態(tài)則剝離開來,由具體的場景決定。

          3. 實現

          在程序中使用了大量的相似對象時,可以利用享元模式來優(yōu)化,減少對象的數量

          舉個栗子,要對某個班進行身體素質測量,僅測量身高體重來評判

          // 健康測量
          function Fitness(name, sex, age, height, weight) {
              this.name=name;
              this.sex=sex;
              this.age=age;
              this.height=height;
              this.weight=weight;
          }
          
          // 開始評判
          Fitness.prototype.judge=function() {
              var ret=this.name + ': ';
          
              if (this.sex==='male') {
                  ret +=this.judgeMale();
              } else {
                  ret +=this.judgeFemale();
              }
          
              console.log(ret);
          };
          
          // 男性評判規(guī)則
          Fitness.prototype.judgeMale=function() {
              var ratio=this.height / this.weight;
          
              return this.age > 20 ? (ratio > 3.5) : (ratio > 2.8);
          };
          
          // 女性評判規(guī)則
          Fitness.prototype.judgeFemale=function() {
              var ratio=this.height / this.weight;
              
              return this.age > 20 ? (ratio > 4) : (ratio > 3);
          };
          
          
          var a=new Fitness('A', 'male', 18, 160, 80);
          var b=new Fitness('B', 'male', 21, 180, 70);
          var c=new Fitness('C', 'female', 28, 160, 80);
          var d=new Fitness('D', 'male', 18, 170, 60);
          var e=new Fitness('E', 'female', 18, 160, 40);
          
          // 開始評判
          a.judge(); // A: false
          b.judge(); // B: false
          c.judge(); // C: false
          d.judge(); // D: true
          e.judge(); // E: true

          評判五個人就需要創(chuàng)建五個對象,一個班就幾十個對象

          可以將對象的公共部分(內部狀態(tài))抽離出來,與外部狀態(tài)獨立。將性別看做內部狀態(tài)即可,其他屬性都屬于外部狀態(tài)。

          這么一來我們只需要維護男和女兩個對象(使用factory對象),而其他變化的部分則在外部維護(使用manager對象)

          // 健康測量
          function Fitness(sex) {
              this.sex=sex;
          }
          
          // 工廠,創(chuàng)建可共享的對象
          var FitnessFactory={
              objs: [],
          
              create: function(sex) {
                  if (!this.objs[sex]) {
                      this.objs[sex]=new Fitness(sex);
                  }
          
                  return this.objs[sex];
              }
          };
          
          // 管理器,管理非共享的部分
          var FitnessManager={
              fitnessData: {},
              
              // 添加一項
              add: function(name, sex, age, height, weight) {
                  var fitness=FitnessFactory.create(sex);
                  
                  // 存儲變化的數據
                  this.fitnessData[name]={
                      age: age,
                      height: height,
                      weight: weight
                  };
          
                  return fitness;
              },
              
              // 從存儲的數據中獲取,更新至當前正在使用的對象
              updateFitnessData: function(name, obj) {
                  var fitnessData=this.fitnessData[name];
          
                  for (var item in fitnessData) {
                      if (fitnessData.hasOwnProperty(item)) {
                          obj[item]=fitnessData[item];
                      }
                  }
              }
          };
          
          // 開始評判
          Fitness.prototype.judge=function(name) {
              // 操作前先更新當前狀態(tài)(從外部狀態(tài)管理器中獲取)
              FitnessManager.updateFitnessData(name, this);
          
              var ret=name + ': ';
          
              if (this.sex==='male') {
                  ret +=this.judgeMale();
              } else {
                  ret +=this.judgeFemale();
              }
          
              console.log(ret);
          };
          
          // 男性評判規(guī)則
          Fitness.prototype.judgeMale=function() {
              var ratio=this.height / this.weight;
          
              return this.age > 20 ? (ratio > 3.5) : (ratio > 2.8);
          };
          
          // 女性評判規(guī)則
          Fitness.prototype.judgeFemale=function() {
              var ratio=this.height / this.weight;
              
              return this.age > 20 ? (ratio > 4) : (ratio > 3);
          };
          
          
          var a=FitnessManager.add('A', 'male', 18, 160, 80);
          var b=FitnessManager.add('B', 'male', 21, 180, 70);
          var c=FitnessManager.add('C', 'female', 28, 160, 80);
          var d=FitnessManager.add('D', 'male', 18, 170, 60);
          var e=FitnessManager.add('E', 'female', 18, 160, 40);
          
          // 開始評判
          a.judge('A'); // A: false
          b.judge('B'); // B: false
          c.judge('C'); // C: false
          d.judge('D'); // D: true
          e.judge('E'); // E: true

          不過代碼可能更復雜了,這個例子可能還不夠充分,只是展示了享元模式如何實現,它節(jié)省了多個相似的對象,但多了一些操作。

          factory對象有點像單例模式,只是多了一個sex的參數,如果沒有內部狀態(tài),則沒有參數的factory對象就更接近單例模式了


          十、職責鏈模式

          1. 定義

          使多個對象都有機會處理請求,從而避免請求的發(fā)送者和接收者之間的耦合關系,將這些對象連成一條鏈,并沿著這條鏈 傳遞該請求,直到有一個對象處理它為止

          2. 核心

          請求發(fā)送者只需要知道鏈中的第一個節(jié)點,弱化發(fā)送者和一組接收者之間的強聯(lián)系,可以便捷地在職責鏈中增加或刪除一個節(jié)點,同樣地,指定誰是第一個節(jié)點也很便捷

          3. 實現

          以展示不同類型的變量為例,設置一條職責鏈,可以免去多重if條件分支

          // 定義鏈的某一項
          function ChainItem(fn) {
              this.fn=fn;
              this.next=null;
          }
          
          ChainItem.prototype={
              constructor: ChainItem,
              
              // 設置下一項
              setNext: function(next) {
                  this.next=next;
                  return next;
              },
              
              // 開始執(zhí)行
              start: function() {
                  this.fn.apply(this, arguments);
              },
              
              // 轉到鏈的下一項執(zhí)行
              toNext: function() {
                  if (this.next) {
                      this.start.apply(this.next, arguments);
                  } else {
                      console.log('無匹配的執(zhí)行項目');
                  }
              }
          };
          
          // 展示數字
          function showNumber(num) {
              if (typeof num==='number') {
                  console.log('number', num);
              } else {
                  // 轉移到下一項
                  this.toNext(num);
              }
          }
          
          // 展示字符串
          function showString(str) {
              if (typeof str==='string') {
                  console.log('string', str);
              } else {
                  this.toNext(str);
              }
          }
          
          // 展示對象
          function showObject(obj) {
              if (typeof obj==='object') {
                  console.log('object', obj);
              } else {
                  this.toNext(obj);
              }
          }
          
          var chainNumber=new ChainItem(showNumber);
          var chainString=new ChainItem(showString);
          var chainObject=new ChainItem(showObject);
          
          // 設置鏈條
          chainObject.setNext(chainNumber).setNext(chainString);
          
          chainString.start('12'); // string 12
          chainNumber.start({}); // 無匹配的執(zhí)行項目
          chainObject.start({}); // object {}
          chainObject.start(123); // number 123

          這時想判斷未定義的時候呢,直接加到鏈中即可

          // 展示未定義
          function showUndefined(obj) {
              if (typeof obj==='undefined') {
                  console.log('undefined');
              } else {
                  this.toNext(obj);
              }
          }
          
          var chainUndefined=new ChainItem(showUndefined);
          chainString.setNext(chainUndefined);
          
          chainNumber.start(); // undefined

          由例子可以看到,使用了職責鏈后,由原本的條件分支換成了很多對象,雖然結構更加清晰了,但在一定程度上可能會影響到性能,所以要注意避免過長的職責鏈。


          十一、中介者模式

          1. 定義

          所有的相關 對象都通過中介者對象來通信,而不是互相引用,所以當一個對象發(fā)生改變時,只需要通知中介者對象即可

          2. 核心

          使網狀的多對多關系變成了相對簡單的一對多關系(復雜的調度處理都交給中介者)

          使用中介者后



          3. 實現

          多個對象,指的不一定得是實例化的對象,也可以將其理解成互為獨立的多個項。當這些項在處理時,需要知曉并通過其他項的數據來處理。

          如果每個項都直接處理,程序會非常復雜,修改某個地方就得在多個項內部修改

          我們將這個處理過程抽離出來,封裝成中介者來處理,各項需要處理時,通知中介者即可。

          var A={
              score: 10,
          
              changeTo: function(score) {
                  this.score=score;
          
                  // 自己獲取
                  this.getRank();
              },
              
              // 直接獲取
              getRank: function() {
                  var scores=[this.score, B.score, C.score].sort(function(a, b) {
                      return a < b;
                  });
          
                  console.log(scores.indexOf(this.score) + 1);
              }
          };
          
          var B={
              score: 20,
          
              changeTo: function(score) {
                  this.score=score;
          
                  // 通過中介者獲取
                  rankMediator(B);
              }
          };
          
          var C={
              score: 30,
          
              changeTo: function(score) {
                  this.score=score;
          
                  rankMediator(C);
              }
          };
          
          // 中介者,計算排名
          function rankMediator(person) {
              var scores=[A.score, B.score, C.score].sort(function(a, b) {
                  return a < b;
              });
          
              console.log(scores.indexOf(person.score) + 1);
          }
          
          // A通過自身來處理
          A.changeTo(100); // 1
          
          // B和C交由中介者處理
          B.changeTo(200); // 1
          C.changeTo(50); // 3

          ABC三個人分數改變后想要知道自己的排名,在A中自己處理,而B和C使用了中介者。B和C將更為輕松,整體代碼也更簡潔

          最后,雖然中介者做到了對模塊和對象的解耦,但有時對象之間的關系并非一定要解耦,強行使用中介者來整合,可能會使代碼更為繁瑣,需要注意。


          十二、裝飾者模式

          1. 定義

          以動態(tài)地給某個對象添加一些額外的職責,而不會影響從這個類中派生的其他對象。

          是一種“即用即付”的方式,能夠在不改變對 象自身的基礎上,在程序運行期間給對象動態(tài)地 添加職責

          2. 核心

          是為對象動態(tài)加入行為,經過多重包裝,可以形成一條裝飾鏈

          3. 實現

          最簡單的裝飾者,就是重寫對象的屬性

          var A={
              score: 10
          };
          
          A.score='分數:' + A.score;

          可以使用傳統(tǒng)面向對象的方法來實現裝飾,添加技能

          function Person() {}
          
          Person.prototype.skill=function() {
              console.log('數學');
          };
          
          // 裝飾器,還會音樂
          function MusicDecorator(person) {
              this.person=person;
          }
          
          MusicDecorator.prototype.skill=function() {
              this.person.skill();
              console.log('音樂');
          };
          
          // 裝飾器,還會跑步
          function RunDecorator(person) {
              this.person=person;
          }
          
          RunDecorator.prototype.skill=function() {
              this.person.skill();
              console.log('跑步');
          };
          
          var person=new Person();
          
          // 裝飾一下
          var person1=new MusicDecorator(person);
          person1=new RunDecorator(person1);
          
          person.skill(); // 數學
          person1.skill(); // 數學 音樂 跑步

          在JS中,函數為一等對象,所以我們也可以使用更通用的裝飾函數

          // 裝飾器,在當前函數執(zhí)行前先執(zhí)行另一個函數
          function decoratorBefore(fn, beforeFn) {
              return function() {
                  var ret=beforeFn.apply(this, arguments);
                  
                  // 在前一個函數中判斷,不需要執(zhí)行當前函數
                  if (ret !==false) {
                      fn.apply(this, arguments);
                  }
              };
          }
          
          
          function skill() {
              console.log('數學');
          }
          
          function skillMusic() {
              console.log('音樂');
          }
          
          function skillRun() {
              console.log('跑步');
          }
          
          var skillDecorator=decoratorBefore(skill, skillMusic);
          skillDecorator=decoratorBefore(skillDecorator, skillRun);
          
          skillDecorator(); // 跑步 音樂 數學


          十三、狀態(tài)模式

          1. 定義

          事物內部狀態(tài)的改變往往會帶來事物的行為改變。在處理的時候,將這個處理委托給當前的狀態(tài)對象即可,該狀態(tài)對象會負責渲染它自身的行為

          2. 核心

          區(qū)分事物內部的狀態(tài),把事物的每種狀態(tài)都封裝成單獨的類,跟此種狀態(tài)有關的行為都被封裝在這個類的內部

          3. 實現

          以一個人的工作狀態(tài)作為例子,在剛醒、精神、疲倦幾個狀態(tài)中切換著

          // 工作狀態(tài)
          function Work(name) {
              this.name=name;
              this.currentState=null;
          
              // 工作狀態(tài),保存為對應狀態(tài)對象
              this.wakeUpState=new WakeUpState(this);
              // 精神飽滿
              this.energeticState=new EnergeticState(this);
              // 疲倦
              this.tiredState=new TiredState(this);
          
              this.init();
          }
          
          Work.prototype.init=function() {
              this.currentState=this.wakeUpState;
              
              // 點擊事件,用于觸發(fā)更新狀態(tài)
              document.body.onclick=()=> {
                  this.currentState.behaviour();
              };
          };
          
          // 更新工作狀態(tài)
          Work.prototype.setState=function(state) {
              this.currentState=state;
          }
          
          // 剛醒
          function WakeUpState(work) {
              this.work=work;
          }
          
          // 剛醒的行為
          WakeUpState.prototype.behaviour=function() {
              console.log(this.work.name, ':', '剛醒呢,睡個懶覺先');
              
              // 只睡了2秒鐘懶覺就精神了..
              setTimeout(()=> {
                  this.work.setState(this.work.energeticState);
              }, 2 * 1000);
          }
          
          // 精神飽滿
          function EnergeticState(work) {
              this.work=work;
          }
          
          EnergeticState.prototype.behaviour=function() {
              console.log(this.work.name, ':', '超級精神的');
              
              // 才精神1秒鐘就發(fā)困了
              setTimeout(()=> {
                  this.work.setState(this.work.tiredState);
              }, 1000);
          };
          
          // 疲倦
          function TiredState(work) {
              this.work=work;
          }
          
          TiredState.prototype.behaviour=function() {
              console.log(this.work.name, ':', '怎么肥事,好困');
              
              // 不知不覺,又變成了剛醒著的狀態(tài)... 不斷循環(huán)呀
              setTimeout(()=> {
                  this.work.setState(this.work.wakeUpState);
              }, 1000);
          };
          
          
          var work=new Work('曹操');

          點擊一下頁面,觸發(fā)更新狀態(tài)的操作


          4. 優(yōu)缺點

          優(yōu)點

          狀態(tài)切換的邏輯分布在狀態(tài)類中,易于維護

          缺點

          多個狀態(tài)類,對于性能來說,也是一個缺點,這個缺點可以使用享元模式來做進一步優(yōu)化

          將邏輯分散在狀態(tài)類中,可能不會很輕易就能看出狀態(tài)機的變化邏輯


          十四、適配器模式

          1. 定義

          是解決兩個軟件實體間的接口不兼容的問題,對不兼容的部分進行適配

          2. 核心

          解決兩個已有接口之間不匹配的問題

          3. 實現

          比如一個簡單的數據格式轉換的適配器

          // 渲染數據,格式限制為數組了
          function renderData(data) {
              data.forEach(function(item) {
                  console.log(item);
              });
          }
          
          // 對非數組的進行轉換適配
          function arrayAdapter(data) {
              if (typeof data !=='object') {
                  return [];
              }
          
              if (Object.prototype.toString.call(data)==='[object Array]') {
                  return data;
              }
          
              var temp=[];
          
              for (var item in data) {
                  if (data.hasOwnProperty(item)) {
                      temp.push(data[item]);
                  }
              }
          
              return temp;
          }
          
          var data={
              0: 'A',
              1: 'B',
              2: 'C'
          };
          
          renderData(arrayAdapter(data)); // A B C


          十五、外觀模式

          1. 定義

          為子系統(tǒng)中的一組接口提供一個一致的界面,定義一個高層接口,這個接口使子系統(tǒng)更加容易使用

          2. 核心

          可以通過請求外觀接口來達到訪問子系統(tǒng),也可以選擇越過外觀來直接訪問子系統(tǒng)

          3. 實現

          外觀模式在JS中,可以認為是一組函數的集合

          信推出小程序后,html5越來越火熱了,未來H5方向的專業(yè)人才必定水漲船高。

          我整理了一些關于html的書籍

          響應式Web設計:HTML5和CSS3實戰(zhàn)

          BenFrain (作者), 王永強 (譯者)

          推薦指數:★★★★

          簡介:全書主要是帶領讀者做一個小網頁來寫的,其實響應式的東西講得比較少,大半內容是介紹H5和css3的基礎知識。如果同時想了解H5和css3和響應式設計入門,這本書不錯。

          HTML5移動Web開發(fā)實戰(zhàn)詳解

          林瓏 (作者)

          推薦指數:★★★★☆

          簡介:本書由淺入深,全面、系統(tǒng)、詳盡地介紹了HTML5相關技術和其在移動開發(fā)領域的應用。書中提供了大量的代碼示例,讀者可以通過這些例子理解知識點,也可以直接在開發(fā)實戰(zhàn)中稍加修改應用這些代碼。《HTML5移動Web開發(fā)實戰(zhàn)詳解》涉及面廣,從基本原理到實戰(zhàn),再到項目工作流,幾乎涉及一個合格的前端開發(fā)工程師需要具備的所有重要知識。

          瘋狂HTML 5/CSS3/JavaScript講義

          李剛 (作者)

          推薦指數:★★★★

          簡介:這是一本全面介紹HTML 5、CSS 3和JavaScript前端開發(fā)技術的圖書,系統(tǒng)地介紹了HTML 5常用的元素和屬性、HTML5的表單元素和屬性、HTML 5的繪圖支持、HTML5的多媒體支持、CSS 3的功能和用法、最前沿的變形與動畫功能等。除此之外,《瘋狂HTML 5/CSS 3/JavaScript講義》還系統(tǒng)地介紹了JavaScript編程知識,包括JavaScript基本語法、DOM編程,以及HTML 5新增的本地存儲、離線應用、JavaScript多線程、客戶端通信支持、WebSocket編程等。

          HTML5觸摸界面設計與開發(fā)

          伍茲 (StephenWoods) (作者), 覃介右 谷岳 (譯者)

          推薦指數:★★★★☆

          簡介:《HTML5觸摸界面設計與開發(fā)》專注于觸摸界面的開發(fā),內容的結構和優(yōu)化網站的思路大概一致。上半部分涵蓋了能使各類網站,特別是移動網站變快的基本概念。書的后半部分是專門講觸摸界面的,特別是盡可能地讓它們更平穩(wěn)和快速。本書適合具有一定經驗的Web開發(fā)者閱讀參考。

          HTML5與CSS3權威指南(上下冊)(第3版)

          陸凌牛(作者)

          推薦指數:★★★★☆

          簡介:本書分為上下兩冊。上冊全面系統(tǒng)地講解了HTML5相關的技術,以HTML5對現有Web應用產生的變革開篇;下冊全面系統(tǒng)地講解了CSS3相關的技術,以CSS3的功能和模塊結構開篇,順序講解了各種選擇器及其使用、文字與字體的相關樣式、盒相關樣式、背景與邊框相關樣式、布局相關樣式、變形處理、動畫、顏色相關樣式等內容。

          HTML5+CSS3+jQuery Mobile輕松構造APP與移動網站

          陳婉凌 著

          推薦指數:★★★★☆

          簡介:這是目前唯一一本真正的HTML5 App開發(fā)教材,目前已在復旦、北大、西安交大、北京理工等五十多所高校投入教學使用。它的特點是由淺入深、由易到難,將開發(fā)技巧、和開發(fā)工具結合在一起闡述,同時選取了多個商業(yè)項目APP的實戰(zhàn)案例進行要點講解,通俗易懂。

          和上面推薦的幾本書不同之處在于,本書是面向應用型本科和高等職業(yè)院校設計的標準化教材,并經過官方權威認證,且該教材能與國家及國際權威認證考試無縫對接。

          html和css都是web前端的基礎,在這里,就給大家推薦幾個新手小白必去的網站!

          1、w3school

          http://www.w3school.com.cn

          用來查找一些標簽的屬性,每一個剛學習前端的伙伴,都會用到這個網站.

          2.csdn

          CSDN上可以獲得一些不錯的資訊,也可以找到很多需要的資料,也是一個交流的平臺,有些不懂得問題也可以找到人去解答.

          3.開源中國

          獲取到許多項目案例的源碼,獲得最新web前端發(fā)展的一些動向,資訊.

          多年開發(fā)老碼農福利贈送:網頁制作,網站開發(fā),web前端開發(fā),從最零基礎開始的的HTML+CSS+JavaScript。jQuery,Vue、React、Ajax,node,angular框架等到移動端小程序項目實戰(zhàn)【視頻+工具+電子書+系統(tǒng)路線圖】都有整理,需要的伙伴可以私信我,發(fā)送“前端”等3秒后就可以獲取領取地址,送給每一位對編程感興趣的小伙伴

          為你強力推薦經典css書籍

          1.精通CSS:高級Web標準解決方案(第2版)(Amazon第一CSS暢銷書全新改版)

          本書將最有用的css書籍技術匯總在一起,還總結了css設計中的最佳實踐,討論了解決各種實際問題的技術,填補了一直以來css圖書的空白。

          2.精通CSS+DIV 網頁樣式與布局

          本書系統(tǒng)地講解了css層疊樣式表的基礎理論和實際運用技術,通過大量實例對css進行深入淺出的分析,主要包括css的基本語法和概念,設置文字、圖片、背景、表格、表單和菜單等網頁元素的方法,以及css濾鏡的使用。

          3. CSS權威指南(第三版)

          最新版《css權威指南》css書籍經過全面更新,涵蓋了internet explorer 7,詳細介紹了各個css屬性以及屬性之間的相互作用,并指導你如何避免一些常見的錯誤。

          希望對你有幫助,下一個優(yōu)秀的前端工程師就是你!


          主站蜘蛛池模板: 亚洲熟女综合一区二区三区| 色欲精品国产一区二区三区AV| 国产乱人伦精品一区二区| 人成精品视频三区二区一区 | 天天视频一区二区三区 | 一区二区三区人妻无码| 国产在线第一区二区三区| 中文无码一区二区不卡αv| 色国产精品一区在线观看| 国产丝袜美女一区二区三区| 中文字幕一区二区三区四区 | 亚洲色偷精品一区二区三区| 国产剧情国产精品一区| 久久毛片免费看一区二区三区| 后入内射国产一区二区| 国产在线视频一区二区三区| 欧洲亚洲综合一区二区三区| 麻豆AV天堂一区二区香蕉| 亚洲一区欧洲一区| 国产精品视频一区二区三区| 国产短视频精品一区二区三区| 中文字幕一区视频| 精品成人乱色一区二区| 无码国产精品一区二区免费式影视 | 亚洲午夜福利AV一区二区无码 | 男人免费视频一区二区在线观看 | 无码人妻一区二区三区兔费| 亚洲国产精品一区二区三区在线观看 | 色婷婷香蕉在线一区二区| 精品无码一区二区三区爱欲| 久久免费精品一区二区| 中文字幕一区二区免费| 亚洲国产一区二区视频网站| 日本精品一区二区三区在线视频一 | 精品视频一区二区三区在线播放| 国产精品视频一区麻豆| 精品亚洲AV无码一区二区三区| 天堂国产一区二区三区| 免费av一区二区三区| 亚洲一区二区三区偷拍女厕| 日韩综合无码一区二区|