??【編譯】代碼是如何運(yùn)行的之JavaScript執(zhí)行過程

Q:代碼是如何運(yùn)行的对供?

代碼是由CPU執(zhí)行的藕甩,而目前的CPU并不能直接執(zhí)行諸如if…else之類的語句橘蜜,它只能執(zhí)行二進(jìn)制指令含鳞。但是二進(jìn)制指令對(duì)人類實(shí)在是太不友好了:我們很難快速準(zhǔn)確的判斷一個(gè)二進(jìn)制指令1000010010101001代表什么?所以科學(xué)家們發(fā)明匯編語言(實(shí)際上就是二進(jìn)制指令的助記符)延曙。

假設(shè)10101010代表讀取內(nèi)存操作豌鹤,內(nèi)存地址是10101111,寄存器地址是11111010枝缔,那么完整的操作101010101010111111111010就代表讀取某個(gè)內(nèi)存地址的值并裝載到寄存器布疙,而匯編語言并沒有改變這種操作方式,它只是二進(jìn)制指令的映射:

LD:10101010 
id:10101111
R:11111010

這樣上述指令就可以表達(dá)為LD id R 愿卸,大大增強(qiáng)了代碼的可讀性灵临。

但是這樣還不夠友好,CPU只能執(zhí)行三地址表達(dá)式趴荸,和人的思考方式儒溉、語言模式相距甚遠(yuǎn)。所以偉大的科學(xué)家們又發(fā)明了高級(jí)語言发钝。

高級(jí)語言之所以稱之為“高級(jí)”顿涣,就是因?yàn)樗臃衔覀兊乃季S和閱讀習(xí)慣。if…else這種語句看起來要比1010101010舒服的多了酝豪。但是計(jì)算機(jī)并不能直接執(zhí)行高級(jí)語言涛碑,所以還需要把高級(jí)語言轉(zhuǎn)化為匯編語言/機(jī)器指令才能執(zhí)行。這個(gè)過程就是編譯寓调。

JavaScript是什么類型的語言锌唾?

  1. JavaScript 動(dòng)態(tài)類型動(dòng)態(tài)語言锄码;
    在運(yùn)行時(shí)代碼可以根據(jù)某些條件改變自身結(jié)構(gòu)夺英,如JavaScript在運(yùn)行時(shí)新的函數(shù)、對(duì)象滋捶、甚至代碼都可以被引進(jìn)(eval)痛悯,因此JavaScript是動(dòng)態(tài)語言。JavaScript數(shù)據(jù)類型不是在編譯階段確定重窟,而是在運(yùn)行時(shí)確定载萌,所以JavaScript是動(dòng)態(tài)類型的語言。
  2. JavaScript是 解釋型語言弱類型巡扇,JavaScript 代碼需要在機(jī)器(node 或者瀏覽器)上安裝一個(gè)工具(JS 引擎)才能執(zhí)行扭仁,這是解釋型語言所需要的。在生成 AST 之后厅翔,就開始一邊解釋乖坠,一邊執(zhí)行。

Q:JavaScript需要編譯嗎刀闷?

MDN定義.png

與傳統(tǒng)的編譯語言不同熊泵,它不是提前編譯的仰迁,編譯結(jié)果也不能在分布式系統(tǒng)中進(jìn)行移植。但是JavaScript引擎進(jìn)行編譯的步驟和傳統(tǒng)的編譯語言非常相似顽分,在某些環(huán)節(jié)可能比預(yù)想的要復(fù)雜徐许,具體表現(xiàn)在:

  • JavaScript引擎在語法分析和代碼生成階段有特定的步驟來對(duì)運(yùn)行性能進(jìn)行優(yōu)化,包括對(duì)冗余元素進(jìn)行優(yōu)化等卒蘸。
  • 與其他編譯語言不同雌隅,JavaScript的編譯過程不是發(fā)生在構(gòu)建之前的,因此JavaScript引擎不會(huì)有大量的時(shí)間進(jìn)行優(yōu)化缸沃。
  • 對(duì)于JavaScript澄步,大部分情況下編譯發(fā)生在代碼執(zhí)行前的幾微秒(甚至更短)。
  • JavaScript引擎用盡了各種辦法(如JIT和泌,可以延遲編譯甚至實(shí)施重編譯)來保證性能最佳村缸。

