JS語法

weixin_34138377發表於2016-08-13

JavaScript基本概念、基礎資料型別、運算子、流程控制語句。


一、CSS和JS在網頁中的放置順序是怎樣的?

CSS和JS在網頁中的順序是非常重要的,它會對其他資源的載入順序產生影響。

  • CSS
    CSS程式碼一般放在head標籤當中,用link標籤將樣式放在頂部。網頁渲染時,是先解析HTML標籤,生成DOM樹,再解析CSS標籤,構成CSSOM樹,然後再把DOM和CSSOM組合成渲染樹。如果把CSS放在後面,可能會出現白屏問題或者FOUC (Flash of Unstyled Content) 無樣式內容閃爍。
  • JS
    JS檔案需要被HTML引用才能在瀏覽器當中執行,而在HTML當中有不同的方式來引用指令碼檔案,這些方式就有可能帶來效能的問題。
    引用JS指令碼檔案必須要使用<script>標籤,當瀏覽器遇到這個標籤的時候,它不知道JS程式碼是否會修改頁面,所以它會先停止解析頁面,執行JS程式碼之後再來繼續渲染頁面 。綜上所述,瀏覽器遇到<script>,它會優先下載並解析裡面的JS程式碼,禁用併發,這樣一來就會阻塞後面頁面的載入和渲染,造成白屏現象。所以JS程式碼一般放置在body裡面的最後面,這樣就可以避免這種情況。
    JS非阻塞載入解決方案也有:
  • 用defer標籤。
  • 用createElement來動態生成,但是載入順序在IE下不一定會是按程式碼寫的順序來載入,可能會影響到依賴項,有些檔案就是必須在另一個檔案前引用(火狐跟opera是按順序載入的)。
  • 是用ajax載入,也是非阻塞似的但是這種方法不支援CDN。

二、解釋白屏和FOUC

白屏和FOUC是瀏覽器載入和頁面的顯示方式不同造成的。

  • 白屏
    當瀏覽器獲取到內容但沒有得到CSS樣式時,對於無樣式內容不會進行渲染,所以這時候就會出現白屏現象。
  • 在IE瀏覽器當中,如果把樣式放在底部,在一些場景當中,比如重新整理頁面、新視窗開啟等,頁面就會出現白屏,而不是內容逐步展現。
  • 如果使用@import標籤,即使CSS放入link當中且也放在頭部,也有可能會出現白屏現象。
  • 對於圖片和CSS,在載入時會併發載入。然而在載入JS檔案時會禁用併發,阻止其他內容的下載。所以把JS程式碼放在頁面頂部也會造成白屏現象。
  • FOUC
    FOUC (Flash of Unstyled Content) ,即無樣式內容閃爍。它指的是這樣一種現象:逐步載入無樣式的內容,等CSS載入完成後頁面突然展現的樣子。也就是說,瀏覽器載入了無樣式內容,又突然解析到了樣式,會對頁面進行重新的渲染,這時候就會產生FOUC現象。
    在IE瀏覽器中,如果把樣式放在底部,某些場景下(點選連結、輸入URL、使用書籤進入等等),就會出現FOUC現象。對於 Firefox 則會一直表現出 FOUC。

三、async和defer的作用是什麼?有什麼區別。

引入JS檔案必須要用到<script>標籤,這個標籤有兩個和效能以及JS檔案下載執行相關的屬性,也就是async和defer。

  • 沒有async和defer
<script src="script.js"></script>

如果沒有async和defer屬性,瀏覽器會立即載入並執行指定的指令碼。這裡的“立即”指的是在渲染該 script 標籤之下的文件元素之前,也就是說不等待後續載入的文件元素,讀到就載入並執行。

  • async
<script async src="script.js"></script>

async是html5中新增的屬性,它的作用是能夠非同步載入和執行指令碼,不會因為載入指令碼而阻塞頁面的載入,載入和渲染後續文件元素的過程將和 script.js 的載入與執行並行進行(非同步)。一旦載入到就會立刻執行,很有可能不是按照原本的順序來執行的。如果js前後有依賴性,用async,就很有可能出錯。

  • defer
<script defer src="script.js"></script>

如果script加了defer屬性,即使放在head裡面,它也會在HTML解析完成之後再去執行,就相當於把script放到了頁面底部。有 了defer,載入後續文件元素的過程將和 script.js 的載入並行進行(非同步),但 script.js 的執行要在所有元素解析完成之後,DOMContentLoaded 事件觸發之前完成。也就是說JS的載入不會阻塞頁面的渲染和資源的載入。但defer會按照原本的JS的順序執行,所以如果前後有依賴關係的JS可以放心使用。

  • 比較async和defer
  • 相同點
    • 載入檔案時不會阻塞頁面的渲染。
    • 對於inline的script無效。
    • 使用這兩個屬性的指令碼中不能呼叫document.write方法。
    • 有指令碼的onload的事件回撥。
  • 不同點
    • async不保證原本的執行順序;defer指令碼延遲到文件解析和顯示後執行,會按照原本的順序執行。
    • HTML5.0中定義了async,HTML4.0中定義了defer。

