摘要:作用域沒有塊級作用域盡量不要在塊中聲明變量。只有函數(shù)級作用域作用域鏈自由變量當(dāng)前作用域沒有定義的變量即為自由變量。自由變量會去其父級作用域找。
1. 題目
說一下對變量提升的理解
說明this的幾種不同使用場景
創(chuàng)建10個a標(biāo)簽,點擊的時候彈出來相應(yīng)的序號
如何理解作用域
實際開發(fā)中閉包的應(yīng)用
手動實現(xiàn)call apply bind
2. 知識點 2.1 執(zhí)行上下文范圍:一段script或者一個函數(shù)
全局:變量定義、函數(shù)聲明 script
函數(shù):變量定義、函數(shù)聲明、this、arguments (執(zhí)行之前)
函數(shù)聲明和函數(shù)表達(dá)式的區(qū)別:
a(); //報錯 函數(shù)表達(dá)式 變量聲明 會提前。 var a = function(){} b(); // 不報錯 函數(shù)聲明 function b(){}
變量定義時會默認(rèn)把他的變量聲明提升:(僅限于他的執(zhí)行上下文,比如一段script和一個函數(shù)中)
console.log(a); var a = 0;
實際上是
var a; console.log(a); a = 0;2.2 this
this要在執(zhí)行時才能確認(rèn),定義時無法確認(rèn)。
var a = { name:"a", fn:function(){ console.log(this.name); } } a.fn(); // a a.fn.apply({name:"b"}); // b a.fn.call({name:"b"}); var fn1 = a.fn(); fn1(); // undefined
this的使用場景
構(gòu)造函數(shù)中(指向構(gòu)造的對象)
function Fun(name){ this.name = name; } var f = new Fun("a"); console.log(f.name);
對象屬性中(指向該對象)
普通函數(shù)中(指向window)
call apply bind
都是用來改變一個函數(shù)的this指向,用法略有不同。
call:后面的參數(shù)為調(diào)用函數(shù)的參數(shù)列表
function greet(name) { console.log(this.animal,name); } var obj = { animal: "cats" }; greet.call(obj,"貓咪");
apply:第二個參數(shù)為調(diào)用函數(shù)的參數(shù)數(shù)組
function greet(name) { console.log(this.animal,name); } var obj = { animal: "cats" }; greet.apply(obj,["貓咪"]);
bind:當(dāng)綁定函數(shù)被調(diào)用時,bind傳入的參數(shù)會被插入到目標(biāo)函數(shù)的參數(shù)列表的開始位置,傳遞給綁定函數(shù)的參數(shù)會跟在它們后面。
var fun = function (name1,name2){ console.log(this); console.log(name); }.bind({a:1},"name1"); fun("name2");
arguments中的this:
var length = 10; function fn(){ alert(this.length) } var obj = { length: 5, method: function(fn) { arguments[0]() } }
obj.method(fn)//輸出1
這里沒有輸出5,也沒有輸出10,反而輸出了1,有趣。這里arguments是javascript的一個內(nèi)置對象(可以參見mdn:arguments - JavaScript),是一個類數(shù)組(就是長的比較像數(shù)組,但是欠缺一些數(shù)組的方法,可以用slice.call轉(zhuǎn)換,具體參見上面的鏈接),其存儲的是函數(shù)的參數(shù)。也就是說,這里arguments[0]指代的就是你method函數(shù)的第一個參數(shù):fn,所以arguments[0]()的意思就是:fn()。
不過這里有個疑問,為何這里沒有輸出5呢?我method里面用this,不應(yīng)該指向obj么,至少也會輸出10呀,這個1是鬧哪樣?
實際上,這個1就是arguments.length,也就是本函數(shù)參數(shù)的個數(shù)。為啥這里的this指向了arguments呢?因為在Javascript里,數(shù)組只不過使用數(shù)字做屬性名的方法,也就是說:arguments[0]()的意思,和arguments.0()的意思差不多(當(dāng)然這么寫是不允許的),你更可以這么理解:
arguments = { 0: fn, //也就是 functon() {alert(this.length)} 1: 第二個參數(shù), //沒有 2: 第三個參數(shù), //沒有 ..., length: 1 //只有一個參數(shù) }
所以這里alert出來的結(jié)果是1。
如果要輸出5應(yīng)該咋寫呢?直接 method: fn 就行了。
2.3 作用域沒有塊級作用域
if(true){ var name = "test" } console.log(name);
盡量不要在塊中聲明變量。
只有函數(shù)級作用域
2.4 作用域鏈自由變量 當(dāng)前作用域沒有定義的變量 即為自由變量。
自由變量會去其父級作用域找。是定義時的父級作用域,而不是執(zhí)行。
var a = 100; function f1(){ var b = 200; function f2(){ var c = 300; console.log(a); //自由變量 console.log(b); //自由變量 console.log(c); } f2(); }; f1();2.5 閉包
一個函數(shù)中嵌套另外一個函數(shù),并且將這個函數(shù)return出去,然后將這個return出來的函數(shù)保存到了一個變量中,那么就創(chuàng)建了一個閉包。
閉包的兩個使用場景
1.函數(shù)作為返回值
function fun(){ var a = 0; return function(){ console.log(a); //自由變量,去定義時的父級作用域找 } } var f1 = fun(); a = 1000; f1();
2.函數(shù)作為參數(shù)
function fun(){ var a = 0; return function(){ console.log(a); //自由變量,去定義時的父級作用域找 } } function fun2(f2){ a = 10000 f2(); } var f1 = fun(); fun2(f1);
具體解釋看 高級-閉包中的說明
閉包的兩個作用:
能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)
可以讓函數(shù)內(nèi)部的變量一直保存在內(nèi)存中
實際應(yīng)用場景1:
閉包可以將一些不希望暴露在全局的變量封裝成“私有變量”。
假如有一個計算乘積的函數(shù),mult函數(shù)接收一些number類型的參數(shù),并返回乘積結(jié)果。為了提高函數(shù)性能,我們增加緩存機(jī)制,將之前計算過的結(jié)果緩存起來,下次遇到同樣的參數(shù),就可以直接返回結(jié)果,而不需要參與運(yùn)算。這里,存放緩存結(jié)果的變量不需要暴露給外界,并且需要在函數(shù)運(yùn)行結(jié)束后,仍然保存,所以可以采用閉包。
上代碼:
function calculate(param){ var cache = {}; return function(){ if(!cache.parame){ return cache.param; }else{ //緩存計算.... //cache.param = result //下次訪問直接取 } } }
實際應(yīng)用場景2
延續(xù)局部變量的壽命
img 對象經(jīng)常用于進(jìn)行數(shù)據(jù)上報,如下所示:
var report = function( src ){ var img = new Image(); img.src = src; }; report( "http://xxx.com/getUserInfo" );
但是通過查詢后臺的記錄我們得知,因為一些低版本瀏覽器的實現(xiàn)存在 bug,在這些瀏覽器
下使用 report 函數(shù)進(jìn)行數(shù)據(jù)上報會丟失 30%左右的數(shù)據(jù),也就是說, report 函數(shù)并不是每一次
都成功發(fā)起了 HTTP 請求。
丟失數(shù)據(jù)的原因是 img 是 report 函數(shù)中的局部變量,當(dāng) report 函數(shù)的
調(diào)用結(jié)束后, img 局部變量隨即被銷毀,而此時或許還沒來得及發(fā)出 HTTP 請求,所以此次請求
就會丟失掉。
現(xiàn)在我們把 img 變量用閉包封閉起來,便能解決請求丟失的問題:
var report = (function(){ var imgs = []; return function( src ){ var img = new Image(); imgs.push( img ); img.src = src; } })();
閉包缺點:浪費資源!
3. 題目解答 3.1 說一下對變量提升的理解變量定義和函數(shù)聲明
注意函數(shù)聲明和函數(shù)表達(dá)式的區(qū)別
變量定義時會默認(rèn)把他的變量聲明提升:(僅限于他的執(zhí)行上下文,比如一段script和一個函數(shù)中)
console.log(a); var a = 0;
實際上是
var a; console.log(a); a = 0;3.2 說明this的幾種不同使用場景
構(gòu)造函數(shù)中(指向構(gòu)造的對象)
對象屬性中(指向該對象)
普通函數(shù)中(指向window)
call apply bind
3.3 創(chuàng)建10個a標(biāo)簽,點擊的時候彈出來相應(yīng)的序號實現(xiàn)方法1:用let聲明i
var body = document.body; console.log(body); for (let i = 0; i < 10; i++) { let obj = document.createElement("i"); obj.innerHTML = i + "
"; body.appendChild(obj); obj.addEventListener("click",function(){ alert(i); }) }
實現(xiàn)方法2 包裝作用域
var body = document.body; console.log(body); for (var i = 0; i < 10; i++) { (function (i) { var obj = document.createElement("i"); obj.innerHTML = i + "3.4 實際開發(fā)中閉包的應(yīng)用
"; body.appendChild(obj); obj.addEventListener("click", function () { alert(i); }) })(i) }
能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)
可以讓函數(shù)內(nèi)部的變量一直保存在內(nèi)存中
封裝變量,權(quán)限收斂
應(yīng)用1
var report = (function(){ var imgs = []; return function( src ){ var img = new Image(); imgs.push( img ); img.src = src; } })();
用于防止變量銷毀。
應(yīng)用2
function isFirstLoad() { var arr = []; return function (str) { if (arr.indexOf(str) >= 0) { console.log(false); } else { arr.push(str); console.log(true); } } } var fun = isFirstLoad(); fun(10); fun(10);
將arr封裝在函數(shù)內(nèi)部,禁止隨意修改,防止變量銷毀。
3.5 手動實現(xiàn)call apply bindcontext 為可選參數(shù),如果不傳的話默認(rèn)上下文為 window;
context 創(chuàng)建一個 Symbol 屬性,調(diào)用后即刪除,不會影響context
Function.prototype.myCall = function (context) { if (typeof this !== "function") { return undefined; // 用于防止 Function.prototype.myCall() 直接調(diào)用 } context = context || window; const fn = Symbol(); context[fn] = this; const args = [...arguments].slice(1); const result = context[fn](...args); delete context[fn]; return result; }
apply實現(xiàn)類似call,參數(shù)為數(shù)組
Function.prototype.myApply = function (context) { if (typeof this !== "function") { return undefined; // 用于防止 Function.prototype.myCall() 直接調(diào)用 } context = context || window; const fn = Symbol(); context[fn] = this; let result; if (arguments[1] instanceof Array) { result = context[fn](...arguments[1]); } else { result = context[fn](); } delete context[fn]; return result; }
1.判斷是否為構(gòu)造函數(shù)調(diào)用
2.注意參數(shù)要插入到目標(biāo)函數(shù)的開始位置
Function.prototype.myBind = function (context) { if (typeof this !== "function") { throw new TypeError("Error") } const _this = this const args = [...arguments].slice(1) return function F() { // 判斷是否用于構(gòu)造函數(shù) if (this instanceof F) { return new _this(...args, ...arguments) } return _this.apply(context, args.concat(...arguments)) } }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/101742.html
摘要:閉包面試題解由于作用域鏈機(jī)制的影響,閉包只能取得內(nèi)部函數(shù)的最后一個值,這引起的一個副作用就是如果內(nèi)部函數(shù)在一個循環(huán)中,那么變量的值始終為最后一個值。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第8天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了...
摘要:使用上一篇文章的例子來說明下自由變量進(jìn)階期深入淺出圖解作用域鏈和閉包訪問外部的今天是今天是其中既不是參數(shù),也不是局部變量,所以是自由變量。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第7天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進(jìn)階計...
摘要:寫在前面對于一個前端開發(fā)者,應(yīng)該沒有不知道作用域的。欺騙詞法作用域有兩個機(jī)制可以欺騙詞法作用域和。關(guān)于你不知道的的第一部分作用域和閉包已經(jīng)結(jié)束了,但是,更新不會就此止住未完待續(xù) 這是《你不知道的JavaScript》的第一部分。 本系列持續(xù)更新中,Github 地址請查閱這里。 寫在前面 對于一個前端開發(fā)者,應(yīng)該沒有不知道作用域的。它是一個既簡單有復(fù)雜的概念,簡單到每行代碼都有它的影子...
摘要:注意由于閉包會額外的附帶函數(shù)的作用域內(nèi)部匿名函數(shù)攜帶外部函數(shù)的作用域,因此,閉包會比其它函數(shù)多占用些內(nèi)存空間,過度的使用可能會導(dǎo)致內(nèi)存占用的增加。 作用域和作用域鏈?zhǔn)莏avascript中非常重要的特性,對于他們的理解直接關(guān)系到對于整個javascript體系的理解,而閉包又是對作用域的延伸,也是在實際開發(fā)中經(jīng)常使用的一個特性,實際上,不僅僅是javascript,在很多語言中都...
摘要:本篇收錄了一些面試中經(jīng)常會遇到的經(jīng)典面試題,并且都給出了我在網(wǎng)上收集的答案。網(wǎng)頁的行為層負(fù)責(zé)回答內(nèi)容應(yīng)該如何對事件做出反應(yīng)這一問題。 本篇收錄了一些面試中經(jīng)常會遇到的經(jīng)典面試題,并且都給出了我在網(wǎng)上收集的答案。眼看新的一年馬上就要開始了,相信很多的前端開發(fā)者會有一些跳槽的悸動,通過對本篇知識的整理以及經(jīng)驗的總結(jié),希望能幫到更多的前端面試者。(如有錯誤或更好的答案,歡迎指正,水平有限,望...
閱讀 3406·2021-09-22 15:17
閱讀 2761·2021-09-02 15:15
閱讀 1794·2019-08-30 15:54
閱讀 2014·2019-08-30 14:02
閱讀 2544·2019-08-29 16:58
閱讀 3003·2019-08-29 16:08
閱讀 1345·2019-08-26 12:24
閱讀 1670·2019-08-26 10:41