async:加載外部腳本文件,通知瀏覽器當即下載,異步執行javascript
defer:腳本能夠延遲到文檔徹底被解析和顯示以後在執行java
noscript:
瀏覽器不支持腳本。
瀏覽器支持腳本,可是腳本被禁用node
複製變量值編程
function setName(obj) { obj.name = "Nicholas"; obj = new Object(); obj.name = "Greg"; } var person = new Object(); setName(person); alert(person.name); //"Nicholas"
在函數重寫obj時,這個變量引用就是一個局部對象。而這個對象會在函數執行完畢後當即被銷燬。數組
檢測類型瀏覽器
使用typeof檢測基本數據類型,可是在檢測引用類型的值是,這個操做符的用處不大,由於使用typeof沒法知道它是什麼類型的對象。爲此,ECMAScript提供了 instanceof操做符。安全
var s = 'test'; var b = true; var i = 22; var u; var n = null; var o = new Object() console.log(typeof s); // string console.log(typeof b); // boolean console.log(typeof i); // number console.log(typeof u); // undefined console.log(typeof n); // object console.log(typeof o); // object
延長做用域鏈閉包
try-catch語句中的catch塊
with語句app
垃圾回收框架
標記清除
引用計數
檢測數組
value instanceof Array Array.isArray(value)
棧方法(後進先出)
隊列方法(先進先出)
重排序方法
操做方法
var colors = ['red', 'green', 'blue', 'yellow', 'purple']; var colors2 = colors.slice(1) var colors3 = colors.slice(4) console.log(colors2); // ["green", "blue", "yellow", "purple"] console.log(colors3); // ["purple"]
var colors = ["red", "green", "blue"]; var removed = colors.splice(0,1); // 刪除第一項 alert(colors); // green,blue alert(removed); // red,返回的數組中只包含一項 removed = colors.splice(1, 0, "yellow", "orange"); // 從位置1 開始插入兩項 alert(colors); // green,yellow,orange,blue alert(removed); // 返回的是一個空數組 removed = colors.splice(1, 1, "red", "purple"); // 插入兩項,刪除一項 alert(colors); // green,red,purple,orange,blue alert(removed); // yellow,返回的數組中只包含一項
位置方法
迭代方法
縮小方法
var values = [1, 2, 3, 4, 5]; var sum = values.reduce((prev, cur, index, array) => { return prev + cur; }); console.log(sum); // 15
函數內部屬性
屬性和方法
每一個函數都包含兩個屬性
每一個函數都包含兩個非繼承而來的方法
function sum (num1, num2) { return num1 + num2; } function callSum1 (num1, num2) { return sum.apply(this, [num1, num2]); } function callSum2 (num1, num2) { return sum.call(this, num1, num2); } callSum1(10, 10); // 20 callSum2(10, 10); // 20 var callSum3 = sum.bind(null) callSum3(10, 10) // 20
Global對象
encodeURI()編碼後的結果是除了空格以外的其餘字符都原封不動,只有空格被替換成了%20,對應decodeURI()方法
屬性類型
ECMAScript中有兩種屬性:數據屬性和訪問器屬性
數據屬性
要修改屬性默認的特性,必須使用Object.defineProperty()方法。這個方法接收三個參數:屬性所在對象、屬性的名字和一個描述符對象。其中,描述符對象的屬性必須是:configurabel、enumerable、writable、和value。設置其中的一個或多個值。
var person = {} Object.defineProperty(person, 'name', { writable: false, configurable: false, value: 'Nicholas' }); console.log(person.name); // Nicholas person.name = 'Greg'; console.log(person.name); // Nicholas delete person.name console.log(person.name); // Nicholas
Object.defineProperty()方法會對configurable爲false的屬性修改作限制
訪問器屬性
訪問器屬性不能直接定義,必須使用Object.defineProperty()來定義
var book = { _year: 2004, edition: 1 }; Object.defineProperty(book, 'year', { get: function() { return this._year }, set: function(newValue) { if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004 } } }); book.year = 2005; console.log(book.edition); // 2
定義多個屬性
ECMAScript5定義了一個Object.defineProperties()方法。利用這個方法能夠經過描述符一次定義多個屬性。這個方法接收兩個對象參數。
var book = {}; Object.defineProperties(book, { _year: { value: 2004 }, edition: { value: 1 }, year: { get: function() { return this._year }, set: function(newValue) { if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004 } } } });
讀取屬性的特性
使用Object.getOwnPropertyDescriptor()方法,能夠取得給定屬性的描述符
var descriptor = Object.getOwnPropertyDescriptor(book, '_year') console.log(descriptor.value); // 2004 console.log(descriptor.configurable); // false
工廠模式
function createPerson (name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function () { console.log(this.name); }; return o; } var person1 = createPerson('Nicholas', 29, 'Software Engineer'); var person2 = createPerson('Greg', 27, 'Doctor');
構造函數模式
function Person (name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function () { console.log(this.name); }; } var person1 = new Person('Nicholas', 29, 'Software Engineer'); var person2 = new Person('Greg', 27, 'Doctor');
原型模式
理解原型對象
function Person () {} Person.prototype.name = 'Nicholas'; Person.prototype.age = 29; Person.prototype.job = 'Software Engineer'; Person.prototype.sayName = function () { console.log(this.name); }; var person1 = new Person(); var person2 = new Person();
在默認狀況下,全部原型對象都會自動得到一個constructor(構造函數)屬性,這個屬性包含一個指向一個prototype屬性所在函數的指針。例如,Person.prototype.constructor指向Person
咱們能夠經過isPrototypeof()方法來肯定對象之間是否存在原型關係。從本質上講,若是[[Prototype]]指向調用isPrototypeof()方法的對象(Person.prototye),那麼這個方法就返回true。
console.log(Person.prototype.isPrototypeOf(person1)); // true console.log(Person.prototype.isPrototypeOf(person2)); // true
ECMAScript5增長了一個新方法,叫Object.getPrototypeOf(),在全部支持的實現中,這個方法返回[[Prototype]]的值。例如:
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
雖然能夠經過對象實例訪問保存在原型中的值,但卻不能經過對象實例重寫原型中的值。若是咱們在實例中添加了一個屬性,而該屬性與實例原型中的一個屬性同名,那咱們就在實例中建立該屬性,該屬性將會屏蔽原型中的那個屬性。
function Person() {} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function() { alert(this.name); }; var person1 = new Person(); person1.name = "Greg"; console.log(person1.name); //"Greg" — 來自實例 delete person1.name; console.log(person1.name); //"Nicholas" — 來自原型
經過delete操做符刪除實例的屬性,就恢復了對原型中name屬性的鏈接。所以接下來再調用person1.name是,就返回了原型中name屬性的值了。
Object.hasOwnProperty()方法能夠檢測一個屬性是否存在於實例中,仍是存在於原型中。
原型與in操做符
function hasPrototypeProperty (object, name) { if (name in object) { return object.hasOwnProperty(name) // true:屬性在實例中,false:屬性在對象中 } else { console.log('沒有該屬性'); } }
function Person () {} Person.prototype.name = 'Nicholas'; Person.prototype.age = 29; Person.prototype.job = 'Software Engineer'; Person.prototype.sayName = function () { console.log(this.name); }; Object.keys(Person.prototype); // ["name", "age", "job", "sayName"] var person1 = new Person(); person1.name = 'Rob'; person1.age = 31; Object.keys(person1); // ["name", "age"]
Object.getOwnPropertyNames(Person.prototype); // ["constructor", "name", "age", "job", "sayName"]
更簡單的原型方法
function Person () {} Person.prototype = { name: 'Nicholas', age: 29, job: 'Software Engineer', sayName: function () { console.log(this.name); } }
咱們將Person.prototype設置爲一個新的對象,本質上是徹底重寫了默認的prototype對象。可是這樣有一個例外,constructor屬性再也不指向Person了,而是指向Object。因此咱們須要將他的constructor屬性設置成Person
function Person () {} Person.prototype = { constructor: Person, name: 'Nicholas', age: 29, job: 'Software Engineer', sayName: function () { console.log(this.name); } }
可是這種方式重設constructor屬性會致使它的[[Enumerable]]的特性被設置爲true,默認狀況下,原生的constructor屬性是不可枚舉的。
function Person () {} Person.prototype = { name: 'Nicholas', age: 29, job: 'Software Engineer', sayName: function () { console.log(this.name); } } Object.defineProperty(Person.prototype, 'constructor', { enumerable: false, value: Person });
原型的動態性
重寫整個原型對象會切斷構造函數與最初原型之間的聯繫。記住:實例中的指針僅指向原型,而不指向構造函數
function Person () {} var friend = new Person(); Person.prototype = { constructor: Person, name: 'Nicholas', age: 29, job: 'Software Engineer', sayName: function () { console.log(this.name); } } friend.sayName(); // error
原生對象的原型
原型模式的重要性不只體如今建立自定義類型方面,就連全部的原生的引用類型,都是採用這種模式建立的。全部原生引用類型(Object、Array、String,等等)都在其構造函數的原型上定義了方法。
原型對象的問題
原型模式的全部實例在默認狀況下都將取得相同的屬性值,最大的問題是其共享的本性所致使的。
function Person () {} Person.prototype = { constructor: Person, name: 'Nicholas', age: 29, job: 'Software Engineer', friends: ['Shelby', 'Court'], sayName: function () { console.log(this.name); } } var person1 = new Person(); var person2 = new Person(); person1.friends.push('Van'); console.log(person1.friends); // ["Shelby", "Court", "Van"] console.log(person2.friends); // ["Shelby", "Court", "Van"]
組合使用構造函數和原型模式
建立自定義類型的最多見的方式,構造函數模式用於定義實例屬性,而原型模式用於定義方法和共享的屬性。
每一個實例都會有本身的一份實例屬性的副本,但同事又共享着對方法的引用,最大的節省了內存
function Person (name, age, job) { this.name = name; this.age = age; this.job = job; this.friends = ['Shelby', 'Court']; } Person.prototype = { constructor: Person, sayName: function () { console.log(this.name); }; } var person1 = new Person('Nicholas', 29, 'Software Engineer'); var person2 = new Person('Greg', 27, 'Doctor'); person1.friends.push('Van'); console.log(person1.friends); // ["Shelby", "Court", "Van"] console.log(person2.friends); // ["Shelby", "Court"]
動態原型模式
經過在構造函數中初始化原型(僅在必要的狀況下),又保持同時使用構造函數和原型模式的優勢。換句話說,能夠經過檢查某個應該存在的方法是否有效,來決定是否須要初始化原型。
function Person (name, age, job) { this.name = name; this.age = age; this.job = job; this.friends = ['Shelby', 'Court']; if (typeof this.sayName === 'function') { Person.prototype.sayName = function () { console.log(this.name); }; } }
原型鏈
構造函數、原型和實例的關係:每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。
function SuperType () { this.property = true; } SuperType.prototype.getSuperValue = function () { return this.property }; function SubType () { this.subproperty = false } // 繼承了SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subproperty; }; var instance = new SubType(); console.log(instance.getSuperValue()); // true
別忘記默認原型
所用引用類型默認都繼承Object,而這個繼承也是經過原型鏈實現的。全部函數的默認原型都是Object的實例,所以默認原型都會包含一個內部指針,指向Object.prototype。
肯定原型與實例的關係
instanceof操做符
console.log(instance instanceof Object); // true console.log(instance instanceof SuperType); // true console.log(instance instanceof SubType); // true
isPrototypeOf()方法,只要是原型鏈中出現過的原型,均可以說是該原型鏈所派生的實例的原型
console.log(Object.isPrototypeOf(instance)); // true console.log(SuperType.isPrototypeOf(instance)); // true console.log(SubType.isPrototypeOf(instance)); // true
謹慎的定義方法
子類型有時須要重寫超類型中的某個方法,或者須要添加超類型中不存在的某個方法。但無論怎樣,給原型添加方法的代碼必定要放在替換原型語句以後。
原型鏈的問題
借用構造函數
在解決原型中包含引用類型值所帶來問題的過程當中,開始使用借用構造函數的技術。即在子類型構造函數的內部調用超類型構造函數
function SuperType () { this.colors = ['red', 'blue', 'green']; } function SubType () { // 繼承了SuperType SuperType.call(this); // SuperType.apply(this); } var instance1 = new SubType(); instance1.colors.push('black'); console.log(instance1.colors); // ["red", "blue", "green", "black"] var instance2 = new SubType(); console.log(instance2.colors); // ["red", "blue", "green"]
傳遞參數
相對於原型鏈而言,借用構造函數有一個很大的優點,既能夠在子類型構造函數中向超類型構造函數傳遞參數
function SuperType (name) { this.name = name; } function SubType () { // 繼承了SuperType SuperType.call(this, 'Nicholas'); this.age = 29 } var instance = new SubType(); console.log(instance.name); // 'Nicholas' console.log(instance.age); // 29
借用構造函數的問題
方法都在構造函數中定義,由於函數複用就無從談起
組合繼承
既能經過在原型上定義方法實現了函數複用,又能保證每一個實例都有它本身的屬性
function SuperType (name) { this.name = name; this.colors = ['red', 'blue', 'green']; } SuperType.prototype.sayName = function () { console.log(this.name); }; function SubType (name, age) { // 繼承了SuperType SuperType.call(this, name); this.age = age } // 繼承了SuperType SubType.prototype = new SuperType(); SubType.prototype.sayAge = function () { console.log(this.age); }; var instance1 = new SubType('Nicholas', 29); instance1.colors.push('black'); console.log(instance1.colors); // ["red", "blue", "green", "black"] instance1.sayName(); // 'Nicholas' instance1.sayAge(); // 29 var instance2 = new SubType('Greg', 27); console.log(instance2.colors); // ["red", "blue", "green"] instance2.sayName(); // 'Greg' instance2.sayAge(); // 27
定義函數的方式有兩種:一種是函數聲明,另外一種是函數表達式。
函數聲明的特徵是函數聲明提高,意思是在執行代碼以前會先讀取函數聲明。
函數做用域鏈
當某個函數第一次被調用時,會建立一個執行環境及相應的做用域鏈,並把做用域鏈賦值給一個特殊的內部屬性(即[[Scope]])。而後,使用this.arguments和其餘命名參數的值來初始化函數的活動對象。但在做用域鏈中,外部函數的活動對象始終處於第二位,外部函數的外部函數的活動對象始終處於第三位,......直至做爲做用域鏈終點的全局執行環境。
閉包與變量
// i 最終爲10 function createFunctions () { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function () { return i } } return result; } // i 爲 0,1,2...9 function createFunctions () { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function (num) { return function (arguments) { return num; }; }(i) } return result; }
關於this對象
this對象是在運行中基於函數的執行環境綁定的:在全局函數中,this等於window,而當函數被做爲某個對象的方法調用時,this等於那個對象。不過匿名函數的執行環境具備全局性,所以其this對象一般指向window。固然,再經過call()和apply()改變執行函數執行環境的狀況下,this就會指向其餘對象
var name = 'The Window'; var object = { name: 'My Object', getNameFunc: function () { return function () { return this.name } } } console.log(object.getNameFunc()()); // 'The Window'
模仿塊級做用域
匿名函數能夠用來模仿塊級做用域並避免這個問題。用塊級做用域(一般稱爲私有做用域)的匿名函數的語法以下所示。
(function(){ })()
私有變量
function Person(name) { this.getName = function() { retirm name; } this.setName = function(value) { name = value } } var person = new Person('Nicholas'); console.log(person.getName()); // 'Nicholas' person.setName('Greg'); console.log(person.getName()); // 'Greg'
以上代碼的構造函數中定義了兩個特權方法:getName()和setName()。這兩個方法均可以在構造函數外部使用,並且都有權訪問私有變量name。但在Person構造函數外部,沒有任何方法訪問name。因爲這兩個方法是在構造函數內部定義的,它們做爲閉包可以經過做用域鏈訪問name。
靜態私有變量
全局做用域
拋開全局變量會成爲window對象的屬性不談,定義全局變量與在window對象上直接定義屬相仍是有一點差異:全局變量不能經過delete屬性操做符刪除,而直接在window對象上的定義的屬性能夠。
var age = 29; window.color = 'red'; delete window.age; // 不會報錯 delete window.color // 不會報錯 返回true var newValue = oldValue; // 會拋出錯誤,由於oldValue未定義 var newValue = window.oldValue; // 不會報錯,由於這是一次屬性查詢
窗口關係及框架
瞭解frameset和frame
窗口位置
下列代碼能夠跨瀏覽器取得窗口左邊和上邊的位置
Opera支持screenX,screenY。其餘瀏覽器支持screenLeft,screenTop
var leftPops = (typeof window.screenLeft === 'number') ? window.screenLeft : window.screenX; var topPops = (typeof window.screenTop === 'number') ? window.screenLeft : window.screenY;
locatoin對象的屬性
navigator對象
識別瀏覽器的信息
DOM是針對HTML和XML文檔的一個API。DOM描繪了一個層次的節點樹。
NODE類型
每一個節點都有一個nodeType屬性,用於代表節點的類型。
if (someNode.nodeType == 1) { console.log('Node is an element'); }
nodeName和nodeValue屬性
nodeName返回節點的標籤名,如p,div,span等
nodeValue的值始終是null
節點關係
操做節點
事件流
事件冒泡
IE的事件流叫作事件冒泡,即事件開始由最具體的元素接收,而後逐級向上傳播到較爲不具體的節點
事件捕獲
Netscape的事件流叫事件捕獲,即不太具體的節點應該更早接收事件,而最具體的節點應該最後接收事件
DOM事件流
包括三個階段:事件捕獲階段。處於目標階段和事件冒泡階段
事件處理程序
DOM2級時間處理程序
addEventListener
removeEventListener
定義了兩個方法用於處理指定和刪除事件處理程序的操做。全部的DOM節點中都包含這兩個方法,接受三個參數:事件名、事件處理程序和布爾值。最後這個布爾值若是是true,表示在捕獲階段調用事件處理程序;false表示在冒泡階段調用事件處理程序,默認是false。
經過addEventListener()添加的事件處理程序只能使用removeEventListener()來移除。若是經過addEventListener()添加的匿名函數將沒法移除。傳入的函數要相同,不然沒法移除
attachEvent
detachEvent
這兩個方法接受兩個參數:事件名(帶on)和事件處理函數。
var btn = document.getElementById('myBtn');
var handler = function(){ console.log('clicked') };
btn.attachEvent('onclick', handler);
btn.detachEvent('onclick', handler); // 有效
事件對象
DOM的事件對象
事件類型
UI事件
load:當頁面徹底加載後在window上面觸發,img圖片加載完
unload:當頁面徹底卸載
abort:當用戶中止下載過程
error:當發生JavaScript錯誤時在window觸發
select:當用戶選擇文本框中的一個或者多個觸發
resize:當窗口大小變化是觸發
scroll:用戶滾動時觸發
內存和性能
事件委託利用了時間冒泡,指定一個事件處理程序,就能夠管理某一個類型的全部事件
HTML5腳本編程
跨文檔消息傳遞
核心方法是postMessage()方法,接受兩個參數:一條消息和一個表示消息接收方來自哪一個域的字符串。
// 注意:全部支持XDM的瀏覽器也支持iframe的contentWindow
屬性
var iframeWindow = document.getElementById('myframe').contentWindow; iframeWindow.postMessage('A secret', 'http://www.wrox.com');
高級技巧
高級函數
安全的類型檢測
function isArray (value) { return Object.prototype.toString.call(value) === '[object Array]'; } function isFunction (value) { return Object.prototype.toString.call(value) === '[object Function]'; } function isRegExp (value) { return Object.prototype.toString.call(value) === '[object RegExp]'; }
做用域安全的構造函數
防止this指向window對象
function Person (name, age, job) { if (this instanceof Person) { this.name = name; this.age = age; this.job = job; } else { return new Person(name, age, jon); } } 惰性載入函數 function createXHR(){ if (typeof XMLHttpRequest != "undefined"){ return new XMLHttpRequest(); } else if (typeof ActiveXObject != "undefined"){ if (typeof arguments.callee.activeXString != "string"){ var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"], i,len; for (i=0,len=versions.length; i < len; i++){ try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch (ex){ //跳過 } } } return new ActiveXObject(arguments.callee.activeXString); } else { throw new Error("No XHR object available."); } }
第一種實現方法: function createXHR () { if (typeof XMLHttpRequest != 'undefined') { createXHR = function () { return new XMLHttpRequest(); }; } else if (typeof ActiveXObjext != 'undefined') { createXHR = function () { if (typeof arguments.callee.activeXString != 'string') { var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"], i,len; for (i = 0, len = versions.length; i < len; i++) { try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch (e) { // skip } } } return new ActiveXObject(arguments.callee.activeXString); }; } else { createXHR = function () { throw new Error('No XHR object available.'); } } return createXHR(); } 第二種改法: var createXHR = (function () { if (typeof XMLHttpRequest != 'undefined') { return function () { return new XMLHttpRequest(); }; } else if (typeof ActiveXObjext != 'undefined') { return function () { if (typeof arguments.callee.activeXString != 'string') { var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"], i,len; for (i = 0, len = versions.length; i < len; i++) { try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch (e) { // skip } } } return new ActiveXObject(arguments.callee.activeXString); }; } else { return function () { throw new Error('No XHR object available.'); } } })();
函數綁定
bind函數: function bind (fn, context) { return function () { fn.call(context, arguments) } }
函數柯里化 function curry (fn) { var args = Array.prototype.slice.call(arguments, 1); return function () { var innerArgs = Array.prototype.slice.call(arguments) var finalArgs = args.concat(innerArgs) return fn.apply(null, finalArgs); } } function bind (fn, context) { var args = Array.prototype.slice.call(arguments, 2); return function () { var innerArgs = Array.prototype.slice.call(arguments) var finalArgs = args.concat(innerArgs) return fn.apply(context, finalArgs); } }