摘要:因為一旦代碼丟出來了,還會涉及到繼承,構造函數,原型鏈,閉包等一系列問題在后面等著你,面試管為了掏你的底細會一問再問,問到你懵逼。不可以當作構造函數,也就是說,不可以使用命令,否則會拋出一個錯誤。
上一篇2018年3月面試心得《跨域問題》
話說我在面試的時候,有那么幾天,不知道是中了什么邪,面試的幾家公司開始瘋狂的問我this,
各種的this,繞著彎的問我this,后來我做夢都是this、this、this……你妹的this!
那么先從面試官會怎么問你來說吧。
面試官會問什么樣的問題呢請說一下作用域和上下文(this)
this會指向哪里
如何改變this的指向
出各種繞彎子的題目讓你說this是啥
其實這個雖然題目我記得不多了,但是真的問起來還是很惡心的。
因為一旦代碼丟出來了,還會涉及到繼承,構造函數,原型鏈,閉包等一系列問題在后面等著你,面試管為了掏你的底細會一問再問,問到你懵逼。
那么我們先來說說第一個問題,作用域和上下文。
什么是作用域?
問得好,我也不知道,待我查一下………………
作用域是在運行時代碼中的某些特定部分中變量,函數和對象的可訪問性。換句話說,作用域決定了代碼區塊中變量和其他資源的可見性。
emmmm…………不好理解的話,我說一個比喻吧。
現在我為一個房子專門定一個木窗窗沿,這個窗沿只適合這個房子里面所有需要用的地方,其他房子不能用,那么這個房子就是我木窗的作用域~~~
就像我在
function 房子(){ var 木窗; }
我的木窗只屬于我的房子,我不可以在房子的外面,例如小區里面直接拿到我的木窗,我必須進入房子去找這個木窗,這是不可以改變的作用域。
然后我們再來看一下
var 景色 = "大海" function 房子() { var 景色 = "大草原"; this.木窗 = function() { console.log(景色) } 木窗() } 房子()
請問這個時候輸出的景色是什么呢~
機智的小朋友肯定會說是大草原~~~
為什么呢~
因為景色我從下往上找啊,找到最近的一張圖我就不用繼續找了,拿出來用就好啦。
怎么樣,驚不驚喜,意不意外,刺不刺激~~~
js中有全局作用域,函數作用域,塊級作用域(es6)。
全局作用域
很好解釋,我所有地方都可以調用的到的,就像路邊的廣告牌,我們都可以看得到,不需要想辦法進誰家里去看。
函數作用域
是產生在函數中的,一個函數內部會出現一塊作用域。可以這么理解,函數是我們的房子,我們站在房子外面(全局)的時候,無法看到房子里面的東西。但是我們站在房子里面(函數內部),是可以去透過窗子湊湊整個外面的世界的。
塊級作用域
哇塞這個就厲害了,在我們es6里面,新增了一些像let、const之類的語法,可以產生一個塊級作用域。
大家應該都做過一個簡單的題目,就是一個for循環里面丟一個setTimeout,下面放出代碼。
for(var i = 0; i<5; i++) { setTimeout(function() { console.log(i) }, 1000) }
我們看一下這一個題目,首先會讓你回答打印出來的是什么。
機智的小伙伴肯定會回答:5 5 5 5 5
為什么咧,因為這個var的i成了全局的,并不是只在循環里面去使用,setTimeout是一個異步函數,所以我們執行完了for才會去console.log。
這個還涉及到同步異步之類的,會多帶帶開一章來講,光是基礎都已經讓人頭昏眼花,啊西吧~
那如果我們要讓他12345怎么辦呢,可以把i保留下來做一個閉包傳入,還有一個最方便的辦法就是用let來聲明他。
ps: 不過一般面試官會跟你說不要用es6,給我一個es5的辦法2333333333
=3=
另外說一下let和const和var主要的區別:
如果你要在塊級聲明的變量存在全局變量,但是塊級作用域內又let或者const了一個局部變量,導致后者綁定這個塊級作用域,就會…boom…爆炸,也就是報錯~
var大家都知道,進行變量提升,你在第十行聲明賦值,我們代碼執行的時候其實是會放到最頂部先聲明,再執行到第十行的位置進行復制,還沒賦值前都是undefind。但是let和const沒有哦,如果你提前實用的話就等著報錯吧~
const聲明一個只讀的常量。一旦聲明,常量的值就不能改變。但是呢,你可以改變他的屬性。
就像
const i = 10; i = 9; // 這樣是不行的!!! const u = {a: "a",b: "b"} u.b = "c" // 這樣是可以的
其實很好理解,不過我還是舉個?吧……
我const一個大爺,我改變這個大爺的屬性,類似給他換個衣服或者換個鞋子,可是大爺還是大爺所以不會報錯。
但是我const一個妹子,完事了把妹子改成了大爺,那我不依,報錯。
那什么是上下文呢?
這個也好理解,比如我窗戶裝在客廳,看到的是客廳里面的景色,裝到了廚房,就是廚房的景色,當然這個上下文是可以更改的,我可以個這個窗沿里面貼上珠穆拉瑪峰的照片,那么這個上下文就被我改了。
function 房子() { var 景色 = "大草原"; this.木窗 = function() { console.log(this.景色) } 木窗() } 房子()
你猜打印出來的是什么~~~
當然是undefind!
想什么呢~不會以為是大草原吧~
為什么咧,因為他的this變了。
下面我把這行代碼改為直接打印this,打印出來的是一個window。
為什么?因為我調用房子的地方在window下,我調用了房子,房子調用了木窗,所以this成了window。
那么我們剛剛是不是在房子里面給了一張大草原的畫呢,這是不是在房子外面(window層)就找不到了呢,所以是個window。
哈哈哈哈哈
如果你在這里就昏了,那么一定要往下看,不然你永遠都搞不清楚this到底會指向哪里。
再說一下第三個問題,改變this的指向。
這個其實也很簡單,call或者apply都可以,這個自己看api去~考官會問你兩個的區別的,乖寶寶要自己看文檔哦~
最后一題,繞著彎子讓你說this是啥。
既然你看到了這里,那么正題開始了!!!
到底this指向哪里!!!問得好,我自己也懵逼。
沒關系,我們可以一起捋一捋。
容老夫先沐浴更衣,焚香祈禱你們不要被我帶歪了。
首先,跟我一起讀一遍下面的兩句話(當然,如果有補充可以留言):
1: 誰調用指向誰,沒有誰就是window2: 除了call、apply、bind和箭頭函數
首先我們在全局下打印一下this,控制臺輸出的是window,這個沒有疑問吧~
我們再來通過函數調用一下
function 房子() { this.木窗 = function() { console.log(this) } 木窗() } 房子()
那么我們再回過頭去看剛剛的房子窗子。
我們是哪里開始調用房子的?
當然是window,其實房子()就等同于window.房子()。
所以呢,調用房子的是window,那么房子最底下會調用木窗,所以真正的調用者被抓出來了。
window => 房子 => 木窗
函數的調用者就是this,請抓住始作俑者,這道題就是window這個小婊渣,就是他~
那么我們再看下一個。
通過對象的屬性來調用:
var obj = { say: function() { console.log(this); } }; obj.say();
打印的結果是: {say: ?}
誰調用就指向誰,這個obj.say()的執行方法調用者是前面的obj,所以當前的這個this指向了obj。
再看一個
var obj = { say: function() { var hehe = function() { console.log(this) } hehe() } }; obj.say();
這個this指向了window。
為什么?因為這個hehe并不是obj上面的屬性,沒有找到調用者是誰,所以默認指向window。
下面我們來看一下其余幾個指向誰。
call和apply大家都知道,可以改變this的指向。
var obj = {a: "a"}; function b() { console.log(this) } b.apply(obj)
打印結果:{a: "a"}
call差不多,兩者差別只是參數傳的不一樣,一個可以傳數組。
具體情況,寶寶們文檔走一波~
然后再來看一下bind
哇塞這個也厲害了
直接上代碼
var axiba = {hehe: "hehe"} var obj = { a: "a", b:function() { console.log(this) }.bind(axiba) }; b() // 或者 var axiba = {hehe: "hehe"} var obj = { a: "a", b:function() { console.log(this) } }; var u = obj.b.bind(axiba) u()
執行的是神馬~
當然是指向axiba~
最后一個,就是es6的箭頭函數
我要多帶帶為箭頭函數加個粗
官網逛一逛,發現了幾句話。
箭頭函數有幾個使用注意點。(1)函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。
(2)不可以當作構造函數,也就是說,不可以使用new命令,否則會拋出一個錯誤。
(3)不可以使用arguments對象,該對象在函數體內不存在。如果要用,可以用 rest 參數代替。
(4)不可以使用yield命令,因此箭頭函數不能用作 Generator 函數。
上面四點中,第一點尤其值得注意。this對象的指向是可變的,但是在箭頭函數中,它是固定的。
要理解其實也很容易,首先……
上代碼……
function foo() { setTimeout(() => { console.log("id:", this.id); }, 100); } var id = 21; foo.call({ id: 42 });
代碼中,setTimeout的參數是一個箭頭函數,這個箭頭函數的定義生效是在foo函數生成時,而它的真正執行要等到 100 毫秒后。如果是普通函數,執行時this應該指向全局對象window,這時應該輸出21。但是,箭頭函數導致this總是指向函數定義生效時所在的對象(本例是{id: 42}),所以輸出的是42。
所以以后有人問你es6的箭頭函數的this和es5函數中的this的區別,那么就是es6不可改變,始終指向定義的對象,es5是根據調用環境的,如果沒有調用者,默認window。
因為考官會出得題目千千萬,題海戰術幾乎無用,我之前也是作用域和上下文傻傻分不清,雖然現在也懵懂階段,不過學無止盡嘛。
有什么表達不當的地方,悄悄告訴我哦~
么么噠
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93816.html
摘要:在上家公司裸辭之后,經過一段時間休整,月中下旬面試了一些公司,由于本人框架使用的是,所以面試題涉及到框架的都是,現將面試題整理一下列舉常用的特性。事件冒泡以及事件捕獲。其他前端分頁和后端分頁優缺點。包含多個子節點及孫節點,遍歷。 在上家公司裸辭之后,經過一段時間休整,5月中下旬面試了一些公司,由于本人框架使用的是vue,所以面試題涉及到框架的都是vue,現將面試題整理一下: es6 ...
摘要:在上家公司裸辭之后,經過一段時間休整,月中下旬面試了一些公司,由于本人框架使用的是,所以面試題涉及到框架的都是,現將面試題整理一下列舉常用的特性。事件冒泡以及事件捕獲。其他前端分頁和后端分頁優缺點。包含多個子節點及孫節點,遍歷。 在上家公司裸辭之后,經過一段時間休整,5月中下旬面試了一些公司,由于本人框架使用的是vue,所以面試題涉及到框架的都是vue,現將面試題整理一下: es6 ...
Create by jsliang on 2019-2-11 15:30:34 Recently revised in 2019-3-17 21:30:36 Hello 小伙伴們,如果覺得本文還不錯,記得給個 star , 小伙伴們的 star 是我持續更新的動力!GitHub 地址 并不是只有特定的季節才能跑路,只因為人跑得多了,這條路就定下來了。 金三銀四跳槽季,jsliang 于 2019...
摘要:前言騰訊一面,相比阿里一面來說,騰訊一面先給打電話預定時間,這也給了我們這些面試者去準備的時間。其實閉包也就是指有權訪問另一個函數作用域的函數而已。常用的創建閉包的方法就是在函數內部創建另一個函數。 前言 騰訊一面,相比阿里一面來說,騰訊一面先給打電話預定時間,這也給了我們這些面試者去準備的時間。但是也正是因為這種確定性,也有在等待電話的時候的心情的忐忑。 背景 我是一名大三學生,大一...
摘要:自我沉淀工作有周報月總結季度年終等各種總結,那么自我學習呢也一樣,今天寫下的點滴,就是對明天的自己最好的饋贈禮物。 showImg(https://segmentfault.com/img/bVbm9ZZ?w=1008&h=298); 前言 歲月不居,時節如流,轉眼間都到2019年1月中旬了,時間過的好快,說好的周末睡到自然醒,但還是跟以往一樣,到上班時間就醒了,這算不算心里只有工作呢...
閱讀 2204·2021-09-02 15:11
閱讀 1513·2019-08-30 15:43
閱讀 2081·2019-08-29 13:48
閱讀 2798·2019-08-26 13:55
閱讀 2107·2019-08-23 15:09
閱讀 2905·2019-08-23 14:40
閱讀 3432·2019-08-23 14:23
閱讀 2642·2019-08-23 14:20