js語(yǔ)法難點(diǎn)

javascript語(yǔ)法難點(diǎn)問(wèn)題

Javascript的變量
javascript語(yǔ)言和java語(yǔ)言一樣變量是分為兩種類型:基本數(shù)據(jù)類型和引用類型。

基本類型是指:Undefined、Null、Boolean、Number和String;
引用類型是指:多個(gè)值構(gòu)成的對(duì)象,所以javascript的對(duì)象指的是引用類型。

1. var str = "sharpxiajun";
2. str.attr01 = "hello world";
3. console.log(str);// 運(yùn)行結(jié)果:sharpxiajun
4. console.log(str.attr01);// 運(yùn)行結(jié)果:undefined

注意:無(wú)法為基本類型指定屬性和方法~
Javascript里的基本變量是存放在棧區(qū)的(棧區(qū)指內(nèi)存里的棧內(nèi)存),它的存儲(chǔ)結(jié)構(gòu)如下圖所示:

Alt text
Alt text

javascript里引用變量的存儲(chǔ)就比基本類型存儲(chǔ)要復(fù)雜多,引用類型的存儲(chǔ)需要內(nèi)存的棧區(qū)和堆區(qū)(堆區(qū)是指內(nèi)存里的堆內(nèi)存)共同完成,如下圖所示:

Alt text
Alt text

在javascript語(yǔ)言里變量定義沒(méi)有使用var,則變量必須要有賦值操作,只有賦值操作的變量是賦予給window,這其實(shí)是javascript語(yǔ)言設(shè)計(jì)者提升javascript安全性的一個(gè)做法。

Alt text
Alt text

復(fù)制變量的值和函數(shù)傳遞參數(shù)
基本類型變量的賦值

Alt text
Alt text

引用類型變量賦值

Alt text
Alt text

函數(shù)傳參

Alt text
Alt text

實(shí)參與形參遵循基本類型變量賦值與引用類型變量賦值的規(guī)則,即 基本類型 傳的是值, 引用類型對(duì)象傳遞的是引用即變量的一個(gè)別名,指向同一塊堆區(qū),下面是詳細(xì)解釋:
在javascript里變量的存儲(chǔ)包含三個(gè)部分:   
部分一:棧區(qū)的變量標(biāo)示符;   
部分二:棧區(qū)變量的值;   
部分三:堆區(qū)存儲(chǔ)的對(duì)象。

Alt text
Alt text
Alt text
Alt text

在javascript里變量的復(fù)制(函數(shù)傳參也是變量賦值)本質(zhì)是傳值,這個(gè)值就是棧區(qū)的值,而基本類型的內(nèi)容是存放在棧區(qū)的值里,所以復(fù)制基本變量后,兩個(gè)變量是獨(dú)立的互不影響,但是當(dāng)復(fù)制的是引用類型時(shí)候,復(fù)制操作還是復(fù)制棧區(qū)的值,但是這個(gè)時(shí)候值是堆區(qū)對(duì)象的地址,因?yàn)閖avascript語(yǔ)言是不允許操作堆內(nèi)存,因此堆內(nèi)存的變量并沒(méi)有被復(fù)制,所以復(fù)制引用對(duì)象復(fù)制的值就是堆內(nèi)存的地址,而復(fù)制雙方的兩個(gè)變量使用的對(duì)象是相同的,因此復(fù)制的變量其中一個(gè)修改了對(duì)象,另一個(gè)變量也會(huì)受到影響。

函數(shù)執(zhí)行會(huì)將參數(shù)存在棧區(qū),變量名 與 實(shí)參傳遞過(guò)來(lái)的值, 當(dāng)我們?cè)诤瘮?shù)中為參數(shù)進(jìn)行賦值,只是改變了參數(shù)在棧區(qū)的值,實(shí)參并不會(huì)收到影響,如下圖所示:

Alt text
Alt text

注意:javascript里變量復(fù)制和函數(shù)傳參都是在傳遞棧區(qū)的值。

作用域鏈相關(guān)的問(wèn)題
講作用域鏈?zhǔn)紫纫獜淖饔糜蛑v起,下面是百度百科里對(duì)作用域的定義:
作用域在許多程序設(shè)計(jì)語(yǔ)言中非常重要。通常來(lái)說(shuō),一段程序代碼中所用到的名字并不總是有效/可用的,而限定這個(gè)名字的可用性的代碼范圍就是這個(gè)名字的作用域。作用域的使用提高了程序邏輯的局部性,增強(qiáng)程序的可靠性,減少名字沖突。

