作用域,上下文,闭包
作用域
作用域決定了你的代碼里的變量和其他資源在各個(gè)區(qū)域中的可見性,為代碼提供了一個(gè)安全層級(jí),用戶只能訪問他們當(dāng)前需要的東西。
在 JavaScript 中有兩種作用域:
全局作用域:定義在函數(shù)之外的變量會(huì)被保存在全局作用域中,在代碼的任何地方都是可訪問的。
局部作用域(函數(shù)作用域):在函數(shù)內(nèi)定義的變量(局部變量)或者函數(shù)的參數(shù),只在當(dāng)前函數(shù)體內(nèi)以及這個(gè)函數(shù)體嵌套的任意函數(shù)體可訪問,函數(shù)外部不能訪問。
?
訪問當(dāng)前作用域的變量速度比訪問其他作用域的快,因?yàn)闀?huì)順著作用域鏈查找,直到找到你要的或者沒有結(jié)果。
?
在一個(gè)函數(shù)中,如果局部變量和全局變量同名,局部變量會(huì)覆蓋全局變量。在函數(shù)體內(nèi)訪問這個(gè)變量,是局部變量的值。所以,在不同的作用域,可以命名相同的變量而不導(dǎo)致沖突,解決了不同范圍的同名變量命名問題
var num = 1; //聲明一個(gè)全局變量function func() {var num = 2; //聲明一個(gè)局部變量return num;}console.log(func()); //輸出:2局部變量在函數(shù)開始執(zhí)行時(shí)創(chuàng)建,函數(shù)執(zhí)行完后局部變量會(huì)自動(dòng)銷毀。(當(dāng)通過閉包在函數(shù)外面引用了局部變量,當(dāng)函數(shù)執(zhí)行完,局部變量不會(huì)被銷毀)
一個(gè)應(yīng)用中全局作用域的生存周期與該應(yīng)用相同。局部作用域只在該函數(shù)調(diào)用執(zhí)行期間存在。?
?
變量聲明提前
變量在整個(gè)函數(shù)體內(nèi)都是有定義的,所以在賦值前是能訪問的,即變量聲明提升到當(dāng)前函數(shù)體頂部(要是當(dāng)前函數(shù)嵌套了其他函數(shù),其他函數(shù)中聲明的變量,由于作用域鏈的關(guān)系,其他函數(shù)體的變量,在當(dāng)前函數(shù)體內(nèi)是不可訪問的,不存在聲明提升到當(dāng)前函數(shù)體的說法)
function func() {console.log(num); //輸出:undefined,而非報(bào)錯(cuò),因?yàn)樽兞縩um在整個(gè)函數(shù)體內(nèi)都是有定義的var num = 1; //聲明num 在整個(gè)函數(shù)體func內(nèi)都有定義console.log(num);?//輸出:1?}func();
?
作用域鏈
從當(dāng)前作用域出發(fā),決定了哪些數(shù)據(jù)能被訪問,不講大道理,直接畫圖吧。
比如下圖:想在函數(shù)3里訪問變量a,過程是:先在函數(shù)3里找,沒有就在函數(shù)2里找,再?zèng)]有就在函數(shù)1里找,直到找到全局作用域,看是否有這么個(gè)變量a,是不是像順著一根鏈子找呢,這個(gè)就是作用域鏈。(上一層的作用域也叫做父級(jí)作用域)
?
ECMAScript 6 引入了let和const關(guān)鍵字。這些關(guān)鍵字可以代替var。
let likes = 'Coding'; const skills = 'Javascript and PHP';?
和var關(guān)鍵字不同,let和const關(guān)鍵字支持在塊級(jí)聲明中創(chuàng)建使用局部作用域。
if (true) { var name = 'Hammad'; let likes = 'Coding';、 const skills = 'JavaScript and PHP'; } console.log(name); // logs 'Hammad' console.log(likes); // Uncaught ReferenceError: likes is not defined console.log(skills); // Uncaught ReferenceError: skills is not defined?
上下文
上下文指的是在相同的作用域中的this的值。可以使用函數(shù)方法改變上下文,上下文是執(zhí)行的時(shí)候確定的。
在全局作用域中,上下文總是 Window 對(duì)象。
作為一個(gè)對(duì)象的方法,上下文就是這個(gè)方法所在的那個(gè)對(duì)象。
使用new關(guān)鍵字調(diào)用函數(shù)時(shí)上下文,上下文會(huì)設(shè)置為被調(diào)用的函數(shù)的實(shí)例
當(dāng)在嚴(yán)格模式(strict mode)中調(diào)用函數(shù)時(shí),上下文默認(rèn)是 undefined。
?
使用 .call(), .apply() 和 .bind() 改變上下文
Call 和 Apply 函數(shù)來改變函數(shù)調(diào)用時(shí)的上下文。context={a:1,y:2};
function hello(a,b) {
alert(this.a);
alert(a);
alert(b);
}
hello();
hello.call(context,"cc","dd"); //1,cc,dd
hello.apply(context,["cc","dd"]); //
?
Bind 并不是自己調(diào)用函數(shù),它只是在函數(shù)調(diào)用之前綁定上下文和其他參數(shù)。
(function introduce(name, interest) { console.log('Hi! I'm '+ name +' and I like '+ interest +'.'); console.log('The value of this is '+ this +'.') }).bind(window, 'Hammad', 'Cosmology')();?
函數(shù)的運(yùn)行過程
第一階段是創(chuàng)建階段,是函數(shù)剛被調(diào)用但代碼并未執(zhí)行的時(shí)候。創(chuàng)建階段主要發(fā)生了 3 件事。
創(chuàng)建變量對(duì)象
創(chuàng)建作用域鏈
設(shè)置上下文(this)的值
第二個(gè)階段就是代碼執(zhí)行階段,進(jìn)行其他賦值操作并且代碼最終被執(zhí)行。
?
詞法作用域
詞法作用域(靜態(tài)作用域)的意思是在函數(shù)嵌套中,內(nèi)層函數(shù)可以訪問父級(jí)作用域的變量等資源,在定義時(shí)就確定。
?
閉包
當(dāng)內(nèi)部函數(shù)試著訪問外部函數(shù)的作用域鏈(詞法作用域之外的變量)時(shí)產(chǎn)生閉包。閉包包括它們自己的作用域鏈、父級(jí)作用域鏈和全局作用域。
閉包不僅能訪問外部函數(shù)的變量,也能訪問外部函數(shù)的參數(shù)。
即使函數(shù)已經(jīng)return,閉包仍然能訪問外部函數(shù)的變量。這意味著return的函數(shù)允許持續(xù)訪問外部函數(shù)的所有資源。
當(dāng)你的外部函數(shù)return一個(gè)內(nèi)部函數(shù),調(diào)用外部函數(shù)時(shí)return的函數(shù)并不會(huì)被調(diào)用。你必須先用一個(gè)單獨(dú)的變量保存外部函數(shù)的調(diào)用,然后將這個(gè)變量當(dāng)做函數(shù)來調(diào)用。
用閉包實(shí)現(xiàn)共有作用域和私有作用域:
(function () { // private scope })();?
函數(shù)結(jié)尾的括號(hào)告訴解析器立即執(zhí)行此函數(shù)。我們可以在其中加入變量和函數(shù),外部無法訪問。但如果我們想在外部訪問它們,也就是說我們希望它們一部分是公開的,一部分是私有的。我們可以使用閉包的一種形式,稱為模塊模式(Module Pattern),它允許我們用一個(gè)對(duì)象中的公有作用域和私有作用域來劃分函數(shù)。
模塊模式
var Module = (function() { function privateMethod() { // do something } return { publicMethod: function() { // can call privateMethod(); } }; })(); Module.publicMethod(); // works Module.privateMethod(); // Uncaught ReferenceError: privateMethod is not defined?
Module 的return語句包含了我們的公共函數(shù)。私有函數(shù)并沒有被return。函數(shù)沒有被return確保了它們?cè)?Module 命名空間無法訪問。但我們的共有函數(shù)可以訪問我們的私有函數(shù),方便它們使用有用的函數(shù)、AJAX 調(diào)用或其他東西。
一種習(xí)慣是以下劃線作為開始命名私有函數(shù),并返回包含共有函數(shù)的匿名對(duì)象。這使它們?cè)诤荛L(zhǎng)的對(duì)象中很容易被管理。向下面這樣:
var Module = (function () { function _privateMethod() { // do something } function publicMethod() { // do something } return { publicMethod: publicMethod, } })();?
立即執(zhí)行函數(shù)表達(dá)式(IIFE)
另一種形式的閉包是立即執(zhí)行函數(shù)表達(dá)式(Immediately-Invoked Function Expression,IIFE)。這是一種在 window 上下文中自調(diào)用的匿名函數(shù),也就是說this的值是window。它暴露了一個(gè)單一全局接口用來交互。如下所示:
(function(window) { // do anything })(this);?
?兩段好玩的閉包代碼
1、返回一個(gè)函數(shù)
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");
//createComparisonFunction函數(shù)返回后,閉包的作用域鏈開始初始化,包含了Comparison作用鏈上的對(duì)象;Comparison返回后作用域鏈被銷毀,但是上面的活動(dòng)對(duì)象內(nèi)存得不到釋放,直到匿名函數(shù)被銷毀 var result = compare({ name: "Nicholas" }, { name: "Greg" }); compare=' ';//解除對(duì)匿名函數(shù)的引用,釋放內(nèi)存。此時(shí)result已經(jīng)達(dá)到目的指向了需要引用的函數(shù)
2,循環(huán)引用造成的內(nèi)存泄漏
?
轉(zhuǎn)載于:https://www.cnblogs.com/yaoyao-sun/p/10365422.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的作用域,上下文,闭包的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [BZOJ 2555] SubStrin
- 下一篇: udp/tcp流程