<!DOCTYPE>聲明位于位于HTML文檔中的第一行,處于 <html>標簽之前。告知瀏覽器的解析器,用什么文檔標準解析這個文檔。DOCTYPE不存在或格式不正確會導致文檔以兼容模式呈現。
標準模式的排版 和JS運作模式都是以該瀏覽器支持的最高標準運行。在兼容模式中,頁面以寬松的向后兼容的方式顯示,模擬老式瀏覽器的行為以防止站點無法工作。
HTML5 不基于 SGML,因此不需要對DTD進行引用,但是需要doctype來規范瀏覽器的行為(讓瀏覽器按照它們應該的方式來運行);
而HTML4.01基于SGML,所以需要對DTD進行引用,才能告知瀏覽器文檔所使用的文檔類型。**
首先:CSS規范規定,每個元素都有display屬性,確定該元素的類型,每個元素都有默認的display值,如div的display默認值為“block”,則為“塊級”元素;span默認display屬性值為“inline”,是“行內”元素。
(1)行內元素有:a b span img input select strong(強調的語氣)
(2)塊級元素有:div ul ol li dl dt dd h1 h2 h3 h4…p
(3)常見的空元素:
<img><input><link><meta>
鮮為人知的是:
<area><base><col><command><embed><keygen><param><source><track><wbr>
4.頁面導入樣式時,使用link和@import有什么區別?
link屬于XHTML標簽,除了加載CSS外,還能用于定義RSS, 定義rel連接屬性等作用;而@import是CSS提供的,只能用于加載CSS;
頁面被加載的時,link會同時被加載,而@import引用的CSS會等到頁面被加載完再加載;
import是CSS2.1 提出的,只在IE5以上才能被識別,而link是XHTML標簽,無兼容問題;
主要分成兩部分:渲染引擎(layout engineer或Rendering Engine)和JS引擎。
渲染引擎:負責取得網頁的內容(HTML、XML、圖像等等)、整理訊息(例如加入CSS等),以及計算網頁的顯示方式,然后會輸出至顯示器或打印機。瀏覽器的內核的不同對于網頁的語法解釋會有不同,所以渲染的效果也不相同。所有網頁瀏覽器、電子郵件客戶端以及其它需要編輯、顯示網絡內容的應用程序都需要內核。
JS引擎則:解析和執行javascript來實現網頁的動態效果。
最開始渲染引擎和JS引擎并沒有區分的很明確,后來JS引擎越來越獨立,內核就傾向于只指渲染引擎。
Trident內核:IE,MaxThon,TT,The World,360,搜狗瀏覽器等。[又稱MSHTML]
Gecko內核:Netscape6及以上版本,FF,MozillaSuite/SeaMonkey等
Presto內核:Opera7及以上。 [Opera內核原為:Presto,現為:Blink;]
Webkit內核:Safari,Chrome等。 [ Chrome的:Blink(WebKit的分支)]
移除的元素:
純表現的元素:basefont,big,center,font, s,strike,tt,u;
對可用性產生負面影響的元素:frame,frameset,noframes;
用正確的標簽做正確的事情。
html語義化讓頁面的內容結構化,結構更清晰,便于對瀏覽器、搜索引擎解析;
即使在沒有樣式CSS情況下也以一種文檔格式顯示,并且是容易閱讀的;
搜索引擎的爬蟲也依賴于HTML標記來確定上下文和各個關鍵字的權重,利于SEO;
使閱讀源代碼的人對網站更容易將網站分塊,便于閱讀維護理解。
在用戶沒有連網時,可以正常訪問站點或應用,在用戶與網絡連接時更新用戶機器上的緩存文件。
原理:HTML5的離線存儲是基于一個新建的.appcache文件的緩存機制(不是存儲技術),通過這個文件上的解析清單離線存儲資源,這些資源就會像cookie一樣被存儲了下來。之后當網絡在處于離線狀態下時,瀏覽器會通過被離線存儲的數據進行頁面展示。
如何使用:
頁面頭部像下面一樣加入一個manifest的屬性;
在cache.manifest文件的編寫離線存儲的資源;
CACHE MANIFEST
#v0.11
CACHE:
js/app.js
css/style.css
NETWORK:
resourse/logo.png
FALLBACK:
/ /offline.html
在離線狀態時,操作window.applicationCache進行需求實現。
在線的情況下,瀏覽器發現html頭部有manifest屬性,它會請求manifest文件,如果是第一次訪問app,那么瀏覽器就會根據manifest文件的內容下載相應的資源并且進行離線存儲。如果已經訪問過app并且資源已經離線存儲了,那么瀏覽器就會使用離線的資源加載頁面,然后瀏覽器會對比新的manifest文件與舊的manifest文件,如果文件沒有發生改變,就不做任何操作,如果文件改變了,那么就會重新下載文件中的資源并進行離線存儲。
離線的情況下,瀏覽器就直接使用離線存儲的資源。
iframe會阻塞主頁面的Onload事件;
搜索引擎的檢索程序無法解讀這種頁面,不利于SEO;
iframe和主頁面共享連接池,而瀏覽器對相同域的連接有限制,所以會影響頁面的并行加載。
使用iframe之前需要考慮這兩個缺點。如果需要使用iframe,最好是通過javascript
動態給iframe添加src屬性值,這樣可以繞開以上兩個問題。
label標簽來定義表單控制間的關系,當用戶選擇該標簽時,瀏覽器會自動將焦點轉到和標簽相關的表單控件上。
<label for="Name">Number:</label>
<input type=“text“name="Name" id="Name"/>
<label>Date:<input type="text" name="B"/></label>
WebSocket、也可以調用localstorge、cookies等本地存儲方式,還可以使用頁面的路有參數傳遞
localstorge另一個瀏覽上下文里被添加、修改或刪除時,它都會觸發一個事件,
我們通過監聽事件,控制它的值來進行頁面信息通信;
14.如何在頁面上實現一個圓形的可點擊區域?
map+area或者svg
border-radius
純js實現 需要求一個點在不在圓上簡單算法、獲取鼠標坐標等等
title屬性沒有明確意義只表示是個標題,H1則表示層次明確的標題,對頁面信息的抓取也有很大的影響;
strong是標明重點內容,有語氣加強的含義,使用閱讀設備閱讀網絡時:會重讀,而是展示強調內容。
i內容展示為斜體,em表示強調的文本;
h5新增的屬性
可以通過ele.dataset獲取到標簽上的data-x的屬性
返回一個對象
解決:解決方案是做成PNG8.
解決:方案是加一個全局的*{margin:0;padding:0;}來統一。
解決:解決方案是在float的標簽樣式控制中加入 ——_display:inline;將其轉化為行內屬性。(_這個符號只有ie6會識別)
漸進識別的方式,從總體中逐漸排除局部。
首先,巧妙的使用“9”這一標記,將IE游覽器從所有情況中分離出來。 接著,再次使用“+”將IE8和IE7、IE6分離開來,這樣IE8已經獨立識別。
css
.bb{
background-color:#f1ee18;/*所有識別*/
.background-color:#00deff\9; /*IE6、7、8識別*/
+background-color:#a200ff;/*IE6、7識別*/
_background-color:#1e0bd1;/*IE6識別*/
}
解決:解決方法:統一通過getAttribute()獲取自定義屬性.
解決方法:(條件注釋)缺點是在IE瀏覽器下可能會增加額外的HTTP請求數。
解決:可通過加入 CSS 屬性 -webkit-text-size-adjust: none; 解決.
解決:方法是改變CSS屬性的排列順序:L-V-H-A : a:link {} a:visited {} a:hover {} a:active {}
該標簽可聲明三種 DTD 類型,分別表示嚴格版本、過渡版本以及基于框架的 HTML 文檔。
HTML 4.01 規定了三種文檔類型:Strict、Transitional 以及 Frameset。
XHTML 1.0 規定了三種 XML 文檔類型:Strict、Transitional 以及 Frameset。
Standards (標準)模式(也就是嚴格呈現模式)用于呈現遵循最新標準的網頁,而 Quirks(包容)模式(也就是松散呈現模式或者兼容模式)用于呈現為傳統瀏覽器而設計的網頁。
1)所有的標記都必須要有一個相應的結束標記
2)所有標簽的元素和屬性的名字都必須使用小寫
3)所有的XML標記都必須合理嵌套
4)所有的屬性必須用引號""括起來
5)把所有<和&特殊符號用編碼表示
6)給所有屬性賦一個值
7)不要在注釋內容中使“--”
8)圖片必須有說明文字
title是global attributes之一,用于為元素提供附加的advisory information。通常當鼠標滑動到元素上的時候顯示。
alt是<img>的特有屬性,是圖片內容的等價描述,用于圖片無法加載時顯示、讀屏器閱讀圖片。可提圖片高可訪問性,除了純裝飾圖片外都必須設置有意義的值,搜索引擎會重點分析。
改版的時候更方便 只要改css文件。
頁面加載速度更快、結構化清晰、頁面顯示簡潔。
表現與結構相分離。
易于優化(seo)搜索引擎更友好,排名更容易靠前。
派生選擇器(用HTML標簽申明)
id選擇器(用DOM的ID申明)
類選擇器(用一個樣式類名申明)
屬性選擇器(用DOM的屬性申明,屬于CSS2,IE6不支持,不常用,不知道就算了)
除了前3種基本選擇器,還有一些擴展選擇器,包括
后代選擇器(利用空格間隔,比如div .a{ })
群組選擇器(利用逗號間隔,比如p,div,#a{ })
那么問題來了,CSS選擇器的優先級是怎么樣定義的?
一般而言,選擇器越特殊,它的優先級越高。也就是選擇器指向的越準確,它的優先級就越高。
復雜的計算方法:
用1表示派生選擇器的優先級
用10表示類選擇器的優先級
用100標示ID選擇器的優先級
div.test1 .span var 優先級 1+10 +10 +1
span#xxx .songs li 優先級1+100 + 10 + 1
xxx li 優先級 100 +1
那么問題來了,看下列代碼,<p>標簽內的文字是什么顏色的?
<style>
.classA{ color:blue;}
.classB{ color:red;}
</style>
<body>
<p class='classB classA'> 123 </p>
</body>
答案:red。與樣式定義在文件中的先后順序有關,即是后面的覆蓋前面的,與在<p class=’classB classA’>中的先后關系無關。
塊級元素(block)特性:
總是獨占一行,表現為另起一行開始,而且其后的元素也必須另起一行顯示;
寬度(width)、高度(height)、內邊距(padding)和外邊距(margin)都可控制;
內聯元素(inline)特性:
和相鄰的內聯元素在同一行;
寬度(width)、高度(height)、內邊距的top/bottom(padding-top/padding-bottom)和外邊距的top/bottom(margin-top/margin-bottom)都不可改變(也就是padding和margin的left和right是可以設置的),就是里面文字或圖片的大小。
那么問題來了,瀏覽器還有默認的天生inline-block元素(擁有內在尺寸,可設置高寬,但不會自動換行),有哪些?
答案:<input> 、<img> 、<button> 、<texterea> 、<label>。
外邊距重疊就是margin-collapse。
在CSS當中,相鄰的兩個盒子(可能是兄弟關系也可能是祖先關系)的外邊距可以結合成一個單獨的外邊距。這種合并外邊距的方式被稱為折疊,并且因而所結合成的外邊距稱為折疊外邊距。
折疊結果遵循下列計算規則:
兩個相鄰的外邊距都是正數時,折疊結果是它們兩者之間較大的值。
兩個相鄰的外邊距都是負數時,折疊結果是兩者絕對值的較大值。
兩個外邊距一正一負時,折疊結果是兩者的相加的和。
rgba()和opacity都能實現透明效果,但最大的不同是opacity作用于元素,以及元素內的所有內容的透明度,
而rgba()只作用于元素的顏色或其背景色。(設置rgba透明的元素的子元素不會繼承透明效果!)
* 1.id選擇器( # myid)
2.類選擇器(.myclassname)
3.標簽選擇器(div, h1, p)
4.相鄰選擇器(h1 + p)
5.子選擇器(ul < li)
6.后代選擇器(li a)
7.通配符選擇器( * )
8.屬性選擇器(a[rel = "external"])
9.偽類選擇器(a: hover, li: nth - child)
* 可繼承: font-size font-family color, UL LI DL DD DT;
* 不可繼承 :border padding margin width height ;
* 優先級就近原則,樣式定義最近者為準;
* 載入樣式以最后載入的定位為準;
優先級為:
!important > id > class > tag
important 比 內聯優先級高
CSS3新增偽類舉例:
p:first-of-type 選擇屬于其父元素的首個 <p> 元素的每個 <p> 元素。
p:last-of-type 選擇屬于其父元素的最后 <p> 元素的每個 <p> 元素。
p:only-of-type 選擇屬于其父元素唯一的 <p> 元素的每個 <p> 元素。
p:only-child 選擇屬于其父元素的唯一子元素的每個 <p> 元素。
p:nth-child(2) 選擇屬于其父元素的第二個子元素的每個 <p> 元素。
:enabled、:disabled 控制表單控件的禁用狀態。
:checked,單選框或復選框被選中。
給div設置一個寬度,然后添加margin:0 auto屬性
div{
width:200px;
margin:0 auto;
}
居中一個浮動元素
確定容器的寬高 寬500 高 300 的層
設置層的外邊距
.div {
Width:500px ; height:300px;//高度可以不設
Margin: -150px 0 0 -250px;
position:relative;相對定位
background-color:pink;//方便看效果
left:50%;
top:50%;
}
* IE瀏覽器的內核Trident、 Mozilla的Gecko、google的WebKit、Opera內核Presto;
* png24為的圖片在iE6瀏覽器上出現背景,解決方案是做成PNG8.
* 瀏覽器默認的margin和padding不同。解決方案是加一個全局的*{margin:0;padding:0;}來統一。
* IE6雙邊距bug:塊屬性標簽float后,又有橫行的margin情況下,在ie6顯示margin比設置的大。
浮動ie產生的雙倍距離 #box{ float:left; width:10px; margin:0 0 0 100px;}
這種情況之下IE會產生20px的距離,解決方案是在float的標簽樣式控制中加入 ——_display:inline;將其轉化為行內屬性。(_這個符號只有ie6會識別)
漸進識別的方式,從總體中逐漸排除局部。
首先,巧妙的使用“\9”這一標記,將IE游覽器從所有情況中分離出來。
接著,再次使用“+”將IE8和IE7、IE6分離開來,這樣IE8已經獨立識別。
css
.bb{
background-color:#f1ee18;/*所有識別*/
.background-color:#00deff\9; /*IE6、7、8識別*/
+background-color:#a200ff;/*IE6、7識別*/
_background-color:#1e0bd1;/*IE6識別*/
}
* IE下,可以使用獲取常規屬性的方法來獲取自定義屬性,
也可以使用getAttribute()獲取自定義屬性;
Firefox下,只能使用getAttribute()獲取自定義屬性.
解決方法:統一通過getAttribute()獲取自定義屬性.
* IE下,even對象有x,y屬性,但是沒有pageX,pageY屬性;
Firefox下,event對象有pageX,pageY屬性,但是沒有x,y屬性.
* (條件注釋)缺點是在IE瀏覽器下可能會增加額外的HTTP請求數。
* Chrome 中文界面下默認會將小于 12px 的文本強制按照 12px 顯示, 可通過加入 CSS 屬性 -webkit-text-size-adjust: none; 解決.
超鏈接訪問過后hover樣式就不出現了 被點擊訪問過的超鏈接樣式不在具有hover和active了解決方法是改變CSS屬性的排列順序:
L-V-H-A : a:link {} a:visited {} a:hover {} a:active {}
!important > id > class > 標簽
!important 比 內聯優先級高
可繼承: font-size font-family color, ul li dl dd dt;
不可繼承 :border padding margin width height ;
講 DOM 先從 HTML 講起,講 HTML 先從 XML 講起。XML 是一種可擴展的標記語言,所謂可擴展就是它可以描述任何結構化的數據,它是一棵樹!
document.write只能重繪整個頁面
innerHTML可以重繪頁面的一部分
createDocumentFragment() //創建一個DOM片段
createElement() //創建一個具體的元素
createTextNode() //創建一個文本節點
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有的子節點前插入一個新的子節點
getElementsByTagName() //通過標簽名稱
getElementsByName() //通過元素的Name屬性的值(IE容錯能力較強,會得到一個數組,其中包括id等于name值的)
getElementById() //通過元素Id,唯一性
attribute是dom元素在文檔中作為html標簽擁有的屬性;
property就是dom元素在js中作為對象擁有的屬性。
所以:
對于html的標準屬性來說,attribute和property是同步的,是會自動更新的,
但是對于自定義的屬性來說,他們是不同步的,
src用于替換當前元素,href用于在當前文檔和引用資源之間確立聯系。
src是source的縮寫,指向外部資源的位置,指向的內容將會嵌入到文檔中當前標簽所在位置;在請求src資源時會將其指向的資源下載并應用到文檔內,當瀏覽器解析到該元素時,會暫停其他資源的下載和處理,直到將該資源加載、編譯、執行完畢,圖片和框架等元素也如此,類似于將所指向資源嵌入當前標簽內。這也是為什么將js腳本放在底部而不是頭部。
Src source,指向外部資源的位置,如果我們添加<script src ="js.js"></script>瀏覽器會暫停其他資源的下載和處理,直到該資源加載,編譯,執行完畢(圖片和框架也是如此),這也就是為什么js腳本要放在底部。
src用于替換當前元素,href用于在當前文檔和引入資源之間建立聯系。
cookie 本身不是用來做服務器端存儲的(計算機領域有很多這種“狗拿耗子”的例子,例如 CSS 中的 float),它是設計用來在服務器和客戶端進行信息傳遞的,因此我們的每個 HTTP 請求都帶著 cookie。但是 cookie 也具備瀏覽器端存儲的能力(例如記住用戶名和密碼),因此就被開發者用上了。
使用起來也非常簡單,document.cookie = ....即可。
但是 cookie 有它致命的缺點:
存儲量太小,只有 4KB
所有 HTTP 請求都帶著,會影響獲取資源的效率
API 簡單,需要封裝才能用
后來,HTML5 標準就帶來了sessionStorage和localStorage,先拿localStorage來說,它是專門為了瀏覽器端緩存而設計的。
存儲量增大到 5MB
不會帶到 HTTP 請求中
API 適用于數據存儲 localStorage.setItem(key, value) localStorage.getItem(key)
sessionStorage的區別就在于它是根據 session 過去時間而實現,而localStorage會永久有效,應用場景不同。例如,一些需要及時失效的重要信息放在sessionStorage中,一些不重要但是不經常設置的信息,放在localStorage中。
標簽閉合、標簽小寫、不亂嵌套、提高搜索機器人搜索幾率、使用外 鏈css和js腳本、結構行為表現的分離、文件下載與頁面速度更快、內容能被更多的用戶所訪問、內容能被更廣泛的設備所訪問、更少的代碼和組件,容易維 護、改版方便,不需要變動頁面內容、提供打印版本而不需要復制內容、提高網站易用性;
參考答案
防抖
觸發高頻事件后n秒內函數只會執行一次,如果n秒內高頻事件再次被觸發,則重新計算時間
每次觸發事件時都取消之前的延時調用方法
function debounce(fn) {
let timeout = null; // 創建一個標記用來存放定時器的返回值
return function () {
clearTimeout(timeout); // 每當用戶輸入的時候把前一個 setTimeout clear 掉
timeout = setTimeout(() => { // 然后又創建一個新的 setTimeout, 這樣就能保證輸入字符后的 interval 間隔內如果還有字符輸入的話,就不會執行 fn 函數
fn.apply(this, arguments);
}, 500);
};
}
function sayHi() {
console.log('防抖成功');
}
var inp = document.getElementById('inp');
inp.addEventListener('input', debounce(sayHi)); // 防抖
節流
高頻事件觸發,但在n秒內只會執行一次,所以節流會稀釋函數的執行頻率
每次觸發事件時都判斷當前是否有等待執行的延時函數
function throttle(fn) {
let canRun = true; // 通過閉包保存一個標記
return function () {
if (!canRun) return; // 在函數開頭判斷標記是否為true,不為true則return
canRun = false; // 立即設置為false
setTimeout(() => { // 將外部傳入的函數的執行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout執行完畢后再把標記設置為true(關鍵)表示可以執行下一次循環了。當定時器沒有執行的時候標記永遠是false,在開頭被return掉
canRun = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));
誤區:我們經常說get請求參數的大小存在限制,而post請求的參數大小是無限制的。
參考答案
實際上HTTP 協議從未規定 GET/POST 的請求長度限制是多少。對get請求參數的限制是來源與瀏覽器或web服務器,瀏覽器或web服務器限制了url的長度。為了明確這個概念,我們必須再次強調下面幾點:
補充補充一個get和post在緩存方面的區別:
可從IIFE、AMD、CMD、CommonJS、UMD、webpack(require.ensure)、ES Module、<script type="module"> 這幾個角度考慮。
參考答案
模塊化主要是用來抽離公共代碼,隔離作用域,避免變量沖突等。
IIFE:使用自執行函數來編寫模塊化,特點:在一個單獨的函數作用域中執行代碼,避免變量沖突。
(function(){
return {
data:[]
}
})()
AMD:使用requireJS 來編寫模塊化,特點:依賴必須提前聲明好。
define('./index.js',function(code){
// code 就是index.js 返回的內容
})
CMD:使用seaJS 來編寫模塊化,特點:支持動態引入依賴文件。
define(function(require, exports, module) {
var indexCode = require('./index.js');
})
CommonJS:nodejs 中自帶的模塊化。
var fs = require('fs');
UMD:兼容AMD,CommonJS 模塊化語法。
webpack(require.ensure):webpack 2.x 版本中的代碼分割。
ES Modules:ES6 引入的模塊化,支持import 來引入另一個 js 。
import a from 'a';
參考答案
輸入 npm install 命令并敲下回車后,會經歷如下幾個階段(以 npm 5.5.1 為例):
參考答案
ES5的繼承時通過prototype或構造函數機制來實現。ES5的繼承實質上是先創建子類的實例對象,然后再將父類的方法添加到this上(Parent.apply(this))。
ES6的繼承機制完全不同,實質上是先創建父類的實例對象this(所以必須先調用父類的super()方法),然后再用子類的構造函數修改this。
具體的:ES6通過class關鍵字定義類,里面有構造方法,類之間通過extends關鍵字實現繼承。子類必須在constructor方法中調用super方法,否則新建實例報錯。因為子類沒有自己的this對象,而是繼承了父類的this對象,然后對其進行加工。如果不調用super方法,子類得不到this對象。
ps:super關鍵字指代父類的實例,即父類的this對象。在子類構造函數中,調用super后,才可使用this關鍵字,否則報錯。
參考答案
參考答案
**因為js是單線程的,瀏覽器遇到setTimeout或者setInterval會先執行完當前的代碼塊,在此之前會把定時器推入瀏覽器的待執行事件隊列里面,等到瀏覽器執行完當前代碼之后會看一下事件隊列里面有沒有任務,有的話才執行定時器的代碼。**所以即使把定時器的時間設置為0還是會先執行當前的一些代碼。
function test(){
var aa = 0;
var testSet = setInterval(function(){
aa++;
console.log(123);
if(aa<10){
clearInterval(testSet);
}
},20);
var testSet1 = setTimeout(function(){
console.log(321)
},1000);
for(var i=0;i<10;i++){
console.log('test');
}
}
test()
輸出結果:
test //10次
undefined
123
321
參考答案
輸出:[1, NaN, NaN]
var new_array = arr.map(function callback(currentValue[, index[, array]]) { // Return element for new_array }[, thisArg])這個callback一共可以接收三個參數,其中第一個參數代表當前被處理的元素,而第二個參數代表該元素的索引。
參考答案
Doctype聲明于文檔最前面,告訴瀏覽器以何種方式來渲染頁面,這里有兩種模式,嚴格模式和混雜模式。
參考答案
fetch發送post請求的時候,總是發送2次,第一次狀態碼是204,第二次才成功?
原因很簡單,因為你用fetch的post請求的時候,導致fetch 第一次發送了一個Options請求,詢問服務器是否支持修改的請求頭,如果服務器支持,則在第二次中發送真正的請求。
history對象
Navigator對象
參考答案
共同點:都是保存在瀏覽器端,并且是同源的
補充說明一下cookie的作用:
參考答案
XSS(跨站腳本攻擊)是指攻擊者在返回的HTML中嵌入javascript腳本,為了減輕這些攻擊,需要在HTTP頭部配上,set-cookie:
結果應該是這樣的:Set-Cookie=.....
參考答案
其中一個主要的區別在于瀏覽器的event loop 和nodejs的event loop 在處理異步事件的順序是不同的,nodejs中有micro event;其中Promise屬于micro event 該異步事件的處理順序就和瀏覽器不同.nodejs V11.0以上 這兩者之間的順序就相同了.
function test () {
console.log('start')
setTimeout(() => {
console.log('children2')
Promise.resolve().then(() => {console.log('children2-1')})
}, 0)
setTimeout(() => {
console.log('children3')
Promise.resolve().then(() => {console.log('children3-1')})
}, 0)
Promise.resolve().then(() => {console.log('children1')})
console.log('end')
}
test()
// 以上代碼在node11以下版本的執行結果(先執行所有的宏任務,再執行微任務)
// start
// end
// children1
// children2
// children3
// children2-1
// children3-1
// 以上代碼在node11及瀏覽器的執行結果(順序執行宏任務和微任務)
// start
// end
// children1
// children2
// children2-1
// children3
// children3-1
參考答案
https協議由 http + ssl 協議構成,具體的鏈接過程可參考SSL或TLS握手的概述
中間人攻擊過程如下:
防范方法:
參考答案
(1). 減少HTTP請求數
這條策略基本上所有前端人都知道,而且也是最重要最有效的。都說要減少HTTP請求,那請求多了到底會怎么樣呢?首先,每個請求都是有成本的,既包 含時間成本也包含資源成本。一個完整的請求都需要經過DNS尋址、與服務器建立連接、發送數據、等待服務器響應、接收數據這樣一個“漫長”而復雜的過程。時間成本就是用戶需要看到或者“感受”到這個資源是必須要等待這個過程結束的,資源上由于每個請求都需要攜帶數據,因此每個請求都需要占用帶寬。
另外,由于瀏覽器進行并發請求的請求數是有上限的,因此請求數多了以后,瀏覽器需要分批進行請求,因此會增加用戶的等待時間,會給 用戶造成站點速度慢這樣一個印象,即使可能用戶能看到的第一屏的資源都已經請求完了,但是瀏覽器的進度條會一直存在。減少HTTP請求數的主要途徑包括:
(2). 從設計實現層面簡化頁面
如果你的頁面像百度首頁一樣簡單,那么接下來的規則基本上都用不著了。保持頁面簡潔、減少資源的使用時最直接的。如果不是這樣,你的頁面需要華麗的皮膚,則繼續閱讀下面的內容。
(3). 合理設置HTTP緩存
緩存的力量是強大的,恰當的緩存設置可以大大的減少HTTP請求。以有啊首頁為例,當瀏覽器沒有緩存的時候訪問一共會發出78個請求,共600多K 數據(如圖1.1),而當第二次訪問即瀏覽器已緩存之后訪問則僅有10個請求,共20多K數據(如圖1.2)。(這里需要說明的是,如果直接F5刷新頁面 的話效果是不一樣的,這種情況下請求數還是一樣,不過被緩存資源的請求服務器是304響應,只有Header沒有Body,可以節省帶寬)
怎樣才算合理設置?原則很簡單,能緩存越多越好,能緩存越久越好。例如,很少變化的圖片資源可以直接通過HTTP Header中的Expires設置一個很長的過期頭;變化不頻繁而又可能會變的資源可以使用Last-Modifed來做請求驗證。盡可能的讓資源能夠 在緩存中待得更久。
(4). 資源合并與壓縮
如果可以的話,盡可能的將外部的腳本、樣式進行合并,多個合為一個。另外,CSS、Javascript、Image都可以用相應的工具進行壓縮,壓縮后往往能省下不少空間。
(5). CSS Sprites
合并CSS圖片,減少請求數的又一個好辦法。
(6). Inline Images
使用data: URL scheme的方式將圖片嵌入到頁面或CSS中,如果不考慮資源管理上的問題的話,不失為一個好辦法。如果是嵌入頁面的話換來的是增大了頁面的體積,而且無法利用瀏覽器緩存。使用在CSS中的圖片則更為理想一些。
(7). Lazy Load Images
這條策略實際上并不一定能減少HTTP請求數,但是卻能在某些條件下或者頁面剛加載時減少HTTP請求數。對于圖片而言,在頁面剛加載的時候可以只 加載第一屏,當用戶繼續往后滾屏的時候才加載后續的圖片。這樣一來,假如用戶只對第一屏的內容感興趣時,那剩余的圖片請求就都節省了。有啊首頁曾經的做法 是在加載的時候把第一屏之后的圖片地址緩存在Textarea標簽中,待用戶往下滾屏的時候才“惰性”加載。
參考答案
重繪(Repaint)和回流(Reflow)
重繪和回流是渲染步驟中的一小節,但是這兩個步驟對于性能影響很大。
回流必定會發生重繪,重繪不一定會引發回流。回流所需的成本比重繪高的多,改變深層次的節點很可能導致父節點的一系列回流。
所以以下幾個動作可能會導致性能問題:
很多人不知道的是,重繪和回流其實和 Event loop 有關。
減少重繪和回流
參考答案
vue和react都是采用diff算法來對比新舊虛擬節點,從而更新節點。在vue的diff函數中(建議先了解一下diff算法過程)。在交叉對比中,當新節點跟舊節點頭尾交叉對比沒有結果時,會根據新節點的key去對比舊節點數組中的key,從而找到相應舊節點(這里對應的是一個key => index 的map映射)。如果沒找到就認為是一個新增節點。而如果沒有key,那么就會采用遍歷查找的方式去找到對應的舊節點。一種一個map映射,另一種是遍歷查找。相比而言。map映射的速度更快。vue部分源碼如下:
// vue項目 src/core/vdom/patch.js -488行
// 以下是為了閱讀性進行格式化后的代碼
// oldCh 是一個舊虛擬節點數組
if (isUndef(oldKeyToIdx)) {
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
}
if(isDef(newStartVnode.key)) {
// map 方式獲取
idxInOld = oldKeyToIdx[newStartVnode.key]
} else {
// 遍歷方式獲取
idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
}
創建map函數
function createKeyToOldIdx (children, beginIdx, endIdx) {
let i, key
const map = {}
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key
if (isDef(key)) map[key] = i
}
return map
}
遍歷尋找
// sameVnode 是對比新舊節點是否相同的函數
function findIdxInOld (node, oldCh, start, end) {
for (let i = start; i < end; i++) {
const c = oldCh[i]
if (isDef(c) && sameVnode(node, c)) return i
}
}
參考答案
在React中,如果是由React引發的事件處理(比如通過onClick引發的事件處理),調用setState不會同步更新this.state,除此之外的setState調用會同步執行this.state。所謂“除此之外”,指的是繞過React通過addEventListener直接添加的事件處理函數,還有通過setTimeout/setInterval產生的異步調用。
**原因:**在React的setState函數實現中,會根據一個變量isBatchingUpdates判斷是直接更新this.state還是放到隊列中回頭再說,而isBatchingUpdates默認是false,也就表示setState會同步更新this.state,但是,有一個函數batchedUpdates,這個函數會把isBatchingUpdates修改為true,而當React在調用事件處理函數之前就會調用這個batchedUpdates,造成的后果,就是由React控制的事件處理過程setState不會同步更新this.state。
class Example extends React.Component {
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 1 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 2 次 log
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 3 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 4 次 log
}, 0);
}
render() {
return null;
}
};
1、第一次和第二次都是在 react 自身生命周期內,觸發時 isBatchingUpdates 為 true,所以并不會直接執行更新 state,而是加入了 dirtyComponents,所以打印時獲取的都是更新前的狀態 0。
2、兩次 setState 時,獲取到 this.state.val 都是 0,所以執行時都是將 0 設置成 1,在 react 內部會被合并掉,只執行一次。設置完成后 state.val 值為 1。
3、setTimeout 中的代碼,觸發時 isBatchingUpdates 為 false,所以能夠直接進行更新,所以連著輸出 2,3。
輸出: 0 0 2 3
參考答案
虛擬dom相當于在js和真實dom中間加了一個緩存,利用dom diff算法避免了沒有必要的dom操作,從而提高性能。
具體實現步驟如下:
用 JavaScript 對象結構表示 DOM 樹的結構;然后用這個樹構建一個真正的 DOM 樹,插到文檔當中
當狀態變更的時候,重新構造一棵新的對象樹。然后用新的樹和舊的樹進行比較,記錄兩棵樹差異
把2所記錄的差異應用到步驟1所構建的真正的DOM樹上,視圖就更新了。
參考答案
結構:display:none: 會讓元素完全從渲染樹中消失,渲染的時候不占據任何空間, 不能點擊, visibility: hidden:不會讓元素從渲染樹消失,渲染元素繼續占據空間,只是內容不可見,不能點擊 opacity: 0: 不會讓元素從渲染樹消失,渲染元素繼續占據空間,只是內容不可見,可以點擊
繼承:display: none:是非繼承屬性,子孫節點消失由于元素從渲染樹消失造成,通過修改子孫節點屬性無法顯示。visibility: hidden:是繼承屬性,子孫節點消失由于繼承了hidden,通過設置visibility: visible;可以讓子孫節點顯式。
性能:displaynone : 修改元素會造成文檔回流,讀屏器不會讀取display: none元素內容,性能消耗較大 visibility:hidden: 修改元素只會造成本元素的重繪,性能消耗較少讀屏器讀取visibility: hidden元素內容 opacity: 0 :修改元素會造成重繪,性能消耗較少
聯系:它們都能讓元素不可見
參考答案
常用的一般為三種.clearfix, clear:both,overflow:hidden;
比較好是 .clearfix,偽元素萬金油版本,后兩者有局限性.
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
<!--
為毛沒有 zoom ,_height 這些,IE6,7這類需要 csshack 不再我們考慮之內了
.clearfix 還有另外一種寫法,
-->
.clearfix:before, .clearfix:after {
content:"";
display:table;
}
.clearfix:after{
clear:both;
overflow:hidden;
}
.clearfix{
zoom:1;
}
<!--
用display:table 是為了避免外邊距margin重疊導致的margin塌陷,
內部元素默認會成為 table-cell 單元格的形式
-->
clear:both:若是用在同一個容器內相鄰元素上,那是賊好的,有時候在容器外就有些問題了, 比如相鄰容器的包裹層元素塌陷
overflow:hidden:這種若是用在同個容器內,可以形成 BFC避免浮動造成的元素塌陷
參考答案
概念:將多個小圖片拼接到一個圖片中。通過 background-position 和元素尺寸調節需要顯示的背景圖案。
優點:
缺點:
參考答案
參考答案
block元素特點:
1.處于常規流中時,如果width沒有設置,會自動填充滿父容器 2.可以應用margin/padding 3.在沒有設置高度的情況下會擴展高度以包含常規流中的子元素 4.處于常規流中時布局時在前后元素位置之間(獨占一個水平空間) 5.忽略vertical-align
inline元素特點
1.水平方向上根據direction依次布局
2.不會在元素前后進行換行
3.受white-space控制
4.margin/padding在豎直方向上無效,水平方向上有效
5.width/height屬性對非替換行內元素無效,寬度由元素內容決定
6.非替換行內元素的行框高由line-height確定,替換行內元素的行框高由height,margin,padding,border決定 7.浮動或絕對定位時會轉換為block8.vertical-align屬性生效
參考答案
/**
* 在標準瀏覽器下使用
* 1 content內容為空格用于修復opera下文檔中出現
* contenteditable屬性時在清理浮動元素上下的空白
* 2 使用display使用table而不是block:可以防止容器和
* 子元素top-margin折疊,這樣能使清理效果與BFC,IE6/7
* zoom: 1;一致
**/
.clearfix:before,
.clearfix:after {
content: " "; /* 1 */
display: table; /* 2 */
}
.clearfix:after {
clear: both;
}
/**
* IE 6/7下使用
* 通過觸發hasLayout實現包含浮動
**/
.clearfix {
*zoom: 1;
}
參考答案
GIF:
JPEG:
PNG:
參考答案
參考答案
參考答案
七種數據類型
(ES6之前)其中5種為基本類型:string,number,boolean,null,undefined,
ES6出來的Symbol也是原始數據類型 ,表示獨一無二的值
Object為引用類型(范圍挺大),也包括數組、函數,
參考答案
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
輸出結果是:
1
2
4
3
promise構造函數是同步執行的,then方法是異步執行的
Promise new的時候會立即執行里面的代碼 then是微任務 會在本次任務執行完的時候執行 setTimeout是宏任務 會在下次任務執行的時候執行
參考答案
工廠模式
簡單的工廠模式可以理解為解決多個相似的問題;
function CreatePerson(name,age,sex) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sex = sex;
obj.sayName = function(){
return this.name;
}
return obj;
}
var p1 = new CreatePerson("longen",'28','男');
var p2 = new CreatePerson("tugenhua",'27','女');
console.log(p1.name); // longen
console.log(p1.age); // 28
console.log(p1.sex); // 男
console.log(p1.sayName()); // longen
console.log(p2.name); // tugenhua
console.log(p2.age); // 27
console.log(p2.sex); // 女
console.log(p2.sayName()); // tugenhua
單例模式
只能被實例化(構造函數給實例添加屬性與方法)一次
// 單體模式
var Singleton = function(name){
this.name = name;
};
Singleton.prototype.getName = function(){
return this.name;
}
// 獲取實例對象
var getInstance = (function() {
var instance = null;
return function(name) {
if(!instance) {//相當于一個一次性閥門,只能實例化一次
instance = new Singleton(name);
}
return instance;
}
})();
// 測試單體模式的實例,所以a===b
var a = getInstance("aa");
var b = getInstance("bb");
沙箱模式
將一些函數放到自執行函數里面,但要用閉包暴露接口,用變量接收暴露的接口,再調用里面的值,否則無法使用里面的值
let sandboxModel=(function(){
function sayName(){};
function sayAge(){};
return{
sayName:sayName,
sayAge:sayAge
}
})()
發布者訂閱模式
就例如如我們關注了某一個公眾號,然后他對應的有新的消息就會給你推送,
//發布者與訂閱模式
var shoeObj = {}; // 定義發布者
shoeObj.list = []; // 緩存列表 存放訂閱者回調函數
// 增加訂閱者
shoeObj.listen = function(fn) {
shoeObj.list.push(fn); // 訂閱消息添加到緩存列表
}
// 發布消息
shoeObj.trigger = function() {
for (var i = 0, fn; fn = this.list[i++];) {
fn.apply(this, arguments);//第一個參數只是改變fn的this,
}
}
// 小紅訂閱如下消息
shoeObj.listen(function(color, size) {
console.log("顏色是:" + color);
console.log("尺碼是:" + size);
});
// 小花訂閱如下消息
shoeObj.listen(function(color, size) {
console.log("再次打印顏色是:" + color);
console.log("再次打印尺碼是:" + size);
});
shoeObj.trigger("紅色", 40);
shoeObj.trigger("黑色", 42);
代碼實現邏輯是用數組存貯訂閱者, 發布者回調函數里面通知的方式是遍歷訂閱者數組,并將發布者內容傳入訂閱者數組
參考答案
1.字面量
let obj={'name':'張三'}
2.Object構造函數創建
let Obj=new Object()
Obj.name='張三'
3.使用工廠模式創建對象
function createPerson(name){
var o = new Object();
o.name = name;
};
return o;
}
var person1 = createPerson('張三');
4.使用構造函數創建對象
function Person(name){
this.name = name;
}
var person1 = new Person('張三');
參考答案
HTML中與javascript交互是通過事件驅動來實現的,例如鼠標點擊事件onclick、頁面的滾動事件onscroll等等,可以向文檔或者文檔中的元素添加事件偵聽器來預訂事件。想要知道這些事件是在什么時候進行調用的,就需要了解一下“事件流”的概念。
什么是事件流:事件流描述的是從頁面中接收事件的順序,DOM2級事件流包括下面幾個階段。
addEventListener:addEventListener是DOM2 級事件新增的指定事件處理程序的操作,這個方法接收3個參數:要處理的事件名、作為事件處理程序的函數和一個布爾值。最后這個布爾值參數如果是true,表示在捕獲階段調用事件處理程序;如果是false,表示在冒泡階段調用事件處理程序。
IE只支持事件冒泡。
參考答案
獲取一個對象的原型,在chrome中可以通過__proto__的形式,或者在ES6中可以通過Object.getPrototypeOf的形式。
那么Function.proto是什么么?也就是說Function由什么對象繼承而來,我們來做如下判別。
Function.__proto__==Object.prototype //false
Function.__proto__==Function.prototype//true
我們發現Function的原型也是Function。
我們用圖可以來明確這個關系:
參考答案
這里來舉個栗子,以Object為例,我們常用的Object便是一個構造函數,因此我們可以通過它構建實例。
// 實例
const instance = new Object()
則此時, 實例為instance, 構造函數為Object,我們知道,構造函數擁有一個prototype的屬性指向原型,因此原型為:
// 原型
const prototype = Object.prototype
這里我們可以來看出三者的關系:
實例.__proto__ === 原型
原型.constructor === 構造函數
構造函數.prototype === 原型
// 這條線其實是是基于原型進行獲取的,可以理解成一條基于原型的映射線
// 例如:
// const o = new Object()
// o.constructor === Object --> true
// o.__proto__ = null;
// o.constructor === Object --> false
實例.constructor === 構造函數
參考答案
在 JS 中,繼承通常指的便是 原型鏈繼承,也就是通過指定原型,并可以通過原型鏈繼承原型上的屬性或者方法。
參考答案
在函數式編程中,函數是一等公民。那么函數柯里化是怎樣的呢?
函數柯里化指的是將能夠接收多個參數的函數轉化為接收單一參數的函數,并且返回接收余下參數且返回結果的新函數的技術。
函數柯里化的主要作用和特點就是參數復用、提前返回和延遲執行。
在一個函數中,首先填充幾個參數,然后再返回一個新的函數的技術,稱為函數的柯里化。通常可用于在不侵入函數的前提下,為函數 預置通用參數,供多次重復調用。
const add = function add(x) {
return function (y) {
return x + y
}
}
const add1 = add(1)
add1(2) === 3
add1(20) === 21
參考答案
call 和 apply 都是為了解決改變 this 的指向。作用都是相同的,只是傳參的方式不同。
除了第一個參數外,call 可以接收一個參數列表,apply 只接受一個參數數組。
let a = {
value: 1
}
function getValue(name, age) {
console.log(name)
console.log(age)
console.log(this.value)
}
getValue.call(a, 'yck', '24')
getValue.apply(a, ['yck', '24'])
bind和其他兩個方法作用也是一致的,只是該方法會返回一個函數。并且我們可以通過 bind實現柯里化。
(下面是對這三個方法的擴展介紹)
如何實現一個 bind 函數
對于實現以下幾個函數,可以從幾個方面思考
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var _this = this
var args = [...arguments].slice(1)
// 返回一個函數
return function F() {
// 因為返回了一個函數,我們可以 new F(),所以需要判斷
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}
如何實現一個call函數
Function.prototype.myCall = function (context) {
var context = context || window
// 給 context 添加一個屬性
// getValue.call(a, 'yck', '24') => a.fn = getValue
context.fn = this
// 將 context 后面的參數取出來
var args = [...arguments].slice(1)
// getValue.call(a, 'yck', '24') => a.fn('yck', '24')
var result = context.fn(...args)
// 刪除 fn
delete context.fn
return result
}
如何實現一個apply函數
Function.prototype.myApply = function (context) {
var context = context || window
context.fn = this
var result
// 需要判斷是否存儲第二個參數
// 如果存在,就將第二個參數展開
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
參考答案
function a() {
return () => {
return () => {
console.log(this)
}
}
}
console.log(a()()())
箭頭函數其實是沒有 this的,這個函數中的 this只取決于他外面的第一個不是箭頭函數的函數的 this。在這個例子中,因為調用 a符合前面代碼中的第一個情況,所以 this是 window。并且 this一旦綁定了上下文,就不會被任何代碼改變。
function sayHi() {
console.log(name);
console.log(age);
var name = "Lydia";
let age = 21;
}
sayHi();
參考答案
在函數中,我們首先使用var關鍵字聲明了name變量。這意味著變量在創建階段會被提升(JavaScript會在創建變量創建階段為其分配內存空間),默認值為undefined,直到我們實際執行到使用該變量的行。我們還沒有為name變量賦值,所以它仍然保持undefined的值。
使用let關鍵字(和const)聲明的變量也會存在變量提升,但與var不同,初始化沒有被提升。在我們聲明(初始化)它們之前,它們是不可訪問的。這被稱為“暫時死區”。當我們在聲明變量之前嘗試訪問變量時,JavaScript會拋出一個ReferenceError。
關于let的是否存在變量提升,我們何以用下面的例子來驗證:
let name = 'ConardLi'
{
console.log(name) // Uncaught ReferenceError: name is not defined
let name = 'code秘密花園'
}
let變量如果不存在變量提升,console.log(name)就會輸出ConardLi,結果卻拋出了ReferenceError,那么這很好的說明了,let也存在變量提升,但是它存在一個“暫時死區”,在變量未初始化或賦值前不允許訪問。
變量的賦值可以分為三個階段:
關于let、var和function:
var a = 10;
(function () {
console.log(a)
a = 5
console.log(window.a)
var a = 20;
console.log(a)
})()
依次輸出:undefined -> 10 -> 20
在立即執行函數中,var a = 20; 語句定義了一個局部變量 a,由于js的變量聲明提升機制,局部變量a的聲明會被提升至立即執行函數的函數體最上方,且由于這樣的提升并不包括賦值,因此第一條打印語句會打印undefined,最后一條語句會打印20。
由于變量聲明提升,a = 5; 這條語句執行時,局部的變量a已經聲明,因此它產生的效果是對局部的變量a賦值,此時window.a 依舊是最開始賦值的10,
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor;
}
constructor({ newColor = "green" } = {}) {
this.newColor = newColor;
}
}
const freddie = new Chameleon({ newColor: "purple" });
freddie.colorChange("orange");
答案: D
colorChange方法是靜態的。靜態方法僅在創建它們的構造函數中存在,并且不能傳遞給任何子級。由于freddie是一個子級對象,函數不會傳遞,所以在freddie實例上不存在freddie方法:拋出TypeError。
var a = ?;
if(a == 1 && a == 2 && a == 3){
conso.log(1);
}
參考答案
因為==會進行隱式類型轉換 所以我們重寫toString方法就可以了
var a = {
i: 1,
toString() {
return a.i++;
}
}
if( a == 1 && a == 2 && a == 3 ) {
console.log(1);
}
var obj = {
'2': 3,
'3': 4,
'length': 2,
'splice': Array.prototype.splice,
'push': Array.prototype.push
}
obj.push(1)
obj.push(2)
console.log(obj)
參考答案
1.使用第一次push,obj對象的push方法設置 obj[2]=1;obj.length+=12.使用第二次push,obj對象的push方法設置 obj[3]=2;obj.length+=13.使用console.log輸出的時候,因為obj具有 length 屬性和 splice 方法,故將其作為數組進行打印 4.打印時因為數組未設置下標為 0 1 處的值,故打印為empty,主動 obj[0] 獲取為 undefined
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x)
console.log(b.x)
參考答案
undefined {n:2}
首先,a和b同時引用了{n:2}對象,接著執行到a.x = a = {n:2}語句,盡管賦值是從右到左的沒錯,但是.的優先級比=要高,所以這里首先執行a.x,相當于為a(或者b)所指向的{n:1}對象新增了一個屬性x,即此時對象將變為{n:1;x:undefined}。之后按正常情況,從右到左進行賦值,此時執行a ={n:2}的時候,a的引用改變,指向了新對象{n:2},而b依然指向的是舊對象。之后執行a.x = {n:2}的時候,并不會重新解析一遍a,而是沿用最初解析a.x時候的a,也即舊對象,故此時舊對象的x的值為{n:2},舊對象為 {n:1;x:{n:2}},它被b引用著。后面輸出a.x的時候,又要解析a了,此時的a是指向新對象的a,而這個新對象是沒有x屬性的,故訪問時輸出undefined;而訪問b.x的時候,將輸出舊對象的x的值,即{n:2}。
function checkAge(data) {
if (data === { age: 18 }) {
console.log("You are an adult!");
} else if (data == { age: 18 }) {
console.log("You are still an adult.");
} else {
console.log(`Hmm.. You don't have an age I guess`);
}
}
checkAge({ age: 18 });
參考答案
Hmm.. You don't have an age I guess
在比較相等性,原始類型通過它們的值進行比較,而對象通過它們的引用進行比較。JavaScript檢查對象是否具有對內存中相同位置的引用。
我們作為參數傳遞的對象和我們用于檢查相等性的對象在內存中位于不同位置,所以它們的引用是不同的。
這就是為什么{ age: 18 } === { age: 18 }和 { age: 18 } == { age: 18 }返回 false的原因。
const obj = { 1: "a", 2: "b", 3: "c" };
const set = new Set([1, 2, 3, 4, 5]);
obj.hasOwnProperty("1");
obj.hasOwnProperty(1);
set.has("1");
set.has(1);
參考答案
true` `true` `false` `true
所有對象鍵(不包括Symbols)都會被存儲為字符串,即使你沒有給定字符串類型的鍵。這就是為什么obj.hasOwnProperty('1')也返回true。
上面的說法不適用于Set。在我們的Set中沒有“1”:set.has('1')返回false。它有數字類型1,set.has(1)返回true。
// example 1
var a={}, b='123', c=123;
a[b]='b';
a[c]='c';
console.log(a[b]);
---------------------
// example 2
var a={}, b=Symbol('123'), c=Symbol('123');
a[b]='b';
a[c]='c';
console.log(a[b]);
---------------------
// example 3
var a={}, b={key:'123'}, c={key:'456'};
a[b]='b';
a[c]='c';
console.log(a[b]);
參考答案
這題考察的是對象的鍵名的轉換。
// example 1
var a={}, b='123', c=123;
a[b]='b';
// c 的鍵名會被轉換成字符串'123',這里會把 b 覆蓋掉。
a[c]='c';
// 輸出 c
console.log(a[b]);
// example 2
var a={}, b=Symbol('123'), c=Symbol('123');
// b 是 Symbol 類型,不需要轉換。
a[b]='b';
// c 是 Symbol 類型,不需要轉換。任何一個 Symbol 類型的值都是不相等的,所以不會覆蓋掉 b。
a[c]='c';
// 輸出 b
console.log(a[b]);
// example 3
var a={}, b={key:'123'}, c={key:'456'};
// b 不是字符串也不是 Symbol 類型,需要轉換成字符串。
// 對象類型會調用 toString 方法轉換成字符串 [object Object]。
a[b]='b';
// c 不是字符串也不是 Symbol 類型,需要轉換成字符串。
// 對象類型會調用 toString 方法轉換成字符串 [object Object]。這里會把 b 覆蓋掉。
a[c]='c';
// 輸出 c
console.log(a[b]);
(() => {
let x, y;
try {
throw new Error();
} catch (x) {
(x = 1), (y = 2);
console.log(x);
}
console.log(x);
console.log(y);
})();
參考答案
1` `undefined` `2
catch塊接收參數x。當我們傳遞參數時,這與變量的x不同。這個變量x是屬于catch作用域的。
之后,我們將這個塊級作用域的變量設置為1,并設置變量y的值。現在,我們打印塊級作用域的變量x,它等于1。
在catch塊之外,x仍然是undefined,而y是2。當我們想在catch塊之外的console.log(x)時,它返回undefined,而y返回2。
function Foo() {
Foo.a = function() {
console.log(1)
}
this.a = function() {
console.log(2)
}
}
Foo.prototype.a = function() {
console.log(3)
}
Foo.a = function() {
console.log(4)
}
Foo.a();
let obj = new Foo();
obj.a();
Foo.a();
參考答案
輸出順序是 4 2 1
function Foo() {
Foo.a = function() {
console.log(1)
}
this.a = function() {
console.log(2)
}
}
// 以上只是 Foo 的構建方法,沒有產生實例,此刻也沒有執行
Foo.prototype.a = function() {
console.log(3)
}
// 現在在 Foo 上掛載了原型方法 a ,方法輸出值為 3
Foo.a = function() {
console.log(4)
}
// 現在在 Foo 上掛載了直接方法 a ,輸出值為 4
Foo.a();
// 立刻執行了 Foo 上的 a 方法,也就是剛剛定義的,所以
// # 輸出 4
let obj = new Foo();
/* 這里調用了 Foo 的構建方法。Foo 的構建方法主要做了兩件事:
1. 將全局的 Foo 上的直接方法 a 替換為一個輸出 1 的方法。
2. 在新對象上掛載直接方法 a ,輸出值為 2。
*/
obj.a();
// 因為有直接方法 a ,不需要去訪問原型鏈,所以使用的是構建方法里所定義的 this.a,
// # 輸出 2
Foo.a();
// 構建方法里已經替換了全局 Foo 上的 a 方法,所以
// # 輸出 1
作者:靜觀流葉
原文地址:https://mp.weixin.qq.com/s/PTSaytcf3xgOp6C9l3Pvjw
**2024 高頻前端面試題匯總之CSS篇(二)**
**引言:**
在Web前端領域,CSS作為視覺呈現的靈魂,一直是各大公司面試考核的重點之一。為了幫助廣大前端開發者充分準備即將到來的面試,本文將繼續盤點2024年高頻CSS面試題目,并通過詳盡的解析與實例代碼,帶您鞏固和拓展CSS知識體系,提升面試成功率。
## **一、CSS布局進階**
**題目1:Flexbox布局的基本原理與應用場景**
**解答:**
Flexbox布局(彈性盒布局)是一種現代化的CSS布局方式,通過`display: flex`聲明父元素為彈性容器,然后通過一系列相關的屬性(如`flex-direction`、`justify-content`、`align-items`等)對子元素進行靈活的定位與尺寸分配。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.container {
display: flex;
flex-direction: row; /* 主軸方向,默認為水平 */
justify-content: space-around; /* 主軸上的對齊方式 */
align-items: center; /* 交叉軸上的對齊方式 */
}
.box {
flex: 1; /* 子元素按比例分配空間 */
border: 1px solid black;
padding: 1em;
margin: 0.5em;
}
</style>
</head>
<body>
<div class="container">
<div class="box">Box 1</div>
<div class="box">Box 2</div>
<div class="box">Box 3</div>
</div>
</body>
</html>
```
**題目2:Grid布局的特點與難點**
**解答:**
CSS Grid布局提供了一個二維網格系統,用于定義行與列,并精確放置和對齊項目。相較于Flexbox,Grid布局更適合處理復雜的二維布局問題。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 創建3等寬的列 */
grid-gap: 10px; /* 設置行與列之間的間距 */
}
.grid-item {
border: 1px solid black;
padding: 1em;
}
</style>
</head>
<body>
<div class="grid-container">
<div class="grid-item">Item 1</div>
<div class="grid-item">Item 2</div>
<div class="grid-item">Item 3</div>
<!-- ... 更多網格項 -->
</div>
</body>
</html>
```
## **二、CSS選擇器與優先級**
**題目3:CSS選擇器的層級關系與權重計算**
**解答:**
CSS選擇器的優先級由內聯樣式(1000)、ID選擇器(100)、類選擇器(10)、屬性選擇器(10)、偽類(10)、元素選擇器(1)和通用選擇器(0)依次遞減。當多個選擇器應用于同一元素時,優先級高的規則生效。
```css
/* 例子:優先級比較 */
#id .class[attr] p:hover {
color: red; /* 優先級:1013 */
}
.class p {
color: blue; /* 優先級:11 */
}
```
## **三、CSS單位與響應式設計**
**題目4:rem、em與vw/vh單位在響應式設計中的應用**
**解答:**
- `rem`是相對于根元素(通常是`html`)的字體大小的單位,便于實現整體布局的響應式調整。
- `em`是相對于父元素字體大小的單位,常用于局部相對單位的設定。
- `vw/vh`分別代表視窗寬度和高度的百分比,適合創建完全依賴視口大小的響應式設計。
```css
/* 示例 */
html {
font-size: 16px; /* 根元素字體大小 */
}
.container {
width: 80%; /* 百分比單位 */
}
.title {
font-size: 2rem; /* 相對于根元素字體大小 */
}
.subtitle {
font-size: 1.2em; /* 相對于父元素字體大小 */
}
.hero-image {
width: 100vw; /* 占滿整個視口寬度 */
height: 50vh; /* 占視口高度的一半 */
}
```
## **四、CSS性能優化**
**題目5:如何優化CSS以提高渲染性能?**
**解答:**
- 減少CSS文件體積,通過合并、壓縮和精簡CSS代碼。
- 采用現代CSS特性替代舊有的布局和定位方式,如Flexbox和Grid布局。
- 避免過于復雜的選擇器,減少CSS解析時間和渲染性能損耗。
- 使用媒體查詢優化不同設備的加載與渲染,實現按需加載CSS。
- 利用CSS3硬件加速屬性(如`transform`和`will-change`)提高動畫性能。
以上只是部分CSS高頻面試題的解析與示例,希望能幫助您在面試前全方位地復習和掌握CSS的核心知識點。持續關注本系列教程,我們將持續提供更多有價值的前端面試攻略,助您在職業生涯中乘風破浪,披荊斬棘!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。