在javascript里作用域有一個(gè)專門的定義execution context,有的書里把這個(gè)名字翻譯成執(zhí)行上下文,有的書籍里把它翻譯成執(zhí)行環(huán)境,我更傾向于后者執(zhí)行環(huán)境,下文我提到的執(zhí)行環(huán)境就是execution context。這個(gè)命名非常形象,這個(gè)形象體現(xiàn)在execution這個(gè)單詞,execution含義就是執(zhí)行,我們來(lái)想想javascript里那些情況是執(zhí)行:
當(dāng)頁(yè)面加載時(shí)候在script標(biāo)簽下的javascript代碼會(huì)按順序執(zhí)行,而這些能被執(zhí)行的代碼都是屬于window的變量或函數(shù);
當(dāng)函數(shù)的名字后面加上小括號(hào)(),例如ftn(),這也是在執(zhí)行,不過(guò)它執(zhí)行的是函數(shù)。

如此說(shuō)來(lái),javascript里的執(zhí)行環(huán)境有兩類一類是全局執(zhí)行環(huán)境,即window代表的全局環(huán)境,一類是函數(shù)代表的函數(shù)執(zhí)行環(huán)境,這也就是我們常說(shuō)的局部作用域。

執(zhí)行環(huán)境在javascript語(yǔ)言里并非是一個(gè)抽象的概念,而是有具體的實(shí)現(xiàn),這個(gè)實(shí)現(xiàn)其實(shí)是個(gè)對(duì)象,這個(gè)對(duì)象也有個(gè)名字叫做variable object,這個(gè)變量有的書里翻譯為變量對(duì)象,這是直譯,有的書里把它稱為上下文變量,這里我還是傾向于后者上下文變量,下文里提到的上下文變量就是指代variable object。上下文變量存儲(chǔ)的是上下文變量所處執(zhí)行環(huán)境里定義的所有的變量和函數(shù)。

全局執(zhí)行環(huán)境的上下文變量是可以訪問(wèn)到的,它就是window對(duì)象,所以我們說(shuō)window能代表全局作用域是有道理的,但是局部作用域即函數(shù)的執(zhí)行環(huán)境里的上下文變量是代碼不能訪問(wèn)到的,不過(guò)javascript引擎在處理數(shù)據(jù)時(shí)候會(huì)使用到它。

在javascript語(yǔ)言里還有一個(gè)概念,它的名字叫做execution context stack,翻譯成中文就是執(zhí)行環(huán)境棧,每個(gè)要被執(zhí)行的函數(shù)都會(huì)先把函數(shù)的執(zhí)行環(huán)境壓入到執(zhí)行環(huán)境棧里,函數(shù)執(zhí)行完畢后,這個(gè)函數(shù)的執(zhí)行環(huán)境就會(huì)被執(zhí)行環(huán)境棧彈出,例如上面的例子:函數(shù)執(zhí)行時(shí)候函數(shù)的執(zhí)行環(huán)境會(huì)被壓入到執(zhí)行環(huán)境棧里,函數(shù)執(zhí)行完畢,執(zhí)行環(huán)境棧會(huì)把這個(gè)環(huán)境彈出,執(zhí)行環(huán)境棧的控制權(quán)就會(huì)交由全局環(huán)境,如果函數(shù)后面還有代碼,那么代碼就是接著執(zhí)行。如果函數(shù)里嵌套了函數(shù),那么嵌套函數(shù)執(zhí)行完畢后,執(zhí)行環(huán)境棧的控制權(quán)就交由了外部函數(shù),然后依次類推,最后就是全局執(zhí)行環(huán)境了。

講到這里我們大名鼎鼎的作用域鏈要登場(chǎng)了,函數(shù)的執(zhí)行環(huán)境被壓入到執(zhí)行環(huán)境棧里后,函數(shù)就要執(zhí)行了,函數(shù)執(zhí)行的第一步不是執(zhí)行函數(shù)里的第一行代碼而是在上下文變量里構(gòu)造一個(gè)作用域鏈,作用域鏈的英文名字叫做scope chain,作用域鏈的作用是保證執(zhí)行環(huán)境里有權(quán)訪問(wèn)的變量和函數(shù)是有序的,這個(gè)概念里有兩個(gè)關(guān)鍵意思:有權(quán)訪問(wèn)和有序,我們看看下面的代碼:

Alt text
Alt text

這個(gè)例子我們發(fā)現(xiàn),ftn2函數(shù)可以訪問(wèn)變量b1,b2,這個(gè)體現(xiàn)了有權(quán)訪問(wèn)的概念,當(dāng)ftn1作用域里改變了b1的值并且把b1變量重新定義為ftn1的局部變量,那么ftn2訪問(wèn)到的b1就是ftn1的,ftn2訪問(wèn)到b1后就不會(huì)在全局作用域里查找b1了,這個(gè)體現(xiàn)了有序性。