JavaScript是如何運(yùn)行的

在傳統(tǒng)編譯語言的流程中,程序中的一段源代碼在執(zhí)行之前會(huì)經(jīng)歷一系列步驟武氓,統(tǒng)稱為“編譯”梯皿。
常見編譯型語言(例如:Java)來說,編譯步驟分為:詞法分析->語法分析->語義檢查->代碼優(yōu)化和字節(jié)碼生成县恕。
對(duì)于解釋型語言(例如 JavaScript)來說东羹,通過詞法分析 -> 語法分析 -> 語法樹,生成 AST 之后忠烛,就開始一邊解釋属提,一邊執(zhí)行。

在JavaScript的執(zhí)行過程主要有以下幾個(gè)關(guān)鍵角色:

  • 引擎:從頭到尾負(fù)責(zé)整個(gè)JavaScript程序的編譯及執(zhí)行過程美尸;
  • 編譯器:負(fù)責(zé)語法分析以及代碼生成等冤议;
  • 作用域:負(fù)責(zé)收集并維護(hù)由所有聲明的標(biāo)識(shí)符(變量)組成的一系列查詢,并實(shí)施一套非常嚴(yán)格的規(guī)則师坎,確定當(dāng)前執(zhí)行的代碼對(duì)這些標(biāo)識(shí)符的訪問權(quán)限恕酸。作用域本質(zhì)上就是程序存儲(chǔ)和訪問變量的規(guī)則

我們帶著Q:變量住在哪里胯陋?它們儲(chǔ)存在哪里蕊温?程序需要時(shí)如何找到它們?一起看看JavaScript的具體執(zhí)行過程:

  1. 分詞/詞法分析(Tokenizing/Lexing)
    編譯器將由字符組成的字符串分解成(對(duì)編程語言來說)有意義的代碼塊遏乔,這些代碼塊被稱為詞法單元(token)义矛。如果是有狀態(tài)的解析,還會(huì)賦予單詞語義盟萨。
    詞法單元生成器在判斷a是一個(gè)獨(dú)立的詞法單元還是其他詞法單元的一部分時(shí)凉翻,調(diào)用的是有狀態(tài)的解析規(guī)則,那么這個(gè)過程就被稱為詞法分析鸯旁。

For example

如程序var a = 2;通常會(huì)被分解為詞法單元:var噪矛、a量蕊、=、2艇挨、; 具體如下圖所示残炮。并且給它們加上標(biāo)注,代表這是一個(gè)變量還是一個(gè)操作缩滨∈凭停空格是否會(huì)被當(dāng)作詞法單元,取決于空格在這門語言中是否具有意義脉漏。

分詞.png

  1. 解析/語法分析(Parsing)
    語法分析的任務(wù)是在詞法分析的基礎(chǔ)上將單詞序列組合成各類語法短語苞冯,如“程序”,“語句”侧巨,“表達(dá)式”等等舅锄。
    語法分析程序判斷源程序在結(jié)構(gòu)上是否正確。如var str ='s ;這就是典型的語法錯(cuò)誤司忱,這種代碼無法生成AST皇忿,在詞法分析階段就會(huì)報(bào)錯(cuò)。通常我們這么寫代碼坦仍,IDE 就會(huì)報(bào)錯(cuò)鳍烁。這是IDE的優(yōu)化工作,和詞法分析相關(guān)繁扎。
    將詞法單元流(數(shù)組)轉(zhuǎn)換成一個(gè)由元素逐級(jí)嵌套所組成的代表了程序語法結(jié)構(gòu)的樹幔荒。這個(gè)樹被稱為“抽象語法樹”(Abstract Syntax Tree,AST)梳玫。