詳細資料還可以參考script的defer和async

四、簡述網頁的渲染機制。

  • 解析HTML標籤, 構建DOM樹。
  • 解析CSS標籤, 構建CSSOM樹。
  • 把DOM和CSSOM組合成 渲染樹 (render tree)。
  • 在渲染樹的基礎上進行佈局, 計算每個節點的幾何結構。
  • 把每個節點繪製到螢幕上 (painting)。

詳細資料可以參考:
前端必讀:瀏覽器內部工作原理
How browsers work

五、JavaScript 定義了幾種資料型別? 哪些是簡單型別?哪些是複雜型別?

JavaScript語言的每一個值都屬於某一種資料型別。JavaScript的資料型別一共有六種,分為五種簡單資料型別(也稱為基本資料型別):

  • 數值(number):整數和小數,比如:1和3.14 。
  • 字串(string):字元組成的文字,比如:"Hello World"。
  • 布林值(boolean):truefalse兩個特定的值。
  • undefined:表示未定義或者不存在,即此處目前沒有任何值。
  • null:表示空缺,即此處應該有一個值,但目前為空。

還有一種複雜資料型別—object,也就是物件,它表示各種值組成的集合。物件又可以分成三個子型別。

  • 狹義的物件(object)
  • 陣列(array)
  • 函式(function)
    通常將數值、字串、布林值稱為原始型別(primitive type)的值,即它們是最基本的資料型別,不能再細分。而將物件稱為合成型別(complex type)的值,因為一個物件往往是多個原始型別的值的合成,可以看作是一個存放各種值的容器。至於undefinednull,一般將它們看成兩個特殊值。

六、NaN、undefined、null分別代表什麼?

  • NaN
    NaN的含義是“Not a Number”,表示非數字。這個數值用於表示一個本來要返回數值的運算元未返回數值的情況(這樣就不會丟擲錯誤)。NaN有兩個不一般的特點,第一,NaN和任何值都不相等,包括自己;第二,任何涉及NaN的操作(比如NaN/1)都會返回NaN。
    針對NaN的這兩個特點,ECMAScript定義了isNaN()函式。它只有一個引數,這個引數可以是任何型別,該函式會幫我們確定這個引數是否“不是數值”。


    2244513-ce1e2b8238d2f072.png
    控制檯測試
  • undefined
    undefined表示不存在值,就是此處目前不存在任何值。典型用法是:
  • 變數被宣告瞭,但沒有賦值時,就等於undefined。
  • 呼叫函式時,應該提供的引數沒有提供,該引數等於undefined。
  • 物件沒有賦值的屬性,該屬性的值為undefined。
  • 函式沒有返回值時,預設返回undefined。


    2244513-be47cefddb2fd983.png
    控制檯測試
  • null
    null表示空值,即該處的值現在為空。典型用法是:
  • 作為函式的引數,表示該函式的引數是一個沒有任何內容的物件。
  • 作為物件原型鏈的終點。

七、typeof和instanceof的作用和區別?