作用域鏈的相關(guān)問(wèn)題,這個(gè)標(biāo)題定義的含義是指作用域鏈?zhǔn)谴竺Χα?,但是作用域鏈在廣大程序員的理解里其實(shí)包含的意義已經(jīng)超越了作用域鏈在javascript語(yǔ)言本身的定義。廣大程序員對(duì)作用域鏈的理解有兩塊一塊是作用域,而作用域在javascript語(yǔ)言里指的是執(zhí)行環(huán)境execution context,執(zhí)行環(huán)境在javascript引擎里是通過(guò)上下文變量體現(xiàn)的variable object,javascript引擎里還有一個(gè)概念就是執(zhí)行環(huán)境棧execution context stack,當(dāng)某一個(gè)函數(shù)的執(zhí)行環(huán)境壓入到了執(zhí)行環(huán)境棧里,這個(gè)時(shí)候就會(huì)在上下文變量里構(gòu)造一個(gè)對(duì)象,這個(gè)對(duì)象就是作用域鏈scope chain,而這個(gè)作用域鏈就是廣大程序員理解的第二塊知識(shí),作用域鏈的作用是保證執(zhí)行環(huán)境里有權(quán)訪問(wèn)的變量和函數(shù)是有序的,作用域鏈的變量只能向上訪問(wèn),變量訪問(wèn)到window對(duì)象即被終止,作用域鏈向下訪問(wèn)變量是不被允許的。

this指針構(gòu)造是和作用域鏈同時(shí)發(fā)生的,也就是說(shuō)在上文變量構(gòu)建作用域鏈的同時(shí)還會(huì)構(gòu)造一個(gè)this對(duì)象,this對(duì)象也是屬于上下文變量,而this變量的值就是當(dāng)前執(zhí)行環(huán)境外部的上下文變量的一份拷貝,這個(gè)拷貝里是沒(méi)有作用域鏈變量

就上一個(gè)例子我們看一下chrome控制臺(tái)的情況:

Alt text
Alt text

這是ftn2執(zhí)行時(shí)的作用域鏈 local(ftn2) -> closure(ftn1) -> global(window)

Alt text
Alt text

這是ftn1執(zhí)行時(shí)的作用域鏈
最外層棧為一個(gè)anonymous function(匿名函數(shù))

this、new、call和apply的相關(guān)問(wèn)題
在面向?qū)ο缶幊汤镉袃蓚€(gè)重要的概念:一個(gè)是類,一個(gè)是實(shí)例化的對(duì)象,類是一個(gè)抽象的概念,用個(gè)形象的比喻表述的話,類就像一個(gè)模具,而實(shí)例化對(duì)象就是通過(guò)這個(gè)模具制造出來(lái)的產(chǎn)品,實(shí)例化對(duì)象才是我們需要的實(shí)實(shí)在在的東西,類和實(shí)例化對(duì)象有著很密切的關(guān)系,但是在使用上類的功能是絕對(duì)不能取代實(shí)例化對(duì)象,就像模具和模具制造的產(chǎn)品的關(guān)系,二者的用途是不相同的。

其實(shí)javascript里的this指針邏輯上的概念也是實(shí)例化對(duì)象,這一點(diǎn)和java語(yǔ)言里的this指針是一致的,但是javascript里的this指針卻比java里的this難以理解的多,有三個(gè)原因:

原因一:javascript是一個(gè)函數(shù)編程語(yǔ)言,怪就怪在它也有this指針,說(shuō)明這個(gè)函數(shù)編程語(yǔ)言也是面向?qū)ο蟮恼Z(yǔ)言,說(shuō)的具體點(diǎn),javascript里的函數(shù)是一個(gè)高階函數(shù),編程語(yǔ)言里的高階函數(shù)是可以作為對(duì)象傳遞的,同時(shí)javascript里的函數(shù)還有可以作為構(gòu)造函數(shù),這個(gè)構(gòu)造函數(shù)可以創(chuàng)建實(shí)例化對(duì)象,結(jié)果導(dǎo)致方法執(zhí)行時(shí)候this指針的指向會(huì)不斷發(fā)生變化,很難控制。

原因二:javascript里的全局作用域?qū)his指針有很大的影響,由上面java的例子我們看到,this指針只有在使用new操作符后才會(huì)生效,但是javascript里的this在沒(méi)有進(jìn)行new操作也會(huì)生效,這時(shí)候this往往會(huì)指向全局對(duì)象window。