For example

上述例子var a = 2;被分解的詞法單元在語法分析階段會(huì)被轉(zhuǎn)換成如下結(jié)構(gòu):

AST.png

  1. 預(yù)編譯(開放內(nèi)存空間爹梁,存放變量和函數(shù))
    當(dāng)JavaScript引擎解析腳本時(shí),它會(huì)先在預(yù)編譯期對(duì)所有聲明的變量和函數(shù)進(jìn)行處理汽纠!編譯階段的一部分工作就是找到所有的聲明卫键,并用合適的作用域?qū)⑺鼈冴P(guān)聯(lián)起來傀履,因此這個(gè)過程編譯器和作用域會(huì)進(jìn)行如下互動(dòng):
    預(yù)編譯-編譯器.png

    ?? 預(yù)編譯階段沒有初始化行為(賦值)虱朵,匿名函數(shù)不參與預(yù)編譯。只有在解釋執(zhí)行階段才會(huì)進(jìn)行變量初始化钓账。
    JavaScript的作用域才有詞法作用域工作模型碴犬,JavaScript 的變量和函數(shù)作用域是在定義時(shí)決定的,而不是執(zhí)行時(shí)決定的梆暮。

例:看一個(gè)簡(jiǎn)單的聲明語句var name = 'bubble'服协;在JS引擎眼里,它包含兩個(gè)聲明啦粹,其中

  • var name編譯時(shí)(此步驟由編譯器)處理偿荷,
  • name=bubble運(yùn)行時(shí)處理窘游,即第4步(解釋執(zhí)行由JS引擎處理)。
  1. 解釋執(zhí)行
    在執(zhí)行過程中跳纳,JavaScript 引擎是嚴(yán)格按著作用域機(jī)制(scope)來執(zhí)行的忍饰。引擎在運(yùn)行時(shí)會(huì)完成對(duì)變量的賦值操作,因此和作用域有如下互動(dòng):
    解釋執(zhí)行-JS引擎.png

    作用域套作用域寺庄,就有了作用域鏈:
    JavaScript 引擎通過作用域鏈(scope chain)把多個(gè)嵌套的作用域串連在一起艾蓝,并借助這個(gè)鏈條幫助 JavaScript 解釋器檢索變量的值。這個(gè)作用域鏈相當(dāng)于一個(gè)索引表斗塘,并通過編號(hào)來存儲(chǔ)它們的嵌套關(guān)系赢织。當(dāng) JavaScript 解釋器檢索變量的值,會(huì)按著這個(gè)索引編號(hào)進(jìn)行快速查找馍盟,直到找到全局對(duì)象(global object)為止于置,如果沒有找到值,則傳遞一個(gè)特殊的 undefined 值贞岭。
var scope = "global";
scopeTest();
function scopeTest(){  
    console.log(scope);  
    var scope = "local";  
    console.log(scope); 
}
打印結(jié)果:undefined俱两,local;

而引擎查找變量的方式會(huì)直接影響到查找的結(jié)果曹步,尤其在變量未聲明的情況下:


引擎查找變量的方式.png

總結(jié)一下宪彩,任何JavaScript代碼片段在執(zhí)行前都要進(jìn)行編譯(通常就在執(zhí)行前)。因此讲婚,JavaScript編譯器首先會(huì)對(duì)var a = 2;這段程序進(jìn)行編譯尿孔,然后做好執(zhí)行它的準(zhǔn)備,并且通常馬上就會(huì)執(zhí)行它筹麸。

讓我們看看引擎對(duì)下面這段代碼做了什么吧活合!

<script>
    var a = 1; // 變量聲明
    function b(y){ //函數(shù)聲明
        var x = 1;
        console.log('so easy');
    };
    var c = function(){ //變量聲明
        //...
    }
    b(100);
