JavaScript基本概念旷赖、基礎(chǔ)數(shù)據(jù)類型、運(yùn)算符泣崩、流程控制語(yǔ)句少梁。
一、CSS和JS在網(wǎng)頁(yè)中的放置順序是怎樣的矫付?
CSS和JS在網(wǎng)頁(yè)中的順序是非常重要的凯沪,它會(huì)對(duì)其他資源的加載順序產(chǎn)生影響。
- CSS
CSS代碼一般放在head標(biāo)簽當(dāng)中买优,用link標(biāo)簽將樣式放在頂部妨马。網(wǎng)頁(yè)渲染時(shí),是先解析HTML標(biāo)簽杀赢,生成DOM樹烘跺,再解析CSS標(biāo)簽,構(gòu)成CSSOM樹脂崔,然后再把DOM和CSSOM組合成渲染樹滤淳。如果把CSS放在后面,可能會(huì)出現(xiàn)白屏問(wèn)題或者FOUC (Flash of Unstyled Content) 無(wú)樣式內(nèi)容閃爍砌左。 - JS
JS文件需要被HTML引用才能在瀏覽器當(dāng)中運(yùn)行脖咐,而在HTML當(dāng)中有不同的方式來(lái)引用腳本文件铺敌,這些方式就有可能帶來(lái)性能的問(wèn)題。
引用JS腳本文件必須要使用<script>
標(biāo)簽屁擅,當(dāng)瀏覽器遇到這個(gè)標(biāo)簽的時(shí)候偿凭,它不知道JS代碼是否會(huì)修改頁(yè)面,所以它會(huì)先停止解析頁(yè)面煤蹭,執(zhí)行JS代碼之后再來(lái)繼續(xù)渲染頁(yè)面 笔喉。綜上所述取视,瀏覽器遇到<script>
硝皂,它會(huì)優(yōu)先下載并解析里面的JS代碼,禁用并發(fā)作谭,這樣一來(lái)就會(huì)阻塞后面頁(yè)面的加載和渲染稽物,造成白屏現(xiàn)象。所以JS代碼一般放置在body里面的最后面折欠,這樣就可以避免這種情況贝或。
JS非阻塞加載解決方案也有: - 用defer標(biāo)簽。
- 用createElement來(lái)動(dòng)態(tài)生成锐秦,但是加載順序在IE下不一定會(huì)是按代碼寫的順序來(lái)加載咪奖,可能會(huì)影響到依賴項(xiàng),有些文件就是必須在另一個(gè)文件前引用(火狐跟opera是按順序加載的)酱床。
- 是用ajax加載羊赵,也是非阻塞似的但是這種方法不支持CDN。
二扇谣、解釋白屏和FOUC
白屏和FOUC是瀏覽器加載和頁(yè)面的顯示方式不同造成的昧捷。
- 白屏
當(dāng)瀏覽器獲取到內(nèi)容但沒(méi)有得到CSS樣式時(shí),對(duì)于無(wú)樣式內(nèi)容不會(huì)進(jìn)行渲染罐寨,所以這時(shí)候就會(huì)出現(xiàn)白屏現(xiàn)象靡挥。 - 在IE瀏覽器當(dāng)中,如果把樣式放在底部鸯绿,在一些場(chǎng)景當(dāng)中跋破,比如刷新頁(yè)面、新窗口打開等瓶蝴,頁(yè)面就會(huì)出現(xiàn)白屏幔烛,而不是內(nèi)容逐步展現(xiàn)。
- 如果使用
@import
標(biāo)簽囊蓝,即使CSS放入link當(dāng)中且也放在頭部饿悬,也有可能會(huì)出現(xiàn)白屏現(xiàn)象。 - 對(duì)于圖片和CSS聚霜,在加載時(shí)會(huì)并發(fā)加載狡恬。然而在加載JS文件時(shí)會(huì)禁用并發(fā)珠叔,阻止其他內(nèi)容的下載。所以把JS代碼放在頁(yè)面頂部也會(huì)造成白屏現(xiàn)象弟劲。
- FOUC
FOUC (Flash of Unstyled Content) 祷安,即無(wú)樣式內(nèi)容閃爍。它指的是這樣一種現(xiàn)象:逐步加載無(wú)樣式的內(nèi)容兔乞,等CSS加載完成后頁(yè)面突然展現(xiàn)的樣子汇鞭。也就是說(shuō),瀏覽器加載了無(wú)樣式內(nèi)容庸追,又突然解析到了樣式霍骄,會(huì)對(duì)頁(yè)面進(jìn)行重新的渲染,這時(shí)候就會(huì)產(chǎn)生FOUC現(xiàn)象淡溯。
在IE瀏覽器中读整,如果把樣式放在底部,某些場(chǎng)景下(點(diǎn)擊鏈接咱娶、輸入U(xiǎn)RL米间、使用書簽進(jìn)入等等),就會(huì)出現(xiàn)FOUC現(xiàn)象膘侮。對(duì)于 Firefox 則會(huì)一直表現(xiàn)出 FOUC屈糊。
三、async和defer的作用是什么琼了?有什么區(qū)別逻锐。
引入JS文件必須要用到<script>
標(biāo)簽,這個(gè)標(biāo)簽有兩個(gè)和性能以及JS文件下載執(zhí)行相關(guān)的屬性表伦,也就是async和defer谦去。
- 沒(méi)有async和defer
<script src="script.js"></script>
如果沒(méi)有async和defer屬性,瀏覽器會(huì)立即加載并執(zhí)行指定的腳本蹦哼。這里的“立即”指的是在渲染該 script 標(biāo)簽之下的文檔元素之前鳄哭,也就是說(shuō)不等待后續(xù)載入的文檔元素,讀到就加載并執(zhí)行纲熏。
- async
<script async src="script.js"></script>
async是html5中新增的屬性妆丘,它的作用是能夠異步加載和執(zhí)行腳本,不會(huì)因?yàn)榧虞d腳本而阻塞頁(yè)面的加載局劲,加載和渲染后續(xù)文檔元素的過(guò)程將和 script.js 的加載與執(zhí)行并行進(jìn)行(異步)勺拣。一旦加載到就會(huì)立刻執(zhí)行,很有可能不是按照原本的順序來(lái)執(zhí)行的鱼填。如果js前后有依賴性药有,用async,就很有可能出錯(cuò)。
- defer
<script defer src="script.js"></script>
如果script加了defer屬性愤惰,即使放在head里面苇经,它也會(huì)在HTML解析完成之后再去執(zhí)行,就相當(dāng)于把script放到了頁(yè)面底部宦言。有 了defer扇单,加載后續(xù)文檔元素的過(guò)程將和 script.js 的加載并行進(jìn)行(異步),但 script.js 的執(zhí)行要在所有元素解析完成之后奠旺,DOMContentLoaded 事件觸發(fā)之前完成蜘澜。也就是說(shuō)JS的加載不會(huì)阻塞頁(yè)面的渲染和資源的加載。但defer會(huì)按照原本的JS的順序執(zhí)行响疚,所以如果前后有依賴關(guān)系的JS可以放心使用鄙信。
- 比較async和defer
- 相同點(diǎn)
- 加載文件時(shí)不會(huì)阻塞頁(yè)面的渲染。
- 對(duì)于inline的script無(wú)效稽寒。
- 使用這兩個(gè)屬性的腳本中不能調(diào)用document.write方法扮碧。
- 有腳本的onload的事件回調(diào)趟章。
- 不同點(diǎn)
- async不保證原本的執(zhí)行順序杏糙;defer腳本延遲到文檔解析和顯示后執(zhí)行,會(huì)按照原本的順序執(zhí)行蚓土。
- HTML5.0中定義了async宏侍,HTML4.0中定義了defer。
詳細(xì)資料還可以參考script的defer和async蜀漆。
四谅河、簡(jiǎn)述網(wǎng)頁(yè)的渲染機(jī)制。
- 解析HTML標(biāo)簽, 構(gòu)建DOM樹确丢。
- 解析CSS標(biāo)簽, 構(gòu)建CSSOM樹绷耍。
- 把DOM和CSSOM組合成 渲染樹 (render tree)。
- 在渲染樹的基礎(chǔ)上進(jìn)行布局, 計(jì)算每個(gè)節(jié)點(diǎn)的幾何結(jié)構(gòu)鲜侥。
- 把每個(gè)節(jié)點(diǎn)繪制到屏幕上 (painting)褂始。
詳細(xì)資料可以參考:
前端必讀:瀏覽器內(nèi)部工作原理
How browsers work
五、JavaScript 定義了幾種數(shù)據(jù)類型? 哪些是簡(jiǎn)單類型?哪些是復(fù)雜類型?
JavaScript語(yǔ)言的每一個(gè)值都屬于某一種數(shù)據(jù)類型描函。JavaScript的數(shù)據(jù)類型一共有六種崎苗,分為五種簡(jiǎn)單數(shù)據(jù)類型(也稱為基本數(shù)據(jù)類型):
- 數(shù)值(number):整數(shù)和小數(shù),比如:1和3.14 舀寓。
- 字符串(string):字符組成的文本胆数,比如:"Hello World"。
- 布爾值(boolean):
true
和false
兩個(gè)特定的值互墓。 - undefined:表示未定義或者不存在必尼,即此處目前沒(méi)有任何值。
- null:表示空缺篡撵,即此處應(yīng)該有一個(gè)值判莉,但目前為空齿诞。
還有一種復(fù)雜數(shù)據(jù)類型—object,也就是對(duì)象骂租,它表示各種值組成的集合祷杈。對(duì)象又可以分成三個(gè)子類型。
- 狹義的對(duì)象(object)
- 數(shù)組(array)
- 函數(shù)(function)
通常將數(shù)值渗饮、字符串但汞、布爾值稱為原始類型(primitive type)的值,即它們是最基本的數(shù)據(jù)類型互站,不能再細(xì)分私蕾。而將對(duì)象稱為合成類型(complex type)的值,因?yàn)橐粋€(gè)對(duì)象往往是多個(gè)原始類型的值的合成胡桃,可以看作是一個(gè)存放各種值的容器踩叭。至于undefined
和null
,一般將它們看成兩個(gè)特殊值翠胰。
六容贝、NaN、undefined之景、null分別代表什么?
-
NaN
NaN的含義是“Not a Number”斤富,表示非數(shù)字。這個(gè)數(shù)值用于表示一個(gè)本來(lái)要返回?cái)?shù)值的操作數(shù)未返回?cái)?shù)值的情況(這樣就不會(huì)拋出錯(cuò)誤)锻狗。NaN有兩個(gè)不一般的特點(diǎn)满力,第一,NaN和任何值都不相等轻纪,包括自己油额;第二,任何涉及NaN的操作(比如NaN/1)都會(huì)返回NaN刻帚。
針對(duì)NaN的這兩個(gè)特點(diǎn)潦嘶,ECMAScript定義了isNaN()函數(shù)。它只有一個(gè)參數(shù)我擂,這個(gè)參數(shù)可以是任何類型衬以,該函數(shù)會(huì)幫我們確定這個(gè)參數(shù)是否“不是數(shù)值”。
- undefined
undefined表示不存在值校摩,就是此處目前不存在任何值看峻。典型用法是: - 變量被聲明了,但沒(méi)有賦值時(shí)衙吩,就等于undefined互妓。
- 調(diào)用函數(shù)時(shí),應(yīng)該提供的參數(shù)沒(méi)有提供,該參數(shù)等于undefined冯勉。
- 對(duì)象沒(méi)有賦值的屬性澈蚌,該屬性的值為undefined。
-
函數(shù)沒(méi)有返回值時(shí)灼狰,默認(rèn)返回undefined宛瞄。
- null
null表示空值,即該處的值現(xiàn)在為空交胚。典型用法是: - 作為函數(shù)的參數(shù)份汗,表示該函數(shù)的參數(shù)是一個(gè)沒(méi)有任何內(nèi)容的對(duì)象。
- 作為對(duì)象原型鏈的終點(diǎn)蝴簇。
七杯活、typeof和instanceof的作用和區(qū)別?
因?yàn)镋CMAScript是松散型的,所以需要一種手段來(lái)檢測(cè)給定變量的數(shù)據(jù)類型熬词。JS中有三種方法來(lái)確定一個(gè)值到底是什么類型旁钧。分別為:typeof
運(yùn)算符、instanceof
運(yùn)算符以及`Object.prototype.toString方法互拾。
- typeof
typeof運(yùn)算符可以返回一個(gè)值的數(shù)據(jù)類型歪今,可能有以下結(jié)果。 - 原始類型
數(shù)值摩幔、字符串彤委、布爾值分別返回number
鞭铆、string
或衡、boolean
。
- 函數(shù)
函數(shù)返回function
车遂。
- undefined
undefined
返回undefined
封断。
利用這一點(diǎn),typeof可以檢查一個(gè)沒(méi)有聲明的變量而不報(bào)錯(cuò)舶担。
事實(shí)上坡疼,這個(gè)特點(diǎn)也經(jīng)常用于判斷語(yǔ)句當(dāng)中。 - 其他
除此以外衣陶,其他的都是返回object
柄瑰。
- instanceof
在使用typeof采用引用類型存儲(chǔ)值時(shí),無(wú)論引用的是什么類型的對(duì)象剪况,都會(huì)返回object
教沾。針對(duì)這個(gè)問(wèn)題,ECMAScript引入了instanceof
來(lái)解決這個(gè)問(wèn)題译断。與typeof
不同授翻,instanceof
只有true
和false
兩個(gè)輸出。
oStringObject 是 String 對(duì)象的實(shí)例,因此結(jié)果是true
堪唐。所以在 typeof 方法返回 "object" 的情況下巡语,instanceof 方法還是很有用的。
更詳細(xì)的用法可以參考JavaScript instanceof 運(yùn)算符深入剖析淮菠。
八男公、代碼
- 完成如下代碼判斷一個(gè)變量是否是數(shù)字、字符串合陵、布爾理澎、函數(shù) (難度*)。
ps: 做完后可參考 underscore.js 源碼中部分實(shí)現(xiàn)曙寡。
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
2.以下代碼的輸出結(jié)果是?(難度**)
console.log(1+1); //2糠爬。 加法計(jì)算
console.log("2"+"4"); //24。 有一個(gè)是字符串,轉(zhuǎn)換為字符串的拼接怯伊。
console.log(2+"4"); //24筛婉。 有一個(gè)是字符串,轉(zhuǎn)換為字符串的拼接镀琉。
console.log(+new Date()); //1471048600952。 獲得當(dāng)日的日期蕊唐,用new Date() 參與計(jì)算會(huì)自動(dòng)轉(zhuǎn)換為從1970.1.1開始的毫秒數(shù)屋摔。
console.log(+"4"); //4。 只有一個(gè)字符串替梨,會(huì)轉(zhuǎn)換成數(shù)字钓试。
JS中的運(yùn)算符主要用于連接簡(jiǎn)單的表達(dá)式,組成一個(gè)復(fù)雜的表達(dá)式副瀑。常見(jiàn)的有算數(shù)表達(dá)式弓熏、比較表達(dá)式、邏輯表達(dá)式糠睡、賦值表達(dá)式等挽鞠,也有單目運(yùn)算符,指操作原始表達(dá)式狈孔。大多數(shù)運(yùn)算符都由標(biāo)點(diǎn)符號(hào)組成(+信认、>=、!)均抽,也有關(guān)鍵字表示的運(yùn)算符嫁赏,如typeof、delete到忽、instanceof等橄教。
在JavaScript中運(yùn)算符通常會(huì)根據(jù)需要對(duì)操作數(shù)進(jìn)行類型轉(zhuǎn)換清寇,乘法操作符*希望操作數(shù)是數(shù)字,但是 "3" * "5" 也是合法的护蝶,JavaScript會(huì)自動(dòng)將其轉(zhuǎn)換為數(shù)字計(jì)算华烟,返回Number 15。
有些操作符對(duì)不同的數(shù)據(jù)類型有不同的含義持灰,比如:+
- 在兩個(gè)操作數(shù)都是數(shù)字的時(shí)候盔夜,會(huì)做加法運(yùn)算。
- 兩個(gè)參數(shù)都是字符串或在有一個(gè)參數(shù)是字符串的情況下會(huì)把另外一個(gè)參數(shù)轉(zhuǎn)換為字符串做字符串拼接堤魁。
- 在參數(shù)有對(duì)象的情況下會(huì)調(diào)用其valueOf或toString喂链。
- 在只有一個(gè)字符串參數(shù)的時(shí)候會(huì)嘗試將其轉(zhuǎn)換為數(shù)字。
- 在只有一個(gè)數(shù)字參數(shù)的時(shí)候返回其正數(shù)值妥泉。
3.以下代碼的輸出結(jié)果是? (難度***)
var a = 1;
a+++a; //3
typeof a+2; //number2
在a+++a
表達(dá)式當(dāng)中椭微,后置遞增的優(yōu)先級(jí)最高。所以相當(dāng)于(a++)+a
盲链。a一開始賦值為1蝇率,a++
表示先賦值再自增,所以a++
的計(jì)算結(jié)果為1刽沾,且此時(shí)a等于2本慕。所以a+++a
表達(dá)式的計(jì)算結(jié)果為3。
而typeof a+2
中侧漓,typeof的優(yōu)先級(jí)比“+”高锅尘,所以它會(huì)先計(jì)算typeof a
,得到的輸出是"number"布蔗。然后是一個(gè)字符串加上一個(gè)數(shù)字藤违,會(huì)把數(shù)字轉(zhuǎn)換成字符串。所以得到的輸出為"number2"的字符串何鸡。
運(yùn)算符優(yōu)先級(jí)
4.遍歷數(shù)組纺弊,把數(shù)組里的打印數(shù)組每一項(xiàng)的平方(難度**)。
var arr = [3,4,5]
// todo..//
輸出 9, 16, 25
5.遍歷 JSON, 打印里面的值(難度**)骡男。
var obj = {
name: 'hunger',
sex: 'male',
age: 28
}
//todo ...
// 輸出 name: hunger, sex: male, age:28
6.下面代碼的輸出是? 為什么 (難度***)
console.log(a);
var a = 1;
console.log(a);
console.log(b);
JavaScript引擎的工作方式是,先解析代碼傍睹,獲取所有被聲明的變量隔盛,給他初始值undefined,然后再一行一行地運(yùn)行拾稳。這造成的結(jié)果吮炕,就是所有的變量的聲明語(yǔ)句,都會(huì)被提升到代碼的頭部访得,這就叫做變量提升龙亲。
所以在上面的代碼中陕凹,變量a先被聲明,再執(zhí)行console.log(a);
鳄炉,也就是相當(dāng)于執(zhí)行了var a; console.log(a);
杜耙。因?yàn)榇藭r(shí)a沒(méi)有賦值,所以console.log(a);
結(jié)果為undefined拂盯。然后a被賦值為1佑女,再執(zhí)行console.log(a);
,所以console.log(a);
結(jié)果為1谈竿。最后团驱,console.log(b);
,由于b變量不存在空凸,所以結(jié)果報(bào)錯(cuò)嚎花。
這也正體現(xiàn)了變量提升的作用。我們?cè)谑褂靡粋€(gè)變量之前必須聲明變量呀洲,但是由于變量提升贩幻,我們?nèi)绻暶髁俗兞浚词乖诼暶髡Z(yǔ)句前使用也是可以的两嘴,只不過(guò)其值是初始值undefined丛楚。