面試準備

1、javascript

XMLHttpRequestphp

  • 在不從新加載頁面的狀況下更新網頁
  • 在頁面已加載後從服務器請求數據
  • 在頁面已加載後從服務器接收數據
  • 在後臺向服務器發送數據

XMLHttpRequest 的建立:css

function createXHR2() {html

  var xhr = null;前端

  if (window.XMLHttpRequest) {
    xhr = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    try {
        xhr = new ActiveXObject('Msxml2.XMLHTTP');
      } catch (e) {
          try {
            xhr = new ActiveXObject('Microsoft.XMLHTTP');
              } catch (e) {}
      }
  }html5

  return xhr;
}java

發送:node

var xhr = createXHR2();
xhr.open('POST', '/keyword/hit', true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader('test', 'value');
xhr.send(JSON.stringify({
keyword: 'c',
other: 'test'
})); // urlencodedjquery

接收判斷:程序員

xhr.onreadystatechange = function(e) {
if (xhr.readyState === 4 && xhr.status == 200) {

console.log(xhr.responseText);

// console.log(xhr.responseXML);
console.log(xhr.getAllResponseHeaders());
console.log(xhr.getResponseHeader("Content-Type"));
console.log(xhr.status);
console.log(xhr.statusText);
} // readyState 5種狀態
// 0: 未初始化
// 1: 鏈接創建、請求發出
// 2: 服務器返回響應
// 3: 交互(處理響應數據)
// 4: 完成,數據可交付客戶端使用
// console.log('onreadystatechange: readyState:%d, status:%d, responseText:%s', xhr.readyState, xhr.status, xhr.responseText);
// demo
}

readyState

表示XMLHttpRequest對象的狀態:0:未初始化。對象已建立,未調用open

1open方法成功調用,但Sendf方法未調用;

2send方法已經調用,還沒有開始接受數據;

3:正在接受數據。Http響應頭信息已經接受,但還沒有接收完成;

4:完成,即響應數據接受完成。

Onreadystatechange

請求狀態改變的事件觸發器(readyState變化時會調用這個屬性上註冊的javascript函數)。

responseText

服務器響應的文本內容

responseXML

服務器響應的XML內容對應的DOM對象

Status

服務器返回的http狀態碼。200表示「成功」,404表示「未找到」,500表示「服務器內部錯誤」等。

statusText

服務器返回狀態的文本信息。

 

方法

說明

Open(string method,string url,boolean asynch,

String username,string password)

指定和服務器端交互的HTTP方法,URL地址,即其餘請求信息;

Method:表示http請求方法,通常使用"GET","POST".

url:表示請求的服務器的地址;

asynch:表示是否採用異步方法,true爲異步,false爲同步;

後邊兩個能夠不指定,usernamepassword分別表示用戶名和密碼,提供http認證機制須要的用戶名和密碼。

Send(content)

向服務器發出請求,若是採用異步方式,該方法會當即返回。

content能夠指定爲null表示不發送數據,其內容能夠是DOM對象,輸入流或字符串。

SetRequestHeader(String header,String Value)

設置HTTP請求中的指定頭部header的值爲value.

此方法需在open方法之後調用,通常在post方式中使用。

getAllResponseHeaders()

返回包含Http的全部響應頭信息,其中相應頭包括Content-length,date,uri等內容。

返回值是一個字符串,包含全部頭信息,其中每一個鍵名和鍵值用冒號分開,每一組鍵之間用CRLF(回車加換行符)來分隔!

getResponseHeader(String header)

返回HTTP響應頭中指定的鍵名header對應的值

Abort()

中止當前http請求。對應的XMLHttpRequest對象會復位到未初始化的狀態。

 
2、

http狀態碼
100-199 用於指定客戶端應相應的某些動做。 
200-299 用於表示請求成功。 
300-399 用於已經移動的文件而且常被包含在定位頭信息中指定新的地址信息。 
400-499 用於指出客戶端的錯誤。 
500-599 用於支持服務器錯誤。
 
200 (OK/正常)
404 (Not Found/未找到)
500 (Internal Server Error/內部服務器錯誤)
 3、

「Cache-control」常見的取值有private、no-cache、max-age、must-revalidate等

 
網頁的緩存是由HTTP消息頭中的「Cache-control」來控制的,常見的取值有private、no-cache、max-age、must-revalidate等,默認爲private。其做用根據不一樣的從新瀏覽方式分爲如下幾種狀況:
(1) 打開新窗口
若是指定cache-control的值爲private、no-cache、must-revalidate,那麼打開新窗口訪問時都會從新訪問服務器。而若是指定了max-age值,那麼在此值內的時間裏就不會從新訪問服務器,例如:
Cache-control: max-age=5
表示當訪問此網頁後的5秒內再次訪問不會去服務器
(2) 在地址欄回車
若是值爲private或must-revalidate(和網上說的不同),則只有第一次訪問時會訪問服務器,之後就再也不訪問。若是值爲no-cache,那麼每次都會訪問。若是值爲max-age,則在過時以前不會重複訪問。
(3) 按後退按扭
若是值爲private、must-revalidate、max-age,則不會重訪問,而若是爲no-cache,則每次都重複訪問
(4) 按刷新按扭
不管爲什麼值,都會重複訪問
 
4、聖盃佈局
5、IE兼容性

前言

瀏覽器兼容是前端開發人員必須掌握的一個技能,可是初入前端的同窗或者其餘後臺web開發同窗每每容易選擇忽略,而造成兩個極端:

1 我最開始都是使用IE6,IE6上沒問題,其它瀏覽器坑爹(多出現與前端後端一塊兒搞的同窗,小生2年前就這種狀態,鼓勵人家用ie6.。。。)

2 我要遵循標準,我只要ff就好,IE就是坑爹的玩意,我沒必要去理他(小生一年前的心態。。。)

如今看來,以前的想法都是不對的,咱們誠然應該追求最新的瀏覽器使用最新的技術,可是漸進加強,向後兼容的思想必定要有,

由於就如今IE6在中國的份額也是不容小視的。

拋開以前的大道理,咱們說點實際的問題,哪次前端面試不問兼容性問題?哪次咱們又能回答的很好?反正我就沒一次說好的,知不足而後能改,

我前端時間便通過整理造成這篇文章,文章有不少不足,但願各位指正、補充,後面如果能造成一篇較全面的前端兼容文章就善莫大焉了!

爲何會有兼容問題?

因爲市場上瀏覽器種類衆多,而不一樣瀏覽器其內核亦不盡相同,因此各個瀏覽器對網頁的解析就有必定出入,這也是致使瀏覽器兼容問題出現的主要緣由,咱們的網頁須要在主流瀏覽器上正常運行,就須要作好瀏覽器兼容。

使用Trident內核的瀏覽器:IE、Maxthon、TT;

使用Gecko內核的瀏覽器:Netcape6及以上版本、FireFox;

使用Presto內核的瀏覽器:Opera7及以上版本;

使用Webkit內核的瀏覽器:Safari、Chrome。

而我如今所說的兼容性問題,主要是說IE與幾個主流瀏覽器如firefox,google等。

而對IE瀏覽器來講,IE7又是個跨度,由於以前的版本更新甚慢,bug甚多。從IE8開始,IE瀏覽器漸漸遵循標準,到IE9後因爲你們都一致認爲標準很重要,能夠說在兼容性上比較好了,可是在中國來講,因爲xp的佔有率問題,使用IE7如下的用戶仍然不少,因此咱們不得不考慮低版本瀏覽器的兼容。

對瀏覽器兼容問題,我通常是這樣分類的,HTML,Javascript兼容,CSS兼容。 其中html相關問題比較容易處理,無非是高版本瀏覽器用了低版本瀏覽器沒法識別的元素,致使其不能解析,因此平時注意一點就是。特別是HTML5增長了許多新標籤,低版本瀏覽器有點影響時代進步啊;

javascript兼容性問題

在javascript中,各個瀏覽器基本語法差距不大,其兼容問題主要出如今各個瀏覽器的實現上,尤爲對事件的支持有很大問題,在此我就說說我知道的幾個問題。

① 在標準的事件綁定中綁定事件的方法函數爲 addEventListener,而IE使用的是attachEvent

② 標準瀏覽器採用事件捕獲的方式對應IE的事件冒泡機制(即標準由最外元素至最內元素或者IE由最內元素到最外元素)最後標準方亦以爲IE這方面的比較合理,因此便將事件冒泡歸入了標準,這也是addEventListener第三個參數的由來,並且事件冒泡做爲了默認值。

③ 事件處理中很是有用的event屬性得到亦不相同,標準瀏覽器是做爲參數帶人,而ie是window.event方式得到,得到目標元素ie爲e.srcElement 標準瀏覽器爲e.target

④ 而後在ie中是不能操做tr的innerHtml的

⑤ 而後ie日期函數處理與其它瀏覽器不大一致,好比: var year= new Date().getYear(); 在IE中會得到當前年,可是在firefox中則會得到當前年與1900的差值。

⑥ 得到DOM節點的方法有所差別,其得到子節點方法不一致。

IE:parentElement parentElement.children Firefox:parentNode parentNode.childNodes childNodes的下標的含義在IE和Firefox中不一樣,Firefox使用DOM規範,childNodes中會插入空白文本節點。通常能夠經過node.getElementsByTagName()來回避這個問題。

當html中節點缺失時,IE和Firefox對parentNode的解釋不一樣。例如:

<form> <table> <input/> </table> </form> IE:input.parentNode的值爲空節點 Firefox:input.parentNode的值爲form 解決方法:Firefox中節點沒有removeNode方法,必須使用以下方法 node.parentNode.removeChild(node)

⑦ 關於AJAX的實現上亦有所不一樣;

就javascript來講,各大瀏覽器之間的差別仍是很多的,可是具體我變得這裏都不大關注了,由於咱們開發過程當中通常都會使用類庫,如果不使用,都會本身積累造成一個類庫,因此就js而言,兼容性問題基本解決了。

讓人頭疼的CSS兼容

由於以前對css的理解不夠深刻,也沒有通過系統的學習,因此一度認爲css是前端最難的東西,但真的學習後,才發現css真的很難。。。有不少東西啊!!!

我以爲最讓人頭疼的問題仍是CSS問題,由於一點點佈局上的bug,可能致使整個頁面的錯位,在用戶看來這是極不專業的。

如今我就簡要說說我對CSS兼容問題的認識: 先說點Hack的知識(真正的高手是不用Hack的,但要成爲高手必須經過Hack這一關)

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
/* CSS屬性級Hack */
 
color:red; /* 全部瀏覽器可識別*/
 
_color:red; /* 僅IE6 識別 */
 
*color:red; /* IE六、IE7 識別 */
 
+color:red; /* IE六、IE7 識別 */
 
*+color:red; /* IE六、IE7 識別 */
 
[color:red; /* IE六、IE7 識別 */
 
color:red\9; /* IE六、IE七、IE八、IE9 識別 */
 
color:red\0; /* IE八、IE9 識別*/
 
color:red\9\0; /* 僅IE9識別 */
 
color:red \0; /* 僅IE9識別 */
 
color:red!important; /* IE6 不識別!important 有危險*/
 
/* CSS選擇符級Hack */
 
*html #demo { color:red;} /* 僅IE6 識別 */
 
*+html #demo { color:red;} /* 僅IE7 識別 */
 
body:nth-of-type(1) #demo { color:red;} /* IE9+、FF3.5+、Chrome、Safari、Opera 能夠識別 */
 
head:first-child+body #demo { color:red; } /* IE7+、FF、Chrome、Safari、Opera 能夠識別 */
 
:root #demo { color:red\9; } : /* 僅IE9識別 */
 
/* IE條件註釋Hack */
 
<!--[if IE 6]>此處內容只有IE6.0可見<![endif]-->
 
<!--[if IE 7]>此處內容只有IE7.0可見<![endif]-->

接下來講說一些我知道的BUG:

① css盒模型在IE6下解析有問題,咱們知道就width來講,一個塊級元素的magin、padding、boder,width7個屬性的寬度之和,應該等於其父級元素的內容區域(width),而咱們通常設置寬度如果未達到其長度,瀏覽器就會重置margin-right的值,將之它們的和等於其值,固然如果咱們爲margin設置負值,那麼元素的width可能超出其父元素。

在標準下,width爲padding所佔區域,可是再ie6中設置width後,其真實width爲所設width-其padding與border*2,我通常採用CSShack技術處理

② IE6的雙倍邊距BUG,在塊級元素浮動後原本外邊距10px,但IE解釋爲20px,解決辦法是加上display: inline

問題:在IE6下若是某個標籤使用了float屬性,同時設置了其外補丁「margin:10px 0 0 10px」能夠看出,上邊距和左邊距一樣爲10px,但第一個對象距左邊有20px。

解決辦法:當將其display屬性設置爲inline時問題就都解決了。

說明:這是由於塊級對象默認的display屬性值是block,當設置了浮動的同時,還設置了它的外邊距 就會出現這種狀況。

也許你會問:「爲何第二個對象和第一個對象之間就不存在雙倍邊距的BUG」?

由於浮動都有其相對應的對象,只有相對於其父對象的浮動 對象纔會出現這樣的問題。

第一個對象是相對父對象的,而第二個對象是相對第一個對象的,因此第二個對象在設置後不會出現問題。

另外在一些特殊佈局中,可能須要組合使用display:block;和display:inline;才能達到預期效果。

固然最壞的狀況下,咱們就能夠使用」margin:10px 0 0 10px;*margin:10px 0 0 10px;_margin:10px 0 0 5px」,

這種「標準屬性;*IE7識別屬性;_IE6識別屬性」HACK方式解決

總結:這個現象僅當塊級對象設置了浮動屬性後纔會出現,內聯對象(行級對象)不會出現此問題。而且只有設置左邊距和右邊距的值纔會出問題,上下邊距不會出現問題。

1
2
3
4
<div style="width:200px;height:50px;background:#ccc;">
<div style="width:100px; height:50px;float:left;margin-left:10px; background:#eee;">
</div>
</div>

margin雙佈局能夠說是IE6下經典的bug之一。產生的條件是:block元素+浮動+margin。

還記得我自認爲會css的那個階段,這個問題我常常碰到,會很熟練的用hack解決這個問題,當時還自覺得是,洋洋得意。如今看來,當時的本身嫩的就像個 豆芽菜。

真正css厲害的人基本上是不會碰到這個bug的,若是您時不時遇到這個bug,說明您的css還有好一段路要走。

個人體會是越少的浮動,就會越少的代碼,會有更靈活的頁面,會有擴展性更強的頁面。這很少說,歸結爲到必定水平了,浮動會用的較少。

另外,您也會避免使用浮動+margin的用法。因此,越後來越不易遇到這種bug。

這裏提一下解決方法,使用hack我是不推薦的,使用hack屬於比初學者稍高一點的層次水平。一個頁面,沒有一個hack,可是各個瀏覽器下表現一致,這纔是水平。

使用display:inline;能夠解決這個問題。

而爲何display:inline能夠解決這個雙邊距bug,首先是inline元素或inline-block元素是不存在雙邊距問題的。

而後,float:left等浮動屬性可讓inline元素haslayout,會讓inline元素表現得跟inline-block元素的特性同樣, 支持高寬,垂直margin和padding等,因此div class的全部樣式能夠用在這個display inline的元素上。

以上即是我所記得的一些bug,在這裏我再順帶提一下haslayout(IE8廢棄該屬性)。

在IE低版本瀏覽器時基本是表格佈局的時代,幾乎是全部的元素(除內聯元素)都是一個盒子,內容不會超過表格的單元格,表格的單元格也不會超出表格。

在IE6推出後,CSS改變這一假設——由於CSS容許內容超出元素。 所以haslayout這個屬性就誕生了。

在IE6,IE7中,每一個元素都有haslayout這個屬性,能夠設置爲 true 或者 false。

若是設置爲true,元素就必須去自我佈局和渲染,所以元素會擴展去包含它溢出的內容,例如浮動或沒截斷的單詞。

若是haslayout 沒有被設置成true,那麼元素需依靠某個祖先元素來渲染它。這就是不少的ie bugs誕生的地方。IE瀏覽器下的不少bug都是haslayout = false 引發的,

layout元素有如下特色:

擁有佈局(haslayout=true)元素不會收縮,因此可能會發生文字截斷、消失的現象;

佈局元素對浮動自動清理;

相對定位的元素沒有佈局,這可能致使絕對元素定位誤差;

擁有佈局的元素外邊距不疊加;

滾動時,頁面會有所跳動;

邊框消失

像素誤差

haslayout不是一個CSS屬性,因此咱們不能這樣的來設置它 haslayout:true;

一個元素被設置成haslayout:true將被渲染成一個 having haslayout,

反之同理。 一些元素自己該屬性爲true,如果須要觸發,最好的方法是設置其zoom屬性; 哪些元素自己就 haslayout:true

<html>, <body><table>, <tr>, <th>, <td><iframe>, <embed> (non-standard element),

<object>, <applet> <img><hr><input>, <button>, <select>, <textarea>, <fieldset>, <legend>

zoom:1,被認爲是最好的觸發Layout的方法,由於它對當前元素沒有影響。 觸發haslayout,相對來講比haslayout=false要簡單。

如下屬性和值將給定一個元素進行佈局

position: absolute float: left or right display: inline-block;width: any value other than auto;

height: any value other than auto;zoom: any value other than normal (*);writing-mode: tb-rl

最後,由於各個瀏覽器對一些元素的默認值設置不一致也會致使表現差別,好比瀏覽器默認字體,默認行高,默認間距等。因此咱們通常會爲幾個主要元素設置默認值。

結語

以上即是我對瀏覽器兼容的簡單認識,可是仍是有不少不足的地方,因爲技術所限,這裏提出來和各位高手交流,但願在交流學習中和之後工做中積累相關經驗,作出知足主流瀏覽器的網頁

6、跨域

什麼是跨域

JavaScript出於安全方面的考慮,不容許跨域調用其餘頁面的對象。但在安全限制的同時也給注入iframe或是ajax應用上帶來了很多麻煩。這裏把涉及到跨域的一些問題簡單地整理一下:

首先什麼是跨域,簡單地理解就是由於JavaScript同源策略的限制,a.com 域名下的js沒法操做b.com或是c.a.com域名下的對象。更詳細的說明能夠看下錶:

URL 說明 是否容許通訊
http://www.a.com/a.js
http://www.a.com/b.js
同一域名下 容許
http://www.a.com/lab/a.js
http://www.a.com/script/b.js
同一域名下不一樣文件夾 容許
http://www.a.com:8000/a.js
http://www.a.com/b.js
同一域名,不一樣端口 不容許
http://www.a.com/a.js
https://www.a.com/b.js
同一域名,不一樣協議 不容許
http://www.a.com/a.js
http://70.32.92.74/b.js
域名和域名對應ip 不容許
http://www.a.com/a.js
http://script.a.com/b.js
主域相同,子域不一樣 不容許
http://www.a.com/a.js
http://a.com/b.js
同一域名,不一樣二級域名(同上) 不容許(cookie這種狀況下也不容許訪問)
http://www.cnblogs.com/a.js
http://www.a.com/b.js
不一樣域名 不容許
特別注意兩點:
第一,若是是協議和端口形成的跨域問題「前臺」是無能爲力的,
第二:在跨域問題上,域僅僅是經過「URL的首部」來識別而不會去嘗試判斷相同的ip地址對應着兩個域或兩個域是否在同一個ip上。
「URL的首部」指window.location.protocol +window.location.host,也能夠理解爲「Domains, protocols and ports must match」。
這裏說的js跨域是指經過js在不一樣的域之間進行數據傳輸或通訊,好比用ajax向一個不一樣的域請求數據,或者經過js獲取頁面中不一樣域的框架中(iframe)的數據。只要協議、域名、端口有任何一個不一樣,都被看成是不一樣的域。
下表給出了相對http://store.company.com/dir/page.html同源檢測的結果:
QQ截圖20130613230631
要解決跨域的問題,咱們能夠使用如下幾種方法:
1、經過jsonp跨域
在js中,咱們直接用XMLHttpRequest請求不一樣域上的數據時,是不能夠的。可是,在頁面上引入不一樣域上的js腳本文件倒是能夠的,jsonp正是利用這個特性來實現的。
好比,有個a.html頁面,它裏面的代碼須要利用ajax獲取一個不一樣域上的json數據,假設這個json數據地址是 http://example.com/data.php,那麼a.html中的代碼就能夠這樣:
QQ截圖20130613230631
咱們看到獲取數據的地址後面還有一個callback參數,按慣例是用這個參數名,可是你用其餘的也同樣。固然若是獲取數據的jsonp地址頁面不是你本身能控制的,就得按照提供數據的那一方的規定格式來操做了。
由於是當作一個js文件來引入的,因此 http://example.com/data.php返回的必須是一個能執行的js文件,因此這個頁面的php代碼多是這樣的:
最終那個頁面輸出的結果是:
因此經過 http://example.com/data.php?callback=dosomething獲得的js文件,就是咱們以前定義的dosomething函數,而且它的參數就是咱們須要的json數據,這樣咱們就跨域得到了咱們須要的數據。
這樣jsonp的原理就很清楚了,經過script標籤引入一個js文件,這個js文件載入成功後會執行咱們在url參數中指定的函數,而且會把咱們須要的json數據做爲參數傳入。因此jsonp是須要服務器端的頁面進行相應的配合的。
知道jsonp跨域的原理後咱們就能夠用js動態生成script標籤來進行跨域操做了,而不用特地的手動的書寫那些script標籤。若是你的頁面使用jquery,那麼經過它封裝的方法就能很方便的來進行jsonp操做了。
QQ截圖20130613230631
原理是同樣的,只不過咱們不須要手動的插入script標籤以及定義回掉函數。jquery會自動生成一個全局函數來替換callback=?中的問號,以後獲取到數據後又會自動銷燬,實際上就是起一個臨時代理函數的做用。$.getJSON方法會自動判斷是否跨域,不跨域的話,就調用普通的ajax方法;跨域的話,則會以異步加載js文件的形式來調用jsonp的回調函數。
 
二、經過修改document.domain來跨子域
瀏覽器都有一個同源策略,其限制之一就是第一種方法中咱們說的不能經過ajax的方法去請求不一樣源中的文檔。 它的第二個限制是瀏覽器中不一樣域的框架之間是不能進行js的交互操做的。有一點須要說明,不一樣的框架之間(父子或同輩),是可以獲取到彼此的window對象的,但蛋疼的是你卻不能使用獲取到的window對象的屬性和方法(html5中的postMessage方法是一個例外,還有些瀏覽器好比ie6也能夠使用top、parent等少數幾個屬性),總之,你能夠當作是隻能獲取到一個幾乎無用的window對象。好比,有一個頁面,它的地址是 http://www.example.com/a.html  , 在這個頁面裏面有一個iframe,它的src是 http://example.com/b.html, 很顯然,這個頁面與它裏面的iframe框架是不一樣域的,因此咱們是沒法經過在頁面中書寫js代碼來獲取iframe中的東西的:
  
QQ截圖20130613230631
這個時候,document.domain就能夠派上用場了,咱們只要把 http://www.example.com/a.html和  http://example.com/b.html這兩個頁面的document.domain都設成相同的域名就能夠了。但要注意的是,document.domain的設置是有限制的,咱們只能把document.domain設置成自身或更高一級的父域,且主域必須相同。例如:a.b.example.com 中某個文檔的document.domain 能夠設成a.b.example.com、b.example.com 、example.com中的任意一個,可是不能夠設成 c.a.b.example.com,由於這是當前域的子域,也不能夠設成 baidu.com,由於主域已經不相同了。
在頁面  http://www.example.com/a.html中設置document.domain:
QQ截圖20130613230631
在頁面  http://example.com/b.html中也設置document.domain,並且這也是必須的,雖然這個文檔的domain就是example.com,可是仍是必須顯示的設置document.domain的值:
QQ截圖20130613230631
這樣咱們就能夠經過js訪問到iframe中的各類屬性和對象了。
不過若是你想在 http://www.example.com/a.html頁面中經過ajax直接請求 http://example.com/b.html頁面,即便你設置了相同的document.domain也仍是不行的,因此修改document.domain的方法只適用於不一樣子域的框架間的交互。若是你想經過ajax的方法去與不一樣子域的頁面交互,除了使用jsonp的方法外,還能夠用一個隱藏的iframe來作一個代理。原理就是讓這個iframe載入一個與你想要經過ajax獲取數據的目標頁面處在相同的域的頁面,因此這個iframe中的頁面是能夠正常使用ajax去獲取你要的數據的,而後就是經過咱們剛剛講得修改document.domain的方法,讓咱們能經過js徹底控制這個iframe,這樣咱們就可讓iframe去發送ajax請求,而後收到的數據咱們也能夠得到了。
 
三、使用window.name來進行跨域
window對象有個name屬性,該屬性有個特徵:即在一個窗口(window)的生命週期內,窗口載入的全部的頁面都是共享一個window.name的,每一個頁面對window.name都有讀寫的權限,window.name是持久存在一個窗口載入過的全部頁面中的,並不會因新頁面的載入而進行重置。
好比:有一個頁面a.html,它裏面有這樣的代碼:
再看看b.html頁面的代碼:
a.html頁面載入後3秒,跳轉到了b.html頁面,結果爲:
咱們看到在b.html頁面上成功獲取到了它的上一個頁面a.html給window.name設置的值。若是在以後全部載入的頁面都沒對window.name進行修改的話,那麼全部這些頁面獲取到的window.name的值都是a.html頁面設置的那個值。固然,若是有須要,其中的任何一個頁面均可以對window.name的值進行修改。注意,window.name的值只能是字符串的形式,這個字符串的大小最大能容許2M左右甚至更大的一個容量,具體取決於不一樣的瀏覽器,但通常是夠用了。
上面的例子中,咱們用到的頁面a.html和b.html是處於同一個域的,可是即便a.html與b.html處於不一樣的域中,上述結論一樣是適用的,這也正是利用window.name進行跨域的原理。
下面就來看一看具體是怎麼樣經過window.name來跨域獲取數據的。仍是舉例說明。
好比有一個 www.example.com/a.html頁面,須要經過a.html頁面裏的js來獲取另外一個位於不一樣域上的頁面 www.cnblogs.com/data.html裏的數據。
data.html頁面裏的代碼很簡單,就是給當前的window.name設置一個a.html頁面想要獲得的數據值。data.html裏的代碼:
QQ截圖20130613230631
那麼在a.html頁面中,咱們怎麼把data.html頁面載入進來呢?顯然咱們不能直接在a.html頁面中經過改變window.location來載入data.html頁面,由於咱們想要即便a.html頁面不跳轉也能獲得data.html裏的數據。答案就是在a.html頁面中使用一個隱藏的iframe來充當一箇中間人角色,由iframe去獲取data.html的數據,而後a.html再去獲得iframe獲取到的數據。
充當中間人的iframe想要獲取到data.html的經過window.name設置的數據,只須要把這個iframe的src設爲 www.cnblogs.com/data.html就好了。而後a.html想要獲得iframe所獲取到的數據,也就是想要獲得iframe的window.name的值,還必須把這個iframe的src設成跟a.html頁面同一個域才行,否則根據前面講的同源策略,a.html是不能訪問到iframe裏的window.name屬性的。這就是整個跨域過程。
看下a.html頁面的代碼:
上面的代碼只是最簡單的原理演示代碼,你能夠對使用js封裝上面的過程,好比動態的建立iframe,動態的註冊各類事件等等,固然爲了安全,獲取完數據後,還能夠銷燬做爲代理的iframe。網上也有不少相似的現成代碼,有興趣的能夠去找一下。
經過window.name來進行跨域,就是這樣子的。
 
四、使用HTML5中新引進的window.postMessage方法來跨域傳送數據
window.postMessage(message,targetOrigin)  方法是html5新引進的特性,能夠使用它來向其它的window對象發送消息,不管這個window對象是屬於同源或不一樣源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。
調用postMessage方法的window對象是指要接收消息的那一個window對象,該方法的第一個參數message爲要發送的消息,類型只能爲字符串;第二個參數targetOrigin用來限定接收消息的那個window對象所在的域,若是不想限定域,能夠使用通配符 *  。
須要接收消息的window對象,但是經過監聽自身的message事件來獲取傳過來的消息,消息內容儲存在該事件對象的data屬性中。
上面所說的向其餘window對象發送消息,其實就是指一個頁面有幾個框架的那種狀況,由於每個框架都有一個window對象。在討論第二種方法的時候,咱們說過,不一樣域的框架間是能夠獲取到對方的window對象的,並且也能夠使用window.postMessage這個方法。下面看一個簡單的示例,有兩個頁面
QQ截圖20130613230631
 
QQ截圖20130613230631
咱們運行a頁面後獲得的結果:
咱們看到b頁面成功的收到了消息。
使用postMessage來跨域傳送數據仍是比較直觀和方便的,可是缺點是IE六、IE7不支持,因此用不用還得根據實際須要來決定。
 
結語:
除了以上幾種方法外,還有flash、在服務器上設置代理頁面等跨域方式,這裏就不作介紹了。
以上四種方法,能夠根據項目的實際狀況來進行選擇應用,我的認爲window.name的方法既不復雜,也能兼容到幾乎全部瀏覽器,這真是極好的一種跨域方法。
 
 7、JS數據類型
JS數據類型
js中有5種數據類型:Undefined、Null、Boolean、Number和String。
還有一種複雜的數據類型Object,Object本質是一組無序的名值對組成的。
Undefined類型只有一個值,即undefined,使用var聲明變量,可是未對初始化的,這個變量就是Undefined類型的,例子:
var i;
alert(i == undefined);//true
var i;與var i = undefined;這兩句是等價的。
包含Undefined值的變量和未定義的變量是不同的。
Null類型也只有一個值:null.null表示一個空對象的指針。
Boolean類型:只有兩個字面量true和false。可是js中多有的變量均可以使用Boolean()函數轉換成一個Boolean類型的值。
Number類型:整數和浮點數。NaN:Not a Number。這個數值用於原本要返回一個數值,可是卻未能放回一個數值的狀況,以防止報錯。

例如:1/0 返回的就是NaN。NaN的特色:一、任何涉及NaN的操做都會返回NaN。二、NaN對任何值都不相等,包括本身NaN自己。
針對NaN特性,JS內置了isNaN()函數,來肯定數值是否是NaN類型。
String類型:略
typeof操做符:對一個變量進行推斷變量的類型,可能返回如下字符串:
"undefined" 若是這個值,未定義或者爲初始化
"boolean" 布爾值
"string" 字符串
"number" 數值
"object" 對象
"function" 函數
用法:typeof 95;  或者  typeof(95); 會返回"number".

 

8、頁面加載

輸入地址
瀏覽器查找域名的 IP 地址
這一步包括 DNS 具體的查找過程,包括:瀏覽器緩存->系統緩存->路由器緩存...
瀏覽器向 web 服務器發送一個 HTTP 請求
服務器的永久重定向響應(從 http://example.com 到 http://www.example.com)
瀏覽器跟蹤重定向地址
服務器處理請求
服務器返回一個 HTTP 響應
瀏覽器顯示 HTML
瀏覽器發送請求獲取嵌入在 HTML 中的資源(如圖片、音頻、視頻、CSS、JS等等)
瀏覽器發送異步請求解析

 

html以構建dom樹->構建render樹->佈局render樹->繪製render樹
現代瀏覽器的工做原理

 

渲染引擎開始解析html,並將標籤轉化爲內容樹中的dom節點。接着,它解析外部CSS文件及style標籤中的樣式信息。這些樣式信息以及html中的可見性指令將被用來構建另外一棵樹——render樹。

Render樹由一些包含有顏色和大小等屬性的矩形組成,它們將被按照正確的順序顯示到屏幕上。

Render樹構建好了以後,將會執行佈局過程,它將肯定每一個節點在屏幕上的確切座標。再下一步就是繪製,即遍歷render樹,並使用UI後端層繪製每一個節點。

值得注意的是,這個過程是逐步完成的,爲了更好的用戶體驗,渲染引擎將會盡量早的將內容呈現到屏幕上,並不會等到全部的html都解析完成以後再去構建和佈局render樹。它是解析完一部份內容就顯示一部份內容,同時,可能還在經過網絡下載其他內容。

9、jsonp原理

JSONP跨域GET請求是一個經常使用的解決方案,下面咱們來看一下JSONP跨域是如何實現的,而且探討下JSONP跨域的原理
 
JavaScript是一種在Web開發中常用的前端動態腳本技術。在JavaScript中,有一個很重要的安全性限制,被稱爲「Same-Origin Policy」(同源策略)。這一策略對於JavaScript代碼可以訪問的頁面內容作了很重要的限制,即JavaScript只能訪問與包含它的文檔在同一域下的內容。

JavaScript這個安全策略在進行多iframe或多窗口編程、以及Ajax編程時顯得尤其重要。根據這個策略,在baidu.com下的頁面中包含的JavaScript代碼,不能訪問在google.com域名下的頁面內容;甚至不一樣的子域名之間的頁面也不能經過JavaScript代碼互相訪問。對於Ajax的影響在於,經過XMLHttpRequest實現的Ajax請求,不能向不一樣的域提交請求,例如,在abc.example.com下的頁面,不能向def.example.com提交Ajax請求,等等。

然而,當進行一些比較深刻的前端編程的時候,不可避免地須要進行跨域操做,這時候「同源策略」就顯得過於苛刻。JSONP跨域GET請求是一個經常使用的解決方案,下面咱們來看一下JSONP跨域是如何實現的,而且探討下JSONP跨域的原理。

利用在頁面中建立<script>節點的方法向不一樣域提交HTTP請求的方法稱爲JSONP,這項技術能夠解決跨域提交Ajax請求的問題。JSONP的工做原理以下所述:

假設在http://example1.com/index.php這個頁面中向http://example2.com/getinfo.php提交GET請求,咱們能夠將下面的JavaScript代碼放在http://example1.com/index.php這個頁面中來實現:
複製代碼 代碼以下:

var eleScript= document.createElement("script");
eleScript.type = "text/javascript";
eleScript.src = "http://example2.com/getinfo.php";
document.getElementsByTagName("HEAD")[0].appendChild(eleScript);

當GET請求從http://example2.com/getinfo.php返回時,能夠返回一段JavaScript代碼,這段代碼會自動執行,能夠用來負責調用http://example1.com/index.php頁面中的一個callback函數。

JSONP的優勢是:它不像XMLHttpRequest對象實現的Ajax請求那樣受到同源策略的限制;它的兼容性更好,在更加古老的瀏覽器中均可以運行,不須要XMLHttpRequest或ActiveX的支持;而且在請求完畢後能夠經過調用callback的方式回傳結果。

JSONP的缺點則是:它只支持GET請求而不支持POST等其它類型的HTTP請求;它只支持跨域HTTP請求這種狀況,不能解決不一樣域的兩個頁面之間如何進行JavaScript調用的問題。
再來一個例子:
複製代碼 代碼以下:

var qsData = {'searchWord':$("#searchWord").attr("value"),'currentUserId':
$("#currentUserId").attr("value"),'conditionBean.pageSize':$("#pageSize").attr("value")};
$.ajax({
async:false,
url: http://跨域的dns/document!searchJSONResult.action,
type: "GET",
dataType: 'jsonp',
jsonp: 'jsoncallback',
data: qsData,
timeout: 5000,
beforeSend: function(){
//jsonp 方式此方法不被觸發.緣由多是dataType若是指定爲jsonp的話,就已經不是ajax事件了
},
success: function (json) {//客戶端jquery預先定義好的callback函數,成功獲取跨域服務器上的json數據後,會動態執行這個callback函數
if(json.actionErrors.length!=0){
alert(json.actionErrors);
}
genDynamicContent(qsData,type,json);
},
complete: function(XMLHttpRequest, textStatus){
$.unblockUI({ fadeOut: 10 });
},
error: function(xhr){
//jsonp 方式此方法不被觸發.緣由多是dataType若是指定爲jsonp的話,就已經不是ajax事件了
//請求出錯處理
alert("請求出錯(請檢查相關度網絡情況.)");
}
});
有時也會看到這樣的寫法:
$.getJSON("http://跨域的dns/document!searchJSONResult.action?name1="+value1+"&jsoncallback=?",
function(json){
if(json.屬性名==值){
// 執行代碼
}
});

這種方式實際上是上例$.ajax({..}) api的一種高級封裝,有些$.ajax api底層的參數就被封裝而不可見了。
這樣,jquery就會拼裝成以下的url get請求:
複製代碼 代碼以下:

http://跨域的dns/document!searchJSONResult.action?&jsoncallback=jsonp1236827957501&_=1236828192549&searchWord=
%E7%94%A8%E4%BE%8B¤tUserId=5351&conditionBean.pageSize=15

在響應端(http://跨域的dns/document!searchJSONResult.action),經過 jsoncallback = request.getParameter("jsoncallback") 獲得jquery端隨後要回調的js function name:jsonp1236827957501 而後 response的內容爲一個Script Tags:"jsonp1236827957501("+按請求參數生成的json數組+")"; jquery就會經過回調方法動態加載調用這個js tag:jsonp1236827957501(json數組); 這樣就達到了跨域數據交換的目的。

JSONP原理
JSONP的最基本的原理是:動態添加一個<script>標籤,而script標籤的src屬性是沒有跨域的限制的。這樣說來,這種跨域方式其實與ajax XmlHttpRequest協議無關了。
這樣其實"jQuery AJAX跨域問題"就成了個僞命題,jquery $.ajax方法名有誤導人之嫌。
若是設爲dataType: 'jsonp',這個$.ajax方法就和ajax XmlHttpRequest沒什麼關係了,取而代之的則是JSONP協議。JSONP是一個非官方的協議,它容許在服務器端集成Script tags返回至客戶端,經過javascript callback的形式實現跨域訪問。

JSONP即JSON with Padding。因爲同源策略的限制,XmlHttpRequest只容許請求當前源(域名、協議、端口)的資源。若是要進行跨域請求, 咱們能夠經過使用html的script標記來進行跨域請求,並在響應中返回要執行的script代碼,其中能夠直接使用JSON傳遞javascript對象。 這種跨域的通信方式稱爲JSONP。

jsonCallback 函數jsonp1236827957501(....):是瀏覽器客戶端註冊的,獲取跨域服務器上的json數據後,回調的函數

Jsonp的執行過程以下:
首先在客戶端註冊一個callback (如:'jsoncallback'), 而後把callback的名字(如:jsonp1236827957501)傳給服務器。注意:服務端獲得callback的數值後,要用jsonp1236827957501(......)把將要輸出的json內容包括起來,此時,服務器生成 json 數據才能被客戶端正確接收。

而後以 javascript 語法的方式,生成一個function, function 名字就是傳遞上來的參數 'jsoncallback'的值 jsonp1236827957501 .
最後將 json 數據直接以入參的方式,放置到 function 中,這樣就生成了一段 js 語法的文檔,返回給客戶端。

客戶端瀏覽器,解析script標籤,並執行返回的 javascript 文檔,此時javascript文檔數據,做爲參數, 傳入到了客戶端預先定義好的 callback 函數(如上例中jquery $.ajax()方法封裝的的success: function (json))裏。
能夠說jsonp的方式原理上和<script src="http://跨域/...xx.js"></script>是一致的(qq空間就是大量採用這種方式來實現跨域數據交換的)。JSONP是一種腳本注入(Script Injection)行爲,因此有必定的安全隱患。
那jquery爲何不支持post方式跨域呢?

雖然採用post+動態生成iframe是能夠達到post跨域的目的(有位js牛人就是這樣把jquery1.2.5 打patch的),但這樣作是一個比較極端的方式,不建議採用。
也能夠說get方式的跨域是合法的,post方式從安全角度上,被認爲是不合法的,萬不得已仍是不要劍走偏鋒。

client端跨域訪問的需求看來也引發w3c的注意了,看資料說html5 WebSocket標準支持跨域的數據交換,應該也是一個未來可選的跨域數據交換的解決方案。

來個超簡單的例子:
代碼以下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Test Jsonp</title>
<script type="text/javascript">
function jsonpCallback(result)
{
alert(result.msg);
}
</script>
<script type="text/javascript" src="http://crossdomain.com/jsonServerResponse?jsonp=jsonpCallback"></script>
</head>
<body>
</body>
</html>

其中 jsonCallback 是客戶端註冊的,獲取跨域服務器上的json數據後,回調的函數。http://crossdomain.com/jsonServerResponse?jsonp=jsonpCallback 這個 url 是跨域服務器取 json 數據的接口,參數爲回調函數的名字,返回的格式爲:jsonpCallback({msg:'this is json data'})

簡述原理與過程:首先在客戶端註冊一個callback, 而後把callback的名字傳給服務器。此時,服務器先生成 json 數據。 而後以 javascript 語法的方式,生成一個function , function 名字就是傳遞上來的參數 jsonp。最後將 json 數據直接以入參的方式,放置到 function 中,這樣就生成了一段 js 語法的文檔,返回給客戶端。

客戶端瀏覽器,解析script標籤,並執行返回的 javascript 文檔,此時數據做爲參數,傳入到了客戶端預先定義好的 callback 函數裏。(動態執行回調函數) 

10、CSS合併方法

何爲CSS樣式合併,所謂CSS樣式合併,指的是一些不可分離的樣式(按鈕,圖標等),將他們公共的樣式部分進行合併,非公共的再次獨立出來,以減少CSS文件的大小

 

在項目開發環境下,咱們會把 JS 代碼儘量模塊化,方便管理和修改,這就避免不了會出現一個項目自身 JS 文件數量達到 10 個或者更多。

  而項目上線後,會要求將全部 JS 文件合併爲 1 個或者幾個,手動的操做雖然也不是問題,但每次修改更新都要手動操做合併一遍,這就確定是個噩夢了。

  這種狀況下,一些工具也就隨之產生,好比在線合併,一些網站提供js文件上傳,而後合併,但這仍是很麻煩,若是開發環境沒有網絡呢?

  這會我就想到了 windows 系統下的 cmd 裏的 copy 命令,它雖然是個複製的功能,但實則也是能夠實現合併文件的需求,下面就看下這句代碼:

copy a.js+b.js+c.js abc.js /b

  相信不會太多編程的人閱讀上面那句代碼也能大體讀懂意思:經過 copy 命令將 a.js b.js c.js 合併爲一個 abc.js,最後的 /b 表示文件爲二進位文件,copy 命令的其它參數能夠在 cmd 裏輸入 copy /? 學習,這裏就再也不細述。

  說到這裏,其實 windows 自己就能夠完成咱們的需求,也不用安裝什麼其它工具了,下面咱們要作的就是將這一切操做更簡單。

  咱們在項目存放 JS 的文件夾下新建一個 TXT 文件,將代碼複製進去,並修改須要合併哪些文件,最後保存並將 TXT 修改成 BAT 後綴,如:

copy core.js+hros.app.js+hros.appmanage.js+hros.base.js+hros.copyright.js+hros.desktop.js+hros.dock.js+hros.folderView.js+hros.grid.js+hros.maskBox.js+hros.navbar.js+hros.popupMenu.js+hros.searchbar.js+hros.startmenu.js+hros.taskbar.js+hros.uploadFile.js+hros.wallpaper.js+hros.widget.js+hros.window.js+hros.zoom.js+templates.js+util.js core.min.js /b

  接下來咱們雙擊下那個 BAT 文件,看到效果了吧?這就是咱們想要的。之後每次上線前,只需雙擊下這個文件,系統就會自動合併並生成一個合併好的文件,比起其它什麼工具,這個的效率簡直沒法直視。

  若是你本地還安裝過 UglifyJS 這個工具,能夠在代碼後面加一句壓縮的代碼,如:

copy core.js+hros.app.js+hros.appmanage.js+hros.base.js+hros.copyright.js+hros.desktop.js+hros.dock.js+hros.folderView.js+hros.grid.js+hros.maskBox.js+hros.navbar.js+hros.popupMenu.js+hros.searchbar.js+hros.startmenu.js+hros.taskbar.js+hros.uploadFile.js+hros.wallpaper.js+hros.widget.js+hros.window.js+hros.zoom.js+templates.js+util.js core.min.js /b
uglifyjs core.min.js -m -o core.min.js

  這樣每次合併好後就自動壓縮了,又省了一步操做。

  CSS 合併同理。

 

11、盒子模型

標準W3C盒子模型和IE盒子模型CSS佈局經典盒子模型(轉)

 

盒子模型是css中一個重要的概念,理解了盒子模型才能更好的排版。其實盒子模型有兩種,分別是 ie 盒子模型和標準 w3c 盒子模型。他們對盒子模型的解釋各不相同,先來看看咱們熟知的標準盒子模型:

  

  從上圖能夠看到標準 w3c 盒子模型的範圍包括 margin、border、padding、content,而且 content 部分不包含其餘部分。

  ie 盒子模型

 

 

 

 

  從上圖能夠看到 ie 盒子模型的範圍也包括 margin、border、padding、content,和標準 w3c 盒子模型不一樣的是:ie 盒子模型的 content 部分包含了 border 和 pading。

   例:一個盒子的 margin 爲 20px,border 爲 1px,padding 爲 10px,content 的寬爲 200px、高爲 50px,假如用標準 w3c 盒子模型解釋,那麼這個盒子須要佔據的位置爲:寬 20*2+1*2+10*2+200=262px、高 20*2+1*2*10*2+50=112px,盒子的實際大小爲:寬 1*2+10*2+200=222px、高 1*2+10*2+50=72px;假如用ie 盒子模型,那麼這個盒子須要佔據的位置爲:寬 20*2+200=240px、高 20*2+50=70px,盒子的實際大小爲:寬 200px、高 50px。

  那應該選擇哪中盒子模型呢?固然是「標準 w3c 盒子模型」了。怎麼樣纔算是選擇了「標準 w3c 盒子模型」呢?很簡單,就是在網頁的頂部加上 doctype 聲明。假如不加 doctype 聲明,那麼各個瀏覽器會根據本身的行爲去理解網頁,即 ie 瀏覽器會採用 ie 盒子模型去解釋你的盒子,而 ff 會採用標準 w3c 盒子模型解釋你的盒子,因此網頁在不一樣的瀏覽器中就顯示的不同了。反之,假如加上了 doctype 聲明,那麼全部瀏覽器都會採用標準 w3c 盒子模型去解釋你的盒子,網頁就能在各個瀏覽器中顯示一致了。

  再用 jquery 作的例子來證明一下。

  代碼1:

<html>
<head>
<title>你用的盒子模型是?</title>
<script language="javascript" src="jquery.min.js"></script>
<script language="javascript">
var sbox = $.boxmodel ? "標準w3c":"ie";
document.write("您的頁面目前支持:"+sbox+"盒子模型");
</script>
</head>
<body>
</body>
</html>

  上面的代碼沒有加上 doctype 聲明,在 ie 瀏覽器中顯示「ie盒子模型」,在 ff 瀏覽器中顯示「標準 w3c 盒子模型」。

  代碼2:

<!doctype html public "-//w3c//dtd xhtml 1.0 transitional//en" "http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd">
<html>
<head>
<title>你用的盒子模型是標準w3c盒子模型</title>
<script language="javascript" src="jquery.min.js"></script>
<script language="javascript">
var sbox = $.boxmodel ? "標準w3c":"ie";
document.write("您的頁面目前支持:"+sbox+"盒子模型");
</script>
</head>
<body>
</body>
</html>

  代碼2 與代碼1 惟一的不一樣的就是頂部加了 doctype 聲明。在全部瀏覽器中都顯示「標準 w3c 盒子模型」。

  因此爲了讓網頁能兼容各個瀏覽器,讓咱們用標準 w3c 盒子模型。

 

12、事件委託

 

現在的JavaScript技術界裏最火熱的一項技術應該是‘事件委託(event delegation)’了。使用事件委託技術能讓你避免對特定的每一個節點添加事件監聽器;相反,事件監聽器是被添加到它們的父元素上。事件監聽器會分析從子元素冒泡上來的事件,找到是哪一個子元素的事件。基本概念很是簡單,但仍有不少人不理解事件委託的工做原理。這裏我將要解釋事件委託是如何工做的,並提供幾個純JavaScript的基本事件委託的例子。

假定咱們有一個UL元素,它有幾個子元素:

<ul id="parent-list"> <li id="post-1">Item 1</li> <li id="post-2">Item 2</li> <li id="post-3">Item 3</li> <li id="post-4">Item 4</li> <li id="post-5">Item 5</li> <li id="post-6">Item 6</li> </ul>

咱們還假設,當每一個子元素被點擊時,將會有各自不一樣的事件發生。你能夠給每一個獨立的li元素添加事件監聽器,但有時這些li元素可能會被刪除,可能會有新增,監聽它們的新增或刪除事件將會是一場噩夢,尤爲是當你的監聽事件的代碼放在應用的另外一個地方時。可是,若是你將監聽器安放到它們的父元素上呢?你如何能知道是那個子元素被點擊了?

簡單:當子元素的事件冒泡到父ul元素時,你能夠檢查事件對象的target屬性,捕獲真正被點擊的節點元素的引用。下面是一段很簡單的JavaScript代碼,演示了事件委託的過程:

// 找到父元素,添加監聽器...
document.getElementById("parent-list").addEventListener("click",function(e) { // e.target是被點擊的元素! // 若是被點擊的是li元素 if(e.target && e.target.nodeName == "LI") { // 找到目標,輸出ID! console.log("List item ",e.target.id.replace("post-")," was clicked!"); } });

第一步是給父元素添加事件監聽器。當有事件觸發監聽器時,檢查事件的來源,排除非li子元素事件。若是是一個li元素,咱們就找到了目標!若是不是一個li元素,事件將被忽略。這個例子很是簡單,ULli是標準的父子搭配。讓咱們試驗一些差別比較大的元素搭配。假設咱們有一個父元素div,裏面有不少子元素,但咱們關心的是裏面的一個帶有」classA」 CSS類的A標記:

// 得到父元素DIV, 添加監聽器...
document.getElementById("myDiv").addEventListener("click",function(e) { // e.target是被點擊的元素 if(e.target && e.target.nodeName == "A") { // 得到CSS類名 var classes = e.target.className.split(" "); // 搜索匹配! if(classes) { // For every CSS class the element has... for(var x = 0; x < classes.length; x++) { // If it has the CSS class we want... if(classes[x] == "classA") { // Bingo! console.log("Anchor element clicked!"); // Now do something here.... } } } } });

上面這個例子中不只比較了標籤名,並且比較了CSS類名。雖然稍微複雜了一點,但仍是很具表明性的。好比,若是某個A標記裏有一個span標記,則這個span將會成爲target元素。這個時候,咱們須要上溯DOM樹結構,找到裏面是否有一個 A.classA 的元素。

由於大部分程序員都會使用jQuery等工具庫來處理DOM元素和事件,我建議你們都使用裏面的事件委託方法,由於這裏工具庫裏都提供了高級的委託方法和元素甄別方法。

但願這篇文章能幫助你理解JavaScript事件委託的幕後原理,但願你也感覺到了事件委託的強大用處!

 

十3、模塊化

AMD異步加載,依賴前置,提早執行,CMD同步加載,依賴就近,延遲執行

十4、拖拽實現

在我工做的將近一年時間,印象中拖拽是我作的最多的一種效果,在這裏和你們分享一下。
    以後有時間還會作兩個稍微複雜點的效果,這裏先講一下簡單拖拽的實現
    
    首先,先分解一下拖拽的步驟:
    1)在目標元素上按下鼠標(這裏沒有細分左右鍵),也就是mousedown事件
    2)按下鼠標的同事,拖動鼠標,至關於在mousedown事件中綁定mousemove事件
    3)中止拖拽,釋放鼠標按鍵,也就是mouseup事件
    代碼表示以下:
    targetNode.onmousedown=function(){
        ...
        document.onmousemove=function(){
            ....
        }
        document.onmouseup=function(){
            ....
        }
    }
    這裏的mousemove和mouseup事件都綁定在document上而不是目標元素上

    是由於拖拽的時候鼠標隨時可能離開目標元素,這樣將致使mouseover和mouseup

    找不到目標而影響拖拽的效果,具體代碼以下(ps:下面的代碼在火狐下是有bug的,這是由於拖拽元素的innerHTML是空的,解決辦法能夠加空的<span></span>標籤或讓其裏面有內容便可,有興趣可  以試一下)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>無標題文檔</title>
<style type="text/css">
*{ margin:0; padding:0;}
#dragDiv{ width:200px; height:200px; position:absolute; top:100px; left:100px; background:#ddd;}
</style>
</head>

<body>
<div id="dragDiv"></div>
<script type="text/javascript">
var dragDiv=document.getElementById('dragDiv');
//鼠標按下動做
dragDiv.onmousedown=function(event){
var e=event||window.event,
t=e.target||e.srcElement,
//鼠標按下時的座標x1,y1
x1=e.clientX,
y1=e.clientY,
//鼠標按下時的左右偏移量
dragLeft=this.offsetLeft,
dragTop=this.offsetTop;
document.onmousemove=function(event){
var e=event||window.event,
t=e.target||e.srcElement,
//鼠標移動時的動態座標
x2=e.clientX,
y2=e.clientY,
//鼠標移動時的座標的變化量
x=x2-x1,
y=y2-y1;
dragDiv.style.left=(dragLeft+x)+'px';
dragDiv.style.top=(dragTop+y)+'px';
}
document.onmouseup=function(){
this.onmousemove=null;
}
}
</script>
</body>
</html>

 
e.target和e.srcElement表示的都是事件的目標
e.srcElement兼容IE,e.target兼容非IE
IE9,chrome(個人版本,具體不知道哪一版)兩個屬性都兼容
e.target||e.srcElement能夠保證在任何一個瀏覽器上都有值
十5、JS繼承實現
 

js是門靈活的語言,實現一種功能每每有多種作法,ECMAScript沒有明確的繼承機制,而是經過模仿實現的,根據js語言的自己的特性,js實現繼承有如下通用的幾種方式
1.使用對象冒充實現繼承(該種實現方式能夠實現多繼承)
實現原理:讓父類的構造函數成爲子類的方法,而後調用該子類的方法,經過this關鍵字給全部的屬性和方法賦值

Js代碼   收藏代碼
  1. function Parent(firstname)  
  2. {  
  3.     this.fname=firstname;  
  4.     this.age=40;  
  5.     this.sayAge=function()  
  6.     {  
  7.         console.log(this.age);  
  8.     }  
  9. }  
  10. function Child(firstname)  
  11. {  
  12.     this.parent=Parent;  
  13.     this.parent(firstname);  
  14.     delete this.parent;  
  15.     this.saySomeThing=function()  
  16.     {  
  17.         console.log(this.fname);  
  18.         this.sayAge();  
  19.     }  
  20. }  
  21. var mychild=new  Child("李");  
  22. mychild.saySomeThing();  

 

2.採用call方法改變函數上下文實現繼承(該種方式不能繼承原型鏈,若想繼承原型鏈,則採用5混合模式)
實現原理:改變函數內部的函數上下文this,使它指向傳入函數的具體對象

Js代碼   收藏代碼
  1. function Parent(firstname)  
  2. {  
  3.     this.fname=firstname;  
  4.     this.age=40;  
  5.     this.sayAge=function()  
  6.     {  
  7.         console.log(this.age);  
  8.     }  
  9. }  
  10. function Child(firstname)  
  11. {  
  12.   
  13.     this.saySomeThing=function()  
  14.     {  
  15.         console.log(this.fname);  
  16.         this.sayAge();  
  17.     }  
  18.    this.getName=function()  
  19.    {  
  20.        return firstname;  
  21.    }  
  22.   
  23. }  
  24. var child=new Child("張");  
  25. Parent.call(child,child.getName());  
  26. child.saySomeThing();  

 

3.採用Apply方法改變函數上下文實現繼承(該種方式不能繼承原型鏈,若想繼承原型鏈,則採用5混合模式)
實現原理:改變函數內部的函數上下文this,使它指向傳入函數的具體對象

Js代碼   收藏代碼
  1. function Parent(firstname)  
  2. {  
  3.     this.fname=firstname;  
  4.     this.age=40;  
  5.     this.sayAge=function()  
  6.     {  
  7.         console.log(this.age);  
  8.     }  
  9. }  
  10. function Child(firstname)  
  11. {  
  12.   
  13.     this.saySomeThing=function()  
  14.     {  
  15.         console.log(this.fname);  
  16.         this.sayAge();  
  17.     }  
  18.     this.getName=function()  
  19.     {  
  20.         return firstname;  
  21.     }  
  22.   
  23. }  
  24. var child=new Child("張");  
  25. Parent.apply(child,[child.getName()]);  
  26. child.saySomeThing();  

 

4.採用原型鏈的方式實現繼承
實現原理:使子類原型對象指向父類的實例以實現繼承,即重寫類的原型,弊端是不能直接實現多繼承

Js代碼   收藏代碼
  1. function Parent()  
  2. {  
  3.   
  4.     this.sayAge=function()  
  5.     {  
  6.         console.log(this.age);  
  7.     }  
  8. }  
  9. function Child(firstname)  
  10. {  
  11.     this.fname=firstname;  
  12.     this.age=40;  
  13.     this.saySomeThing=function()  
  14.     {  
  15.         console.log(this.fname);  
  16.         this.sayAge();  
  17.     }  
  18. }  
  19.   
  20. Child.prototype=new  Parent();  
  21. var child=new Child("張");  
  22. child.saySomeThing();  

 

5.採用混合模式實現繼承

Js代碼   收藏代碼
  1. function Parent()  
  2. {  
  3.   
  4.     this.sayAge=function()  
  5.     {  
  6.         console.log(this.age);  
  7.     }  
  8. }  
  9.   
  10. Parent.prototype.sayParent=function()  
  11. {  
  12.    alert("this is parentmethod!!!");  
  13. }  
  14.   
  15. function Child(firstname)  
  16. {  
  17.     Parent.call(this);  
  18.     this.fname=firstname;  
  19.     this.age=40;  
  20.     this.saySomeThing=function()  
  21.     {  
  22.         console.log(this.fname);  
  23.         this.sayAge();  
  24.     }  
  25. }  
  26.   
  27. Child.prototype=new  Parent();  
  28. var child=new Child("張");  
  29. child.saySomeThing();  
  30. child.sayParent();  

 

 

 

這些天讀了John Resig的《Secrets of JavaScript Ninja》,其中討論到JS中實現繼承的方案,很是有趣,本身探索了一下,造成了筆記,放到這裏。

這個方案在Resig的博客上也有,雖然代碼略微有點不一致,但核心思想是同樣的

<html>
<head>
    <title></title>
</head>
<body>
<script type="text/javascript">
    // call a immediate funciton,prevent global namespace from being polluted.
    (function(){
        // 這個initializing變量用於標識當前是否處於類的初始建立階段,下面會繼續詳述
        var initializing = false,
        // 這是一個技巧性的寫法,用於檢測當前環境下函數是否可以序列化
        // 附一篇討論函數序列化的文章:http://www.cnblogs.com/ziyunfei/archive/2012/12/04/2799603.html
        // superPattern引用一個正則對象,該對象用於驗證被驗證函數中是否有使用_super方法
            superPattern = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

        Object.subClass = function(properties){
            // 當前對象(父類)的原型對象
            var _super = this.prototype;

            // initializing = true表示當前處於類的初始建立階段。
            // this構造函數裏會判斷initializing的狀態,若是爲false則不執行Init方法。
            // 事實上這也是很是須要的,由於在這個時候,咱們須要的只是一個乾淨的虛構的構造函數,徹底不須要其執行init函數,以免污染。init方法只有在當前類被實例化的時候才須要被執行,而當前正執行繼承行爲,不該該執行Init方法。
            initializing = true;
            // 當前對象(父類)的一個實例對象
            var proto = new this();
            // 初始建立階段完成,置initializing爲false
            initializing = false;

            // 在properties裏提供的屬性,做爲當前對象(父類)實例的公共屬性,供其子類實例共享;
            // 在properties裏提供的方法,做爲當前對象(父類)實例的公共方法,供其子類實例共享。
            for(var name in properties){
                proto[name] = typeof properties[name] == 'function' && //檢測當前提供的是否爲函數
                              typeof _super[name] == 'function' && //檢測當前提供的函數名是否已經存在於父類的原型對象中,若是是,則須要下面的操做,以保證父類中的方法不會被覆蓋且能夠以某種方式被調用,若是否,則直接將該函數賦值爲父類實例的方法
                              superPattern.test(properties[name]) ? f//檢測當前提供的函數內是否使用了_super方法,若是有使用_super方法,則須要下面的操做,以保證父類中的方法不會被覆蓋且能夠以某種方式被調用,若是沒有用到_super方法,則直接將該函數賦值爲父類實例的方法,即便父類原型中已經擁有同名方法(覆蓋)

                    // 使用一個立刻執行的函數,返回一個閉包,這樣每一個閉包引用的都是各自的name和fn。
                    (function(name, fn){
                        return function() {
                            // 首先將執行方法的當前對象(子類的實例化對象)的_super屬性保存到tmp變量裏。
                            // 這是很是必要的, 由於this永遠指向當前正在被調用的對象。
                            // 當C繼承B,B繼承A,而A\B\C均有一個dance方法且B\C的dance方法均使用了this._super來引用各自父類的方法時,下面這句操做就顯得很是重要了。它使得在方法調用時,this._super永遠指向「當前類」的父類的原型中的同名方法,從而避免this._super被隨便改寫。
                            var tmp = this._super;
                            
                            // 而後將父類的原型中的同名方法賦值給this._super,以便子類的實例化對象能夠在其執行name方法時經過this._super使用對應的父類原型中已經存在的方法
                            this._super = _super[name];

                            // 執行建立子類時提供的函數,並經過arguments傳入參數
                            var ret = fn.apply(this, arguments);
                            
                            // 將tmp裏保存的_super屬性從新賦值回this._super中
                            this._super = tmp;

                            // 返回函數的執行結果
                            return ret;
                        };
                    })(name, properties[name]) : 
                    properties[name];
            }

            // 內部定義個名叫Class的類,構造函數內部只有一個操做:執行當前對象中可能存在的init方法
            // 這樣作的緣由:新建一個類(閉包),能夠防止不少干擾(詳細可對比JS高級設計第三版)
            function Class(){
                // 若是不是正在實現繼承,而且當前類的init方法存在,則執行init方法
                // 每當subClass方法執行完畢後,都會返回這個Class構造函數,當用戶使用new 方法時,就會執行這裏面的操做
                // 本質:每次調用subClass都新建一個類(閉包)
                if(!initializing && this.init){
                    // 這是子類的初始化方法,裏面能夠定義子類的私有屬性,公共屬性請在上方所述處添加
                    this.init.apply(this, arguments);
                }
            }

            // 重寫Class構造函數的prototype,使其再也不指向了Class原生的原型對象,而是指向了proto,即當前對象(類)的一個實例
            // 本質:一個類的原型是另外一個類的實例(繼承)
            Class.prototype = proto;
            // 爲何要重寫Class的構造函數?由於這個Class函數,它原來的constructor指向的是Function對象,這裏修正它的指向,使其指向本身。
            Class.constructor = Class;
            // 就是這個操做,使得每次調用subClass都會新生命的Class對象,也擁有subClass方法,能夠繼續被繼承下去
            // 本質:使得每次繼承的子類都擁有被繼承的能力
            Class.subClass = arguments.callee;
            // 返回這個內部新定義的構造函數(閉包)
            return Class;
        };
    })();


    var Person = Object.subClass({
        init: function(isDancing) {
            this.dancing = isDancing;
        },
        dance: function(){
            console.log('i am a person,i dance.');
            return this.dancing;
        }
    });

    var Ninja = Person.subClass({
        init:function(){

        },
        dance: function() {
            console.log('i am an Ninja,i dance.');
            this._super();
            return;
        },
        swingSword:function(){
            return true;
        }
    });

    var Chileung = Ninja.subClass({
        dance: function(){
            console.log('i am Chileung.i dance.');
            this._super();
            return;
        }
    });

    var p = new Person();
    p.dance();

    var n = new Ninja();
    n.dance();

    var c = new Chileung();
    c.dance();
</script>
</body>
</html>十6、前端優化、前端安全
相關文章
相關標籤/搜索