由於曾經在高中買來《C Primer Plus》和大學買來的《Clean Code》(挑戰本身買的英文版的結果就啃了一點)給我一種經典的書都特別厚的一本就夠讀大半年的感受。加上剛上大學圖便宜買的有關作網站的舊書(應該是 Table 佈局和 Dreamweaver 比較火的時代的書,這些書卻是很薄)讓我一度認爲作網頁不就是 table 而後 tr、td 什麼的套唄,高大上點不就是 div+CSS 嘛有什麼大不了,給我設計好什麼網頁不都 ok 能作出來麼?這種感受。而後看網絡課程,在網上找資料學習後才發現之前的本身太逗了,以後就一直靠網絡課程、MDN、博客、百科這些渠道學習,但一想到網上大牛們評價特別高的書還沒看過就總感受少了點什麼。最近將這些書看了看,發現之前只知道要這麼作比較好的地方如今也更加明白這樣作的意義,也糾正了之前理解的一些錯誤。javascript
我這裏我只總結一些我之前掌握不紮實的和我認爲比較重要的。php
ECMAScript 中全部數值都是以 IEEE-754 64 位格式存儲,但位操做符並不直接操做 64 位的值。而是先將 64 位的值轉換成 32 位的整數,而後執行操做,最後再將結果轉換爲 64 位。(對於開發人員 64 位存儲格式是透明的,所以整個過程像是隻存在 32 位的整數同樣)css
function func(para1, para2){ console.log('修改前', arguments , para1, para2); arguments [0] = 'arguements_0'; arguments [1] = 'arguements_1'; console.log('修改後', arguments , para1, para2); } func('para1')
基本類型:html
基本類型的值沒法添加屬性(儘管這樣不會報錯),eg:前端
var name = "zhang"; name.age = 17; console.log(name.age);// undefined
引用類型:html5
ECMAScript 中全部函數的參數都是按值傳遞的。java
eg:這裏的 obj 形參是建立了一個指針讓他與 person 指針指向同一個地址,obj 和 person 都指向同一個地址,但改變 obj 的指向 person 不改變css3
function setName(obj) { obj.name = "Nicholas"; obj = new Object(); obj.name = "Greg"; } var person = new Object(); setName(person); alert(person.name); //"Nicholas"
使用 var 聲明的變量會自動被添加到最接近的環境中。在函數內部,最接近的環境就是函數的局部環境;在 with 語句中,最接近的環境是函數環境。若是初始化變量時沒有使用 var 聲明,該變量會自動被添加到全局環境。程序員
// 1.if if (true) { var color = "blue"; } alert(color); //"blue" // 2.for for (var i=0; i < 10; i++){ } alert(i); //10 // 3.function function add(num1, num2) { var sum = num1 + num2; return sum; } alert(sum); //因爲 sum 不是有效的變量,所以會致使錯誤
如今使用 ES6 的let
會聲明塊級做用域變量。web
經常使用標記清除,不經常使用引用計數
若是 slice() 方法的參數中有一個負數,則用數組長度加上該數來肯定相應的位置。例如,在一個包含 5 項的數組上調用 slice(-2,-1) 與調用 slice(3,4) 獲得的結果相同。若是結束位置小於起始位置,則返回空數組。
ECMAScript 5 還新增了兩個歸併數組的方法: reduce() 和 reduceRight()。這兩個方法都會迭代數組的全部項,而後構建一個最終返回的值。其中, reduce() 方法從數組的第一項開始,逐個遍歷到最後。而 reduceRight() 則從數組的最後一項開始,向前遍歷到第一項。
工廠模式(使用函數建立對象,爲對象添加屬性和方法而後返回對象,被構造函數模式取代)
function createPersion(name, age) { return { name, age } } var p1 = createPersion("zhang", 17); var p2 = createPersion("foo", 18);
構造函數模式(每一個實例不共用屬性和方法,JS 中函數是對象)
function Persion(name, age) { this.name = name; this.age = age; this.print = function () { console.log(this.name); } } var p1 = new Persion("zhang", 17); var p2 = new Persion("foo", 18);
原型模式(每一個實例共用一套屬性和方法)
function Persion() {} Persion.prototype.name = "zhang"; Persion.prototype.age = 17; Persion.prototype.print = function () { console.log(this.name); } var p1 = new Persion(); var p2 = new Persion();
組合使用構造函數模式和原型模式(既有共用又有不共用的屬性和方法)
這是目前使用最普遍的建立自定義類型的方法。
使用對象覆蓋原型(字面量方式修改原型)要注意已經建立的實例的constructor
指針不會自動變(仍是覆蓋前的原型)。
function Persion(name, age) { this.name = name; this.age = age; } Persion.prototype = { constructor: Persion, print: function () { console.log(this.name); } } var p1 = new Persion("zhang", 17); var p2 = new Persion("foo", 18);
動態原型模式(給原型添加屬性的操做放到構造函數內)
function Persion(name, age) { this.name = name; this.age = age; // 第一次執行時初始化 if (typeof this, print !== "function") { Persion.prototype.print = function () { console.log(this.name); } } } var p1 = new Persion("zhang", 17); var p2 = new Persion("foo", 18);
寄生構造函數模式(和工廠模式幾乎同樣,能夠爲對象建立構造函數)
function MyArray(){ let arr = new Array(); arr.push.apply(arr, arguments); arr.toPipeString = function(){ return this.join("|"); } return arr; } var marr = new MyArray(1,2,3); console.log(marr.toPipeString());
穩妥構造函數模式
this
對象。適合用在安全環境中,或防止數據被其餘程序改動。function Persion(name, age) { var o = {}; // 除了使用 print 沒有其餘方法能獲取到 name o.print = function () { console.log(name); } return o; } var p1 = new Persion("zhang", 17); var p2 = new Persion("foo", 18);
OO 語言支持兩種繼承:接口繼承和實現繼承。因爲函數沒有簽名,ES 沒法實現接口繼承。
原型鏈(有兩個問題:屬性共享和參數傳遞)
function SuperType(para) { this.property = true; this.arr = [1, 2, 3]; this.para = para; } SuperType.prototype.getSuperProp = function () { return this.property; } function SubType() { this.subproperty = false; } // 繼承(這時 SubType.prototype.constructor 屬性沒有了,SubType.[[prototype]].constructor 爲 SuperType) SubType.prototype = new SuperType(); SubType.prototype.getSubProp = function () { return this.subproperty; } // 問題1:沒法向父類構造函數傳值 var instance1 = new SubType(); var instance2 = new SubType(); // 問題2:instance2.arr 也改變了 instance1.arr.push(4);
借用構造函數(和構造函數的問題同樣:每一個實例不共用屬性和方法)
function SuperType(para) { this.para = para; this.getPara = function(){ return this.para; } } function SubType() { SuperType.apply(this, arguments); } // 能夠向父類構造函數傳值 var instance1 = new SubType("instance1"); var instance2 = new SubType("instance2");
組合繼承(JS 中最經常使用的繼承模式)
function SuperType(para) { this.property = true; this.arr = [1, 2, 3]; this.para = para; } SuperType.prototype.getSuperProp = function () { return this.property; } function SubType() { SuperType.apply(this, arguments); this.subproperty = false; } // 繼承 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.getSubProp = function () { return this.subproperty; } // 能夠向父類構造函數傳值 var instance1 = new SubType("instance1"); var instance2 = new SubType("instance2"); // instance2.arr 不變 instance1.arr.push(4);
原型式繼承(Object.create()
)
function createObj(obj) { function f() {}; f.prototype = obj; return new f(); } var person = { name: "zhang", age: [17] } // person、 zi 和 ma 共用引用屬性 age var zi = createObj(person); var ma = createObj(person); zi.age.push(18); ma.age.push(17); console.log(person.age);
寄生式繼承(和寄生構造函數相似)
function createObjParasitic(obj) { let o = Object.create(obj); // o 目前是以下對象: // { // [[prototype]] = obj // } o.sayHi = function () { console.log("hi") } return o; } var person = { name: "zhang", age: [17] } var zi = createObjParasitic(person); zi.sayHi();
寄生組合式繼承(將new SuperType()
改成Object.create(SuperType.prototype)
減小一次父類的調用,還避免了在SubType
上添加沒必要要的屬性)
function inheritPrototype(sub, sup) { // 使 sub.prototype 的 [[prototype]] 「指針」指向 sup.prototype sub.prototype = Object.create(sup.prototype); sub.prototype.constructor = sub; } function SuperType() { this.property = true; this.arr = [1, 2, 3]; } SuperType.prototype.getSuperProp = function () { return this.property; } function SubType() { SuperType.apply(this, arguments); this.subproperty = false; } inheritPrototype(SubType, SuperType); console.log((new SubType()).getSuperProp());
定義函數有兩種形式:函數聲明(會函數聲明提高)和函數表達式。
閉包指有權訪問另外一個函數做用域中的變量的函數。
每一個函數在被調用時都會自動取得兩個特殊變量: this 和 arguments。內部函數在搜索這兩個變量時,只會搜索到其活動對象爲止,所以永遠不可能直接訪問外部函數中的這兩個變量。不過,把外部做用域中的 this 對象保存在一個閉包可以訪問到的變量裏,就可讓閉包訪問該對象了。
若是想訪問做用域中的 this 和 arguments 對象必須把她們保存在閉包能夠訪問的變量中。
var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { return function () { // this指向window return this.name; }; } }; console.log(object.getNameFunc()()); //"The Window"(在非嚴格模式下) var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { var that = this; return function () { // 閉包能夠訪問that // this指向window,that指向object return that.name; }; } }; console.log(object.getNameFunc()()); //"My Object"
JS 中沒有使用成員的概念,全部對象屬性都是共有的。但在函數中定義的變量均可以認爲是私有比阿尼浪,由於不能在函數外部訪問這些變量(包括:參數、局部變量和函數內部定義的其餘函數)。
JS 以字面量的方式建立單例對象。
var singleton = { name: "xxx", method: function () { // ... } }
// 這時 function(){...}() 是當作語句解析的 var application = function(){ //私有變量和函數 var components = new Array(); //初始化 components.push(new BaseComponent()); //建立 application 的一個局部副本 var app = new BaseComponent(); //公共接口 app.getComponentCount = function(){ return components.length; }; app.registerComponent = function(component){ if (typeof component == "object"){ components.push(component); } }; //返回這個副本 return app; }();
BOM 的核心是 window 對象,它表示瀏覽器的一個實例,同時也是 ES 中規定的 Global 對象。
若是頁面中包含框架,則每一個框架都擁有本身的 window 對象,而且保存在 frames 集合中。在 frames 集合中,能夠經過數值索引(從 0 開始,從左至右,從上到下)或者框架名稱來訪問相應的 window 對象。每一個 window 對象都有一個 name 屬性,其中包含框架的名稱。
在使用框架的狀況下,瀏覽器中會存在多個 Global 對象。在每一個框架中定義的全局變量會自動成爲框架中 window 對象的屬性。因爲每一個 window 對象都包含原生類型的構造函數,所以每一個框架都有一套本身的構造函數,這些構造函數一一對應,但並不相等。例如,
top.Object
並不等於top.frames[0].Object
。這個問題會影響到對跨框架傳遞的對象使用instanceof
操做符。
window.top
對象始終指向最外層框架,也就是瀏覽器窗口window.parent
指向父窗口window.self
指向 window窗口相對屏幕的位置:window.screenLeft
和window.screenTop
窗口位置移動(默認是禁用的):window.moveTo()
和window.moveBy()
瀏覽器窗口大小(不一樣瀏覽器對這些屬性的解釋不一樣):window.innerHeight
、window.innerWidth
、window.outerWidth
和window.outerWidth
頁面視口信息:window.document.documentElement.clientWidth
和window.document.documentElement.clientHeight
瀏覽器窗口大小改變(默認是禁用的):window.resizeTo()
和window.resizeBy()
通常認爲,使用超時調用來模擬間歇調用是一種最佳模式。在開發環境下,不多使用真正的間歇調用,緣由是後一個間歇調用可能會在前一個間歇調用結束以前啓動。而像下面示例中那樣使用超時調用,則徹底能夠避免這一點。因此,最好不要使用間歇調用。
// 超時調用模擬間歇調用 var num = 0; var max = 10; function incrementNumber() { num++; //若是執行次數未達到 max 設定的值,則設置另外一次超時調用 if (num < max) { setTimeout(incrementNumber, 500); } else { alert("Done"); } } setTimeout(incrementNumber, 500);
// 基本 location.assign("https://developer.mozilla.org") // 下面兩種和和顯示調用 assign 方法同樣 window.location = "https://developer.mozilla.org" location.href = "https://developer.mozilla.org" // 其餘 location.hash = "#123" location.pathname = "zh-CN" // replace 的 location 不記錄在歷史記錄中 location.replace("https://developer.mozilla.org")
獲取瀏覽器信息、主語言、插件、設置等信息。
用來獲取瀏覽器窗口外部的顯示器信息,如像素寬度、高度等。
// 跳轉到最近的 mdn 界面 history.go("developer.mozilla.org");
function isHostMethod(object, property) { var t = typeof object[property]; return t === 'function' || (!!(t === 'object' && object[property])) || t === 'unknown'; } console.log(isHostMethod([], 'find'));
// 是否有將屬性不列舉的bug var hasDontEnumQuirk = function() { var o = { // 這裏新的 toString 應該要列舉出來的,由於新的 toString 已經覆蓋了舊的 [[Enumerable]] false 的 toString // 但 IE8 及更低版本的瀏覽器會不將 toString 列舉。 toString: function () {} }; for (var prop in o) { if (prop === 'toString') { return false; } } return true; }(); console.log(hasDontEnumQuirk());
用戶代理檢測:檢測呈現引擎、瀏覽器、平臺、設備和操做系統
由於用戶代理字符串有很長的發展歷史,在此期間瀏覽器供應商試圖在用戶代理字符串中添加一些欺騙信息,讓網站相信本身是另外一種瀏覽器,使得用戶代理檢測比較複雜。但經過navigator.userAgent
(大部分信息在這個字符串中),navigator.platform
和window
的屬性仍是能夠檢測出來呈現引擎、瀏覽器、平臺、設備和操做系統這些信息的。
VUE 使用 DocumentFragment 優化 DOM 節點操做
在全部節點類型中,只有 DocumentFragment 在文檔中沒有對應的標記。 DOM 規定文檔片斷(document fragment)是一種 「輕量級」 的文檔,能夠包含和控制節點,但不會像完整的文檔那樣佔用額外的資源。
HTML5 也添加了輔助管理 DOM 焦點的功能。首先就是 document.activeElement 屬性,這個屬性始終會引用 DOM 中當前得到了焦點的元素。元素得到焦點的方式有頁面加載、用戶輸入(一般是經過按 Tab 鍵)和在代碼中調用 focus() 方法。
HTML5 規定能夠爲元素添加非標準的屬性,但要添加前綴 data-,目的是爲元素提供與渲染無關的信息,或者提供語義信息。這些屬性能夠任意添加、隨便命名,只要以 data - 開頭便可。
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div> <script> //本例中使用的方法僅用於演示 var div = document.getElementById("myDiv"); //取得自定義屬性的值 var appId = div.dataset.appId; var myName = div.dataset.myname; //設置值 div.dataset.appId = 23456; div.dataset.myname = "Michael"; //有沒有"myname"值呢? if (div.dataset.myname){ alert("Hello, " + div.dataset.myname); } </script>
DOM2 級和 3 級的目的在於擴展 DOM API,以知足操做 XML 的全部需求,同時提供更好的錯誤處理及特性檢測能力。從某種意義上講,實現這一目的很大程度意味着對命名空間的支持。「DOM2 級核心」 沒有引入新類型,它只是在 DOM1 級的基礎上經過增長新方法和新屬性來加強了既有類型。「DOM3 級核心」 一樣加強了既有類型,但也引入了一些新類型。
多數狀況下,均可以經過簡單地轉換屬性名的格式來實現轉換。其中一個不能直接轉換的 CSS 屬性就是 float。因爲 float 是 JavaScript 中的保留字,所以不能用做屬性名。「DOM2 級樣式」 規範規定樣式對象上相應的屬性名應該是 cssFloat; Firefox、 Safari、 Opera 和 Chrome 都支持這個屬性,而 IE 支持的則是 styleFloat。
<div id="myDiv">myDiv</div> <script> var myDiv = document.getElementById("myDiv"); //浮動 myDiv.style.cssFloat = "left"; //背景顏色 myDiv.style.backgroundColor = "red"; //改變大小 myDiv.style.width = "100px"; myDiv.style.height = "200px"; //指定邊框 myDiv.style.border = "1px solid black"; </script>
爲了讓開發人員更方便地控制頁面,「DOM2 級遍歷和範圍」 模塊定義了 「範圍」(range)接口。經過範圍能夠選擇文檔中的一個區域,而沒必要考慮節點的界限(選擇在後臺完成,對用戶是不可見的)。在常規的 DOM 操做不能更有效地修改文檔時,使用範圍每每能夠達到目的。
「DOM2 級事件」 規定的事件流包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。
使用 DOM0 級方法指定的事件處理程序被認爲是元素的方法。所以,這時候的事件處理程序是在
元素的做用域中運行;換句話說,程序中的 this 引用當前元素。來看一個例子。
<button id="myBtn">myBtn</button> <script> var btn = document.getElementById("myBtn"); btn.onclick = function(){ alert(this.id); //"myBtn" }; </script>
將事件處理程序屬性的值設置爲 null 能夠刪除經過 DOM0 級方法指定的事件處理程序:
btn.onclick = null;
「DOM2 級事件」 定義了兩個方法,用於處理指定和刪除事件處理程序的操做: addEventListener() 和 removeEventListener()。全部 DOM 節點中都包含這兩個方法,而且它們都接受 3 個參數:要處理的事件名、做爲事件處理程序的函數和一個布爾值。最後這個布爾值參數若是是 true,表示在捕獲階段調用事件處理程序;若是是 false,表示在冒泡階段調用事件處理程序。
<button id="myBtn">myBtn</button> <script> var btn = document.getElementById("myBtn"); btn.addEventListener("click", function(){ alert(this.id); }, false); btn.addEventListener("click", function(){ alert("Hello world!"); }, false); </script>
eg:刪除事件時必須傳入綁定的事件的 「指針」
IE 實現了與 DOM 中相似的兩個方法: attachEvent() 和 detachEvent()。這兩個方法接受相同的兩個參數:事件處理程序名稱與事件處理程序函數。因爲 IE8 及更早版本只支持事件冒泡,因此經過 attachEvent() 添加的事件處理程序都會被添加到冒泡階段。
兼容 DOM 的瀏覽器會將一個 event 對象傳入到事件處理程序中。不管指定事件處理程序時使用什麼方法(DOM0 級或 DOM2 級),都會傳入 event 對象。
<button id="myBtn">myBtn</button> <script> var btn = document.getElementById("myBtn"); btn.onclick = function(event){ alert(event.type); //"click" }; btn.addEventListener("click", function(event){ alert(event.type); //"click" }, false); </script>
iOS 和 Android 設備的實現很是特別,由於這些設備沒有鼠標。在面向 iPhone 和 iPod 中的 Safari 開發時,要記住如下幾點。
在 HTML 中,表單是由
因爲富文本編輯是使用 iframe 而非表單控件實現的,所以從技術上說,富文本編輯器並不屬於表單。換句話說,富文本編輯器中的 HTML 不會被自動提交給服務器,而須要咱們手工來提取並提交 HTML。爲此,一般能夠添加一個隱藏的表單字段,讓它的值等於從 iframe 中提取出的 HTML。具體來講,就是在提交表單以前,從 iframe 中提取出 HTML,並將其插入到隱藏的字段中。
若是你知道未來還要返回某組屬性與變換的組合,能夠調用 save() 方法。調用這個方法後,當時的全部設置都會進入一個棧結構,得以妥善保管。而後能夠對上下文進行其餘修改。等想要回到以前保存的設置時,能夠調用 restore() 方法,在保存設置的棧結構中向前返回一級,恢復以前的狀態。連續調用 save() 能夠把更多設置保存到棧結構中,以後再連續調用 restore() 則能夠一級一級返回。
<canvas id="drawing" width=" 200" height="200">A drawing of something.</canvas> <script> var drawing = document.getElementById("drawing"); //肯定瀏覽器支持<canvas>元素 if (drawing.getContext){ var context = drawing.getContext("2d"); context.fillStyle = "#ff0000"; context.save(); //save1 context.fillStyle = "#00ff00"; context.translate(100, 100); context.save(); //save2 context.fillStyle = "#0000ff"; context.fillRect(0, 0, 100, 200); //從點(100,100)開始繪製藍色矩形 context.restore(); //返回save2 context.fillRect(10, 10, 100, 200); //從點(110,110)開始繪製綠色矩形 context.restore(); //返回save2 context.fillRect(0, 0, 50, 50); //從點(0,0)開始繪製紅色矩形 } </script>
書上的太難理解了!
跨文檔消息傳送(cross-document messaging),有時候簡稱爲 XDM,指的是在來自不一樣域的頁面間傳遞消息。例如, www.wrox.com 域中的頁面與位於一個內嵌框架中的 p2p.wrox.com 域中的頁面通訊。在 XDM 機制出現以前,要穩妥地實現這種通訊須要花不少工夫。 XDM 把這種機制規範化,讓咱們能既穩妥又簡單地實現跨文檔通訊。
XDM 的核心是 postMessage() 方法。在 HTML5 規範中,除了 XDM 部分以外的其餘部分也會提到這個方法名,但都是爲了同一個目的:向另外一個地方傳遞數據。對於 XDM 而言, 「另外一個地方」 指的是包含在當前頁面中的元素,或者由當前頁面彈出的窗口。
拖動某元素時,將依次觸發下列事件:
當某個元素被拖動到一個有效的放置目標上時,下列事件會依次發生:
ECMA-262 第 3 版引入了 try-catch 語句,做爲 JavaScript 中處理異常的一種標準方式。這與 Java 中的 try-catch 語句是徹底相同的。
try{ // 可能會致使錯誤的代碼 } catch(error){ // 在錯誤發生時怎麼處理 }
只要代碼中包含 finally 子句,則不管 try 或 catch 語句塊中包含什麼代碼——甚至 return 語句,都不會阻止 finally 子句的執行。來看下面這個函數。
<script> alert(testFinally());// 0 function testFinally(){ try { return 2; alert('try');// 不執行 } catch (error){ return 1; } finally { return 0; } } </script>
非致命錯誤,能夠根據下列一或多個條件來肯定:
eg:非致命錯誤添加 try-catch 可使非致命錯誤發生後後續代碼繼續執行,後面的模塊繼續加載
致命錯誤,能夠經過如下一或多個條件來肯定:
創建這樣一種 JavaScript 錯誤記錄系統,首先須要在服務器上建立一個頁面(或者一個服務器入口點),用於處理錯誤數據。這個頁面的做用無非就是從查詢字符串中取得數據,而後再將數據寫入錯誤日誌中。這個頁面可能會使用以下所示的函數:
function logError(sev, msg){ var img = new Image(); img.src = "log.php?sev=" + encodeURIComponent(sev) + "&msg=" + encodeURIComponent(msg); }
這個 logError() 函數接收兩個參數:表示嚴重程度的數值或字符串(視所用系統而異)及錯誤消息。其中,使用了 Image 對象來發送請求,這樣作很是靈活,主要表現以下幾方面。
XML 相關
JSON 的語法能夠表示如下三種類型的值。
與 JavaScript 的對象字面量相比, JSON 對象有兩個地方不同。首先,沒有聲明變量(JSON 中沒有變量的概念)。其次,沒有末尾的分號(由於這不是 JavaScript 語句,因此不須要分號)。再說一遍,對象的屬性必須加雙引號,這在 JSON 中是必需的。屬性的值能夠是簡單值,也能夠是複雜類型值。
JSON.stringify() 除了要序列化的 JavaScript 對象外,還能夠接收另外兩個參數,這兩個參數用於指定以不一樣的方式序列化 JavaScript 對象。第一個參數是個過濾器,能夠是一個數組,也能夠是一個函數;第二個參數是一個選項,表示是否在 JSON 字符串中保留縮進。
var xhr = createXHR(); xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); } } }; xhr.open("get", "example.txt", true); xhr.send(null);
CORS(Cross-Origin Resource Sharing,跨源資源共享)是 W3C 的一個工做草案,定義了在必須訪問跨源資源時,瀏覽器與服務器應該如何溝通。 CORS 背後的基本思想,就是使用自定義的 HTTP 頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功,仍是應該失敗。
好比一個簡單的使用 GET 或 POST 發送的請求,它沒有自定義的頭部,而主體內容是 text/plain。在發送該請求時,須要給它附加一個額外的 Origin 頭部,其中包含請求頁面的源信息(協議、域名和端口),以便服務器根據這個頭部信息來決定是否給予響應。下面是 Origin 頭部的一個示例:
Origin: http://www.nczonline.net
若是服務器認爲這個請求能夠接受,就在 Access-Control-Allow-Origin 頭部中回發相同的源信息(若是是公共資源,能夠回發 "*")。例如:
Access-Control-Allow-Origin: http://www.nczonline.net
若是沒有這個頭部,或者有這個頭部但源信息不匹配,瀏覽器就會駁回請求。正常狀況下,瀏覽器會處理請求。注意,請求和響應都不包含 cookie 信息。
var img = new Image(); img.onload = img.onerror = function(){ alert("Done!"); }; img.src = "http://www.example.com/test?name=Nicholas";
function handleResponse(response){ alert("You’ re at IP address " + response.ip + ", which is in " + response.city + ", " + response.region_name); } var script = document.createElement("script"); script.src = "http://freegeoip.net/json/?callback=handleResponse"; document.body.insertBefore(script, document.body.firstChild);
Comet Ajax 是一種從頁面向服務器請求數據的技術,而 Comet 則是一種服務器向頁面推送數據的技術。 Comet 可以讓信息近乎實時地被推送到頁面上,很是適合處理體育比賽的分數和股票報價。
SSE 服務器發送事件(Server-Sent Events),是圍繞只讀 Comet 交互推出的 API 或者模式。 SSE API 用於建立到服務器的單向鏈接,服務器經過這個鏈接能夠發送任意數量的數據。
Web Sockets Web Sockets 的目標是在一個單獨的持久鏈接上提供全雙工、雙向通訊。
面對某個具體的用例,在考慮是使用 SSE 仍是使用 Web Sockets 時,能夠考慮以下幾個因素。
首先,你是否有自由度創建和維護 Web Sockets 服務器?由於 Web Socket 協議不一樣於 HTTP,因此現有服務器不能用於 Web Socket 通訊。 SSE 卻是經過常規 HTTP 通訊,所以現有服務器就能夠知足需求。
第二個要考慮的問題是到底需不須要雙向通訊。若是用例只需讀取服務器數據(如比賽成績),那麼 SSE 比較容易實現。若是用例必須雙向通訊(如聊天室),那麼 Web Sockets 顯然更好。別忘了,在不能選擇 Web Sockets 的狀況下,組合 XHR 和 SSE 也是能實現雙向通訊的。
惰性載入表示函數執行的分支僅會發生一次。有兩種實現惰性載入的方式
第一種就是在函數被調用時再處理函數。在第一次調用的過程當中,該函數會被覆蓋爲另一個按合適方式執行的函數,這樣任何對原函數的調用都不用再通過執行的分支了。
第二種實現惰性載入的方式是在聲明函數時就指定適當的函數。這樣,第一次調用函數時就不會損失性能了,而在代碼首次加載時會損失一點性能。如下就是按照這一思路重寫前面例子的結果
另外一個日益流行的高級技巧叫作函數綁定。函數綁定要建立一個函數,能夠在特定的 this 環境中以指定參數調用另外一個函數。該技巧經常和回調函數與事件處理程序一塊兒使用,以便在將函數做爲變量傳遞的同時保留代碼執行環境。
運行在瀏覽器中的 JavaScript 都被分配了一個肯定數量的資源。不一樣於桌面應用每每可以隨意控制他們要的內存大小和處理器時間, JavaScript 被嚴格限制了,以防止惡意的 Web 程序員把用戶的計算機搞掛了。其中一個限制是長時間運行腳本的制約,若是代碼運行超過特定的時間或者特定語句數量就不讓它繼續執行。若是代碼達到了這個限制,會彈出一個瀏覽器錯誤的對話框,告訴用戶某個腳本會用過長的時間執行,詢問是容許其繼續執行仍是中止它。全部 JavaScript 開發人員的目標就是,確保用戶永遠不會在瀏覽器中看到這個使人費解的對話框。定時器是繞開此限制的方法之一。
瀏覽器中某些計算和處理要比其餘的昂貴不少。例如, DOM 操做比起非 DOM 交互須要更多的內存和 CPU 時間。連續嘗試進行過多的 DOM 相關操做可能會致使瀏覽器掛起,有時候甚至會崩潰。尤爲在 IE 中使用 onresize 事件處理程序的時候容易發生,當調整瀏覽器大小的時候,該事件會連續觸發。在 onresize 事件處理程序內部若是嘗試進行 DOM 操做,其高頻率的更改可能會讓瀏覽器崩潰。爲了繞開這個問題,你可使用定時器對該函數進行節流。
函數節流背後的基本思想是指,某些代碼不能夠在沒有間斷的狀況連續重複執行。第一次調用函數,建立一個定時器,在指定的時間間隔以後運行代碼。當第二次調用該函數時,它會清除前一次的定時器並設置另外一個。若是前一個定時器已經執行過了,這個操做就沒有任何意義。然而,若是前一個定時器還沒有執行,其實就是將其替換爲一個新的定時器。目的是隻有在執行函數的請求中止了一段時間以後才執行。
事件是一種叫作觀察者的設計模式,這是一種建立鬆散耦合代碼的技術。對象能夠發佈事件,用來表示在該對象生命週期中某個有趣的時刻到了。而後其餘對象能夠觀察該對象,等待這些有趣的時刻到來並經過運行代碼來響應。
觀察者模式由兩類對象組成: 主體和觀察者。主體負責發佈事件,同時觀察者經過訂閱這些事件來觀察該主體。該模式的一個關鍵概念是主體並不知道觀察者的任何事情,也就是說它能夠獨自存在並正常運做即便觀察者不存在。從另外一方面來講,觀察者知道主體並能註冊事件的回調函數(事件處理程序)。涉及 DOM 上時, DOM 元素即是主體,你的事件處理代碼即是觀察者。
<script> function EventTarget(){ this.handlers = {}; } EventTarget.prototype = { constructor: EventTarget, addHandler: function(type, handler){ if (typeof this.handlers[type] == "undefined"){ this.handlers[type] = []; } this.handlers[type].push(handler); }, fire: function(event){ if (!event.target){ event.target = this; } if (this.handlers[event.type] instanceof Array){ var handlers = this.handlers[event.type]; for (var i=0, len=handlers.length; i < len; i++){ handlers[i](event); } } }, removeHandler: function(type, handler){ if (this.handlers[type] instanceof Array){ var handlers = this.handlers[type]; for (var i=0, len=handlers.length; i < len; i++){ if (handlers[i] === handler){ break; } } handlers.splice(i, 1); } } }; function handleMessage(event){ alert("Message received: " + event.message); } //建立一個新對象 var target = new EventTarget(); //添加一個事件處理程序 target.addHandler("message", handleMessage); //觸發事件 target.fire({ type: "message", message: "Hello world!"}); //刪除事件處理程序 target.removeHandler("message", handleMessage); //再次,應沒有處理程序 target.fire({ type: "message", message: "Hello world!"}); </script>
sessionStorage 對象 sessionStorage 對象存儲特定於某個會話的數據,也就是該數據只保持到瀏覽器關閉。這個對象就像會話 cookie,也會在瀏覽器關閉後消失。存儲在 sessionStorage 中的數據能夠跨越頁面刷新而存在,同時若是瀏覽器支持,瀏覽器崩潰並重啓以後依然可用(Firefox 和 WebKit 都支持, IE 則不行)。由於 seesionStorage 對象綁定於某個服務器會話,因此當文件在本地運行的時候是不可用的。存儲在 sessionStorage 中的數據只能由最初給對象存儲數據的頁面訪問到,因此對多頁面應用有限制。因爲 sessionStorage 對象實際上是 Storage 的一個實例,因此可使用 setItem() 或者直接設置新的屬性來存儲數據。下面是這兩種方法的例子。
globalStorage 對象 Firefox 2 中實現了 globalStorage 對象。做爲最初的 Web Storage 規範的一部分,這個對象的目的是跨越會話存儲數據,但有特定的訪問限制。要使用 globalStorage,首先要指定哪些域能夠訪問該數據。能夠經過方括號標記使用屬性來實現,如如下例子所示。
localStorage 對象 localStorage 對象在修訂過的 HTML 5 規範中做爲持久保存客戶端數據的方案取代了 globalStorage。與 globalStorage 不一樣,不能給 localStorage 指定任何訪問規則;規則事先就設定好了。要訪問同一個 localStorage 對象,頁面必須來自同一個域名(子域名無效),使用同一種協議,在同一個端口上。這至關於 globalStorage[location.host]。
storage 事件 對 Storage 對象進行任何修改,都會在文檔上觸發 storage 事件。當經過屬性或 setItem() 方法保存數據,使用 delete 操做符或 removeItem() 刪除數據,或者調用 clear() 方法時,都會發生該事件。這個事件的 event 對象有如下屬性。
一種最多見的耦合類型是 HTML/JavaScript 耦合。在 Web 上, HTML 和 JavaScript 各自表明瞭解決方案中的不一樣層次: HTML 是數據, JavaScript 是行爲。由於它們天生就須要交互,因此有多種不一樣的方法將這兩個技術關聯起來。可是,有一些方法會將 HTML 和 JavaScript 過於緊密地耦合在一塊兒。
直接寫在 HTML 中的 JavaScript,使用包含內聯代碼的<script > 元素或者是使用 HTML 屬性來分配事件處理程序,都是過於緊密的耦合。請看如下代碼。
<!-- 使用了 <script> 的緊密耦合的 HTML/JavaScript --> <script type="text/javascript"> document.write("Hello world!"); </script> <!-- 使用事件處理程序屬性值的緊密耦合的 HTML/JavaScript --> <input type="button" value="Click Me" onclick="doSomething()" />
通常來講,你應該避免在 JavaScript 中建立大量 HTML。再一次重申要保持層次的分離,這樣能夠很容易的肯定錯誤來源。當使用上面這個例子的時候,有一個頁面佈局的問題,可能和動態建立的 HTML 沒有被正確格式化有關。不過,要定位這個錯誤可能很是困難,由於你可能通常先看頁面的源代碼來查找那段煩人的 HTML,可是卻沒能找到,由於它是動態生成的。對數據或者佈局的更改也會要求更改 JavaScript,這也代表了這兩個層次過於緊密地耦合了。
HTML 呈現應該儘量與 JavaScript 保持分離。當 JavaScript 用於插入數據時,儘可能不要直接插入標記。通常能夠在頁面中直接包含並隱藏標記,而後等到整個頁面渲染好以後,就能夠用 JavaScript 顯示該標記,而非生成它。另外一種方法是進行 Ajax 請求並獲取更多要顯示的 HTML,這個方法可讓一樣的渲染層(PHP、 JSP、 Ruby 等等)來輸出標記,而不是直接嵌在 JavaScript 中。
將 HTML 和 JavaScript 解耦能夠在調試過程當中節省時間,更加容易肯定錯誤的來源,也減輕維護的難度:更改行爲只須要在 JavaScript 文件中進行,而更改標記則只要在渲染文件中。
因爲 CSS 負責頁面的顯示,當顯示出現任何問題時都應該只是查看 CSS 文件來解決。然而,當使用了 JavaScript 來更改某些樣式的時候,好比顏色,就出現了第二個可能已更改和必須檢查的地方。結果是 JavaScript 也在某種程度上負責了頁面的顯示,並與 CSS 緊密耦合了。若是將來須要更改樣式表,CSS 和 JavaScript 文件可能都須要修改。這就給開發人員形成了維護上的噩夢。因此在這兩個層次之間必須有清晰的劃分。
應用和業務邏輯之間鬆散耦合的幾條原則:
一旦你須要訪問的 DOM 部分是已經顯示的頁面的一部分,那麼你就是在進行一個現場更新。之因此叫現場更新,是由於須要當即(現場)對頁面對用戶的顯示進行更新。每個更改,無論是插入單個字符,仍是移除整個片斷,都有一個性能懲罰,由於瀏覽器要從新計算無數尺寸以進行更新。現場更新進行得越多,代碼完成執行所花的時間就越長;完成一個操做所需的現場更新越少,代碼就越快。
有兩種在頁面上建立 DOM 節點的方法:使用諸如 createElement() 和 appendChild() 之類的 DOM 方法,以及使用 innerHTML。對於小的 DOM 更改而言,兩種方法效率都差很少。然而,對於大的 DOM 更改,使用 innerHTML 要比使用標準 DOM 方法建立一樣的 DOM 結構快得多。
大多數 Web 應用在用戶交互上大量用到事件處理程序。頁面上的事件處理程序的數量和頁面響應用戶交互的速度之間有個負相關。爲了減輕這種懲罰,最好使用事件代理。
HTMLCollection 對象的陷阱已經在本書中討論過了,由於它們對於 Web 應用的性能而言是巨大的損害。記住,任什麼時候候要訪問 HTMLCollection,無論它是一個屬性仍是一個方法,都是在文檔上進行一個查詢,這個查詢開銷很昂貴。最小化訪問 HTMLCollection 的次數能夠極大地改進腳本的性能。
JSLint 能夠查找 JavaScript 代碼中的語法錯誤以及常見的編碼錯誤。
當談及 JavaScript 文件壓縮,其實在討論兩個東西:代碼長度和配重(Wire weight)。代碼長度指的是瀏覽器所需解析的字節數,配重指的是實際從服務器傳送到瀏覽器的字節數。在 Web 開發的早期,這兩個數字幾乎是同樣的,由於從服務器端到客戶端原封不動地傳遞了源文件。而在今天的 Web 上,這二者不多相等,實際上也不該相等。
由於 JavaScript 並不是編譯爲字節碼,而是按照源代碼傳送的,代碼文件一般包含瀏覽器執行所不須要的額外的信息和格式。註釋,額外的空白,以及長長的變量名和函數名雖然提升了可讀性,但倒是傳送給瀏覽器時沒必要要的字節。不過,咱們可使用壓縮工具減小文件的大小。
給 httpd.conf 文件或者是. htaccess 文件添加如下代碼啓用對 JavaScript 的自動壓縮:
#告訴 mod_zip 要包含任何以.js 結尾的文件 mod_gzip_item_include file \.js$
或者:
#告訴 mod_deflate 要包含全部的 JavaScript 文件 AddOutputFilterByType DEFLATE application/x-javascript
配重指的是實際從服務器傳送到瀏覽器的字節數。由於如今的服務器和瀏覽器都有壓縮功能,這個字節數不必定和代碼長度同樣。全部的五大 Web 瀏覽器(IE、 Firefox、 Safari、 Chrome 和 Opera)都支持對所接收的資源進行客戶端解壓縮。這樣服務器端就可使用服務器端相關功能來壓縮 JavaScript 文件。一個指定了文件使用了給定格式進行了壓縮的 HTTP 頭包含在了服務器響應中。接着瀏覽器會查看該 HTTP 頭肯定文件是否已被壓縮,而後使用合適的格式進行解壓縮。結果是和原來的代碼量相比在網絡中傳遞的字節數量大大減小了。
不知道用戶是否是正在與頁面交互,這是困擾廣大 Web 開發人員的一個主要問題。若是頁面最小化了或者隱藏在了其餘標籤頁後面,那麼有些功能是能夠停下來的,好比輪詢服務器或者某些動畫效果。而 Page Visibility API(頁面可見性 API)就是爲了讓開發人員知道頁面是否對用戶可見而推出的。
地理定位(geolocation)是最使人興奮,並且獲得了普遍支持的一個新 API。 經過這套 API, JavaScript 代碼可以訪問到用戶的當前位置信息。固然,訪問以前必須獲得用戶的明確許可,即贊成在頁面中共享其位置信息。若是頁面嘗試訪問地理定位信息,瀏覽器就會顯示一個對話框,請求用戶許可共享其位置信息。
不能直接訪問用戶計算機中的文件,一直都是 Web 應用開發中的一大障礙。 2000 年之前,處理文件的惟一方式就是在表單中加入字段,僅此而已。 File API(文件 API)的宗旨是爲 Web 開發人員提供一種安全的方式,以便在客戶端訪問用戶計算機中的文件,並更好地對這些文件執行操做。支持 File API 的瀏覽器有 IE10+、 Firefox 4+、 Safari 5.0.5+、 Opera 11.1 + 和 Chrome。
FileReader 類型實現的是一種異步文件讀取機制。能夠把 FileReader 想象成 XMLHttpRequest,區別只是它讀取的是文件系統,而不是遠程服務器。爲了讀取文件中的數據, FileReader 提供了以下幾個方法。
對象 URL 也被稱爲 blob URL,指的是引用保存在 File 或 Blob 中數據的 URL。使用對象 URL 的好處是能夠沒必要把文件內容讀取到 JavaScript 中而直接使用文件內容。爲此,只要在須要文件內容的地方提供對象 URL 便可。要建立對象 URL,可使用 window.URL.createObjectURL() 方法,並傳入 File 或 Blob 對象。
隨着 Web 應用複雜性的與日俱增,愈來愈複雜的計算在所不免。長時間運行的 JavaScript 進程會致使瀏覽器凍結用戶界面,讓人感受屏幕 「凍結」 了。 Web Workers 規範經過讓 JavaScript 在後臺運行解決了這個問題。瀏覽器實現 Web Workers 規範的方式有不少種,可使用線程、後臺進程或者運行在其餘處理器核心上的進程,等等。具體的實現細節其實沒有那麼重要,重要的是開發人員如今能夠放心地運行 JavaScript,而沒必要擔憂會影響用戶體驗了。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button onclick="alert('clecked')">"click" me</button> </body> </html>
一開始以 Ajax 爲起點設計網站很難作到平穩退化,但一開始用老式的頁面刷新機制設計網站,在此基礎上用攔截請求用 Ajax 技術處理就能夠實現平穩退化。(漸進加強:HTML 所有完成 ->CSS 所有完成 ->JS 所有完成 -> 網站完成)
== 這樣得後端也得作一套頁面展現(點開連接和平穩退化用),前端不該再頁面加載時使 Ajax(應該由後端生成)==
如今主流的 MVVM 框架全是使用 JS 生成 DOM。。
不少大網站都沒見用,FF 裏要 Alt+Shift+c 才能使用下面設置的快捷鍵
<a href="http://www.w3school.com.cn/css/" accesskey="c">CSS</a>
文件名所有使用小寫字母,用短橫線分隔單詞,用 .html 做爲擴展名。混合使用大小寫字
母會增長訪問者輸入正確地址以及找到頁面的難度 文件夾的名稱也應所有用小寫字母。關鍵是保持一致。若是使用小寫字母,訪問者和建立者就
沒必要在大寫字母和小寫字母之間轉換浪費時間了
萬維網的發明者 Tim BernersLee 曾說過一句著名的話:「萬維網的力量在於其普適性。讓包括殘障人士在內的每一個人都能訪問萬維網,是極爲重要的一點。」
small 元素表示的含義是法律聲明等條文細則。默認狀況下,它比其餘的文字顯示得小一些,可是 == 顯示小字號並非使用這個元素的理由 == (之前一直當 small 是小號字體 =_=)
曾經使用 Bootstrap4 的時候沒找到中文翻譯,準備本身翻譯一下翻譯了兩頁有點感受有點多,就往下翻了翻看看到底有多少工做量,而後看見有 Translations,點進去一看發現你們基本都知道但很容易忘的一項翻譯的人給強調出來了:
HTML5 標準的 doctype 頭部定義是首要的,不然會致使樣式失真(中國碼農每每直接抄國外站點將 lang 寫成 en 的小細節也要注意以避免貽笑大方)。
注:之前搜過 lang 相關的裏面的規則很複雜的(網頁頭部的聲明應該是用 lang="zh" 仍是 lang="zh-cn"? - 知乎),但通常國內的頁面如今通常還都是 zh-CN。
Emmet 設置默認生成的 lang 爲 zh-CN: 下面代碼添加到 Emmet 的 Settings-User
{ "snippets": { "variables": { "lang": "zh-CN" } } }
main 元素是 HTML5 新添加的元素。記住,在一個頁面裏僅使用一次。
在 HTML 中,應該將附註欄 (aside) 內容放在 main 的內容以後。出於 SEO 和可訪問性的目的,最好將重要的內容放在前面。能夠經過 CSS 改變它們在瀏覽器中的顯示順序。
WAI-ARIA(Web Accessibility Initiative’s Accessible Rich Internet Applications,無障礙
網頁倡議 – 無障礙的富互聯網應用,也簡稱 ARIA)是一種技術規範,自稱 「有橋樑做用的技術」 。
role="xxx"
title="xxx"
@import 指令會影響頁面的下載速度和呈現速度,在 Internet Explorer 中影響更爲明顯。
@media 規則只有 screen 和 print(或許還應加上 all)瀏覽器支持的很好。
:first-line 的語法爲::first-line。:first-letter 的語法爲::firstletter。注意,它們用兩個冒號代替了單個冒號。這樣修改的目的是將僞元素(有四個,包括::first-line、::first-letter、::before 和::after)與僞類(如: first-child、:link、:hover 等)區分開。
將來,::first-line 和::first-letter 這樣的雙冒號語法是推薦的方式,現代瀏覽器也支持它們。原始的單冒號語法則被廢棄了,但瀏覽器出於向後兼容的目的,仍然支持它們。不過,IE9 以前的 InternetExplorer 版本均不支持雙冒號。所以,你能夠選擇繼續使用單冒號語法,除非你爲 IE8 及如下版本設置了單獨的 CSS。
<meta name="viewport" content="width=device-width, initial-scale=1" />
使用 Respond.js
HTML5 並無提供任何保護媒體內容的方法。所以,若是你很在乎對媒體文件的保護,那麼暫時不要使用 HTML5 原生多媒體。
僞元素:IE6~8 僅支持單冒號,IE9 + 支持僞元素
僞元素爲 DOM 樹沒有定義的虛擬元素。不一樣於其餘選擇器,它不以元素爲最小選擇單元,它選擇的是元素指定內容。
使用僞元素能夠 DOM 的部份內容(並不是整個 DOM)添加樣式
目前:
PS:圖片圓角我測試能夠直接使用,但圖片內陰影直接設置無效須要將圖片設置爲背景,或外面套個 div 實現