</script>
<script>
    var d = 0;
</script>
  1. 頁面產(chǎn)生便創(chuàng)建了GO全局對(duì)象(Global Object),也就是window對(duì)象物赶;
  2. 第一個(gè)script腳本文件加載白指;
  3. 腳本文件加載后,分析語法是否合法酵紫;
  4. 開始預(yù)編譯:
  • 查找變量聲明告嘲,作為GO屬性,值賦予undefined奖地;
  • 查找函數(shù)聲明橄唬,作為GO屬性,值賦予函數(shù)體参歹;
GO/window = {
    //頁面加載創(chuàng)建GO同時(shí)仰楚,創(chuàng)建了document、screen等屬性
    a: undefined,
    c: undefined,
    b: function(y){
        var x = 1;
        console.log('so easy');
    }
}
  1. 解釋執(zhí)行代碼,找到變量并賦值(直到執(zhí)行函數(shù)b)
GO/window = {
    a: 1,
    c: function(){ },
    b: function(y){
        var x = 1;
        console.log('so easy');
    }
}
  1. 執(zhí)行函數(shù)b之前僧界,發(fā)生預(yù)編譯:
  • 創(chuàng)建AO活動(dòng)對(duì)象(Active Object)
  • 查找函數(shù)形參及函數(shù)內(nèi)變量聲明侨嘀,形參名及變量名作為AO對(duì)象的屬性,值為undefined
  • 實(shí)參值賦給形參
AO = {
    //創(chuàng)建AO同時(shí)捂襟,創(chuàng)建了arguments等等屬性飒炎,此處省略
    y: 100,
    x: undefined
}
  1. 解釋執(zhí)行函數(shù)中的代碼;
    x=1 輸出so easy
  2. 第一個(gè)腳本文件執(zhí)行完畢笆豁,加載第二個(gè)腳本文件
  3. 第二個(gè)腳本文件加載完畢后郎汪,進(jìn)行語法分析
  4. 語法分析完畢,開始預(yù)編譯
    重復(fù)最開始的預(yù)編譯步驟……

測(cè)試

  1. 例1
function foo() {
    console.log(a);
    a = 1;
}

foo(); // Uncaught ReferenceError: a is not defined

function bar() {
    a = 1;
    console.log(a);
}
bar(); // 1

這是因?yàn)楹瘮?shù)中的 "a" 并沒有通過 var 關(guān)鍵字聲明闯狱,所有不會(huì)被存放在 AO 中煞赢。沒有 a 的值,然后就會(huì)到全局去找哄孤,全局也沒有照筑,所以會(huì)報(bào)錯(cuò)。

  1. 例2
console.log(foo);

function foo(){
    console.log("foo");
}
var foo = 1;

會(huì)打印函數(shù)瘦陈,而不是 undefined 凝危。
這是因?yàn)樵谶M(jìn)入執(zhí)行上下文時(shí),首先會(huì)處理函數(shù)聲明晨逝,其次會(huì)處理變量聲明蛾默,如果如果變量名稱跟已經(jīng)聲明的形式參數(shù)或函數(shù)相同,則變量聲明不會(huì)干擾已經(jīng)存在的這類屬性捉貌。


下方開始大量擴(kuò)展知識(shí)

編程語言的分類

與硬件的距離

  • 比較低級(jí) Low-level 語言
    最低級(jí)的語言就是機(jī)器語言支鸡,由0和1構(gòu)成,通過面板趁窃、打孔卡輸入牧挣。
    接下來是匯編語言,它對(duì)硬件指令做了簡(jiǎn)單的封裝醒陆,一些操作可以用ADD瀑构、MOVE等英文單詞來表示。目前在內(nèi)核/驅(qū)動(dòng)中會(huì)被用到刨摩。
  • 比較高級(jí) High-level 語言
    除了上面兩種寺晌,其他語言都是高級(jí)語言,將很多細(xì)節(jié)交由計(jì)算機(jī)(編譯器)把控码邻,同時(shí)變得更加抽象折剃。高級(jí)語言中也有相對(duì)低級(jí)和高級(jí)的。如C屬于非常低級(jí)的高級(jí)語言像屋,因?yàn)镃語言中也還是時(shí)不時(shí)的會(huì)用到硬件知識(shí)。而類似Ruby或JS這樣的腳本語言就基本不用操心硬件的事了边篮。

