摘要:函數(shù)表達式定義函數(shù)表達式區(qū)別于函數(shù)聲明,也是一種定義函數(shù)的方式,形似與變量賦值,這個值就是函數(shù)體,例如函數(shù)表達式之匿名函數(shù)函數(shù)表達式之具名函數(shù)匿名函數(shù)之立即執(zhí)行函數(shù)目前知道的是這三種形式,希望高人補充特點區(qū)別于函數(shù)聲明,和普通變量一樣使用前
函數(shù)表達式
定義:函數(shù)表達式區(qū)別于函數(shù)聲明,也是一種定義函數(shù)的方式,形似與變量賦值,這個值就是函數(shù)體,例如:
var a = function(){}; // 函數(shù)表達式之匿名函數(shù) var a = function fn(){}; // 函數(shù)表達式之具名函數(shù) (function(){})(); // 匿名函數(shù)之立即執(zhí)行函數(shù) // 目前知道的是這三種形式, 希望高人補充
特點:
1 . 區(qū)別于函數(shù)聲明,和普通變量一樣使用前必須聲明,不聲明在非嚴格模式下被認為是全局的變量,在嚴格模式下報錯
定義:在一個函數(shù)中調(diào)用自身,遞歸必須要有結(jié)束條件階乘
// fibonacci數(shù)列 function fibonacci(n){ if(n == 1 || n == 2){ // 結(jié)束條件 return 1; }else{ var num = fibonacci(n-1) + fibonacci(n-2); // 遞歸調(diào)用 return num // 每一層遞歸都返回和 } }; console.log(fibonacci(6)); // 8
特點:
1 . 調(diào)用匿名函數(shù)表達式自身,為了便于維護,可以通過arguments.callee(指向當(dāng)前函數(shù)的指針)來調(diào)用當(dāng)前函數(shù),這樣做的好處是當(dāng)遞歸函數(shù)換名稱時不用更換內(nèi)部的函數(shù)名稱
function fibonacci(n){ if(n == 1){ return 1; }else{ var num = arguments.callee(n-1) * n ; return num } }; let a = fibonacci; fibonacci = null; console.log(a(6)); // 函數(shù)內(nèi)在再次調(diào)用fibonacci就會報錯 Uncaught TypeError: fibonacci is not a function
一變
function fibonacci(n){ if(n == 1){ return 1; }else{ var num = arguments.callee(n-1) * n ; return num } }; let a = fibonacci; fibonacci = null; console.log(a(6)); // 720 但是在嚴格模式下回報錯 Uncaught TypeError: "caller", "callee", and "arguments" properties may not be accessed
二變
"use strict"; let a = (function fibonacci(n){ if(n == 1){ return 1; }else{ var num = fibonacci(n-1) * n ; return num } }); let b = a; a=null; console.log(b(4)); // 24 ,這里相當(dāng)于包了一層,如果外邊的變量改變是不會影響函數(shù)內(nèi)部調(diào)用的
注:在嚴格模式下arguments.callee會報錯,可以通過命名函數(shù)表達式來實現(xiàn)
閉包閉包是指有權(quán)訪問另一個作用域中的變量的函數(shù),形式很多,不舉例了,重點在下面
特點:關(guān)于閉包的特點都得先理解執(zhí)行環(huán)境、作用域、活動對象的概念
執(zhí)行環(huán)境: 函數(shù)被調(diào)用時會創(chuàng)建當(dāng)前函數(shù)的執(zhí)行環(huán)境,可以想象成一個對象,對象包含一個屬性,該屬性指向作用域(作用域鏈表)
作用域,也可以看成是作用域鏈表,一個list,list的元素是指向活動對象,包括本作用域內(nèi)的活動對象的指向和對上級的指向,當(dāng)前函數(shù)執(zhí)行完畢作用域就消失了
活動對象,包含當(dāng)前函數(shù)內(nèi)的所有屬性,當(dāng)沒有作用域鏈引用時活動對象被銷毀
注:指向可以理解為引用,像 a=[], a就指向了內(nèi)存中的一個數(shù)組
{ scope: scopeList ----| } | 執(zhí)行環(huán)境(context) | [ | activeObj1: activeObjects1, --|--| activeObj2: activeObjects2, --|--|--| ... | | | ] | | | 作用域鏈(scopeList) <----| | | { | | var1: 1, | | var2: 1, | | var1: [], | | } | | 活動對象(activeObjects1) <--| | { | _var1: 1, | _var2: 1, | _var1: [], | } | 活動對象(activeObjects2) <--| // 可以看出執(zhí)行環(huán)境和作用域鏈?zhǔn)且粚σ坏模?所以當(dāng)執(zhí)行完函數(shù)后執(zhí)行環(huán)境就沒了,作用域沒有被引用了就也沒了,但是活動對象和作用域鏈?zhǔn)嵌鄬Χ嗟模ㄍ局兄徽故玖艘粚Χ啵?,所以就算作用域沒了,當(dāng)前作用域的活動對象也可能被其它作用域引用(例如閉包),所以仍然存在于內(nèi)存中閉包與變量
特點:閉包對外層活動對象是引用不是復(fù)制(也可以說是復(fù)制了引用),這里寫一個親身經(jīng)歷的筆試題
var nAdd = null; function f(){ let n = 99; nAdd = () => { ++n; } return () => { console.log(n); } }; var f1 = f(); var f2 = f(); nAdd(); f1(); f2(); nAdd(); f1(); f2();
我認為這個題挺有意思。這里不給答案,讀者可以自己先猜一下,然后自己跑一下和自己的猜想對對。
我認為這個題目的關(guān)鍵在nAdd是在f函數(shù)的外層,也就是每次實例化這個f函數(shù)都會對nAdd重新賦值,重新賦值后執(zhí)行環(huán)境中n會不同,多次賦值取最后一個,只要能搞清楚執(zhí)行環(huán)境、作用域、活動對象的關(guān)系其實不難,不會也沒關(guān)系,一開始看到我也懵。
定義:this是和執(zhí)行環(huán)境綁定的
特點:特點和定義息息相關(guān),this和執(zhí)行環(huán)境綁定,只要執(zhí)行完畢執(zhí)行環(huán)境不存在了就,例如:
var name = "china,hebei"; var province = { name: "hebei", getName: function(){ return function(){ return this.name; }; } }; console.log(province.getName()()); // china,hebei
從結(jié)果來看this指的是全局的那個this,這個和常規(guī)理解不太一樣,按說getName屬于province這個對象,this指向province才對,想想定義就明白了,province.getName()這個執(zhí)行了getName函數(shù),并返回了一個函數(shù)體,再次執(zhí)行這個函數(shù)體的時候getName()已經(jīng)執(zhí)行完了,執(zhí)行完了執(zhí)行環(huán)境當(dāng)然就不存在了,this就返回給執(zhí)行的環(huán)境(全局環(huán)境),那如何改成指向province呢?
var name = "china,hebei"; var province = { name: "hebei", getName: function(){ let that = this; return function(){ return that.name; }; } }; console.log(province.getName()()); // hebei
很容易理解,that在getName執(zhí)行完畢后并不會消失,因為它所在的活動對象還被最后返回的函數(shù)的作用域鏈引用著,所以最后輸出的就是hebei
塊級作用域
定義:通過創(chuàng)建一個立即執(zhí)行函數(shù)來模仿模塊作用域的效果,普通的{}沒有塊級概念
for(var i=0; i<2; i++){ console.log(i); } console.log(i) // 2
塊級作用域,很簡單,通過函數(shù)作用域封裝一層即可,例如
(function(){ for(var i=0; i<2; i++){ console.log(i); } })() console.log(i) // Uncaught ReferenceError: i is not defined私有變量
定義:在函數(shù)定義的變量和方法都可以看成是私有變量,可以通過在函數(shù)創(chuàng)建閉包實現(xiàn)在函數(shù)外部訪問私有變量,稱之為共有方法(特權(quán)方法),例如:
function Student(){ var name = "jiang"; this.getName = function(){ return name; }; }; var xiaoming = new Student(); console.log(xiaoming.getName()); // jiang 只有這種特權(quán)方法可以訪問到
特點:私有變量只能通過特權(quán)方法在函數(shù)外部被訪問
解決的問題:增強函數(shù)的封裝性,函數(shù)作用域內(nèi)得變量只能通過特權(quán)方法訪問
帶來的問題:每個實例都會重新創(chuàng)建一個特權(quán)方法
定義: 在私有作用域內(nèi)(立即執(zhí)行函數(shù))定義函數(shù)內(nèi)的私有變量和全局的(變量沒有聲明就賦值時)匿名函數(shù),為匿名函數(shù)添加原型方法,原型方法內(nèi)訪問函數(shù)內(nèi)的變量,這樣在函數(shù)外部可以可以通過變量名稱直接訪問全局的匿名函數(shù)上的原型方法,方法內(nèi)部可以訪問函數(shù)私有變量
(function(){ let name = "jiang"; student = function(){}; student.prototype.getName = function(){ return name; }; })() console.log(student.prototype.getName()); // jiang
解決的問題:解決了每個實例都不共享的私有變量和特權(quán)方法的問題
帶來的問題:解決的問題也變成了它自身的問題,最好的方案是私有變量和靜態(tài)私有變量結(jié)合使用
模塊模式定義:模塊模式就是把私有變量和單例模式結(jié)合起來,在JS中通過字面對象來創(chuàng)建對象是最簡單的單例模式,而私有變量是函數(shù)作用域被的,方法就是定義一個變量(單例對象),然后創(chuàng)建一個立即執(zhí)行函數(shù)返回一個字面對象,對象內(nèi)部創(chuàng)建公共的特權(quán)方法和屬性,函數(shù)內(nèi)部定義私有變量。
單例模式,例如
var a = {}; var a = {}; // 總是只有一個a對象
var a = (function(){ var name = "jiang"; // 私有變量 return{ // 單例模式 getName: function(){ return name; } } })() console.log(a.getName());
解決的問題:在單例內(nèi)創(chuàng)建私有變量, 單例模式的應(yīng)用場景是需要重復(fù)使用但不需要同時使用的對象,像錯誤提示彈框
帶來的問題:返回的對象是沒有類型的就是不能通過instanceof確認對象類型
增強版的模塊模式
定義:將函數(shù)內(nèi)返回的對象通過構(gòu)造函數(shù)的方式聲明,然后為其添加特權(quán)方法和屬性,然后將對象返回,這樣的對象就可以通過instanceof確認其類型了
var a = (function(){ var name = "jiang"; function Student(){}; var xiaoming = new Student(); xiaoming.getName = function(){ return name; }; return xiaoming; })()
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/100574.html
摘要:要理解立即執(zhí)行函數(shù),需要先理解一些函數(shù)的基本概念。函數(shù)表達式使用關(guān)鍵字聲明一個函數(shù),但未給函數(shù)命名,最后將匿名函數(shù)賦予一個變量,叫函數(shù)表達式,這是最常見的函數(shù)表達式語法形式。 javascript和其他編程語言相比比較隨意,所以javascript代碼中充滿各種奇葩的寫法,有時霧里看花,當(dāng)然,能理解各型各色的寫法也是對javascript語言特性更進一步的深入理解。 ( functio...
摘要:將匿名函數(shù)賦予一個變量,叫函數(shù)表達式,這是最常見的函數(shù)表達式語法形式。組成這是一個被稱為自執(zhí)行匿名函數(shù)的設(shè)計模式,主要包含兩部分。 一、函數(shù)聲明&函數(shù)表達式 1.1 函數(shù)聲明 (函數(shù)語句) showImg(https://segmentfault.com/img/bVbbqvT?w=278&h=166); (1)使用 function 關(guān)鍵字聲明一個函數(shù),再指定一個函數(shù)名,叫函數(shù)聲明。...
摘要:對象數(shù)組初始化表達式,闖關(guān)記之上文檔對象模型是針對和文檔的一個。闖關(guān)記之?dāng)?shù)組數(shù)組是值的有序集合。數(shù)組是動態(tài)的,根闖關(guān)記之語法的語法大量借鑒了及其他類語言如和的語法。 《JavaScript 闖關(guān)記》之 DOM(下) Element 類型 除了 Document 類型之外,Element 類型就要算是 Web 編程中最常用的類型了。Element 類型用于表現(xiàn) XML 或 HTML 元素...
摘要:函數(shù)表達式對于函數(shù)聲明,函數(shù)的名稱是必須的,而對于函數(shù)表達式而言則是可選的,因此,就出現(xiàn)了匿名函數(shù)表達式和命名函數(shù)表達式。這是因為對命名函數(shù)處理的機制,函數(shù)的名稱永遠在函數(shù)內(nèi)部的作用域中有效。 function 是 Javascript 中的第一類對象,這就意味著函數(shù)可以像其他值一樣被傳遞。一個最常見的用法就是將一個匿名函數(shù)作為回調(diào)函數(shù)傳遞到另外一個異步函數(shù)中。 函數(shù)聲明 func...
摘要:和是兩種立即執(zhí)行函數(shù)的常見寫法,最初我以為是一個括號包裹匿名函數(shù),再在后面加個括號調(diào)用函數(shù),最后達到函數(shù)定義后立即執(zhí)行的目的,后來發(fā)現(xiàn)加括號的原因并非如此。 javascript和其他編程語言相比比較隨意,所以javascript代碼中充滿各種奇葩的寫法,有時霧里看花,當(dāng)然,能理解各型各色的寫法也是對javascript語言特性更進一步的深入理解。 ( function(){…} )...
摘要:字面形式允許你在不需要使用操作符和構(gòu)造函數(shù)顯式創(chuàng)建對象的情況下生成引用值。操作符以一個對象和一個構(gòu)造函數(shù)作為參數(shù)鑒別數(shù)組有前一小結(jié)可以知道鑒別數(shù)組類型可以使用。屬性是函數(shù)獨有的,表明該對象可以被執(zhí)行。這種函數(shù)被稱為匿名函數(shù)。 引子: 1.JavaScript 中的變量類型和類型檢測 1.1原始類型 1.2引用類型 1.3內(nèi)建類型的實例化 1.4函數(shù)的字面形式 1.5正則表達式的字...
閱讀 2900·2021-10-26 09:49
閱讀 3234·2021-10-14 09:42
閱讀 2060·2021-09-13 10:31
閱讀 2601·2019-08-30 11:13
閱讀 2972·2019-08-29 16:31
閱讀 1090·2019-08-29 13:58
閱讀 1869·2019-08-29 12:12
閱讀 3602·2019-08-26 13:48