摘要:作用域安全的構造函數原因如下如果忘記使用操作符,那么,構造函數里的將指向全局,這樣會在對象上添加額外的屬性,可能覆蓋其他有用的屬性導致錯誤。
高級用法 1. 安全的類型檢測
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]" }
這種方法可以檢測一個對象是否是某種類型的原生對象。前提是 Object.prototype.toString() 方法未被修改
在 Web 中能夠區分原生對象和非原生對象很重要。這樣才能確切知道某個對象到底有哪些功能。
原因如下:
function Person (name, age, job) { this.name = name this.age = age this.job = job } var Person = new Person("wfc", 25, "frontend")
如果忘記使用 new 操作符,那么,構造函數里的this將指向全局window,這樣會在window對象上添加額外的屬性,可能覆蓋其他有用的屬性導致錯誤。
安全的做法:
function Person (name, age, job) { if (this instanceof Person) { this.name = name this.age = age this.job = job } else { return new arguments.callee(name, age, job) } }
這樣處理后,如果僅采用構造函數模式來繼承,可能會出問題
如:
function Person (name, age, job) { this.name = name this.age = age this.job = job } function Student (name, age, job, sex) { Person.call(this, name, age, job) this.sex = sex } var ming = new Student("ming", 12, "student", "male") console.log(ming.name) // undefined
所以采用作用域安全的構造函數,要求采用如下措施
function Person (name, age, job) { this.name = name this.age = age this.job = job } function Student (name, age, job, sex) { Person.call(this, name, age, job) this.sex = sex } Student.prototype = new Person() var gang = new Student("gang", 13, "student", "male") console.log(ming.name) // "gang"3. 惰性載入函數
如:
function createXHR () { if (typeof XMLHttpRequest != undefined) { // 這里不用 == 來判斷,因為不同瀏覽器下結果不一樣,safari 得到的是 "object",其他瀏覽器是"function" 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" ] for (var i = 0, len = versions.length; i < len; i++) { try { new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; break; } catch (ex) {} } } else { return new ActiveXObject(arguments.callee.activeXString) } } else { throw new Error("no XHR object available") } }
注:IE7+ 就原生支持 XMLHttpRequest 對象。
每次調用 createXHR() 的時候,它都要對瀏覽器所支持的能力進行檢查,但其實只需要首次載入時檢查一次就可以確定該瀏覽器支持哪個 XHR 對象。
惰性載入表示函數執行的分支僅會發生一次。
方法1:
function createXHR () { if (typeof XMLHttpRequest != undefined) { createXHR = function () { return new XMLHttpRequest() } } else if (typeof ActiveXObject != undefined) { var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp" ] for (var i = 0, len = versions.length; i < len; i++) { try { new ActiveXObject(versions[i]); createXHR = function () { return new ActiveXObject(versions[i]); } break; } catch (ex) {} } } else { createXHR = function () { throw new Error("No XHR object available") } } return createXHR() }
方法2:
var createXHR = (function () { if (typeof XMLHttpRequest != undefined) { // 這里不用 == 來判斷,因為不同瀏覽器下結果不一樣,safari 得到的是 "object",其他瀏覽器是"function" return function () { return new XMLHttpRequest() } } else if (typeof ActiveXObject != undefined) { var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp" ] for (var i = 0, len = versions.length; i < len; i++) { try { new ActiveXObject(versions[i]); return function () { return new ActiveXObject(versions[i]); } break; } catch (ex) {} } } else { throw new Error("no XHR object available") } }())4. 函數綁定
函數綁定指的是創建一個新函數,可以在特定的this環境中以指定參數調用另一個函數。
如:
function bind (fn, context) { return function () { return fn.apply(context, arguments) } } this.name = "wfc" var obj = { name: "abc", sayName: function () { console.log(this.name) } } obj.sayName() // "abc" var newSayName = bind(obj.sayName, this) newSayName() // "wfc"
es5 為所有函數定義了一個原生的 bind() 方法。
被綁定函數與普通函數相比有更多的分銷,它們需要更多的內存,同時也因為多重函數調用稍微慢一點,所以最好只在必要時使用。
// 支持的瀏覽器有:IE 9+、Chrome、Firefox 4+、Safari 5.1+、Opera 11.6+
bind()方法會創建一個新函數。當這個新函數被調用時,bind()的第一個參數將作為它運行時的 this, 之后的一序列參數將會在傳遞的實參前傳入作為它的參數。
語法: fun.bind(thisArg[, arg1[, arg2[, ...]]])
如:
this.x = 9; var module = { x: 81, getX: function() { return this.x; } }; module.getX(); // 返回 81 var retrieveX = module.getX; retrieveX(); // 返回 9, 在這種情況下,"this"指向全局作用域 // 創建一個新函數,將"this"綁定到module對象 // 新手可能會被全局的x變量和module里的屬性x所迷惑 var boundGetX = retrieveX.bind(module); boundGetX(); // 返回 81
偏函數(Partial Functions)
bind()的另一個最簡單的用法是使一個函數擁有預設的初始參數。
如:
function list() { return Array.prototype.slice.call(arguments); } var list1 = list(1, 2, 3); // [1, 2, 3] // Create a function with a preset leading argument var leadingThirtysevenList = list.bind(undefined, 37); var list2 = leadingThirtysevenList(); // [37] var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
在默認情況下,使用 window.setTimeout() 時,this 關鍵字會指向 window (或全局)對象。
當使用類的方法時,需要 this 引用類的實例,你可能需要顯式地把 this 綁定到回調函數以便繼續使用實例。
function LateBloomer() { this.petalCount = Math.ceil(Math.random() * 12) + 1; } // Declare bloom after a delay of 1 second LateBloomer.prototype.bloom = function() { window.setTimeout(this.declare.bind(this), 1000); }; LateBloomer.prototype.declare = function() { console.log("I am a beautiful flower with " + this.petalCount + " petals!"); }; var flower = new LateBloomer(); flower.bloom(); // 一秒鐘后, 調用"declare"方法6. 函數柯里化 (function currying)
函數柯里化 (function currying),用于創建已經設置好了一個或者多個參數的函數。
如:
function curry(fn) { var args = Array.prototype.slice.call(arguments, 1) return function () { var newArgs = args.concat(Array.prototype.slice.call(arguments)) fn.apply(undefined, newArgs) } }
或者
function curry(fn, context) { var args = Array.prototype.slice.call(arguments, 2) return function () { var newArgs = args.concat(Array.prototype.slice.call(arguments)) fn.apply(context, newArgs) } }
es5 的 bind() 方法可以實現函數柯里化。具體見上一條。
需要注意的是 bind() curry() 不應濫用,因為每個函數都會帶來額外的開銷。
默認情況下,所有對象都是可以擴展的。
Object.preventExtensions(obj) 可以阻止給對象添加新的屬性和方法。但是已有的屬性和方法不受任何影響,可以被修改和刪除。
Object.isExtensible() 可以確定對象是否可以擴展。
如:
var obj = { name: "wfc" } Object.isExtensible(obj) // true Object.preventExtensions(obj) obj.age = 18 obj.age // undefined Object.isExtensible(obj) // false obj.name = "ming" obj.name // "ming" delete obj.name // true obj.name // undefined8. 密封的對象。
es5 為對象定義的第二個保護級別是密封對象。
密封對象不可擴展,而且所有成員的 [[Configurable]] 特性被設置成false,這意味著不能刪除對象,但是屬性值是可以修改的。
密封對象的方法是 Object.seal() 檢測對象是否被密封的方法是 Object.isSealed() 所有密封對象用 Object.isExtensible() 檢測都是false。
如:
var person = { name: "gang" } Object.isSealed(person) // false Object.seal(person) Object.isSealed(person) // true Object.isExtensible(person) // false person.name = "wang" person.name // "wang" delete person.name // false person.name // "wang"
但是可以賦值成 null 或者 undefined
如:
person.name = null person.name // null person.name = undefined person.name // undefined9. 凍結的對象
最嚴格的防篡改級別是凍結。凍結的對象既不可擴展,又是密封的,而且對象數據屬性 [[Writable]] 特性會被設置成false。
凍結對象的方法是 Object.freeze()
檢測對象是否凍結的方法是 Object.isFrozen()
如:
var person = { name: "wang" } Object.freeze(person) Object.isFrozen(person) // true delete person.name // false person.name // "wang" person.name = "gang" person.name // "wang"
對 JS 庫的作者而言,凍結對象是很有用的。因為 JS 庫最怕有人意外(或有意)的修改核心對象。
10. 定時器對于 setTimeout() 實際執行時間大于等于設定時間。 對于 setInterval() 兩次執行的實際間隔時間小于等于設定時間。11. 函數循環分割
對于這樣的循環
for (var i = 0, len = arr.length; i < len; i++) { process(arr[i]) }
如果完成 process() 的時間較長,那么可以做如下分割。
function chunk (arr, process, context) { setTimeout(function () { var item = arr.shift() process.call(context, item) if (arr.length > 0) { setTimeout(arguments.callee, 100) } }, 100) }
一旦某個函數需要花 50ms 以上來處理,就應該看看能否分割開來處理了。
12. 函數節流。瀏覽器中某些計算和處理要比其他昂貴很多,比如,DOM 操作比非 DOM 交互需要更多的內存和CPU時間。
函數節流背后的基本思想是:某些代碼不可以在沒有間斷的情況下連續重復執行。
比如連續重復執行的 resize 事件。
以下是該模式的基本形式:
var processor = { timeoutId: null, performProcessor: function () { handleProcess() }, process: function () { clearTimeout(this.timeoutId) var that = this this.timeoutId = setTimeout(function () { that.performProcessor() }, 100) } }
或者使用函數
function throttle (method, context) { clearTimeout(method.tid) method.tid = setTimeout(function () { method.call(context) }, 100) }
只要代碼是周期性執行的,都應該使用節流,但是你不能控制請求執行的速率。這里 throttle() 函數用 100 ms做間隔,可以根據需求來更改。
13. 自定義事件定義:
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) { handlers.splice(i, 1) break } } } } }
使用:
function handleMessage (event) { console.log(event.message) } var target = new EventTarget() target.addHandler("message", handleMessage) target.fire({ type: "message", message: "Hello" }) // "hello" target.removeHandler("message", handleMessage) target.fire({ type: "message", message: "Hello" }) // "undefined"
另外一種用法:
function Person (name, age) { EventTarget.call(this) this.name = name this.age = age } Person.prototype = Object.create(EventTarget.prototype) Person.prototype.say = function (message) { this.fire({ type: "message", message: message }) } var li = new Person("li", 18) li.addHandler("message", handleMessage) li.say("hahaha") // "hahaha"
當代碼中存在多個部分在特定時刻相互交互的情況下,自定義事件就非常有用了。
使用自定義事件有助于結藕相關對象,保持功能的隔絕。
關于《JavaScript高級程序設計(第三版)》這本書,我就選擇性的看了一部分章節,所以,目前的摘要筆記總結已全部結束。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81201.html
摘要:說明此摘要筆記系列是我最近看高級程序設計第版隨手所記。摘要筆記本身沒有系統性,沒有全面性可言,寫在這里供有一定基礎的前端開發者參考交流。對每一項運行給定函數,返回該函數會返回的項組成的數組。是的反操作是的反操作第一部分結束。 說明: 此摘要筆記系列是我最近看《JavaScript高級程序設計(第3版)》隨手所記。 里面分條列舉了一些我認為重要的、需要記下的、對我有幫助的點,是按照我看...
摘要:思路是,使用原型鏈對原型屬性和方法進行繼承,借用構造函數實現對實例屬性的繼承。注意使用寄生式繼承來為對象添加函數,會由于不能做到函數復用而降低效率,這一點與構造函數模式類似。無論什么情況下都會調用兩次超類型的構造函數。 說明: 此摘要筆記系列是我最近看《JavaScript高級程序設計(第3版)》隨手所記。里面分條列舉了一些我認為重要的、需要記下的、對我有幫助的點,是按照我看的順序來的...
摘要:如果重設構造函數的原型對象,那么,會切斷新的原型對象和任何之前已經存在的構造函數實例之間的聯系,它們引用的仍然是最初的原型。說明返回的對象與構造函數或者與構造函數的原型屬性沒有關系。 說明: 此摘要筆記系列是我最近看《JavaScript高級程序設計(第3版)》隨手所記。里面分條列舉了一些我認為重要的、需要記下的、對我有幫助的點,是按照我看的順序來的。摘要筆記本身沒有系統性,沒有全面性...
摘要:說明此摘要筆記系列是我最近看高級程序設計第版隨手所記。其中,描述符對象的屬性必須是設置其中一個或多個值,可以修改對應的特性值。如支持的瀏覽器,可以取得指定屬性的描述符。 說明: 此摘要筆記系列是我最近看《JavaScript高級程序設計(第3版)》隨手所記。里面分條列舉了一些我認為重要的、需要記下的、對我有幫助的點,是按照我看的順序來的。摘要筆記本身沒有系統性,沒有全面性可言,寫在這里...
摘要:函數表達式和閉包函數聲明的一個重要特征是函數聲明提升如遞歸遞歸函數是在一個函數通過名字調用自身的情況下構成的。注意中已經是塊級作用域了,所以這些東西感覺實際用途沒有那么大,但是對理解閉包對作用域鏈中的屬性的引用,這一點還是有作用的。 函數表達式和閉包 1. 函數聲明的一個重要特征是函數聲明提升 如: sayHi() function sayHi () { console.log(h...
閱讀 1405·2021-11-25 09:43
閱讀 2261·2021-09-27 13:36
閱讀 1114·2021-09-04 16:40
閱讀 1957·2019-08-30 11:12
閱讀 3309·2019-08-29 14:14
閱讀 567·2019-08-28 17:56
閱讀 1320·2019-08-26 13:50
閱讀 1246·2019-08-26 13:29