一般來講己莺,跟硬件離的越近奏甫,就越能通過打磨去挖掘硬件潛力,寫成的程序執(zhí)行效率就會(huì)越高凌受,但是開發(fā)效率肯定也就越低阵子。

是否需要編譯

所有語言最終都需要轉(zhuǎn)變?yōu)闄C(jī)器碼,基于其轉(zhuǎn)換為機(jī)器碼的方式胜蛉,高級(jí)語言可大致分為編譯型和解釋型兩類(匯編語言無須編譯或解釋挠进,僅需匯編成機(jī)器碼)

  • 編譯型語言(Compiled Language)
    利用編譯器先將代碼編譯為機(jī)器碼,再加以運(yùn)行誊册。如C++代碼在Windows上會(huì)編譯成.obj文件领突,而在Linux上則生成.o文件。

  • 解釋型語言(Interpreted Language)
    利用解釋器案怯,在運(yùn)行期間君旦,動(dòng)態(tài)將代碼逐行解釋(Interpret)為機(jī)器代碼執(zhí)行。
    有時(shí)也叫腳本語言(Scripting Language)嘲碱。如Python金砍,Ruby、BASIC麦锯、JavaScript恕稠,寫好之后無需編譯,直接運(yùn)行于自己的解釋器之上扶欣。

編譯型語言的運(yùn)行速度更快(因?yàn)橐呀?jīng)預(yù)先編譯好谱俭,運(yùn)行時(shí)無須執(zhí)行解釋這一步驟),而因此宵蛀,編譯型語言的開發(fā)/調(diào)試時(shí)間也較長(zhǎng)昆著,因?yàn)槊看握{(diào)試之前都需要編譯一次。而解釋型語言則可以快速的測(cè)試和調(diào)試术陶,因?yàn)楦布袅艘粚哟斩孕噬弦话闶潜容^低的,但功能上可以更為靈活梧宫。

  • 半解釋半編譯
    Java就是兩種類型的結(jié)合典型接谨。無論是在什么操作系統(tǒng)上.java文件編譯出的都是.class文件(這就是字節(jié)碼文件,一種中間形態(tài)的目標(biāo)代碼)塘匣。然后Java對(duì)不同的系統(tǒng)提供不同的Java虛擬機(jī)用于解釋執(zhí)行字節(jié)碼文件脓豪。解釋執(zhí)行并不生成目標(biāo)代碼,但其最終還是要轉(zhuǎn)為匯編/二進(jìn)制指令來給計(jì)算機(jī)執(zhí)行的忌卤。
    Java采用半解釋半編譯的好處就是大大提升了開發(fā)效率扫夜,然而相應(yīng)的則降低了代碼的執(zhí)行效率,畢竟虛擬機(jī)是有性能損失的。

編程范式(Programming Paradigms)

也叫編程范型笤闯、編程典范堕阔,基于編程語言的特點(diǎn)而進(jìn)行分類的方式,一種語言可以支持超過一種編程范型颗味,常見范式如下:

命令式和聲明式
這是兩個(gè)相對(duì)/并列的范式超陆,命令式編程描述過程 ,聲明式編程描述目標(biāo)浦马。

  1. 命令式編程(Imperative programming)
    命令式編程描述計(jì)算所需作出的行為时呀。幾乎所有的計(jì)算機(jī)硬件都是命令式的。
    子范式:過程式和面向?qū)ο笫骄^程式更靠近機(jī)器谨娜,面向?qū)ο笫礁N近程序員。
  • 過程式編程(Procedural programming)
    把操作轉(zhuǎn)換成語句一步步的去做荤胁,主要使用順序瞧预、條件選擇、循環(huán)三種基本結(jié)構(gòu)來編寫程序仅政。
    來源于結(jié)構(gòu)化編程垢油,其概念基于過程(procedure、routine圆丹、subroutine滩愁、function),過程由一系列可執(zhí)行可計(jì)算的步驟構(gòu)成辫封。
    Fortran硝枉、ALGOL、COBOL倦微、BASIC妻味、Pascal和C等語言采用過程式編程。
  • 面向?qū)ο笫骄幊蹋∣bject-oriented programming)
    具有對(duì)象概念的編程范式欣福,先把數(shù)據(jù)封裝成對(duì)象责球,通過對(duì)象之間的交互來實(shí)現(xiàn)功能。
    重要的面向?qū)ο缶幊陶Z言包括Common Lisp拓劝、Python雏逾、C++、Java郑临、C#栖博、Perl、PHP厢洞、Ruby仇让、Swift等典奉。
  1. 聲明式編程(Declarative programming)
    聲明式編程描述目標(biāo)的性質(zhì),讓計(jì)算機(jī)明白目標(biāo)妹孙,而非流程秋柄。聲明式編程通常被定義為所有的“非命令式編程”获枝。
    聲明式編程包括數(shù)據(jù)庫查詢語言(SQL)蠢正、正則表達(dá)式、邏輯式編程省店、函數(shù)式編程和configuration management嚣崭。聲明式編程通常用作解決人工智能和約束滿足問題。
    子范型:函數(shù)式編程懦傍、邏輯式編程雹舀、約束式編程、數(shù)據(jù)流式編程
  • 函數(shù)式編程(Functional programming)
    又稱泛函編程粗俱,它將計(jì)算視為數(shù)學(xué)上的函數(shù)運(yùn)算说榆,避免變量或狀態(tài)(只有函數(shù)及其參數(shù))。其最重要的基礎(chǔ)是λ演算(lambda calculus)寸认,λ演算的函數(shù)可以接受函數(shù)當(dāng)作輸入和輸出签财。
    分為純函數(shù)式編程(Purely functional programming)和函數(shù)邏輯式編程(Functional logic programming,函數(shù)式編程和邏輯式編程的組合)
  • 邏輯式編程(Logic programming)
    邏輯式編程基于數(shù)理邏輯偏塞,它設(shè)置答案所須匹配的規(guī)則來解決問題唱蒸,而非設(shè)置步驟來解決問題。過程為:事實(shí)+規(guī)則=結(jié)果灸叼。
    最常用的邏輯式編程語言是Prolog神汹,Mercury則較適用于大型方案。
  • 約束式編程(Constraint programming)
    在這種范式中古今,變量之間的關(guān)系是以約束的形式陳述的屁魏,它們并非明確說明了要去執(zhí)行步驟的某一步,而是規(guī)范其解的一些屬性捉腥。
  • 數(shù)據(jù)流式編程(Dataflow programming)
    將程序建模為一個(gè)描述數(shù)據(jù)流的有向圖氓拼。例如BLODI。

