摘要:作用域安全的構(gòu)造函數(shù)原因如下如果忘記使用操作符,那么,構(gòu)造函數(shù)里的將指向全局,這樣會(huì)在對(duì)象上添加額外的屬性,可能覆蓋其他有用的屬性導(dǎo)致錯(cuò)誤。
高級(jí)用法 1. 安全的類型檢測(cè)
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]" }
這種方法可以檢測(cè)一個(gè)對(duì)象是否是某種類型的原生對(duì)象。前提是 Object.prototype.toString() 方法未被修改
在 Web 中能夠區(qū)分原生對(duì)象和非原生對(duì)象很重要。這樣才能確切知道某個(gè)對(duì)象到底有哪些功能。
原因如下:
function Person (name, age, job) { this.name = name this.age = age this.job = job } var Person = new Person("wfc", 25, "frontend")
如果忘記使用 new 操作符,那么,構(gòu)造函數(shù)里的this將指向全局window,這樣會(huì)在window對(duì)象上添加額外的屬性,可能覆蓋其他有用的屬性導(dǎo)致錯(cuò)誤。
安全的做法:
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) } }
這樣處理后,如果僅采用構(gòu)造函數(shù)模式來繼承,可能會(huì)出問題
如:
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
所以采用作用域安全的構(gòu)造函數(shù),要求采用如下措施
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. 惰性載入函數(shù)
如:
function createXHR () { if (typeof XMLHttpRequest != undefined) { // 這里不用 == 來判斷,因?yàn)椴煌瑸g覽器下結(jié)果不一樣,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 對(duì)象。
每次調(diào)用 createXHR() 的時(shí)候,它都要對(duì)瀏覽器所支持的能力進(jìn)行檢查,但其實(shí)只需要首次載入時(shí)檢查一次就可以確定該瀏覽器支持哪個(gè) XHR 對(duì)象。
惰性載入表示函數(shù)執(zhí)行的分支僅會(huì)發(fā)生一次。
方法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) { // 這里不用 == 來判斷,因?yàn)椴煌瑸g覽器下結(jié)果不一樣,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. 函數(shù)綁定
函數(shù)綁定指的是創(chuàng)建一個(gè)新函數(shù),可以在特定的this環(huán)境中以指定參數(shù)調(diào)用另一個(gè)函數(shù)。
如:
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 為所有函數(shù)定義了一個(gè)原生的 bind() 方法。
被綁定函數(shù)與普通函數(shù)相比有更多的分銷,它們需要更多的內(nèi)存,同時(shí)也因?yàn)槎嘀睾瘮?shù)調(diào)用稍微慢一點(diǎn),所以最好只在必要時(shí)使用。
// 支持的瀏覽器有:IE 9+、Chrome、Firefox 4+、Safari 5.1+、Opera 11.6+
bind()方法會(huì)創(chuàng)建一個(gè)新函數(shù)。當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí),bind()的第一個(gè)參數(shù)將作為它運(yùn)行時(shí)的 this, 之后的一序列參數(shù)將會(huì)在傳遞的實(shí)參前傳入作為它的參數(shù)。
語法: 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"指向全局作用域 // 創(chuàng)建一個(gè)新函數(shù),將"this"綁定到module對(duì)象 // 新手可能會(huì)被全局的x變量和module里的屬性x所迷惑 var boundGetX = retrieveX.bind(module); boundGetX(); // 返回 81
偏函數(shù)(Partial Functions)
bind()的另一個(gè)最簡(jiǎn)單的用法是使一個(gè)函數(shù)擁有預(yù)設(shè)的初始參數(shù)。
如:
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]
在默認(rèn)情況下,使用 window.setTimeout() 時(shí),this 關(guān)鍵字會(huì)指向 window (或全局)對(duì)象。
當(dāng)使用類的方法時(shí),需要 this 引用類的實(shí)例,你可能需要顯式地把 this 綁定到回調(diào)函數(shù)以便繼續(xù)使用實(shí)例。
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(); // 一秒鐘后, 調(diào)用"declare"方法6. 函數(shù)柯里化 (function currying)
函數(shù)柯里化 (function currying),用于創(chuàng)建已經(jīng)設(shè)置好了一個(gè)或者多個(gè)參數(shù)的函數(shù)。
如:
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() 方法可以實(shí)現(xiàn)函數(shù)柯里化。具體見上一條。
需要注意的是 bind() curry() 不應(yīng)濫用,因?yàn)槊總€(gè)函數(shù)都會(huì)帶來額外的開銷。
默認(rèn)情況下,所有對(duì)象都是可以擴(kuò)展的。
Object.preventExtensions(obj) 可以阻止給對(duì)象添加新的屬性和方法。但是已有的屬性和方法不受任何影響,可以被修改和刪除。
Object.isExtensible() 可以確定對(duì)象是否可以擴(kuò)展。
如:
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. 密封的對(duì)象。
es5 為對(duì)象定義的第二個(gè)保護(hù)級(jí)別是密封對(duì)象。
密封對(duì)象不可擴(kuò)展,而且所有成員的 [[Configurable]] 特性被設(shè)置成false,這意味著不能刪除對(duì)象,但是屬性值是可以修改的。
密封對(duì)象的方法是 Object.seal() 檢測(cè)對(duì)象是否被密封的方法是 Object.isSealed() 所有密封對(duì)象用 Object.isExtensible() 檢測(cè)都是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. 凍結(jié)的對(duì)象
最嚴(yán)格的防篡改級(jí)別是凍結(jié)。凍結(jié)的對(duì)象既不可擴(kuò)展,又是密封的,而且對(duì)象數(shù)據(jù)屬性 [[Writable]] 特性會(huì)被設(shè)置成false。
凍結(jié)對(duì)象的方法是 Object.freeze()
檢測(cè)對(duì)象是否凍結(jié)的方法是 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"
對(duì) JS 庫的作者而言,凍結(jié)對(duì)象是很有用的。因?yàn)?JS 庫最怕有人意外(或有意)的修改核心對(duì)象。
10. 定時(shí)器對(duì)于 setTimeout() 實(shí)際執(zhí)行時(shí)間大于等于設(shè)定時(shí)間。 對(duì)于 setInterval() 兩次執(zhí)行的實(shí)際間隔時(shí)間小于等于設(shè)定時(shí)間。11. 函數(shù)循環(huán)分割
對(duì)于這樣的循環(huán)
for (var i = 0, len = arr.length; i < len; i++) { process(arr[i]) }
如果完成 process() 的時(shí)間較長(zhǎng),那么可以做如下分割。
function chunk (arr, process, context) { setTimeout(function () { var item = arr.shift() process.call(context, item) if (arr.length > 0) { setTimeout(arguments.callee, 100) } }, 100) }
一旦某個(gè)函數(shù)需要花 50ms 以上來處理,就應(yīng)該看看能否分割開來處理了。
12. 函數(shù)節(jié)流。瀏覽器中某些計(jì)算和處理要比其他昂貴很多,比如,DOM 操作比非 DOM 交互需要更多的內(nèi)存和CPU時(shí)間。
函數(shù)節(jié)流背后的基本思想是:某些代碼不可以在沒有間斷的情況下連續(xù)重復(fù)執(zhí)行。
比如連續(xù)重復(fù)執(zhí)行的 resize 事件。
以下是該模式的基本形式:
var processor = { timeoutId: null, performProcessor: function () { handleProcess() }, process: function () { clearTimeout(this.timeoutId) var that = this this.timeoutId = setTimeout(function () { that.performProcessor() }, 100) } }
或者使用函數(shù)
function throttle (method, context) { clearTimeout(method.tid) method.tid = setTimeout(function () { method.call(context) }, 100) }
只要代碼是周期性執(zhí)行的,都應(yīng)該使用節(jié)流,但是你不能控制請(qǐng)求執(zhí)行的速率。這里 throttle() 函數(shù)用 100 ms做間隔,可以根據(jù)需求來更改。
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"
當(dāng)代碼中存在多個(gè)部分在特定時(shí)刻相互交互的情況下,自定義事件就非常有用了。
使用自定義事件有助于結(jié)藕相關(guān)對(duì)象,保持功能的隔絕。
關(guān)于《JavaScript高級(jí)程序設(shè)計(jì)(第三版)》這本書,我就選擇性的看了一部分章節(jié),所以,目前的摘要筆記總結(jié)已全部結(jié)束。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/81201.html
摘要:說明此摘要筆記系列是我最近看高級(jí)程序設(shè)計(jì)第版隨手所記。摘要筆記本身沒有系統(tǒng)性,沒有全面性可言,寫在這里供有一定基礎(chǔ)的前端開發(fā)者參考交流。對(duì)每一項(xiàng)運(yùn)行給定函數(shù),返回該函數(shù)會(huì)返回的項(xiàng)組成的數(shù)組。是的反操作是的反操作第一部分結(jié)束。 說明: 此摘要筆記系列是我最近看《JavaScript高級(jí)程序設(shè)計(jì)(第3版)》隨手所記。 里面分條列舉了一些我認(rèn)為重要的、需要記下的、對(duì)我有幫助的點(diǎn),是按照我看...
摘要:思路是,使用原型鏈對(duì)原型屬性和方法進(jìn)行繼承,借用構(gòu)造函數(shù)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承。注意使用寄生式繼承來為對(duì)象添加函數(shù),會(huì)由于不能做到函數(shù)復(fù)用而降低效率,這一點(diǎn)與構(gòu)造函數(shù)模式類似。無論什么情況下都會(huì)調(diào)用兩次超類型的構(gòu)造函數(shù)。 說明: 此摘要筆記系列是我最近看《JavaScript高級(jí)程序設(shè)計(jì)(第3版)》隨手所記。里面分條列舉了一些我認(rèn)為重要的、需要記下的、對(duì)我有幫助的點(diǎn),是按照我看的順序來的...
摘要:如果重設(shè)構(gòu)造函數(shù)的原型對(duì)象,那么,會(huì)切斷新的原型對(duì)象和任何之前已經(jīng)存在的構(gòu)造函數(shù)實(shí)例之間的聯(lián)系,它們引用的仍然是最初的原型。說明返回的對(duì)象與構(gòu)造函數(shù)或者與構(gòu)造函數(shù)的原型屬性沒有關(guān)系。 說明: 此摘要筆記系列是我最近看《JavaScript高級(jí)程序設(shè)計(jì)(第3版)》隨手所記。里面分條列舉了一些我認(rèn)為重要的、需要記下的、對(duì)我有幫助的點(diǎn),是按照我看的順序來的。摘要筆記本身沒有系統(tǒng)性,沒有全面性...
摘要:說明此摘要筆記系列是我最近看高級(jí)程序設(shè)計(jì)第版隨手所記。其中,描述符對(duì)象的屬性必須是設(shè)置其中一個(gè)或多個(gè)值,可以修改對(duì)應(yīng)的特性值。如支持的瀏覽器,可以取得指定屬性的描述符。 說明: 此摘要筆記系列是我最近看《JavaScript高級(jí)程序設(shè)計(jì)(第3版)》隨手所記。里面分條列舉了一些我認(rèn)為重要的、需要記下的、對(duì)我有幫助的點(diǎn),是按照我看的順序來的。摘要筆記本身沒有系統(tǒng)性,沒有全面性可言,寫在這里...
摘要:函數(shù)表達(dá)式和閉包函數(shù)聲明的一個(gè)重要特征是函數(shù)聲明提升如遞歸遞歸函數(shù)是在一個(gè)函數(shù)通過名字調(diào)用自身的情況下構(gòu)成的。注意中已經(jīng)是塊級(jí)作用域了,所以這些東西感覺實(shí)際用途沒有那么大,但是對(duì)理解閉包對(duì)作用域鏈中的屬性的引用,這一點(diǎn)還是有作用的。 函數(shù)表達(dá)式和閉包 1. 函數(shù)聲明的一個(gè)重要特征是函數(shù)聲明提升 如: sayHi() function sayHi () { console.log(h...
閱讀 1411·2021-11-25 09:43
閱讀 2268·2021-09-27 13:36
閱讀 1121·2021-09-04 16:40
閱讀 1963·2019-08-30 11:12
閱讀 3318·2019-08-29 14:14
閱讀 572·2019-08-28 17:56
閱讀 1330·2019-08-26 13:50
閱讀 1252·2019-08-26 13:29