摘要:關于函數聲明他的一個重要特征就是函數聲明提升就是在執行代碼之前會先讀取函數聲明這意味著可以把函數聲明放到調用他的語句的后面將聲明放到了后面關于函數表達式創建一個匿名函數然后賦值給一個變量函數體可以返回一個匿名函數返回的函數可以賦值給一個變量
關于函數聲明
他的一個重要特征就是函數聲明提升,就是在執行代碼之前會先讀取函數聲明,這意味著可以把函數聲明放到調用他的語句的后面
sayHi(); //將聲明放到了后面 function sayHi(){ console.log("hi"); }關于函數表達式
創建一個匿名函數然后賦值給一個變量
var functionName = function(arg0,arg1){ //函數體 }
可以返回一個匿名函數,返回的函數可以賦值給一個變量,也可以被其他方式調用
function test(name,age){ //這樣可以返回一個匿名函數 return function (name,age) { console.log(name); console.log(age); } }遞歸
//遞歸,不斷調用自身 function factorial(num) { if (num <= 1) { return 1; } else { //arguments.callee指向正在執行的函數指針,代替函數名 //所以即使后面將函數設置為 null, 不影響遞歸 return num * arguments.callee(num - 1); } } //將函數賦值給變量 var anotherFactorial = factorial; factorial = null;//設置函數factorial 為 null console.log(anotherFactorial(4));// 返回24
不過這種方式在嚴格模式不能使用arguments.callee,需要借助命名函數表達式
//創建一個f 命名的函數表達式,然后將它賦值給變量factorial var factorial = (function f(num) { if (num <= 1) { return 1; } else { return num * f(num - 1); //函數f 依然有效 } }); console.log(factorial(4));//返回24
閉包因為函數是引用類型,即使賦值給變量,也只是賦值了一個引用指針,所以函數本身還是可以直接使用的
閉包是指有權訪問另一個函數作用域的變量的函數
創建閉包的常見方式,就是在一個函數內部創建另一個函數
閉包也可以理解為一些被返回的匿名函數,這些匿名函數可以訪問另一個函數作用域
閉包的作用域包含著他自己的作用域,包含函數的作用域和全局作用域
一般函數作用鏈細節:
當某個函數第一次被調用時,會創建一個執行環境,以及相應的作用域鏈,并把作用域鏈賦值給一個特殊的內部屬性[Scope]
然后,使用 this,arguments 和其他命名參數的值來初始化函數的活動對象
在作用域鏈中,外部函數的活動對象始終處于第二位,外部函數的外部函數的活動對象處于第三位,如此類推,直至作為作用域鏈終點的全局執行環境
作用域鏈本質上是一個指向變量對象的指針列表,他只引用但不實際包含變量的對象
這個是一般的函數,非閉包
function compare(value1, value2) { if (value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } }
?
1.每個執行環境都有一個表示變量的對象-變量對象
2.全局環境的變量對象始終存在
3.像 compare 函數這樣的局部環境的變量對象只會在函數執行過程中存在
4.在創建 compare 函數時,會創建一個預先包含全局變量對象的作用域鏈,這個作用域鏈被保存在內部的[Scope]屬性中
5.如果又有新的活動對象(函數執行),那么就會被推入作用域鏈的最前端
6.無論什么時候在函數中訪問一個變量,就會從作用域鏈中搜索具有相應名字的變量
閉包作用鏈細節:
在另一個函數內部定義的函數會將包含函數(即外部函數)的活動對象添加到他的作用域鏈中
內部定義的匿名函數的作用域鏈實際上會包含外部函數的活動對象
即使外部函數執行完畢,內部匿名函數依然不會被銷毀,然后他依然引用這個活動對象,直至這個匿名函數被銷毀后才會全部銷毀
function createComparisonFunction(propertyName) { return function (object1, object2) { var value1 = object1[propertyName]; var value2 = object2[propertyName]; if (value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } } } var compare = createComparisonFunction("name"); var result = compare({name:"gg"},{name:"bb"});
?
1.首先:內部的匿名函數會添加到createComparisonFunction的作用域鏈中,()即執行正常的一般函數作用域鏈流程,只是全局活動對象改成了局部活動對象
2.然后:被返回后,這個匿名函數的作用域鏈被初始化為包含createComparisonFunction函數的活動對象和全局活動對象,因為被返回到外部,外部是一個全局活動對象,所以他的作用域鏈就有了全局活動對象
3.那么,現在如果createComparisonFunction執行完畢了,但是因為他這個活動對象還被匿名函數引用,所以即使他執行完畢了,依然不會銷毀,除非引用的匿名函數銷毀后,才會銷毀
可以手動將被返回的匿名函數(這就是一個閉包)設置 null 來銷毀
var compare = createComparisonFunction("name"); var result = compare({name:"gg"},{name:"bb"}); compare = null;
閉包與變量將compare(他是被返回的那個匿名函數)的引用解除,就可以通知 gc 進行回收
閉包只能取得包含函數中任何變量的最后一個值,因為閉包保存的是整個變量對象,而不是某個特殊的變量
function createFunctions() { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function () { //這是一個閉包 //因為閉包保存的是整個createFunctions變量對象,所以當他執行完成的時候(for循環結束), //i是等于10的,所以就會是10,由始至終,閉包里面引用的都是一整個變量對象,而不是一個變量 return i; }; } return result; //返回的是一個數組,數組里面每一個項目都是一個function } var test = createFunctions(); for (var i = 0; i < 10; i++) { //需要執行這個 function 才能夠獲取里面的值 console.log(test[i]());//都是10 }
如果想達到我們的效果,返回的 i 是0-9的話:
function createFunctions() { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function (num) {//這是一個匿名函數,參數是 num return function () {// 這是一個閉包,不過這個閉包訪問的num是我們傳入的num,即使閉包保存一整個變量對象,但是我們將變量對象改成了外面這個匿名函數 return num; //相當于在閉包外面包了一層活動對象,將活動對象改變成能夠改變值 num的活動對象 }; }(i);//這個匿名函數會立即執行,并且傳入了 i 作為 num } return result; //返回的是一個數組,數組里面每一個項目都是一個function } var test = createFunctions(); for (var i = 0; i < 10; i++) { //需要執行這個 function 才能夠獲取里面的值 console.log(test[i]());//返回0-9 }關于 this 對象
this 對象是在運行時基于函數的執行環境綁定的,在全局函數中, this 指向 window,而當函數被作為某個對象的方法調用時,this 指向那個對象
在通過 call() 或 apply()改變函數執行環境的時候,也會改變 this 指向
var name = "the window"; var object = { name: "my object", //返回一個匿名函數 getNameFunc: function () { //匿名函數里面又返回一個匿名函數(閉包) //當返回的時候,當前環境已經變成了全局環境了,搜索的話直接在當前活動對象(全局)找到了 return function () { return this.name; //所以返回的是全局環境的 name } } }; //object.getNameFunc()返回一個函數,然后再加上(),這個函數就會執行 console.log(object.getNameFunc()());//返回 the window
如果對 this 進行保存的話,那么可以將當前對象的引用保存起來,賦值到一個變量上來使用
var name = "the window"; var object = { name: "my object", //返回一個匿名函數 getNameFunc: function () { //將當前對象(當前這個function)引用保存起來 var that = this; //匿名函數里面又返回一個匿名函數(閉包) //當返回的時候,使用的是保存起來的這個對象引用,所以會返回這個對象的屬性 return function () { return that.name; } } }; //object.getNameFunc()返回一個函數,然后再加上(),這個函數就會執行 console.log(object.getNameFunc()());//返回 my object
很多時候很有用,在 this 可能會被改變的情況下(尤其在閉包環境下),先保存起來會很方便
下面例子是想說明語法的細微變化,也可能改變this的值
var name = "the window"; var object = { name: "my object", getName: function () { return this.name; } }; //正常執行,返回正常 console.log(object.getName());//返回 my object //加了括號,但是this沒有改變 console.log((object.getName)());//返回 my object //加了賦值運算,this被改變了,因為賦值表達式的值是函數本身,處于全局環境下 console.log((object.getName = object.getName)());//返回 the window模仿塊級作用域
javascript 沒有塊級作用域(私有作用域)的概念
匿名函數可以模仿塊級作用域
(function () { //塊級作用域 }()); ---------分解一下 ( //第一對圓括號 function () { //塊級作用域 } ()//第二對圓括號 ); //將函數聲明包含在第一對中,表示他實際上是一個函數表達式 //而緊隨其后的第二對圓括號會立即調用這個函數
無論什么地方,只要臨時需要一些變量,就可以使用,這種技術經常在全局作用域中被用在函數外部,從而限制響全局作用域中添加過多的變量和函數
這種做法可以減少閉包占用內存的問題,因為沒有指向匿名函數的引用,只要函數執行完畢,就可以立即銷毀其作用域鏈
私有變量把有權訪問私有變量和私有函數的共有方法稱為特權方法
第一種創建方法:
function MyObject() { //私有變量和私有函數 var privateVariable = 10; function privateFunction() { return false; } //特權方法 //因為這里作為閉包有權訪問在構造函數定義的所有變量和函數 //另外設置了this可以被調用 this.publicMethod = function () {//外部只能通過這個方法訪問私有屬性和函數 privateVariable++; return privateFunction(); } }
第二種是:
function Person(name) { //只有方法暴露,里面的屬性是只能由方法訪問 //因為這些方法都是在構造函數內部定義的,他們作為閉包能夠通過作用域鏈訪問屬性 this.getName = function () { return name; }; this.setName = function (value) { name = value; }; } //每一個實例都不一樣,每次new都會調用構造函數重新創建方法 var person = new Person("nico"); console.log(person.getName()); //返回nico person.setName("ggg"); console.log(person.getName());//返回ggg
靜態私有變量使用構造函數模式的缺點就是針對每個實例都會創建同樣一組新方法
通過在私有作用域中定義私有變量或函數,同樣可以創建特權方法
這種方式
私有變量和函數是由實例共享(因為被閉包引用著這個塊級作用域)
由于特權方法是在構造函數的原型上定義的,所以所有實例都使用同一個特權方法
(function () { //私有變量和私有函數 var privateVariable = 10; function privateFunction() { return false; } //構造函數 MyObject = function () { //沒有函數聲明,使用函數表達式創建 //因為函數聲明只能構建局部函數,現在創建了一個全局函數 //因為這是全局函數,所以也相對來說就是一個靜態了. }; //公有/特權方法 //在原型上定義方法,所有這個原型的實例都能共享 //特權方法是一個閉包,保存著對包含作用域的引用(當前這個塊級作用域) MyObject.prototype.publicMethod = function () { //這個是一個閉包 privateVariable++; return privateFunction(); } })();
這種方式,
私有(靜態)變量變成了由所有實例共享的屬性(在當前這個塊級作用域)
構造函數能夠靜態變量,因為所有實例共享,所以每次調用構造函數的時候也會改變
特權方法寫在構造函數的原型上,所有實例共享
(function () { //"靜態"變量,對于每個實例來說都是共享的 var name = ""; //構造函數這里也是全局的,而且每次調用構造函數就會創建一個新的name值 Person = function (value) { name = value; }; //在原型上寫方法,方法在實例之間共享 Person.prototype.getName = function () { return name; }; Person.prototype.setName = function (value) { name = value; }; })(); var person1 = new Person("mico"); console.log(person1.getName());//返回mico person1.setName("oo"); console.log(person1.getName());//返回oo //因為靜態變量共享的關系,name被改了之后,全部實例的name都被改了 var person2 = new Person("gg"); console.log(person1.getName());//返回gg console.log(person2.getName());//返回gg模塊模式
模塊模式: 為單例創建私有變量和特權方法
單例(singleton),指的是只有一個實例的對象
如果必須創建一個對象并以某些數據對其進行初始化,同時還要公開一些能夠訪問的這些私有數據的方法,就可以使用模塊模式
模塊模式創建的單例都是Object的實例,因為都是字面量創建,一般來說,單例都是全局對象,一般不會將他傳遞給一個函數,所以也沒必要檢驗對象類型了
//單例用字面量方式創建 var singleton = { name: value, method: function () { //方法代碼 } };
//模塊模式 var singleton = function () { //這是一個匿名函數 //私有變量和私有函數 var privateVariable = 10; function privateFunction() { return false; } //特權/公有方法和屬性 //返回的這個對象是在匿名函數內部定義的,因此它的公有方法有權訪問私有變量和函數 //從本質上來講,定義的是單例的公共接口 return { //返回了一個單例對象,{}是對象寫法 publicProperty: true, //只包含可以公開的屬性和方法 publicMethod: function () { privateVariable++; return privateFunction(); } }; }();
模塊模式的應用例子
//需要一個單例來管理應用程序級的信息 //創建一個application對象(返回單例對象) var application = function () { //初始化一個數組 var components = new Array(); components.push(new BaseComponent()); //返回的單例可以使用方法,然后獲取數組的長度或者添加數組內容 return { getComponentCount: function () { return components.length; }, registerComponent: function (component) { if (typeof component == "object") { components.push(component); } } } }();增強的模塊模式
改進的模塊模式:在返回對象之前加入對其增強的代碼.例如必須是某種類型的實例,同時還必須添加某些屬性或方法對其加以增強
var singleton = function () { //私有變量和私有函數 var privateVariable = 10; function privateFunction() { return false; } //創建對象 var object = new CustomType(); //添加特權/公有屬性和方法 object.publicProperty = true; object.publicMethod = function () { privateVariable++; return privateFunction(); }; //返回這個對象 return object; }();
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81236.html
摘要:寫在開頭本篇是小紅書筆記的第六篇,也許你會奇怪第六篇筆記才寫語法基礎,筆者是不是穿越了。可移步筆者的文章中替換方式參考文檔高級程序設計作者以樂之名本文原創,有不當的地方歡迎指出。 showImg(https://segmentfault.com/img/bVblGMc?w=600&h=400); 寫在開頭 本篇是小紅書筆記的第六篇,也許你會奇怪第六篇筆記才寫語法基礎,筆者是不是穿越了。...
摘要:對之前看高級程序設計時沒有注意到的一些知識點,結合本書做以補充語法注釋源于的型既可以出現在字符串字面量中,也可能出現在正則表達式字面量中,如故一般建議使用型注釋保留字語句變量參數屬性名運算符和標記等標識符不允許使用保留字,此外在對象字面量中 對之前看《JavaScript高級程序設計》時沒有注意到的一些知識點,結合本書做以補充 語法 注釋 源于PL/I的/* */型既可以出現在字符串字...
摘要:無處不在的理解語言與其他主流語言相比,函數式語言的血統更多一些。函數式語言一類程序設計語言,是一種非馮諾伊曼式的程序設計語言。函數式語言主要成分是原始函數,定義函數和函數型。性能分析內置對象上的和方法。 無處不在的JavaScript 理解JavaScript語言 與其他主流語言相比,JavaScript函數式語言的血統更多一些。 函數式語言一類程序設計語言,是一種非馮.諾伊曼式的程序...
摘要:寫在前面金三銀四又到了一年一度的跳槽季相信大家都在準備自己面試筆記我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結方便自己復習詳細內容會在之后一一對應地補充上去有些在我的個人主頁筆記中也有相關記錄這里暫且放一個我的面試知識點目錄大家 寫在前面: 金三銀四, 又到了一年一度的跳槽季, 相信大家都在準備自己面試筆記, 我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結,方便自...
閱讀 2954·2021-11-17 09:33
閱讀 3118·2021-11-16 11:52
閱讀 482·2021-09-26 09:55
閱讀 2975·2019-08-30 15:52
閱讀 1313·2019-08-30 15:44
閱讀 1257·2019-08-30 13:59
閱讀 796·2019-08-30 13:08
閱讀 1157·2019-08-30 10:50