結(jié)構(gòu)化和非結(jié)構(gòu)化
這是兩個(gè)相對(duì)的范式但狭,非結(jié)構(gòu)化編程是最早的編程范式披诗,現(xiàn)今的計(jì)算機(jī)科學(xué)家都同意結(jié)構(gòu)化編程的好處。

  1. 結(jié)構(gòu)化編程(Structured programming)
    通過子程序立磁、代碼塊呈队、for循環(huán)、while循環(huán)等結(jié)構(gòu)來取代之前的goto語句唱歧,以提高代碼的清晰程度宪摧,獲得更好的可讀性×J現(xiàn)今的大部分高級(jí)語言都是結(jié)構(gòu)化的。
    結(jié)構(gòu)化編程的流程包括順序几于、選擇(if, else, switch)蕊苗、循環(huán)(for, while)幾類。
  2. 非結(jié)構(gòu)化編程(Non-structured programming)
    是最早的編程范式沿彭,相對(duì)于結(jié)構(gòu)化編程朽砰,特點(diǎn)是其控制流是通過(容易引起混亂的)goto語句跳轉(zhuǎn)實(shí)現(xiàn)的。非結(jié)構(gòu)化編程包括機(jī)器語言喉刘、匯編語言瞧柔、MS-DOS batch、以及早期的BASIC及Fortran等等睦裳。

  1. 泛型編程(Generic programming)
    泛型允許程序員在用強(qiáng)類型語言編寫代碼時(shí)使用一些以后才指定的類型造锅。
    Ada碰声、Delphi嗽交、C#萨螺、Java织狐、Swift稱之為泛型(generics)茬末,Scala和Haskell稱之為參數(shù)多態(tài)(parametric polymorphism)赡盘,C++稱之為模板粤策。

動(dòng)態(tài)or靜態(tài)分類

動(dòng)態(tài)語言(Dynamic programming language)在運(yùn)行時(shí)可以改變自身結(jié)構(gòu):新的函數(shù)型奥、對(duì)象甚至代碼可以被引進(jìn)宇驾,已有的函數(shù)可以被刪除或有其他結(jié)構(gòu)上的變化倍靡。JavaScript、PHP课舍、Python塌西、Ruby屬于動(dòng)態(tài)語言,而C和C++則不屬于動(dòng)態(tài)語言筝尾。
大部分動(dòng)態(tài)語言都使用動(dòng)態(tài)類型捡需,但也有些不是。

語言類型系統(tǒng)(Type system)分類

  1. 動(dòng)態(tài)和靜態(tài)類型檢查
  • 靜態(tài)類型檢查
    對(duì)類型的檢查發(fā)生在編譯時(shí)筹淫。編譯語言通常使用靜態(tài)類型檢查站辉。
  • 動(dòng)態(tài)類型檢查
    對(duì)類型的檢查發(fā)生在運(yùn)行時(shí)。動(dòng)態(tài)類型檢查經(jīng)常出現(xiàn)在腳本語言和解釋型語言中损姜。
    大部分動(dòng)態(tài)語言都使用動(dòng)態(tài)類型饰剥,但也有些不是。
  1. 強(qiáng)弱類型
    按照編程語言對(duì)于混入不同數(shù)據(jù)類型的值進(jìn)行運(yùn)算時(shí)的處理方式不同分為強(qiáng)類型和弱類型摧阅。
  • 強(qiáng)類型(Strongly typed)
    強(qiáng)類型的語言遇到函數(shù)形參和實(shí)參的類型不匹配時(shí)通常會(huì)失敗汰蓉。
  • 弱類型(Weakly/Loosely typed)
    弱類型的語言常常會(huì)進(jìn)行隱式的轉(zhuǎn)換(并可能造成不可知的后果)。
  1. 類型安全和內(nèi)存安全
    按照類型運(yùn)算和轉(zhuǎn)換的安全性不同分為類型安全和內(nèi)存安全棒卷。通常來說顾孽,類型安全和內(nèi)存安全是同時(shí)存在的祝钢。
  • 類型安全
    它不允許導(dǎo)致不正確的情況的運(yùn)算或轉(zhuǎn)換,計(jì)算機(jī)科學(xué)就認(rèn)為該語言是類型安全的若厚。
  • 內(nèi)存安全
    指程序不被允許訪問沒有分配給它的內(nèi)存拦英,比如:內(nèi)存安全語言會(huì)做數(shù)組邊界檢查。
    比如以下例子:
var x:= 5
var y:= “37”
var z:= x + y

