摘要:函數表達式和閉包函數聲明的一個重要特征是函數聲明提升如遞歸遞歸函數是在一個函數通過名字調用自身的情況下構成的。注意中已經是塊級作用域了,所以這些東西感覺實際用途沒有那么大,但是對理解閉包對作用域鏈中的屬性的引用,這一點還是有作用的。
函數表達式和閉包 1. 函數聲明的一個重要特征是函數聲明提升
如:
sayHi() function sayHi () { console.log("hi") }2. 遞歸
遞歸函數是在一個函數通過名字調用自身的情況下構成的。
如:
function factorial (num) { if (num <= 1) { return 1 } else { return num * factorial(num - 1) // 這里存在強耦合,不太好 } }
比如下面的代碼會導致它出錯
var anotherFactorial = factorial factorial = null anotherFactorial(3) // 出錯:factorial is not a function arguments.callee 是一個指向正在執行函數的指針,可以做如下改善: function factorial (num) { if (num <= 1) { return 1 } else { return num * arguments.callee(num - 1) } }
嚴格模式下使用 arguments.callee 會出錯。可以做以下改善
var factorial = (function f (num) { if (num <= 1) { return 1 } else { return num * f(num - 1) } })
我的理解:這里的 f 只在函數內部有效,所以不會受到外界影響,外部得不到這個變量。
(function test () { console.log("this is test") }) test() // 報錯。
用小括號括起來的函數聲明在外部是得不到的,test()只有函數內部可以用
3. 閉包閉包指有權訪問另一個函數作用域中的變量的函數。
創建閉包的常用方式,就是在一個函數內部創建另一個函數。
如:
function createComparisonFunction (propertyName) { return function(obj1, obj2) { var value1 = obj1[propertyName] var value2 = obj2[propertyName] return value1 - value2 } } var obj = [{age: 13}, {age: 29}, {age: 18}, {age: 37}, {age: 5}, {age: 14}] var compare = createComparisonFunction("age") var obj2 = obj.sort(function (a, b) { compare(a, b) })
這個例子中,內部函數訪問了外部函數的變量 propertyName,而且即使被返回了在其它地方調用,仍然可以訪問。
是因為內部函數的作用域鏈中包含了 createComparisonFunction 的作用域鏈。
由于閉包會攜帶包含它的函數的作用域,因此會比其它函數占用更多的內存。
4. 閉包與變量閉包作用域鏈的配置機制有一個副作用,閉包只能取得包含函數中任何變量的最后一個值,因為閉包保存的是整個變量對象,不是某個特殊的變量。
如:
function createFunction () { var result = [] for (var i = 0; i < 10; i++) { result[i] = function () { return i } } return result } // 返回數組中每個函數的執行結果都是10
改善版:
function createFunction () { var result = [] for (var i = 0; i < 10; i++) { result[i] = (function (num) { return function () { return num } }(i)) } return result }
這樣就按預期來返回值了,這里通過立即執行函數把變量 i 的當前值作為參數傳遞到了內部閉包函數里,由于函數的基本類型參數是按值傳遞,
所以每個內部閉包函數保存的值都是當前 i 值。
var name = "window"; (function () { this.name = "fn" console.log(this.name) // "fn" var obj = { name: "obj", getFunc: function () { return function () { console.log(this.name) } } } obj.getFunc()() // "fn" }())
閉包函數的this指向它被調用時的作用域對象。
每個函數被調用時都會自動取得兩個特殊的變量:this 和 arguments。在調用這兩個變量時,只會搜索到其活動對象為止。
this 的改變,如:
var name = "the Window" var obj = { name: "obj", getName: function () { console.log(this.name) } } obj.getName(); // "obj" (obj.getName)(); // "obj" (obj.getName = obj.getName)(); // "the Window" var obj2 = { name: "obj2" } (obj2.getName = obj.getName)(); // "the Window" // 這里我的理解是相當于一個立即執行函數,這個立即執行函數是在window環境下被執行的,所以返回window下的變量值。 obj2.getName = obj.getName obj2.getName() // "obj2"6. 通過構造函數創建私有變量和特權方法
如:
function Person (name) { this.getName = function () { return name } this.setName = function (value) { name = value } } var ming = new Person("ming") ming.name // undefined ming.getName() // "ming" ming.setName("ming2") ming.getName() // "ming2"
說明:getName() setName() 在構造函數外部使用,只有這兩個方法有權訪問私有變量name,沒有其它辦法,因為它們作為閉包能夠通過作用域鏈訪問name
這種方法創建私有變量和特權方法有構造函數所共有的缺點——函數沒有復用。
(function () { var name = "" Person = function (value) { name = value } Person.prototype.getName = function () { return name } Person.prototype.setName = function (value) { name = value } }()) var ming = new Person("ming") ming.getName() // "ming" ming.setName("ming2") ming.getName() // "ming2" var li = new Person("li") li.getName() // "li" ming.getName() // "li" // 說明所有實例都是對作用域鏈中的 name 的引用
說明:這里立即執行函數里Person使用函數表達式來定義,而且沒有使用 var , 是為了形成全局變量。
或者先在全局定義一下 Person也可以。 var Person = function () {}
注意:es6 中已經是塊級作用域了,所以這些東西感覺實際用途沒有那么大,但是對理解閉包對作用域鏈中的屬性的引用,這一點還是有作用的。
8. 模塊模式指為單例創建私有變量和特權方法。所謂單例,指的是只有一個實例的對象。
如:
var application = function () { var components = [] components.push(new BaseComponent()) // BaseComponent 指初始化組建 return { getComponentCount: function () { return components.length }, registerComponent: function (component) { if (typeof component === "object") { components.push(component) } } } }()
增強版,適合那種單例必須是某種類型的實例:
var application = function () { var components = [] components.push(new BaseComponent()) var app = new Base() app.getComponentCount = function () { return components.length } app.registerComponent = function (component) { if (typeof component === "object") { components.push(component) } } return app }()
// 函數表達式和閉包部分結束
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86521.html
摘要:說明此摘要筆記系列是我最近看高級程序設計第版隨手所記。摘要筆記本身沒有系統性,沒有全面性可言,寫在這里供有一定基礎的前端開發者參考交流。對每一項運行給定函數,返回該函數會返回的項組成的數組。是的反操作是的反操作第一部分結束。 說明: 此摘要筆記系列是我最近看《JavaScript高級程序設計(第3版)》隨手所記。 里面分條列舉了一些我認為重要的、需要記下的、對我有幫助的點,是按照我看...
摘要:如果重設構造函數的原型對象,那么,會切斷新的原型對象和任何之前已經存在的構造函數實例之間的聯系,它們引用的仍然是最初的原型。說明返回的對象與構造函數或者與構造函數的原型屬性沒有關系。 說明: 此摘要筆記系列是我最近看《JavaScript高級程序設計(第3版)》隨手所記。里面分條列舉了一些我認為重要的、需要記下的、對我有幫助的點,是按照我看的順序來的。摘要筆記本身沒有系統性,沒有全面性...
摘要:說明此摘要筆記系列是我最近看高級程序設計第版隨手所記。其中,描述符對象的屬性必須是設置其中一個或多個值,可以修改對應的特性值。如支持的瀏覽器,可以取得指定屬性的描述符。 說明: 此摘要筆記系列是我最近看《JavaScript高級程序設計(第3版)》隨手所記。里面分條列舉了一些我認為重要的、需要記下的、對我有幫助的點,是按照我看的順序來的。摘要筆記本身沒有系統性,沒有全面性可言,寫在這里...
摘要:關于對象定義了全局對象。支持的瀏覽器有除了接受要序列化的對象外,還可以接受另外兩個參數。如果是數值,則表示每個級別縮進的空格數,最大,超過的值自動轉換成。字符串長度超過,結果中將只出現前個字符。會在結果字符串中插入換行符提高可讀性。 關于JSON 1. JSON 對象 es5 定義了全局對象 JSON。支持的瀏覽器有 IE8+ 、Firefox 3.5+ 、Safari 4+、Chro...
摘要:思路是,使用原型鏈對原型屬性和方法進行繼承,借用構造函數實現對實例屬性的繼承。注意使用寄生式繼承來為對象添加函數,會由于不能做到函數復用而降低效率,這一點與構造函數模式類似。無論什么情況下都會調用兩次超類型的構造函數。 說明: 此摘要筆記系列是我最近看《JavaScript高級程序設計(第3版)》隨手所記。里面分條列舉了一些我認為重要的、需要記下的、對我有幫助的點,是按照我看的順序來的...
閱讀 3684·2021-08-10 09:42
閱讀 584·2019-08-30 15:55
閱讀 880·2019-08-30 15:54
閱讀 3104·2019-08-30 13:45
閱讀 549·2019-08-29 16:23
閱讀 1986·2019-08-29 16:23
閱讀 976·2019-08-29 15:18
閱讀 2256·2019-08-29 12:57