因為ECMAScript是鬆散型的,所以需要一種手段來檢測給定變數的資料型別。JS中有三種方法來確定一個值到底是什麼型別。分別為:typeof運算子、instanceof運算子以及`Object.prototype.toString方法。

  • typeof
    typeof運算子可以返回一個值的資料型別,可能有以下結果。
  • 原始型別
    數值、字串、布林值分別返回numberstringboolean
    2244513-7468db87d5b5ea4c.png
  • 函式
    函式返回function
    2244513-682abc999750c2bf.png
  • undefined
    undefined返回undefined
    2244513-a931ab3df5db5ccd.png

    利用這一點,typeof可以檢查一個沒有宣告的變數而不報錯。
    2244513-53a9e3e9cf3d46b0.png

    事實上,這個特點也經常用於判斷語句當中。
  • 其他
    除此以外,其他的都是返回object
    2244513-aea12934269cac15.png
  • instanceof
    在使用typeof採用引用型別儲存值時,無論引用的是什麼型別的物件,都會返回object。針對這個問題,ECMAScript引入了instanceof來解決這個問題。與typeof不同,instanceof只有truefalse兩個輸出。
    2244513-6eac023e2e9b5875.png

    oStringObject 是 String 物件的例項,因此結果是true。所以在 typeof 方法返回 "object" 的情況下,instanceof 方法還是很有用的。
    更詳細的用法可以參考JavaScript instanceof 運算子深入剖析

八、程式碼

  1. 完成如下程式碼判斷一個變數是否是數字、字串、布林、函式 (難度*)。
    ps: 做完後可參考 underscore.js 原始碼中部分實現。
function isNumber(el){
 // todo ...
}
function isString(el){ 
//todo ...
}
function isBoolean(el){ 
//todo ...
}
function isFunction(el){ 
//todo ...
}
var a = 2,
 b = "jirengu",
 c = false;
alert( isNumber(a) ); //true
alert( isString(a) ); //false
alert( isString(b) ); //true
alert( isBoolean(c) ); //true
alert( isFunction(a)); //false
alert( isFunction( isNumber ) ); //true

DEMO1
DEMO2

2.以下程式碼的輸出結果是?(難度**)

console.log(1+1);    //2。 加法計算
console.log("2"+"4");     //24。 有一個是字串,轉換為字串的拼接。
console.log(2+"4");    //24。 有一個是字串,轉換為字串的拼接。
 console.log(+new Date());    //1471048600952。 獲得當日的日期,用new Date() 參與計算會自動轉換為從1970.1.1開始的毫秒數。
console.log(+"4");    //4。  只有一個字串,會轉換成數字。
2244513-9b6ee8b8a2fd6919.png
執行結果

JS中的運算子主要用於連線簡單的表示式,組成一個複雜的表示式。常見的有算數表示式、比較表示式、邏輯表示式、賦值表示式等,也有單目運算子,指操作原始表示式。大多數運算子都由標點符號組成(+、>=、!),也有關鍵字表示的運算子,如typeof、delete、instanceof等。
在JavaScript中運算子通常會根據需要對運算元進行型別轉換,乘法操作符*希望運算元是數字,但是 "3" * "5" 也是合法的,JavaScript會自動將其轉換為數字計算,返回Number 15。
有些操作符對不同的資料型別有不同的含義,比如:+

  • 在兩個運算元都是數字的時候,會做加法運算。
  • 兩個引數都是字串或在有一個引數是字串的情況下會把另外一個引數轉換為字串做字串拼接。
  • 在引數有物件的情況下會呼叫其valueOf或toString。
  • 在只有一個字串引數的時候會嘗試將其轉換為數字。
  • 在只有一個數字引數的時候返回其正數值。

3.以下程式碼的輸出結果是? (難度***)

var a = 1;
a+++a;  //3
typeof a+2;  //number2

a+++a表示式當中,後置遞增的優先順序最高。所以相當於(a++)+a。a一開始賦值為1,a++表示先賦值再自增,所以a++的計算結果為1,且此時a等於2。所以a+++a表示式的計算結果為3。
typeof a+2中,typeof的優先順序比“+”高,所以它會先計算typeof a,得到的輸出是"number"。然後是一個字串加上一個數字,會把數字轉換成字串。所以得到的輸出為"number2"的字串。

2244513-7da9eb82bfdefc63.png
執行結果

運算子優先順序
4.遍歷陣列,把陣列裡的列印陣列每一項的平方(難度**)。

var arr = [3,4,5]
// todo..// 
輸出 9, 16, 25 
2244513-428e2c0d3ebb1fab.png
執行結果

5.遍歷 JSON, 列印裡面的值(難度**)。

var obj = { 
name: 'hunger', 
sex: 'male', 
age: 28
}
//todo ...
// 輸出 name: hunger, sex: male, age:28
2244513-32001d5ae57fb294.png
執行結果

6.下面程式碼的輸出是? 為什麼 (難度***)

console.log(a);
var a = 1;
console.log(a);
console.log(b);

JavaScript引擎的工作方式是,先解析程式碼,獲取所有被宣告的變數,給他初始值undefined,然後再一行一行地執行。這造成的結果,就是所有的變數的宣告語句,都會被提升到程式碼的頭部,這就叫做變數提升。
所以在上面的程式碼中,變數a先被宣告,再執行console.log(a);,也就是相當於執行了var a; console.log(a);。因為此時a沒有賦值,所以console.log(a);結果為undefined。然後a被賦值為1,再執行console.log(a);,所以console.log(a);結果為1。最後,console.log(b);,由於b變數不存在,所以結果報錯。

2244513-b8943968bd7fe0f6.png
執行結果

這也正體現了變數提升的作用。我們在使用一個變數之前必須宣告變數,但是由於變數提升,我們如果宣告瞭變數,即使在宣告語句前使用也是可以的,只不過其值是初始值undefined。

相關文章