<address id="ttjl9"></address>

      <noframes id="ttjl9"><address id="ttjl9"><nobr id="ttjl9"></nobr></address>
      <form id="ttjl9"></form>
        <em id="ttjl9"><span id="ttjl9"></span></em>
        <address id="ttjl9"></address>

          <noframes id="ttjl9"><form id="ttjl9"></form>

          首頁

          JS作用域、立即執行函數、閉包

          seo達人

          如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

          作用域    

          首先先介紹一下作用域等一些基礎概念。

           每個JavaScript函數都是一個對象,對象中有些屬性我們可以訪問,但有些不可以,這些屬性僅供JavaScript引擎存取,[[scope]]就是其中一個。

          [[scope]] : 指的就是我們所說的作用域,其中存儲了執行期上下文的集合

          作用域鏈 : [[scope]] 中所存儲的執行期上下文對象的集合,這個集合呈鏈式鏈接,我們把這種鏈接叫做作用域鏈。

          運行期上下文  : 當函數執行時,會創建一個稱為執行期上下文的內部對象(AO)。一個執行期上下文定義了一個函數執行的環境,函數每次執行時對應的執行環境都是的,所以多次調用一個函數會導致創建多個執行上下文,當函數執行完畢,它所產生的執行上下文被銷毀。

          查找變量  :從作用域鏈的頂端依次向下查找。

          下面舉一些例子:

          [html] view plain copy
          1. function a(){  
          2.     function b(){  
          3.         function c(){  
          4.   
          5.         }  
          6.         c();  
          7.     }  
          8.     b();  
          9. }  
          10. a();  
          11.   
          12.   
          13. a defined a.[[scope]] ----> 0 : GO          //a定義的時候產生GO對象  
          14. a doing   a.[[scope]] ----> 0 : aAO           //a執行的時候新產生AO對象  
          15.                             1 : GO  
          16.   
          17. b defined  b.[[scope]] ----> 0 : aAO            //子級b定義會繼承父級a運行時產生的對象  
          18.                              1 : GO   
          19. b doing    b.[[scope]] ---->  0 : bAO            //子級b新產生AO對象  
          20.                               1 : aAO   
          21.                               2 : GO   
          22.                                 
          23. c defined  c.[[scope]] ---->  0 : bAO            //c定義時會繼承b運行時產生的屬性  
          24.                               1 : aAO   
          25.                               2 : GO                          
          26. c doing     c.[[scope]] ----> 0 : cAO            //c執行時同時又產生新的AO  
          27.                               1 ;bAO   
          28.                               2 : aAO   
          29.                               3 : GO   

          立即執行函數

          之前學過函數的定義、函數表達式,還有一種函數叫做立即執行函數。

          立即執行函數:函數執行過后立即被銷毀。

          立即執行函數的官方寫法:

          [html] view plain copy
          1. // 立即執行函數的官方寫法  
          2. (function() {} ());  W3C建議此種  
          3. (function() {})();  

          針對初始化功能的函數,可以有參數。

          [html] view plain copy
          1. var num = function (a,b){  
          2.     return a + b;  
          3. }(1,2);  
          4.   
          5. (function abc(){  
          6.     var a = 123;  
          7.     var b = 234;  
          8.     console.log(a+b);  
          9. }())  

          只有表達式才能被執行符號執行,能被執行符號執行的表達式,函數名字會被自動忽略。

          [html] view plain copy
          1. function test(){  
          2.     console.log("a");  
          3. }()    會出現語法解析錯誤,因為括號前面是函數聲明  
          4.   
          5. (+ function test( ){  
          6.     console.log('a');  
          7. }())                    -------->打印出a  

          下面是一道曾阿里面試題

          [html] view plain copy
          1. function test(a, b, c, d){  
          2.     console.log(a + b + c + d);  
          3. }(1, 2, 3, 4);  
          4.   
          5. // 不報錯也沒有執行        

          下面是幾道經典的例題,可以參考一下:

          [html] view plain copy
          1.   
          [html] view plain copy
          1. function test(){  
          2.     var arr = [];  
          3.     for(var i = 0; i < 10; i ++){  
          4.         arr[i] = function (){  
          5.             console.log(i);  
          6.         }  
          7.     }  
          8.     return arr;  
          9. }  
          10. var myArr = test();  
          11. for(var j = 0; j < 10; j++){  
          12.     myArr[j]();  
          13. }    
          [html] view plain copy
          1.   
          [html] view plain copy
          1. // 輸出:10個10  

          那么采用立即執行函數呢?會有怎樣的結果呢?

          [html] view plain copy
          1. function test(){  
          2.     var arr = [];  
          3.     for(var i = 0; i < 10; i ++){  
          4.         (function(j){  
          5.             arr[i] = function (){  
          6.             console.log(j + " ");  
          7.         }  
          8.         }(i))  
          9.     }  
          10.     return arr;  
          11. }  
          12. var myArr = test();  
          13. for(var j = 0; j < 10; j++){  
          14.     myArr[j]();  
          15. }   
          [html] view plain copy
          1.   
          [html] view plain copy
          1. // 輸出結果  0 1 2 3 4 5 6 7 8 9   

          大家可以自行思考一下。

          閉包

          閉包的現象:當內部函數保存到外部時會產生閉包。


          閉包會導致原有的作用域鏈不釋放,造成內存泄漏

          (內存泄漏:內存占用(比如:手握沙子,握得越緊手里剩得就越少))


          閉包觸發的情況:

              兩個或多個函數互相嵌套,把里面的函數保存到外部,這樣的情況一定會產生閉包。從外面還可以調用里面的函數。


          閉包的作用:

                      實現公有變量

                              eg:函數累加器

                      可以做緩存(存儲結構)

                              eg:eater

                         可以實現封裝,屬性私有化

                              eg:person()

                          模塊化開發,防止污染全局變量



          [html] view plain copy
          1. // 函數累加器  
          2. function add(){  
          3.     var count = 0;  
          4.     function demo(){  
          5.         count ++;  
          6.         console.log(count);  
          7.     }  
          8.     return demo;  
          9. }  
          10. var counter = add();  
          11. counter();  
          12. counter();  
          13. counter();  
          14. counter();  
          15. counter();  
          16. counter();  
          17.   
          18.   
          19. // eater  
          20. function test(){  
          21.     var food = "apple";  
          22.     var obj = {  
          23.         eatFood : function (){  
          24.             if(food != ""){  
          25.                 console.log("I am eating  " + food);  
          26.                 food = "";  
          27.             }  
          28.             else{  
          29.                 console.log("There is nothing!");  
          30.             }  
          31.         },  
          32.         pushFood : function (myFood){  
          33.             food = myFood;  
          34.         }  
          35.     }  
          36.     return obj;  
          37. }  
          38. var person = test();  
          39. person.eatFood();  
          40. person.eatFood();  
          41. person.pushFood('banana');  
          42. person.eatFood();  

          附加一個逗號操作符:

                  先看前面的表達式,再看后面的表達式,把后面表達式的計算結構返回

          例題:

          [html] view plain copy
          1. var f =(  
          2.     function f(){  
          3.         return "1";  
          4.     },  
          5.     function g(){  
          6.         return 2;  
          7.     }  
          8. )();  
          9. console.log(typeof(f));   
          10.   
          11. // -------number  
          12.   
          13. var x = 1;  
          14. if(function f(){}){  
          15.     x += typeof f;  
          16. }  
          17. console.log(x);  
          18. // --------> 1undefined  
          19. 藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

          HTML條件注釋用法詮釋

          seo達人

          如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

          HTML條件注釋用法詮釋

          注釋內容以樣式為例,如下:

          1、支持所有IE瀏覽器

          <!--[if IE]>
          <link rel="stylesheet" href="all-ie-only.css" type="text/css"/>
          <![endif]-->
              
          • 1
          • 2
          • 3

          2、支持非IE瀏覽器

          <!--[if !IE]>
          <link rel="stylesheet" href="not-ie.css" type="text/css"/>
          <![endif]-->
              
          • 1
          • 2
          • 3

          上面是除了IE瀏覽器外所有瀏覽器都識別這個樣式,另外CSS-TRICKS《How To Create an IE-Only Stylesheet》一文中提供了另一種寫法:

          <!--[if !IE]><!--> <link rel="stylesheet" type="text/css" href="not-ie.css" /> <!--<![endif]-->
              
          • 1
          • 2
          • 3

          3、僅僅支持IE10

          <!--[if IE 10]>
          <link rel="stylesheet" type="text/css" href="ie10.css">
          <![endif]-->
              
          • 1
          • 2
          • 3

          4、支持IE10以下版本(IE9以及IE9以下版本)

          這種方法是樣式表使用在低于IE10的瀏覽器,換句話說除了IE10以外的所有IE版本都將被支持。

          <!--[if lt IE 10]>
          <link rel="stylesheet" type="text/css" href="ie9-and-down.css">
          <![endif]-->
              
          • 1
          • 2
          • 3

          也可以寫成

          <!--[if lte IE 9]>
          <link rel="stylesheet" type="text/css" href="ie9-and-down.css">
          <![endif]-->
              
          • 1
          • 2
          • 3

          前面我們也說過了lt和lte的區別,lt表示小于版本號,不包括條件版本號本身;而lte是小于或等于版本號,包括了版本號自身。

          上面這幾種方法,使用的是低于(lt)和低于或等于(lte)的方法來判斷,我們也可以使用大于gt和大于或等于gte達到上面的效果:

          5、高于IE9的版本(IE10以及IE10以上版本)

          <!--[if gt IE 9]>
          <link rel="stylesheet" type="text/css" href="ie10-and-up.css">
          <![endif]-->
              
          • 1
          • 2
          • 3

          <!--[if gte IE 10]>
          <link rel="stylesheet" type="text/css" href="ie10-and-up.css">
          <![endif]-->
              
          • 1
          • 2
          • 3

          6、指定多種IE版本

          <!--[if (IE 6)|(IE 7)|(IE 8)]>
          <link rel="stylesheet" type="text/css" href="ie6-7-8.css">
          <![endif]-->
              
          • 1
          • 2
          • 3

          參考: 
          https://www.cnblogs.com/hushufang/p/3708704.html

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服務

          獲取網頁授權

          seo達人

          如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

          在微信登錄中,如何和獲取網頁授權。

          一、登錄微信測試公眾品平臺,修改網頁授權基本信息,輸入授權回調頁面域名(自己的域名)。

          然后重新建立一個tp框架 編寫方法如圖:

          [php] view plain copy
          1. <?php  
          2. namespace Home\Controller;  
          3. use Think\Controller;  
          4. class IndexController extends Controller {  
          5.     public function index(){  
          6.        $appid='wx27f664ab15ecb71d';  
          7.        $redirect_uri=urlencode('http://www.crimson1.top/vote/index.php/home/index/getcode');  
          8.        $url="https://open.weixin.qq.com/connect/oauth2/authorize?appid=$appid&redirect_uri=$redirect_uri&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect";  
          9.   
          10.              header("Location:".$url);  
          11.     }  
          12.   
          13.     public function getcode(){  
          14.         $code=$_GET["code"];  
          15.          $json=$this->access_token($code);  
          16.          echo $json;  
          17.     }  
          18.     public function access_token($code){  
          19.         $appid="wx27f664ab15ecb71d";  
          20.         $appsecret="015756334f2982ed1189c6d66dbc0353";  
          21.         $url="https://api.weixin.qq.com/sns/oauth2/access_token?appid=$appid&secret=$appsecret&code=$code&grant_type=authorization_code";  
          22.   
          23.         $ret=https_request($url);  
          24.         return $ret;  
          25.     }  
          26. }  

          在公共模塊中新建function.php

          [php] view plain copy
          1. <?php  
          2. function https_request($url){  
          3.     $curl=curl_init();  
          4.     curl_setopt($curl, CURLOPT_URL, $url);  
          5.     curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);  
          6.     curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);  
          7.     curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);  
          8.     $data=curl_exec($curl);  
          9.     if(curl_errno($curl)){  
          10.         return 'ERROR'.curl_error($curl);  
          11.     }  
          12.     curl_close($curl);  
          13.     return $data;  
          14. }  

          在自己的手機端訪問,就能獲取access_token;

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

          JavaScript 中的 call()、apply()、bind() 的詳解

          seo達人

          如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

          三種方法的作用

          在 JavaScript 中

          1. call、apply 和 bind 是 Function 對象自帶的三個方法,都是為了改變函數體內部 this 的指向。
          2. call、apply 和 bind 三者第一個參數都是 this 要指向的對象,也就是想指定的上下文
          3. call、apply 和 bind 三者都可以利用后續參數傳參。
          4. bind 是返回對應 函數,便于稍后調用;apply 、call 則是立即調用 。
          舉個栗子
          function fruits() {}
          
          fruits.prototype = {
             color: 'red',
             say: function() { console.log('My color is ' + this.color); 
             }
          } var apple = new fruits;
          apple.say(); // 此時方法里面的this 指的是fruits // 結果: My color is red
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11

          如果我們有一個對象 banana= {color : 'yellow'} ,我們不想重新定義 say 方法,那么我們可以通過 call 或 apply 用 apple 的 say 方法:

          var banana = { color: 'yellow' };
          apple.say.call(banana); // 此時的this的指向已經同過call()方法改變了,指向的是banana,this.color就是banana.color='yellow'; // 結果是My color is yellow 
          
          apple.say.apply(banana); // 同理,此時的this的指向已經同過apply()方法改變了,指向的是banana,this.color就是banana.color ='yellow'; // 結果是My color is yellow
          
          apple.say.apply(null); // nullwindow下的,此時,this 就指向了window ,但是window下并沒有clolr這個屬性,因此this.clolr就是window.color=undefined; // 結果是My color is undefined
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          call 和 apply 的區別

          二者的作用完全一樣,知識接受 參數 的方式不太一樣。

          call 是把參數按順序傳遞進去,而 apply 則是把參數放在 數組 里面。

          var array1 = [12,'foo',{name:'Joe'},-2458]; var array2 = ['Doe' , 555 , 100]; Array.prototype.push.call(array1, array2); // 這里用 call 第二個參數不會把 array2 當成一個數組,而是一個元素 // 等價于 array1.push("'Doe' , 555 , 100"); // array1.length=5; Array.prototype.push.apply(array1, array2); // 這里用 apply 第二個參數是一個數組 // 等價于:  array1.push('Doe' , 555 , 100); // array1.length=7;
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          類(偽)數組使用數組方法
          var divElements = document.getElementsByTagName('div'); // 雖然 divElements 有 length 屬性,但是他是一個偽數組,不能使用數組里面的方法 Array.isArray(divElements);// false var domNodes = Array.prototype.slice.call(document.getElementsByTagName('div')); // 將數組對象 Array 里的 this 指向偽數組 document.getElementsByTagName('div'),  // slice() 方法可從已有的數組中返回選定的元素,不傳參數是,返回整個數組  Array.isArray(domNodes);// true
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          驗證一個對象的類型可以用
          Object.prototype.toString.call(obj)
              
          • 1
          bind() 方法

          bind() 方法會創建一個 新函數,稱為綁定函數,當調用這個綁定函數時,綁定函數會以創建它時傳入 bind() 方法的第一個參數 作為 this,傳入 bind() 方法的 第二個以及以后的參數加上綁定函數運行時本身的參數按照順序作為原函數的參數來調用原函數。

          注意bind()方法創建的函數不會立即調用,在下面的例子中,最后 func() 才調用了函數,這是它與 callapply的區別。

          var bar = function(){ console.log(this.x);
          } var foo = {
              x:3 }
          bar(); // undefined var func = bar.bind(foo); //此時this已經指向了foo,但是用bind()方法并不會立即執行,而是創建一個新函數,如果要直接調用的話 可以bar.bind(foo)() func(); // 3
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11

          在 Javascript 中,多次 bind() 是無效的。更深層次的原因, bind() 的實現,相當于使用函數在內部包了一個 call / apply ,第二次 bind() 相當于再包住第一次 bind() ,故第二次以后的 bind 是無法生效的。

          var bar = function(){ console.log(this.x);
          } var foo = {
            x:3 } var sed = {
            x:4 } var func = bar.bind(foo).bind(sed);
          func(); //3 var fiv = {
            x:5 } var func = bar.bind(foo).bind(sed).bind(fiv);
          func(); //3

          Flex布局-骰子demo

          seo達人

          如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

          最近學習了Flex布局,

          以下是阮一峰老師關于Flex的博客  。在此感謝他讓我get一項新技能!

          Flex語法篇:http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html

          Flex實戰篇:http://www.ruanyifeng.com/blog/2015/07/flex-examples.html

          1、色子數:1

          思路:讓圓點(即子元素)在橫軸上居中在豎軸上居中,分別用justify-content和align-items

          實現代碼:

          <!DOCTYPE html>
          <html>
          <head lang="en">
              <meta charset="UTF-8">
              <title></title>
              <style> *{  margin:0;  padding:0;  }  body{  background:#000;  }  .main {  width: 200px;  height: 200px;  background: #fff;  border-radius: 20px;  margin: 100px auto;  padding: 25px;  -webkit-box-sizing: border-box;  -moz-box-sizing: border-box;  box-sizing: border-box;  display: flex;  justify-content: center;  align-items:center;  }  .main >div{  width:40px;  height:40px;  background:#000;  border-radius:40px;  }  </style>
          </head>
          <body>
          <div class="main">
              <div class="item"></div>
          </div>
          </body>
          </html>
          2、色子數:2

          思路:豎列布局且在主軸方向采用justify-content的兩端對齊布局,這樣兩個圓點會在左邊呈現,然后采用align-items讓其居中

          實現代碼:

          <!DOCTYPE html>
          <html>
          <head lang="en">
              <meta charset="UTF-8">
              <title></title>
              <style> *{  margin:0;  padding:0;  }  body{  background:#000;  }  .main {  width: 200px;  height: 200px;  background: #fff;  border-radius: 20px;  margin: 100px auto;  padding: 25px;  -webkit-box-sizing: border-box;  -moz-box-sizing: border-box;  box-sizing: border-box;  display: flex;  flex-direction: column;  justify-content: space-between;  align-items:center;  }  .main >div{  width:40px;  height:40px;  background:#000;  border-radius:40px;  }  </style>
          </head>
          <body>
          <div class="main">
              <div class="item"></div>
              <div class="item"></div>
          </div>
          </body>
          </html>
          3、色子數:3

          思路:用到align-self屬性讓第二個和第三個圓點有自己的屬性設置,分別在縱軸方向上居中和低端對齊

          實現代碼:

          <!DOCTYPE html>
          <html>
          <head lang="en">
              <meta charset="UTF-8">
              <title></title>
              <style> *{  margin:0;  padding:0;  }  body{  background:#000;  }  .main {  width: 180px;  height: 180px;  background: #fff;  border-radius: 20px;  margin: 100px auto;  padding: 25px;  -webkit-box-sizing: border-box;  -moz-box-sizing: border-box;  box-sizing: border-box;  display: flex;  }  .main >div{  width:40px;  height:40px;  background:#000;  border-radius:40px;  }  .item:nth-child(2){  align-self:center;  }  .item:nth-child(3){  align-self:flex-end;  }  </style>
          </head>
          <body>
          <div class="main">
              <div class="item"></div>
              <div class="item"></div>
              <div class="item"></div>
          </div>
          </body>
          </html>
          4、色子數:4

          思路:先豎著放兩行圓點,每行圓點里橫著放兩個圓點,所以最外層父元素設置align,里面的父元素設置justify-content

          實現代碼:

          <!DOCTYPE html>
          <html>
          <head lang="en">
              <meta charset="UTF-8">
              <title></title>
              <style> *{  margin:0;  padding:0;  }  body{  background:#000;  }  .main {  width: 180px;  height: 180px;  background: #fff;  border-radius: 20px;  margin: 100px auto;  padding: 25px;  -webkit-box-sizing: border-box;  -moz-box-sizing: border-box;  box-sizing: border-box;  display: flex;  flex-wrap:wrap;  align-content:space-between;  }  .column >div{  width:40px;  height:40px;  background:#000;  border-radius:40px;  }  .column{  flex-basis:100%;  display:flex;  justify-content: space-between;  }  </style>
          </head>
          <body>
          <div class="main">
              <div class="column">
                  <div class="item"></div>
                  <div class="item"></div>
              </div>
              <div class="column">
                  <div class="item"></div>
                  <div class="item"></div>
              </div>
          </div>
          </body>
          </html>
          5、色子數:5

          實現代碼:

          <!DOCTYPE html>
          <html>
          <head lang="en">
              <meta charset="UTF-8">
              <title></title>
              <style> *{  margin:0;  padding:0;  }  body{  background:#000;  }  .main {  width: 180px;  height: 180px;  background: #fff;  border-radius: 20px;  margin: 100px auto;  padding: 25px;  -webkit-box-sizing: border-box;  -moz-box-sizing: border-box;  box-sizing: border-box;  display: flex;  flex-wrap:wrap;  align-content:space-between;  }  .column > div{  width:40px;  height:40px;  background:#000;  border-radius:40px;  }  .column{  flex-basis:100%;  display:flex;  justify-content: space-between;  }  .column:nth-child(2){  justify-content: center;  }  </style>
          </head>
          <body>
          <div class="main">
              <div class="column">
                  <div class="item"></div>
                  <div class="item"></div>
              </div>
              <div class="column">
              <div class="item"></div>
              </div>
              <div class="column">
                  <div class="item"></div>
                  <div class="item"></div>
              </div>
          </div>
          </body>
          </html>
          6、色子數:6

          思路:跟四點的一樣,先豎放三行在每行橫放兩個圓點

          實現代碼:

          <!DOCTYPE html>
          <html>
          <head lang="en">
              <meta charset="UTF-8">
              <title></title>
              <style> *{  margin:0;  padding:0;  }  body{  background:#000;  }  .main {  width: 180px;  height: 180px;  background: #fff;  border-radius: 20px;  margin: 100px auto;  padding: 15px;  -webkit-box-sizing: border-box;  -moz-box-sizing: border-box;  box-sizing: border-box;  display: flex;  align-content:space-between;  flex-wrap:wrap;  }  .column > div{  width:40px;  height:40px;  background:#000;  border-radius:40px;  }  .column{  flex-basis:100%;  display:flex;  justify-content: space-between;  }  </style>
          </head>
          <body>
          <div class="main">
              <div class="column">
                  <div class="item"></div>
                  <div class="item"></div>
              </div>
              <div class="column">
                  <div class="item"></div>
                  <div class="item"></div>
              </div>
              <div class="column">
                  <div class="item"></div>
                  <div class="item"></div>
              </div>
          
          </div>
          </body>
          </html>

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服務

          HTML5應用程序緩存

          seo達人

          如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

          HTML5引入了應用程序緩存,意味web應用可以進行緩存,在沒有網絡的情況下使用

          應用程序緩存為應用帶來的三大優勢:

          離線訪問應用

          速度更快——已緩存資源加載的更快

          減少服務器負載——瀏覽器只從服務器下載更新過或更改過的資源

          瀏覽器支持情況:主流瀏覽器都支持,IE要10以上的版本

          HTML5通過在html文件添加manifest屬性,啟用應用程序緩存

          例子:

          <!DOCTYPE HTML>

          <htmlmanifest="demo.appcache">

          ...

          </html>

          每個指定了 manifest 的頁面在用戶對其訪問時都會被緩存。如果未指定 manifest 屬性,則頁面不會被緩存(除非在 manifest 文件中直接指定了該頁面)。

          manifest 文件的建議的文件擴展名是:".appcache"。

          manifest 文件需要配置正確的 MIME-type,即 "text/cache-manifest"。必須在 web 服務器上進行配置。

          Mainifest文件

          manifest 文件是簡單的文本文件,它告知瀏覽器被緩存的內容(以及不緩存的內容)。

          例子:

          CACHE MANIFEST

          # 2012-02-21 v1.0.0

          CACHE:

          cached.js

          cached.css

           

          NETWORK:

          uncached.js

          uncached.css

           

          FALLBACK:

          index.html 404.html


          CACHE MANIFEST 寫在manifest文件開頭,是必須的

          CACHE作用是標識出哪些文件需要緩存,可以是相對路徑也可以是絕對路徑

          NETWORK可選,這一部分是要直接讀取的文件,可以使用通配符 * 。

          FALLBACK可選,指定了一個后備頁面,當資源無法訪問時,瀏覽器會使用該頁面。

          在線的情況下,瀏覽器發現html頭部有manifest屬性,會請求manifest文件,如果是第一次訪問應用,瀏覽器就會根據manifest文件的內容下載相應的資源并且進行離線存儲。如果已經訪問過應用并且資源已經離線存儲了,那么瀏覽器就會使用離線的資源加載頁面,然后瀏覽器會對比新的manifest文件與舊的manifest文件,如果文件沒有發生改變,就不做任何操作,如果文件改變了,那么就會重新下載文件中的資源并進行離線存儲。

          離線的情況下,瀏覽器就直接使用離線存儲的資源。

          注意:

          1.服務器對離線的資源進行了更新,那么必須更新manifest文件之后這些資源才能被瀏覽器重新下載,如果只是更新了資源而沒有更新manifest文件的話,瀏覽器并不會重新下載資源,也就是說還是使用原來離線存儲的資源。

          2.manifest文件進行緩存的時候需要十分小心,因為可能出現一種情況就是你對manifest文件進行了更新,但是http的緩存規則告訴瀏覽器本地緩存的manifest文件還沒過期,這個情況下瀏覽器還是使用原來的manifest文件,所以對于manifest文件最好不要設置緩存。

          3.如果更新中某個資源下載失敗,則整個更新就視作失敗,瀏覽器會依舊采用原來的資源

          4.站點離線存儲的容量限制是5M

          瀏覽器在下載manifest文件中的資源的時候,它會一次性下載所有資源,如果某個資源由于某種原因下載失敗,那么這次的所有更新就算是失敗的,瀏覽器還是會使用原來的資源。

          window.applicationCache對象常用事件

          1.   oncached:當離線資源存儲完成之后觸發這個事件

          2.   onchecking:當瀏覽器對離線存儲資源進行更新檢查的時候會觸發這個事件

          3.   ondownloading:當瀏覽器開始下載離線資源的時候會觸發這個事件

          4.   onprogress:當瀏覽器在下載每一個資源的時候會觸發這個事件,每下載一個資源就會觸發一次。

          5.   onupdateready:當瀏覽器對離線資源更新完成之后會觸發這個事件

          6.   onnoupdate:當瀏覽器檢查更新之后發現沒有資源更新的時候觸發這個事件

          最后一點是該特性已經從web標準刪除,可能在未來某個時間停止,推薦使用Service Workers 代替。

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

          [EX]事件捕獲,事件冒泡,事件委托

          seo達人

          如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

          1事件流

          JavaScript與HTML之間的交互是通過事件實現的。事件,就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間??梢允褂脗陕犉鱽眍A訂事件,以便事件發生時執行相應的代碼。 
           
          事件流的起源:就是在瀏覽器發展到第四代的時候,瀏覽器開發團隊遇到一個問題:頁面的哪一部分會擁有某個特定的事件?要明白這個問題問的是什么,可以想象畫在一張紙上的一組同心圓。如果你把手指放在圓心上,那么你的手指指向的不是一個圓,而是紙上的所有圓。也就是說如果單擊了頁面的某個按鈕,同時也單擊了按鈕的容器元素,甚至單擊了整個頁面。不過呢,IE提出的是冒泡流,而網景提出的是捕獲流。

          示例:

          <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>事件流</title> <style type="text/css"> #content{width: 150px;height: 150px;background-color: red;} #btn{width: 80px;height: 80px;background-color: green;} </style> </head> <body> <div id="content">content <div id="btn">button</div> </div> <script type="text/javascript"> var content = document.getElementById("content"); var btn = document.getElementById('btn');
                  btn.onclick = function(){ alert("btn");
                  };
                  content.onclick = function(){ alert("content");
                  };
                  document.onclick = function(){ alert("document");
                  } </script> </body> </html>
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30

          如果點擊容器#btn,則彈出的順序是:btn-content-document;如果點擊的是容器#content,則彈出的是content-document;如果點擊的是document,彈出的是document。

          由此可以看出JavaScript的事件流機制

          前面說過,IE提出的是冒泡流,而網景提出的是捕獲流,后來在W3C組織的統一之下,JS支持了冒泡流和捕獲流,但是目前低版本的IE瀏覽器還是只能支持冒泡流(IE6,IE7,IE8均只支持冒泡流),所以為了能夠兼容更多的瀏覽器,建議大家使用冒泡流。

          JS事件流原理圖如下: 
           
          這里寫圖片描述 


          2事件冒泡與事件捕獲

          由此可以知道:

          1、一個完整的JS事件流是從window開始,最后回到window的一個過程

          2、事件流被分為三個階段(1~5)捕獲過程、(5~6)目標過程、(6~10)冒泡過程

          示例:

          <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> #wrapDiv, #innerP, #textSpan{ margin: 5px;padding: 5px;box-sizing: border-box;cursor: default; } #wrapDiv{ width: 300px;height: 300px;border: indianred 3px solid; } #innerP{ width: 200px;height: 200px;border: hotpink 3px solid; } #textSpan{ display: block;width: 100px;height: 100px;border: orange 3px solid; } </style> </head> <body> <div id="wrapDiv">wrapDiv <p id="innerP">innerP <span id="textSpan">textSpan</span> </p> </div> <script> var wrapDiv = document.getElementById("wrapDiv"); var innerP = document.getElementById("innerP"); var textSpan = document.getElementById("textSpan"); // 捕獲階段綁定事件 window.addEventListener("click", function(e){ console.log("window 捕獲", e.target.nodeName, e.currentTarget.nodeName);
              }, true);
          
              document.addEventListener("click", function(e){ console.log("document 捕獲", e.target.nodeName, e.currentTarget.nodeName);
              }, true);
          
              document.documentElement.addEventListener("click", function(e){ console.log("documentElement 捕獲", e.target.nodeName, e.currentTarget.nodeName);
              }, true);
          
              document.body.addEventListener("click", function(e){ console.log("body 捕獲", e.target.nodeName, e.currentTarget.nodeName);
              }, true);
          
              wrapDiv.addEventListener("click", function(e){ console.log("wrapDiv 捕獲", e.target.nodeName, e.currentTarget.nodeName);
              }, true);
          
              innerP.addEventListener("click", function(e){ console.log("innerP 捕獲", e.target.nodeName, e.currentTarget.nodeName);
              }, true);
          
              textSpan.addEventListener("click", function(e){ console.log("textSpan 捕獲", e.target.nodeName, e.currentTarget.nodeName);
              }, true); // 冒泡階段綁定的事件 window.addEventListener("click", function(e){ console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName);
              }, false);
          
              document.addEventListener("click", function(e){ console.log("document 冒泡", e.target.nodeName, e.currentTarget.nodeName);
              }, false);
          
              document.documentElement.addEventListener("click", function(e){ console.log("documentElement 冒泡", e.target.nodeName, e.currentTarget.nodeName);
              }, false);
          
              document.body.addEventListener("click", function(e){ console.log("body 冒泡", e.target.nodeName, e.currentTarget.nodeName);
              }, false);
          
              wrapDiv.addEventListener("click", function(e){ console.log("wrapDiv 冒泡", e.target.nodeName, e.currentTarget.nodeName);
              }, false);
          
              innerP.addEventListener("click", function(e){ console.log("innerP 冒泡", e.target.nodeName, e.currentTarget.nodeName);
              }, false);
          
              textSpan.addEventListener("click", function(e){ console.log("textSpan 冒泡", e.target.nodeName, e.currentTarget.nodeName);
              }, false); </script> </body> </html>
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41
          • 42
          • 43
          • 44
          • 45
          • 46
          • 47
          • 48
          • 49
          • 50
          • 51
          • 52
          • 53
          • 54
          • 55
          • 56
          • 57
          • 58
          • 59
          • 60
          • 61
          • 62
          • 63
          • 64
          • 65
          • 66
          • 67
          • 68
          • 69
          • 70
          • 71
          • 72
          • 73
          • 74
          • 75
          • 76
          • 77
          • 78
          • 79
          • 80
          • 81
          • 82
          • 83
          • 84
          • 85
          • 86
          • 87
          • 88
          • 89
          • 90
          • 91

          這個時候,如果點擊一下textSpan這個元素,控制臺會打印出這樣的內容: 
           
          這里寫圖片描述

          從上面所畫的事件傳播的過程能夠看出來,當點擊鼠標后,會先發生事件的捕獲

          · 捕獲階段:首先window會獲捕獲到事件,之后document、documentElement、body會捕獲到,再之后就是在body中DOM元素一層一層的捕獲到事件,有wrapDiv、innerP。

          · 目標階段:真正點擊的元素textSpan的事件發生了兩次,因為在上面的JavaScript代碼中,textSapn既在捕獲階段綁定了事件,又在冒泡階段綁定了事件,所以發生了兩次。但是這里有一點是需要注意,在目標階段并不一定先發生在捕獲階段所綁定的事件,而是先綁定的事件發生,一會會解釋一下。

          · 冒泡階段:會和捕獲階段相反的步驟將事件一步一步的冒泡到window

          上述代碼中的兩個屬性:e.target和e.currentTarget

          target和currentTarget都是event上面的屬性,target是真正發生事件的DOM元素,而currentTarget是當前事件發生在哪個DOM元素上。

          可以結合控制臺打印出來的信息理解下,目標階段也就是 target == currentTarget的時候。我沒有打印它們兩個因為太長了,所以打印了它們的nodeName,但是由于window沒有nodeName這個屬性,所以是undefined。

          那可能有一個疑問,我們不用addEventListener綁定的事件會發生在哪個階段呢,我們來一個測試,順便再演示一下我在上面的目標階段所說的目標階段并不一定先發生捕獲階段所綁定的事件是怎么一回事。

          <script>
              var wrapDiv = document.getElementById("wrapDiv"); var innerP = document.getElementById("innerP"); var textSpan = document.getElementById("textSpan"); // 測試直接綁定的事件到底發生在哪個階段
              wrapDiv.onclick = function(){
                  console.log("wrapDiv onclick 測試直接綁定的事件到底發生在哪個階段")
              }; // 捕獲階段綁定事件
              window.addEventListener("click", function(e){
                  console.log("window 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); document.addEventListener("click", function(e){
                  console.log("document 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); document.documentElement.addEventListener("click", function(e){
                  console.log("documentElement 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); document.body.addEventListener("click", function(e){
                  console.log("body 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); wrapDiv.addEventListener("click", function(e){
                  console.log("wrapDiv 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); innerP.addEventListener("click", function(e){
                  console.log("innerP 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); textSpan.addEventListener("click", function(){
                  console.log("textSpan 冒泡 在捕獲之前綁定的")
              }, false); textSpan.onclick = function(){
                  console.log("textSpan onclick")
              }; textSpan.addEventListener("click", function(e){
                  console.log("textSpan 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); // 冒泡階段綁定的事件
              window.addEventListener("click", function(e){
                  console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); document.addEventListener("click", function(e){
                  console.log("document 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); document.documentElement.addEventListener("click", function(e){
                  console.log("documentElement 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); document.body.addEventListener("click", function(e){
                  console.log("body 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); wrapDiv.addEventListener("click", function(e){
                  console.log("wrapDiv 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); innerP.addEventListener("click", function(e){
                  console.log("innerP 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); textSpan.addEventListener("click", function(e){
                  console.log("textSpan 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); </script>
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41
          • 42
          • 43
          • 44
          • 45
          • 46
          • 47
          • 48
          • 49
          • 50
          • 51
          • 52
          • 53
          • 54
          • 55
          • 56
          • 57
          • 58
          • 59
          • 60
          • 61
          • 62
          • 63
          • 64
          • 65
          • 66
          • 67
          • 68
          • 69
          • 70
          • 71
          • 72
          • 73
          • 74
          • 75
          • 76

          控制臺打印如下:

          這里寫圖片描述

          · textSpan是被點擊的元素,也就是目標元素,所有在textSpan上綁定的事件都會發生在目標階段,在綁定捕獲代碼之前寫了綁定的冒泡階段的代碼,所以在目標元素上就不會遵守先發生捕獲后發生冒泡這一規則,而是先綁定的事件先發生。 

          [在目標元素上就不會遵守先發生捕獲后發生冒泡這一規則,而是先綁定的事件先發生。]

          · 由于wrapDiv不是目標元素,所以它上面綁定的事件會遵守先發生捕獲后發生冒泡的規則。所以很明顯用onclick直接綁定的事件發生在了冒泡階段。


          3事件綁定

          1、直接獲取元素綁定:

          element.onclick = function(e){
                  // ... };
              
          • 1
          • 2
          • 3

          優點:簡單和穩定,可以確保它在你使用的不同瀏覽器中運作一致;處理事件時,this關鍵字引用的是當前元素,這很有幫助。

          缺點:只會在事件冒泡中運行;一個元素一次只能綁定一個事件處理函數,新綁定的事件處理函數會覆蓋舊的事件處理函數;事件對象參數(e)僅非IE瀏覽器可用。


          2、直接在元素里面使用事件屬性

          3、W3C方法:

          element.addEventListener('click', function(e){
                  // ... }, false);
              
          • 1
          • 2
          • 3

          優點:該方法同時支持事件處理的捕獲和冒泡階段;事件階段取決于addEventListener最后的參數設置:false (冒泡) 或 true (捕獲);在事件處理函數內部,this關鍵字引用當前元素;事件對象總是可以通過處理函數的第一個參數(e)捕獲;可以為同一個元素綁定你所希望的多個事件,同時并不會覆蓋先前綁定的事件

          缺點:IE不支持,你必須使用IE的attachEvent函數替代。


          IE下的方法:

          element.attachEvent('onclick', function(){
                  // ... });
              
          • 1
          • 2
          • 3

          優點:可以為同一個元素綁定你所希望的多個事件,同時并不會覆蓋先前綁定的事件。 
           
          缺點:IE僅支持事件捕獲的冒泡階段;事件監聽函數內的this關鍵字指向了window對象,而不是當前元素(IE的一個巨大缺點);事件對象僅存在與window.event參數中;事件必須以ontype的形式命名,比如,onclick而非click;僅IE可用,你必須在非IE瀏覽器中使用W3C的addEventListener。

          注意:不是意味這低版本的ie沒有事件捕獲,它也是先發生事件捕獲,再發生事件冒泡,只不過這個過程無法通過程序控制。 


          4解除事件

          通用:

          element.removeEventListener('click', function(e){
                  // ... }, false);
              
          • 1
          • 2
          • 3

          IE:

          element.detachEvent('onclick', function(){
                  // ... });
              
          • 1
          • 2
          • 3

          5阻止事件傳播

          在支持addEventListener()的瀏覽器中,可以調用事件對象的stopPropagation()方法以阻止事件的繼續傳播。如果在同一對象上定義了其他處理程序,剩下的處理程序將依舊被調用,但調用stopPropagation()之后任何其他對象上的事件處理程序將不會被調用。不僅可以阻止事件在冒泡階段的傳播,還能阻止事件在捕獲階段的傳播。

          IE9之前的IE不支持stopPropagation()方法,而是設置事件對象cancelBubble屬性為true來實現阻止事件進一步傳播。

          <script>
              var wrapDiv = document.getElementById("wrapDiv"); var innerP = document.getElementById("innerP"); var textSpan = document.getElementById("textSpan"); // 測試直接綁定的事件到底發生在哪個階段
              wrapDiv.onclick = function(){
                  console.log("wrapDiv onclick 測試直接綁定的事件到底發生在哪個階段")
              }; // 捕獲階段綁定事件
              window.addEventListener("click", function(e){
                  console.log("window 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); document.addEventListener("click", function(e){
                  console.log("document 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); document.documentElement.addEventListener("click", function(e){
                  console.log("documentElement 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); document.body.addEventListener("click", function(e){
                  console.log("body 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); wrapDiv.addEventListener("click", function(e){
                  console.log("wrapDiv 捕獲", e.target.nodeName, e.currentTarget.nodeName); // 在捕獲階段阻止事件的傳播
                  e.stopPropagation(); }, true); innerP.addEventListener("click", function(e){
                  console.log("innerP 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); textSpan.addEventListener("click", function(){
                  console.log("textSpan 冒泡 在捕獲之前綁定的")
              }, false); textSpan.onclick = function(){
                  console.log("textSpan onclick")
              }; textSpan.addEventListener("click", function(e){
                  console.log("textSpan 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); // 冒泡階段綁定的事件
              window.addEventListener("click", function(e){
                  console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); document.addEventListener("click", function(e){
                  console.log("document 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); document.documentElement.addEventListener("click", function(e){
                  console.log("documentElement 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); document.body.addEventListener("click", function(e){
                  console.log("body 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); wrapDiv.addEventListener("click", function(e){
                  console.log("wrapDiv 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); innerP.addEventListener("click", function(e){
                  console.log("innerP 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); textSpan.addEventListener("click", function(e){
                  console.log("textSpan 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); </script>
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41
          • 42
          • 43
          • 44
          • 45
          • 46
          • 47
          • 48
          • 49
          • 50
          • 51
          • 52
          • 53
          • 54
          • 55
          • 56
          • 57
          • 58
          • 59
          • 60
          • 61
          • 62
          • 63
          • 64
          • 65
          • 66
          • 67
          • 68
          • 69
          • 70
          • 71
          • 72
          • 73
          • 74
          • 75
          • 76
          • 77
          • 78

          這里寫圖片描述

          實際上我們點擊的是textSpan,但是由于在捕獲階段事件就被阻止了傳播,所以在textSpan上綁定的事件根本就沒有發生,冒泡階段綁定的事件自然也不會發生,因為阻止事件在捕獲階段傳播的特性,e.stopPropagation()很少用到在捕獲階段去阻止事件的傳播,大家就以為e.stopPropagation()只能阻止事件在冒泡階段傳播。 


          6阻止事件的默認行為

          e.preventDefault()可以阻止事件的默認行為發生,默認行為是指:點擊a標簽就轉跳到其他頁面、拖拽一個圖片到瀏覽器會自動打開、點擊表單的提交按鈕會提交表單等等,因為有的時候我們并不希望發生這些事情,所以需要阻止默認行為。

          IE9之前的IE中,可以通過設置事件對象的returnValue屬性為false達到同樣的效果。

          function cancelHandler(event){ var event=event||window.event;//兼容IE //取消事件相關的默認行為 if(event.preventDefault) //標準技術 event.preventDefault(); if(event.returnValue) //兼容IE9之前的IE event.returnValue=false; return false; //用于處理使用對象屬性注冊的處理程序 }
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10

          7事件委托

          在JavaScript中,添加到頁面上的事件處理程序數量將直接關系到頁面的整體運行性能。導致這一問題的原因是多方面的。首先,每個函數都是對象,都會占用內存;內存中的對象越多,性能就越差。其次,必須事先指定所有事件處理程序而導致的DOM訪問次數,會延遲整個頁面的交互就緒時間。

          對“事件處理程序過多”問題的解決方案就是事件委托。事件委托利用了事件冒泡,只指定一個事件處理程序,就可以管理某一類型的所有事件。例如,click事件會一直冒泡到document層次。也就是說,我們可以為整個頁面指定一個onclick事件處理程序,而不必給每個可單擊的元素分別添加事件處理程序。

          在父級上定義了函數,當點擊目標時,會向上冒泡,到父級執行操作。每一個子元素,都會統一冒泡到父級然后執行。

          <ul id="color-list"> <li>red</li> <li>yellow</li> <li>blue</li> <li>green</li> <li>black</li> <li>white</li> </ul>
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8

          如果點擊頁面中的li元素,然后輸出li當中的顏色,我們通常會這樣寫:

          (function(){
              var color_list = document.getElementById('color-list'); var colors = color_list.getElementsByTagName('li'); for(var i=0;i<colors.length;i++){ colors[i].addEventListener('click',showColor,false); }; function showColor(e){
                  var x = e.target; alert("The color is " + x.innerHTML); }; })();
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11

          利用事件流的特性,我們只綁定一個事件處理函數也可以完成:

          (function(){
              var color_list = document.getElementById('color-list'); color_list.addEventListener('click',showColor,false); function showColor(e){
                  var x = e.target; if(x.nodeName.toLowerCase() === 'li'){
                      alert('The color is ' + x.innerHTML); } } })();
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10

          事件委托還有一個好處就是添加進來的元素也能綁定事件:

          沒有使用事件委托:

          <body> <ul id="thl"> <li>001</li> <li>002</li> <li>003</li> </ul> <button onclick="fun()">touch</button> <script> var thl= document.getElementById('thl'); var aLi = thl.getElementsByTagName('li'); for (var i = 0; i < aLi.length; i++) {
                aLi[i].onclick = fn;
              } function fn (){ console.log(this.innerHTML);
              } function fun(){ var node=document.createElement("li"); var textnode=document.createTextNode("maomaoliang");
                  node.appendChild(textnode);
                  document.getElementById("thl").appendChild(node);
              } </script> </body>
              
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27

          使用了事件委托:

          <script> var thl= document.getElementById('thl');
              thl.onclick = function(ev) { ev = ev || event; //兼容處理 var target = ev.target || ev.srcElement; //找到li元素 if (target.nodeName.toLowerCase() == 'li') {
                        console.log(target.innerHTML);
                   }
              }; function fun(){ var node=document.createElement("li"); var textnode=document.createTextNode("maomaoliang");
                  node.appendChild(textnode);
                  document.getElementById("thl").appendChild(node);
              } </script>
          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

          關于Cookie的原理、作用,區別以及使用

          seo達人

          如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

          1、cookie的作用:

          我們在瀏覽器中,經常涉及到數據的交換,比如你登錄郵箱,登錄一個頁面。我們經常會在此時設置30天內記住我,或者自動登錄選項。那么它們是怎么記錄信息的呢,答案就是今天的主角cookie了,Cookie是由HTTP服務器設置的,保存在瀏覽器中,但HTTP協議是一種無狀態協議,在數據交換完畢后,服務器端和客戶端的鏈接就會關閉,每次交換數據都需要建立新的鏈接。就像我們去超市買東西,沒有積分卡的情況下,我們買完東西之后,超市沒有我們的任何消費信息,但我們辦了積分卡之后,超市就有了我們的消費信息。cookie就像是積分卡,可以保存積分,商品就是我們的信息,超市的系統就像服務器后臺,http協議就是交易的過程。


          2、機制的區別:

          session機制采用的是在服務器端保持狀態的方案,而cookie機制則是在客戶端保持狀態的方案,cookie又叫會話跟蹤機制。打開一次瀏覽器到關閉瀏覽器算是一次會話。說到這里,講下HTTP協議,前面提到,HTTP協議是一種無狀態協議,在數據交換完畢后,服務器端和客戶端的鏈接就會關閉,每次交換數據都需要建立新的鏈接。此時,服務器無法從鏈接上跟蹤會話。cookie可以跟蹤會話,彌補HTTP無狀態協議的不足。


          3、cookie的分類:

          cookie分為會話cookie和持久cookie,會話cookie是指在不設定它的生命周期expires時的狀態,前面說了,瀏覽器的開啟到關閉就是一次會話,當關閉瀏覽器時,會話cookie就會跟隨瀏覽器而銷毀。當關閉一個頁面時,不影響會話cookie的銷毀。會話cookie就像我們沒有辦理積分卡時,單一的買賣過程,離開之后,信息則銷毀。

          持久cookie則是設定了它的生命周期expires,此時,cookie像商品一樣,有個保質期,關閉瀏覽器之后,它不會銷毀,直到設定的過期時間。對于持久cookie,可以在同一個瀏覽器中傳遞數據,比如,你在打開一個淘寶頁面登陸后,你在點開一個商品頁面,依然是登錄狀態,即便你關閉了瀏覽器,再次開啟瀏覽器,依然會是登錄狀態。這就是因為cookie自動將數據傳送到服務器端,在反饋回來的結果。持久cookie就像是我們辦理了一張積分卡,即便離開,信息一直保留,直到時間到期,信息銷毀。


          4、簡單的使用cookie的代碼

          cookie的幾種常見屬性:document.cookie="key=value;expires=失效時間;path=路徑;domain=域名;secure;(secure表安全級別),

          cookie以字符串的形式保存在瀏覽器中。下面貼段代碼出來,是一個類似購物網站的將商品添加到購物車,再從購物車還原商品信息的過程,是自己用原生JS封裝的函數。

          封裝的cookie的存入,讀取以及刪除的函數:(這里是將信息以對象的形式存放到cookie中的,會用到JSON的知識)

          [javascript] view plain copy
          1. // key : cookie 名  
          2. // value : cookie 值  
          3. // options : 可選配置參數  
          4. //      options = {  
          5. //          expires : 7|new Date(), // 失效時間  
          6. //          path : "/", // 路徑  
          7. //          domain : "", // 域名  
          8. //          secure : true // 安全連接  
          9. //      }  
          10. function cookie(key, value, options) {  
          11.     /* read 讀取 */  
          12.     // 如果沒有傳遞 value ,則表示根據 key 讀取 cookie 值  
          13.     if (typeof value === "undefined") { // 讀取  
          14.         // 獲取當前域下所有的 cookie,保存到 cookies 數組中  
          15.         var cookies = document.cookie.split("; ");  
          16.         // 遍歷 cookies 數組中的每個元素  
          17.         for (var i = 0, len = cookies.length; i < len; i++) {  
          18.             // cookies[i] : 當前遍歷到的元素,代表的是 "key=value" 意思的字符串,  
          19.             // 將字符串以 = 號分割返回的數組中第一個元素表示 key,  
          20.             // 第二個元素表示 value  
          21.             var cookie = cookies[i].split("=");  
          22.             // 判斷是否是要查找的 key,對查找的 key 、value 都要做解碼操作  
          23.             if (decodeURIComponent(cookie[0]) === key) {  
          24.                 return decodeURIComponent(cookie[1]);  
          25.             }  
          26.         }  
          27.         // 沒有查找到指定的 key 對應的 value 值,則返回 null  
          28.         return null;  
          29.     }  
          30.   
          31.     /* 存入 設置 */  
          32.     // 設置 options 默認為空對象  
          33.     options = options || {};  
          34.     // key = value,對象 key,value 編碼  
          35.     var cookie = encodeURIComponent(key) + "=" + encodeURIComponent(value);  
          36.     // 失效時間  
          37.     if ((typeof options.expires) !== "undefined") { // 有配置失效時間  
          38.         if (typeof options.expires === "number") { // 失效時間為數字  
          39.             var days = options.expires,   
          40.                 t = options.expires = new Date();  
          41.             t.setDate(t.getDate() + days);  
          42.         }   
          43.         cookie += ";expires=" + options.expires.toUTCString();  
          44.     }  
          45.     // 路徑  
          46.     if (typeof options.path !== "undefined")  
          47.         cookie += ";path=" + options.path;  
          48.     // 域名  
          49.     if (typeof options.domain !== "undefined")  
          50.         cookie += ";domain=" + options.domain;  
          51.     // 安全連接  
          52.     if (options.secure)  
          53.         cookie += ";secure";  
          54.   
          55.     // 保存  
          56.     document.cookie = cookie;  
          57. }  
          58.   
          59. // 從所有的 cookie 中刪除指定的 cookie  
          60. function removeCookie(key, options) {  
          61.     options = options || {};  
          62.     options.expires = -1; // 將失效時間設置為 1 天前  
          63.     cookie(key, "", options);  
          64. }  

          下面是商品詳情頁的JS代碼

          [javascript] view plain copy
          1. // 找到所有的 “添加到購物車” 超級鏈接  
          2.             var links = $("a", $("#tab"));  
          3.             // 循環,為每個 “添加到購物車” 的超級鏈接添加點擊事件  
          4.             for (var i = 0, len = links.length; i < len; i++) {  
          5.                 links[i].onclick = function(){  
          6.                     // 獲取當前超級鏈接所在行的所有單元格  
          7.                     var _cells = this.parentNode.parentNode.cells;  
          8.                     // 獲取到即將添加到購物車中的商品信息  
          9.                     var _id = _cells[0].innerHTML,  
          10.                         _name = _cells[1].innerHTML,  
          11.                         _price = _cells[2].innerHTML;  
          12.                     // 將商品信息包裝到一個對象中  
          13.                     var product = {  
          14.                         id : _id,  
          15.                         name : _name,  
          16.                         price : _price,  
          17.                         amount : 1  
          18.                     };  
          19.   
          20.                     /* 將當前選購的商品對象保存到 cookie 中去 */  
          21.                     // 從 cookie 中讀取已有的保存購物車的數組結構  
          22.                     var _products = cookie("products");  
          23.                     if (_products === null// cookie 中不存在 products 名的 cookie  
          24.                         _products = [];  
          25.                     else // 存在,則解析 cookie 讀取到的字符串為 數組 結構  
          26.                         _products = JSON.parse(_products);  
          27.   
          28.                     // 將當前選購的商品追加到數組中保存  
          29.                     _products.push(product);  
          30.                     // 繼續將 _products 數組內容存回 cookie  
          31.                     cookie("products", JSON.stringify(_products), {expires:7});  
          32.                 }  
          33.             }  
          html代碼,css代碼大家可以自己寫

          [javascript] view plain copy
          1. <table id="tab">  
          2.         <tr>  
          3.             <td>序號</td>  
          4.             <td>名稱</td>  
          5.             <td>價格</td>  
          6.             <td>操作</td>  
          7.         </tr>  
          8.         <tr>  
          9.             <td>1</td>  
          10.             <td>空調</td>  
          11.             <td>3999</td>  
          12.             <td><a href="javascript:void(0);">添加到購物車</a></td>  
          13.         </tr>  
          14.         <tr>  
          15.             <td>2</td>  
          16.             <td>風扇</td>  
          17.             <td>288</td>  
          18.             <td><a href="javascript:void(0);">添加到購物車</a></td>  
          19.         </tr>  
          20.     </table>  
          21.     <a href="cart_購物車.html" target="_blank">查看購物車</a>  

          購物車還原商品信息:

          [javascript] view plain copy
          1. // 從 cookie 中讀取購物車已有的商品信息  
          2.             var _products = cookie("products");  
          3.             // 判斷購物車是否有商品  
          4.             if (_products === null || (_products = JSON.parse(_products)).length === 0)  
          5.                 return;  
          6.   
          7.             // 如果有商品,則顯示到頁面中  
          8.             $(".result")[0].innerHTML = "";  
          9.             for (var i = 0, len = _products.length; i < len; i++) {  
          10.                 // 當前遍歷到的商品對象  
          11.                 var prod = _products[i];  
          12.                 // 克隆 .row 的節點  
          13.                 var _row = $(".row")[0].cloneNode(true);  
          14.                 // 將當前商品對象的信息替換節點中對應的部分,用class名獲取到的節點返回類型是一個數組所以要在后面加上[0]  
          15.                 $(".index", _row)[0].innerHTML = prod.id; // 編號  
          16.                 $(".name", _row)[0].innerHTML = prod.name; // 名稱  
          17.                 $(".price", _row)[0].innerHTML = prod.price; // 價格  
          18.                 $(".amount", _row)[0].innerHTML = prod.amount; // 數量  
          19.                 $(".oper", _row)[0].innerHTML = "<a href='javascript:void(0);'>刪除</a>"  
          20.   
          21.                 // 將克隆的節點副本追加到 .result 的 div 中  
          22.                 $(".result")[0].appendChild(_row);  
          23.             };  
          24.   
          25.             // 為每個 “刪除” 的超級鏈接綁定點擊事件  
          26.             var links = $("a", $("#container"));  
          27.             for (var i = 0, len = links.length; i < len; i++) {  
          28.                 // links[i].index = i; // 為當前遍歷到的超級鏈接附加數據  
          29.                 links[i].product = _products[i]; //   
          30.                 links[i].onclick = function(){  
          31.                     // alert("你點擊的是第" + (this.index + 1) + "個連接");  
          32.                     var index = inArray(this.product, _products);  
          33.                       
          34.                     if (index !== -1) {  
          35.                         _products.splice(index, 1);  
          36.                     }  
          37.                     // 更新 cookie  
          38.                     cookie("products", JSON.stringify(_products), {expires:7});  
          39.   
          40.                     // 找出頁面中待刪除的行  
          41.                     var _row = this.parentNode.parentNode;  
          42.                     _row.parentNode.removeChild(_row);  
          43.                 };  
          44.             }  
          這里的$(' ')函數是自己封裝的函數,用于獲取到DOM節點,可以看下我關于getElementsByClassName的兼容那篇文章。

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

          你真的了解盒模型么

          seo達人

          如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

          說到前端, 大家第一反應是不是都是vue、react、webpack等這些大大小小的框架或者工具, 但其實這些都是和js相關的, 真正的樣式會被大家忽略。其實真正呈現給大家看到華麗的頁面, 都是樣式才讓他們多了那份色彩。那么大家覺得簡單的css樣式, 真的簡單么? 讓我們一起來看下, 開啟css的入坑之旅, 今天一起跟大家簡單聊聊盒模型的相關問題......

          盒模型

          百度知道對此的解釋, 很有意思, 在此引用一下

          CSS盒子模型, 內容(CONTENT)就是盒子里裝的東西; 而填充(PADDING)就是怕盒子里裝的東西(貴重的)損壞而添加的泡沫或者其它抗震的輔料; 邊框(BORDER)就是盒子本身了; 至于邊界(MARGIN)則說明盒子擺放的時候的不能全部堆在一起,要留一定空隙保持通風,同時也為了方便取出。 —— 百度知道

          640?wx_fmt=png&wxfrom=5&wx_lazy=1

          這段描述很有趣, 很好的解釋margin、border、padding之間的關系, 不同模式下, 盒模型的width也是不同的, 那么好, 盒模型的第一個坑來了, width的范圍問題。

          通常瀏覽器里, 盒模型的分為兩種模式, 兩種模式(怪異模式和標準模式)下width和height的值不同, 怪異模式的width和height包含border、padding和content, 而標準模式下的width和height只包含content, 這就是為啥有些瀏覽器渲染出來的dom標簽排版會亂。解決也很簡單, 在標簽的上面, 加上doctype的設置就好了, 讓瀏覽器統一用同一種標準去解析頁面。 怪異模式(左圖)和標準模式(右圖)的如下:

          640?wx_fmt=png

          當然, 還有用來改變盒模型width范圍的一個css3的屬性, box-sizing:

          當設置為'border-box'時, width = border + padding + content;

          當設置為'content-box'時, width = content。

          640?wx_fmt=png

          
              
          1.   <div class="wrapper z1"></div>

          2.   <div class="wrapper z2"></div>

          
              

             .wrapper{

          1.     width: 100px;

          2.     height: 50px;

          3.     padding: 10px;

          4.     background-color: #dedede;

             }

          1. .z1{

          2.     box-sizing: border-box;

          3. }

          4. .z2{

          5.     box-sizing: content-box;

             }

          那么第一個div的實際寬度為100px, 第二個div的實際寬度為120px。

          說完盒模型的padding和border, 那么再來吐槽下margin, 盒模型的margin的折疊(margin collapsing)問題, 有些也叫外邊距合并。

          通常我們說的折疊, 都是垂直方向上的折疊, 水平方向是不存在的。標準模式下, 上下兩個兄弟的塊級元素, margin是會重疊的, 并且以最大的那個間距為準(都為正數)。

          比如下面這段代碼:

          
              

             <div class="wrapper"></div>

             <div class="wrapper"></div>

          
              

             .wrapper{

          1.      width: 100px;

          2.      height: 50px;

          3.      margin: 10px;

          4.      background-color: #dedede;

          5.   }

          640?wx_fmt=png

          上圖灰色為重疊部分, 重疊10px的間距。

          既然兄弟盒模型會有margin折疊, 那么父子呢? 答案是一定的, 父子也存在margin折疊的問題, 只不過條件稍微苛刻一點, 我們一起來看下。 父子組件的折疊觸發, 要求不能有間隙, 就是父組件不能設置border或padding值, 不能有空余的內容, 且同時有margin值, 比如下面這段代碼:

          
              
          1. <div class="outer">

          2.   <div class="inner"></div>

          3. </div>

          
              

             .outer{

          1.     width: 200px;

          2.     height: 100px;

          3.     margin: 10px;

          4.     background-color: #dedede;

             }

             .inner{

          1.      width: 100px;

          2.      height: 50px;

          3.      margin: 10px;

          4.      background-color: #bcbcbc;

             }

          當然, 折疊后的空余部分, 也是取較大值, 且折疊觸發, 只存在于垂直方向。

          640?wx_fmt=png

          上圖灰色為重疊部分, 重疊10px的間距。

          剛才提到一個詞"間隙", 如果有間隙的話是不會觸發折疊的, 比如父級元素設置了padding, 或者子元素都設置了相對定位和top值等等。如下圖:

          640?wx_fmt=png

          看到這里, 我想有些同學會問了, 對于這些 margin collapsing, 有沒有一個統一的整理, 對于大轉轉的FEer, 我們當然想到了大家的前面, 請看下面:

          • Margins between a floated box and any other box do not collapse (not even between a float and its in-flow children).

          • Margins of elements that establish new block formatting contexts (such as floats and elements with 'overflow' other than 'visible') do not collapse with their in-flow children.

          • Margins of absolutely positioned boxes do not collapse (not even with their in-flow children).

          • Margins of inline-block boxes do not collapse (not even with their in-flow children).

          • The bottom margin of an in-flow block-level element always collapses with the top margin of its next in-flow block-level sibling, unless that sibling has clearance.

          • The top margin of an in-flow block element collapses with its first in-flow block-level child's top margin if the element has no top border, no top padding, and the child has no clearance.

          • The bottom margin of an in-flow block box with a 'height' of 'auto' and a 'min-height' of zero collapses with its last in-flow block-level child's bottom margin if the box has no bottom padding and no bottom border and the child's bottom margin does not collapse with a top margin that has clearance.

          • A box's own margins collapse if the 'min-height' property is zero, and it has neither top or bottom borders nor top or bottom padding, and it has a 'height' of either 0 or 'auto', and it does not contain a line box, and all of its in-flow children's margins (if any) collapse.

          這是從W3C里引用的原文, 這8條規則是特殊的不折疊的情況, 簡單翻譯過來(僅供參考):

          • 浮動的盒模型不會margin折疊

          • 創建BFC與子不折疊

          • 設置定位的盒模型不會折疊

          • 行內塊級元素的盒模型不折疊

          • 兄弟元素有間隙不折疊

          • 父子盒模型元素, 孩子元素有border、padding、有浮動就不折疊

          • height為auto、min-height為0的塊級盒模型, 和它的最后一個沒有border和padding的孩子盒模型底邊距折疊, 且孩子的底部外邊距和被清除浮動上邊距有間隙不折疊。

          • 如果min-height為0, 上下border、上下padding都為0, height為0或auto, 且沒有行內盒模型, 他的孩子節點都會折疊

          有點晦澀難懂, 大家不妨消化一下。說到這, 再補充一下, 盒模型margin折疊的計算問題, 總結了以下幾點:

          • 同為正值時, 取較大者為兩者為間距

          • 一正一負時, 正負相加為間距, 若結果為負值, 則兩者部分重合

          • 都為負值時, 兩者重合, 且重合部分為絕對值大者

          舉個例子:

          
              
          1.    <div class="wrapper z-01"></div>

          2.    <div class="wrapper z-02"></div>

          
              
          1.    .wrapper{

          2.        width: 100px;

          3.        height: 50px;

          4.        background-color: #dedede;

          5.    }

          6.    .z-01{

          7.        margin: -10px;

          8.    }

          9.    .z-02{

          10.        margin: -15px;

          11.    }

          兩者都為負值, 兩個div上下重合, 且重合間距為15px。

          暫時就想到這么多, css的學習之路任重而道遠, 盒模型又是重中之重。上面有描述不對的地方也歡迎各位同學批評指正, 也歡迎大家來到大轉轉FE做客, 一起討論一起研究前端的技術問題。志同道合的同學, 也歡迎加入我們轉轉FE團隊, 咱們一起打拼。

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

          js設計模式——代理模式proxy

          seo達人

          如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

          什么是代理模式

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

          (可以想象一下明星與經紀人的關系,明星是請求的本體,經紀人就是代理proxy)

          如何實現代理模式

          代理對象內部含有對本體對象的引用,因而可以與調用本體的相關方法;同時,代理對象提供與本體對象相同的接口,方便在任何時刻代理本體對象。

          例子(上代碼)

          代理模式的變體有很多,有:保護代理、虛擬代理緩存代理、防火墻代理、遠程代理、智能引用代理、寫時復制代理。具體介紹前三種。

          (1)保護代理

          保護代理主要用于控制不同權限的對象對本體對象的訪問權限。比如很多人想訪問本體A,如果有代理B存在的話,B會首先剔除不滿足A的訪問條件的訪問者,符合條件的才能訪問。

          作用:過濾請求

          例如:權限的劃分和管理就是使用保護代理proxy來完成的。

          注冊普通用戶:code為“001”

          論壇管理者   :code為“002”

          系統管理者   :code為“003”

          游        客    :code為“000”

          論壇開放了四個基礎功能

          1,發帖

          2,帖子審核

          3,清除帖子

          4,留言

          游客不具備任何操作權限,注冊用戶只能發帖,論壇管理者可以審核以及刪帖操作,系統管理者具有所有功能權限。

          [javascript] view plain copy
          1. //用戶本體  
          2. function User(name,code){  
          3.     this.name = name ;  
          4.     this.code = code ;  
          5. } ;  
          6. User.prototype = {  
          7.     getName : function(){  
          8.         return this.name ;  
          9.     } ,  
          10.     getCode : function(){  
          11.         return this.code ;  
          12.     } ,  
          13.     post : function(){  
          14.         console.log("發帖子!") ;  
          15.     } ,  
          16.     remove : function(){  
          17.         console.log("刪除帖子!") ;  
          18.     } ,  
          19.     check : function(){  
          20.         console.log("審核帖子!") ;  
          21.     } ,  
          22.     comment : function(){  
          23.         console.log("回復帖子!") ;  
          24.     }  
          25. } ;  
          26. //代理論壇類  
          27. function Forum(user){  
          28.     this.user = user ;  
          29. } ;  
          30. Forum.prototype = {  
          31.     getUser : function(){  
          32.         return this.user ;  
          33.     } ,  
          34.     post : function(){  
          35.         if(this.user.getCode() == "001" || this.user.getCode() == "003"){  
          36.             return this.user.post() ;  
          37.         }  
          38.         console.log("沒權限發帖子!") ;  
          39.     } ,  
          40.     remove : function(){  
          41.         if(this.user.getCode() == "002" || this.user.getCode() == "003"){  
          42.             return this.user.remove() ;  
          43.         }  
          44.         console.log("沒權限刪除帖子!") ;  
          45.     } ,  
          46.     check : function(){  
          47.         if(this.user.getCode() == "002" || this.user.getCode() == "003"){  
          48.             return this.user.check() ;  
          49.         }  
          50.         console.log("沒權限審核帖子!") ;  
          51.     } ,  
          52.     comment : function(){  
          53.         if(this.user.getCode() == "003"){  
          54.             return this.user.comment() ;  
          55.         }  
          56.         console.log("沒權限回復帖子!") ;  
          57.     }  
          58. } ;  
          59. //功能測試  
          60. function ForumClient(){  
          61.      this.run = function(){  
          62.          new Forum(new User("bigbear","003")).check() ; // 審核帖子  
          63.      }  
          64.  } ;  

          在該例子中,論壇代理有與user本體相同的接口,可以在滿足條件時,執行與本體相同的代碼,與調用方法的人而言,是不透明的,我實現了調用,但不在乎是通過代理實現的,還是本體實現的。

          本案例來源:大熊君大話設計模式JavaScript

          (2)虛擬代理

          虛擬代理是將調用本體方法的請求進行管理,等到本體適合執行時,再執行。

          作用:將開銷很大的對象,延遲到真正需要它的時候再執行。

          比如:利用虛擬代理實現圖片預加載功能:

          [javascript] view plain copy
          1. /**在圖片預加載中實現虛擬代理 */  
          2. var myImage = (function(){  
          3.     var imageNode = document.createElement('img');  
          4.     document.body.appendChild(imageNode);  
          5.   
          6.     return {  
          7.         setSrc: function(src){  
          8.             imageNode.src = src;  
          9.         }  
          10.     }  
          11. })()  
          12.   
          13. //代理類  
          14. var proxyImage = (function(){  
          15.     var img = new Image();  
          16.     img.onload = function(){  
          17.         myImage.setSrc(this.src);  
          18.     }  
          19.   
          20.     return {  
          21.         setSrc: function(src){  
          22.             myImage.setSrc('本地的圖片地址');  
          23.             img.src = src; //緩存完畢之后會觸發img的onload事件  
          24.         }  
          25.     }  
          26. })()  

          比如:利用虛擬代理合并HTTP請求

          [javascript] view plain copy
          1. /**虛擬代理合并http請求 */  
          2. //通過代理函數收集一段時間的請求,一次性發送給服務器,減少頻繁的網絡請求帶來的極大開銷  
          3. //模擬向服務器發送同步請求的函數  
          4. var synchronousFile = function(id){  
          5.     console.log('開始同步上傳文件,id為:'+id);  
          6. }  
          7.   
          8. //代理類收集一段時間的同步請求,統一發送  
          9. var proxySynchronousFile = (function(){  
          10.     var cache = [], //設置緩存數組  
          11.         timer; //定時器,通過閉包訪問定時器的引用  
          12.   
          13.     return function(id){  
          14.         cache.push(id);  
          15.         if(timer){  
          16.             return;  
          17.         }  
          18.         timer = setTimeout(function(){  
          19.             synchronousFile(cache.join(','));  
          20.             clearTimeout(timer);  
          21.             timer = null;  
          22.             cache.length = 0;  
          23.         },2000)  
          24.     }  
          25. })()  
          26.   
          27. var checkbox = document.getElementsByTagName('input');  
          28.   
          29. for(var i=0,c;c=checkbox[i++];){  
          30.     c.onclick = function(){  
          31.         if(this.check === true){  
          32.             proxySynchronousFile(this.id);  
          33.         }  
          34.     }  
          35. }  

          在這些例子中,虛擬代理對請求進行擱置處理,等到合適的時機,對本體的接口進行調用,可以有效提升Web性能。

          (3)緩存代理

          緩存代理可以為開銷大的一些運算結果提供暫時性的存儲,如果再次傳進相同的參數是,直接返回結果,避免大量重復計算。

          [javascript] view plain copy
          1. /**創建緩存代理工廠 */  
          2. //將緩存代理與工廠模式相結合,創建多種運算的緩存代理  
          3. var mult = function(){  
          4.     var a = 1;  
          5.     for(var i=0;i<arguments.length;i++){  
          6.         a = a*arguments[i];  
          7.     }  
          8.     return a;  
          9. }  
          10. var plus = function(){  
          11.     var a = 0;  
          12.     for(var i=0; i<arguments.length; i++){  
          13.         a = a + arguments[i];  
          14.     }  
          15.     return a;  
          16. }  
          17. //高階函數:將函數作為參數或者返回值的函數  
          18. var proxyFactory = function(fn) {  
          19.     var cache = {}; //參數緩存列表  
          20.     return function(){  
          21.         var args = Array.prototype.join.call(arguments,',');  
          22.         if(args in cache){  
          23.             return cache[args];  
          24.         }  
          25.         //參數屬性對應的是函數  
          26.         return cache[args] = fn.apply(this,arguments);  
          27.     }  
          28. }  
          29.   
          30. //測試  
          31. var proxyMult = proxyFactory(mult),  
          32.     proxyPlus = proxyFactory(plus);  
          33.   
          34. console.log(proxyMult(1,2,3,4));  
          35. console.log(proxyMult(1,2,3,4));  
          36. console.log(proxyPlus(5,6,7,8));  
          37. console.log(proxyPlus(5,6,7,8));  

          什么情況下使用代理

          當我們需要使用的對象很復雜或者需要很長時間去構造,這時就可以使用代理模式(Proxy)。例如:如果構建一個對象很耗費時間和計算機資源,代理模式(Proxy)允許我們控制這種情況,直到我們需要使用實際的對象。一個代理(Proxy)通常包含和將要使用的對象同樣的方法,一旦開始使用這個對象,這些方法將通過代理(Proxy)傳遞給實際的對象。

          比如上面的代碼:需要花很長的時間加載很多圖片,復雜的運算過程,頻繁的多次請求處理等;都可以用到代理模式。

          小結

          代理模式的一個好處就是對外部提供統一的接口方法,而代理類在接口中實現對真實類的附加操作行為,從而可以在不影響外部調用情況下,進行系統擴展。也就是說,我要修改真實角色的操作的時候,盡量不要修改他,而是在外部在“包”一層進行附加行為,即代理類。

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務

          日歷

          鏈接

          個人資料

          藍藍設計的小編 http://www.syprn.cn

          存檔

          亚洲va欧美va天堂v国产综合