上例中的z的值為42测秸,不管編寫者有沒有這個(gè)意圖疤估,該語言定義了明確的結(jié)果,且程序不會(huì)就此崩潰乞封,或?qū)⒉幻鞫x的值賦給z做裙。就這方面而言岗憋,這樣的語言就是類型安全的肃晚。
再比如:

int x = 5
char y[] = “37”
char* z = x + y

在這個(gè)例子中,z將會(huì)指向一個(gè)超過y地址5個(gè)字節(jié)的存儲(chǔ)器地址仔戈,相當(dāng)于指向y字符串的指針之后的兩個(gè)空字符之處关串。這個(gè)地址的內(nèi)容尚未定義,且有可能超出存儲(chǔ)器的定址界線监徘,這就是一個(gè)類型不安全/內(nèi)存不安全的語言晋修。

  1. 顯式聲明和隱式暗示
    許多靜態(tài)類型系統(tǒng),如C和Java凰盔,要求變量聲明類型:編寫者必須以指定類型明確地關(guān)系到每一個(gè)變量上墓卦。其它的,如Haskell户敬,則進(jìn)行類型推斷:編譯器根據(jù)編寫者如何運(yùn)用這些變量落剪,以草擬出關(guān)于這個(gè)變量的類型的結(jié)論。
    例如尿庐,給定一個(gè)函數(shù)f(x,y)忠怖,它將x和y加起來,編譯器可以推斷出x和y必須是數(shù)字——因?yàn)榧臃▋H定義給數(shù)字抄瑟。因此凡泣,任何在其它地方以非數(shù)值類型(如字符串或鏈表)作為參數(shù)來調(diào)用f的話,將會(huì)發(fā)出一個(gè)錯(cuò)誤皮假。
    在代碼中數(shù)值鞋拟、字符串常量以及表達(dá)式,經(jīng)橙亲剩可以在詳細(xì)的前后文中暗示類型贺纲。例如,一個(gè)表達(dá)式3.14可暗示浮點(diǎn)數(shù)類型布轿;而[1, 2, 3]則可暗示一個(gè)整數(shù)的鏈表哮笆;通常是一個(gè)數(shù)組来颤。

推薦閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末稠肘,一起剝皮案震驚了整個(gè)濱河市福铅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌项阴,老刑警劉巖滑黔,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異环揽,居然都是意外死亡略荡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門歉胶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汛兜,“玉大人,你說我怎么就攤上這事通今≈嗝” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵辫塌,是天一觀的道長(zhǎng)漏策。 經(jīng)常有香客問我,道長(zhǎng)臼氨,這世上最難降的妖魔是什么掺喻? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮储矩,結(jié)果婚禮上感耙,老公的妹妹穿的比我還像新娘。我一直安慰自己椰苟,他們只是感情好抑月,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著舆蝴,像睡著了一般谦絮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上洁仗,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天层皱,我揣著相機(jī)與錄音,去河邊找鬼赠潦。 笑死叫胖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的她奥。 我是一名探鬼主播瓮增,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼怎棱,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了绷跑?” 一聲冷哼從身側(cè)響起拳恋,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎砸捏,沒想到半個(gè)月后谬运,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡垦藏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年梆暖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掂骏。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡轰驳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芭挽,到底是詐尸還是另有隱情滑废,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布袜爪,位于F島的核電站,受9級(jí)特大地震影響薛闪,放射性物質(zhì)發(fā)生泄漏辛馆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一豁延、第九天 我趴在偏房一處隱蔽的房頂上張望昙篙。 院中可真熱鬧,春花似錦诱咏、人聲如沸苔可。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽焚辅。三九已至,卻和暖如春苟鸯,著一層夾襖步出監(jiān)牢的瞬間同蜻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工早处, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留湾蔓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓砌梆,卻偏偏與公主長(zhǎng)得像默责,于是被迫代替她去往敵國和親贬循。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容