原因三:javascript里call和apply操作符可以隨意改變this指向,這看起來(lái)很靈活,但是這種不合常理的做法破壞了我們理解this指針的本意,同時(shí)也讓寫代碼時(shí)候很難理解this的真正指向.

在javascript語(yǔ)言里全局作用域可以理解為window對(duì)象,記住window是對(duì)象而不是類,也就是說(shuō)window是被實(shí)例化的類,這個(gè)實(shí)例化的過(guò)程是在頁(yè)面加載時(shí)候由javascript引擎完成的,整個(gè)頁(yè)面里的要素都被濃縮到這個(gè)window對(duì)象,因?yàn)槌绦騿T無(wú)法通過(guò)編程語(yǔ)言來(lái)控制和操作這個(gè)實(shí)例化過(guò)程,所以開發(fā)時(shí)候我們就沒(méi)有構(gòu)建這個(gè)this指針的感覺(jué),常常會(huì)忽視它,這就是干擾我們?cè)诖a里理解this指針指向window的情形。

Alt text
Alt text

下面為Person的構(gòu)造函數(shù)

1.<script type="text/javascript">
2. function Person(name,sex,age,job){
3.   this.name = name;
4.   this.sex = sex;
5.   this.age = age;
6.   this.job = job;
7.   this.showPerson = function(){
8.   console.log("姓名:" + this.name);
9.   console.log("性別:" + this.sex);
10.   console.log("年齡:" + this.age);
11.   console.log("工作:" + this.job);
12.   console.log(this);// Person { name="馬云", sex="男", age=46, 更多...}
13.   }
14. }
15. var person = new Person("馬云", "男", 46, "董事長(zhǎng)");
16. person.showPerson();
17.</script>

看this指針的打印,類變成了Person,這表明function Person就是相當(dāng)于在定義一個(gè)類,在javascript里function的意義實(shí)在太多,function既是函數(shù)又可以表示對(duì)象,function是函數(shù)時(shí)候還能當(dāng)做構(gòu)造函數(shù),javascript的構(gòu)造函數(shù)我常認(rèn)為是把類和構(gòu)造函數(shù)合二為一,當(dāng)然在javascript語(yǔ)言規(guī)范里是沒(méi)有類的概念,但是我這種理解可以作為構(gòu)造函數(shù)和普通函數(shù)的一個(gè)區(qū)別,這樣理解起來(lái)會(huì)更加容易些。

《javascript高級(jí)編程》里對(duì)new操作符的解釋:
new操作符會(huì)讓構(gòu)造函數(shù)產(chǎn)生如下變化:

  1. 創(chuàng)建一個(gè)新對(duì)象;
  2. 將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此this就指向了這個(gè)新對(duì)象);
  3. 執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加屬性);
  4. 返回新對(duì)象

Call和apply是改變函數(shù)的作用域(有些書里叫做改變函數(shù)的上下文)  
這個(gè)說(shuō)明我們參見上面new操作符第二條:   
將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此this就指向了這個(gè)新對(duì)象);   
Call和apply是將this指針指向方法的第一個(gè)參數(shù)。

表面原因就是我們定義對(duì)象使用對(duì)象的字面表示法,字面表示法在簡(jiǎn)單的表示里我們很容易知道this指向?qū)ο蟊旧?,但是這個(gè)對(duì)象會(huì)有方法,方法的參數(shù)可能會(huì)是函數(shù),而這個(gè)函數(shù)的定義里也可能會(huì)使用this指針,如果傳入的函數(shù)沒(méi)有被實(shí)例化過(guò)和被實(shí)例化過(guò),this的指向是不同,有時(shí)我們還想在傳入函數(shù)里通過(guò)this指向外部函數(shù)或者指向被定義對(duì)象本身,這些亂七八糟的情況使用交織在一起導(dǎo)致this變得很復(fù)雜,結(jié)果就變得糊里糊涂。
  其實(shí)理清上面情況也是有跡可循的,就以定義對(duì)象里的方法里傳入函數(shù)為例:
  情形一:傳入的參數(shù)是函數(shù)的別名,那么函數(shù)的this就是指向window;
  情形二:傳入的參數(shù)是被new過(guò)的構(gòu)造函數(shù),那么this就是指向?qū)嵗膶?duì)象本身;
  情形三:如果我們想把被傳入的函數(shù)對(duì)象里this的指針指向外部字面量定義的對(duì)象,那么我們就是用apply和call
例子如下:

Alt text
Alt text

注意:如果在javascript語(yǔ)言里沒(méi)有通過(guò)new(包括對(duì)象字面量定義)、call和apply改變函數(shù)的this指針,函數(shù)的this指針都是指向window的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容