摘要:前端基本功常見概念一點這里前端基本功常見概念二點這里前端基本功常見概念三點這里是更完美的,不是全局變量,具有塊級函數作用域,大多數情況不會發生變量提升。
前端基本功-常見概念(一) 點這里
前端基本功-常見概念(二) 點這里
前端基本功-常見概念(三) 點這里
let 是更完美的var,不是全局變量,具有塊級函數作用域,大多數情況不會發生變量提升。
const 定義常量值,不能夠重新賦值,如果值是一個對象,可以改變對象里邊的屬性值
var存在的問題
var有作用域問題(會污染全局作用域)
var可已重復聲明
var會變量提升預解釋
var不能定義常量
let、const特性
let、const不可以重復聲明
let、const不會聲明到全局作用域上
let、const不會預解釋變量
const做常量聲明(一般常量名用大寫)
let聲明的變量具有塊級作用域
let聲明的變量不能通過window.變量名進行訪問
形如for(let x..)的循環是每次迭代都為x創建新的綁定
下面是 var 帶來的不合理場景var a = [] for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i) } } a[5]() // 10
在上述代碼中,變量i是var聲明的,在全局范圍類都有效。所以每一次循環,新的i值都會覆蓋舊值,導致最后輸出都是10
而如果對循環使用let語句的情況,那么每次迭代都是為x創建新的綁定代碼如下
var a = [] for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i) } } a[5]() // 5重溫一下閉包和立即函數兩種方法
閉包的方法
function showNum(i) { return function () { console.log(i) } } var a = [] for (var i = 0; i < 5; i++) { a[i] = showNum(i) }
立即函數的方法
var a = [] for (var i = 0; i < 5; i++) { a[i] = (function (i) { return function () { console.log(i) } })(i) } a[2]()
本節參考文章:前端面試之ES6篇
2.重排/重繪在討論重排(回流)與重繪之前,我們要知道:
瀏覽器使用流式布局模型 (Flow Based Layout)。
瀏覽器會把HTML成DOM,把CSS解析成CSSOM,DOM和CSSOM合并就產生了Render Tree。
有了RenderTree,我們就知道了所有節點的樣式,然后計算他們在頁面上的大小和位置,最后把節點繪制到頁面上。
由于瀏覽器使用流式布局,對Render Tree的計算通常只需要遍歷一次就可以完成,但table及其內部元素除外,他們可能需要多次計算,通常要花3倍于同等元素的時間,這也是為什么要避免使用table布局的原因之一。
一句話:重排必將引起重繪,重繪不一定會引起重排。重排 (Reflow)
當Render Tree中部分或全部元素的尺寸、結構、或某些屬性發生改變時,瀏覽器重新渲染部分或全部文檔的過程稱為重排。
會導致重排的操作:
頁面首次渲染
瀏覽器窗口大小發生改變
元素尺寸或位置發生改變
元素內容變化(文字數量或圖片大小等等)
元素字體大小變化
添加或者刪除可見的DOM元素
激活CSS偽類(例如::hover)
查詢某些屬性或調用某些方法
一些常用且會導致重排的屬性和方法:
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
scrollIntoView()、scrollIntoViewIfNeeded()
getComputedStyle()
getBoundingClientRect()
scrollTo()
重繪 (Repaint)當頁面中元素樣式的改變并不影響它在文檔流中的位置時(例如:color、background-color、visibility等),瀏覽器會將新樣式賦予給元素并重新繪制它,這個過程稱為重繪。
性能影響回流比重繪的代價要更高。
有時即使僅僅回流一個單一的元素,它的父元素以及任何跟隨它的元素也會產生回流。
現代瀏覽器會對頻繁的回流或重繪操作進行優化:
瀏覽器會維護一個隊列,把所有引起回流和重繪的操作放入隊列中,如果隊列中的任務數量或者時間間隔達到一個閾值的,瀏覽器就會將隊列清空,進行一次批處理,這樣可以把多次回流和重繪變成一次。
當你訪問以下屬性或方法時,瀏覽器會立刻清空隊列:
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
width、height
getComputedStyle()
getBoundingClientRect()
因為隊列中可能會有影響到這些屬性或方法返回值的操作,即使你希望獲取的信息與隊列中操作引發的改變無關,瀏覽器也會強行清空隊列,確保你拿到的值是最精確的。
如何避免
CSS
避免使用table布局。
盡可能在DOM樹的最末端改變class。
避免設置多層內聯樣式。
將動畫效果應用到position屬性為absolute或fixed的元素上。
避免使用CSS表達式(例如:calc())。
JavaScript
避免頻繁操作樣式,最好一次性重寫style屬性,或者將樣式列表定義為class并一次性更改class屬性。
避免頻繁操作DOM,創建一個documentFragment,在它上面應用所有DOM操作,最后再把它添加到文檔中。
也可以先為元素設置display: none,操作結束后再把它顯示出來。因為在display屬性為none的元素上進行的DOM操作不會引發回流和重繪。
避免頻繁讀取會引發回流/重繪的屬性,如果確實需要多次使用,就用一個變量緩存起來。
對具有復雜動畫的元素使用絕對定位,使它脫離文檔流,否則會引起父元素及后續元素頻繁回流。
本節參考文章:[瀏覽器的回流與重繪 (Reflow & Repaint)](https://juejin.im/post/5a9923e9518825558251c96a)3.函數節流(throttle)與函數去抖(debounce)
Debounce:一部電梯停在某一個樓層,當有一個人進來后,20秒后自動關門,這20秒的等待期間,又一個人按了電梯進來,這20秒又重新計算,直到電梯關門那一刻才算是響應了事件。
Throttle:好比一臺自動的飲料機,按拿鐵按鈕,在出飲料的過程中,不管按多少這個按鈕,都不會連續出飲料,中間按鈕的響應會被忽略,必須要等這一杯的容量全部出完之后,再按拿鐵按鈕才會出下一杯。
4.強緩存/協商緩存瀏覽器緩存分為強緩存和協商緩存,優先讀取強制緩存。
當客戶端請求某個資源時,獲取緩存的流程如下:
先根據這個資源的一些 http header 判斷它是否命中強緩存,如果命中,則直接從本地獲取緩存資源,不會發請求到服務器;
當強緩存沒有命中時,客戶端會發送請求到服務器,服務器通過另一些request header驗證這個資源是否命中協商緩存,稱為http再驗證,如果命中,服務器將請求返回,但不返回資源,而是告訴客戶端直接從緩存中獲取,客戶端收到返回后就會從緩存中獲取資源;
強緩存和協商緩存共同之處在于,如果命中緩存,服務器都不會返回資源;
區別是,強緩存不對發送請求到服務器,但協商緩存會。
當協商緩存也沒命中時,服務器就會將資源發送回客戶端。
當 ctrl+f5 強制刷新網頁時,直接從服務器加載,跳過強緩存和協商緩存;
當 f5 刷新網頁時,跳過強緩存,但是會檢查協商緩存;
強緩存Expires(該字段是 http1.0 時的規范,值為一個絕對時間的 GMT 格式的時間字符串,代表緩存資源的過期時間)
Cache-Control:max-age(該字段是 http1.1 的規范,強緩存利用其 max-age 值來判斷緩存資源的最大生命周期,它的值單位為秒)
協商緩存協商緩存是利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】這兩對Header來管理的
Last-Modified,If-Modified-Since
Last-Modified 表示本地文件最后修改日期,瀏覽器會在request header加上If-Modified-Since(上次返回的Last-Modified的值),詢問服務器在該日期后資源是否有更新,有更新的話就會將新的資源發送回來
但是如果在本地打開緩存文件,就會造成 Last-Modified 被修改,所以在 HTTP / 1.1 出現了 ETag
ETag、If-None-Match
Etag就像一個指紋,資源變化都會導致ETag變化,跟最后修改時間沒有關系,ETag可以保證每一個資源是唯一的
If-None-Match的header會將上次返回的Etag發送給服務器,詢問該資源的Etag是否有更新,有變動就會發送新的資源回來
ETag的優先級比Last-Modified更高
一些文件也許會周期性的更改,但是他的內容并不改變(僅僅改變的修改時間),這個時候我們并不希望客戶端認為這個文件被修改了,而重新GET;
某些文件修改非常頻繁,比如在秒以下的時間內進行修改,(比方說1s內修改了N次),If-Modified-Since能檢查到的粒度是s級的,這種修改無法判斷(或者說UNIX記錄MTIME只能精確到秒);
某些服務器不能精確的得到文件的最后修改時間。
推薦必讀:http協商緩存VS強緩存、瀏覽器緩存知識小結及應用、緩存(二)——瀏覽器緩存機制:強緩存、協商緩存
5.原始類型 / 引用類型JavaScript中的內存分為棧內存和堆內存。棧內存和堆內存區別:棧內存運行效率比堆內存高,空間相對堆內存來說較小。
區別:
值類型屬于不可變類型, 由于具有固定長度大小, 其地址和具體內容都存在與棧內存中
而引用類型屬于可變類型, 一個對象可以賦予多個屬性及值,屬性值又可以為一個新的引用對象。其地址存在棧內存,其具體內容存在堆內存中。
6.cookie/tokentoken和cookie一樣都是首次登陸時,由服務器下發,都是當交互時進行驗證的功能,作用都是為無狀態的HTTP提供的持久機制。
token存在哪兒都行,localstorage或者cookie。
token和cookie舉例,token就是說你告訴我你是誰就可以。
cookie 舉例:服務員看你的身份證,給你一個編號,以后,進行任何操作,都出示編號后服務員去看查你是誰。token 舉例:直接給服務員看自己身份證,服務器不需要去查看你是誰,不需要保存你的會話。
當用戶logout的時候,cookie和服務器的session都會注銷;但是當logout時候token只是注銷瀏覽器信息,不查庫。
token優勢在于,token由于服務器端不存儲會話,所以可擴展性強,token還可用于APP中。
小結:
Token 完全由應用管理,所以它可以避開同源策略
Token 可以避免 CSRF 攻擊
Token 可以是無狀態的,可以在多個服務間共享
如果你的用戶數據可能需要和第三方共享,或者允許第三方調用 API 接口,用 Token,如果之上自己的那就無所謂了。
本節參考文章:cookie和token的五點區別
推薦必讀:前后端常見的幾種鑒權方式
7.cookie/sessionStorage/localStorage 1.cookiecookie分為cookie機制和session機制(請大神判斷正確性)
Session: 是在服務端保存的一個數據結構,用來跟蹤用戶的狀態,這個數據可以保存在集群、數據庫、文件中,通過在服務器端記錄信息確定用戶身份
Cookie: 是客戶端保存用戶信息的一種機制,用來記錄用戶的一些信息,也是實現Session的一種方式,通過在客戶端記錄信息確定用戶身份
如果說Cookie機制是通過檢查客戶身上的“通行證”來確定客戶身份的話,那么Session機制就是通過檢查服務器上的“客戶明細表”來確認客戶身份。Session相當于程序在服務器上建立的一份客戶檔案,客戶來訪的時候只需要查詢客戶檔案表就可以了。
cookie機制
cookie可以通過設置domain屬性值,可以不同二級域名下共享cookie,而Storage不可以,比如http://image.baidu.com的cookie http://map.baidu.com是可以訪問的,前提是Cookie的domain設置為.http://baidu.com,而Storage是不可以的
session機制
當程序需要為某個客戶端的請求創建一個session時,
服務器首先檢查這個客戶端的請求里是否已包含了一個session標識---稱為session id,
如果已包含則說明以前已經為此客戶端創建過session,服務器就按照session id把這個session檢索出來使用(檢索不到,會新建一個),
如果客戶端請求不包含session id,則為此客戶端創建一個session并且生成一個與此session相關聯的session id,session id的值應該是一個既不會重復,又不容易被找到規律以仿造的字符串,這個session id將被在本次響應中返回給客戶端保存。
比較
session 在服務器端,cookie 在客戶端(瀏覽器)
session保存在服務器,客戶端不知道其中的信息;反之,cookie保存在客戶端,服務器能夠知道其中的信息
session會在一定時間內保存在服務器上,當訪問增多,會比較占用你服務器的性能,考慮到減輕服務器性能方面,應當使用cookie
session中保存的是對象,cookie中保存的是字符串
cookie不是很安全,別人可以分析存放在本地的cookie并進行cookie欺騙,考慮到安全應當使用session。用戶驗證這種場合一般會用 session
用戶驗證這種場合一般會用 session,因此,維持一個會話的核心就是客戶端的唯一標識,即 session id
session 的運行依賴 session id,而 session id 是存在 cookie 中的,也就是說,如果瀏覽器禁用了 cookie ,同時 session 也會失效(但是可以通過其它方式實現,比如在 url 中傳遞?session_id)
session不能區分路徑,同一個用戶在訪問一個網站期間,所有的session在任何一個地方都可以訪問到,而cookie中如果設置了路徑參數,那么同一個網站中不同路徑下的cookie互相是訪問不到的
JavaScript原生的用法。Cookie 以名/值對形式存儲
例如username=John Doe,這里的數據是string類型,如要是其他格式注意進行格式轉換。
JavaScript 可以使用 document.cookie 屬性來創建 、讀取、及刪除 cookie。JavaScript 中,創建 cookie 如下所示:
document.cookie="username=John Doe";
您還可以為 cookie 添加一個過期時間(以 UTC 或 GMT 時間)。默認情況下,cookie 在瀏覽器關閉時刪除:
document.cookie="username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 GMT";
您可以使用 path 參數告訴瀏覽器 cookie 的路徑。默認情況下,cookie 屬于當前頁面。
document.cookie="username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 GMT; path=/";
設置cookie
function setCookie(cname,cvalue,exdays){ var SetTime = new Date(); //設置過期時間 SetTime.setTime(SetTime.getTime()+(exdays*24*60*60*1000)); //設置過期時間 var expires = "expires="+SetTime.toGMTString(); //設置過期時間 document.cookie = cname + "=" + cvalue + "; " + expires; //創建一個cookie }
讀取cookie
function getCookie(c_name){ if (document.cookie.length>0) { c_start=document.cookie.indexOf(c_name + "=") if (c_start!=-1){ c_start=c_start + c_name.length+1 c_end=document.cookie.indexOf(";",c_start) if (c_end==-1) c_end=document.cookie.length return unescape(document.cookie.substring(c_start,c_end)) } } return "" }
刪除cookie
將cookie的有效時間改成昨天。
使用jquery.cookies.2.2.0.min.js插件添加/修改cookie并設定過期時間:
$.cookies.set("cookie_id", "cookie_value", { hoursToLive: 10 });
這里設置的是過期時間是10小時, 還可以這樣設置過期時間:
expireDate = new Date(); expireDate.setTime( expireDate.getTime() + ( 10 * 60 * 60 * 1000 ) ); $.cookies.set("cookie_id", "cookie_value", {expiresAt:expireDate});
獲取cookie
$.cookies.get("cookie_id");
刪除cookie
$.cookies.del("cookie_id");2.Storage:localStorage、sessionStorage
大小:官方建議是5M存儲空間
類型:只能操作字符串,在存儲之前應該使用JSON.stringfy()方法先進行一步安全轉換字符串,取值時再用JSON.parse()方法再轉換一次
存儲的內容: 數組,圖片,json,樣式,腳本。。。(只要是能序列化成字符串的內容都可以存儲)
區別:sessionStorage將數據臨時存儲在session中,瀏覽器關閉,數據隨之消失,localStorage將數據存儲在本地,理論上來說數據永遠不會消失,除非人為刪除
注意:數據是明文存儲,毫無隱私性可言,絕對不能用于存儲
保存數據
localStorage.setItem( key, value ); sessionStorage.setItem(keyName,value); // 將value存儲到key字段中 //或者 sessionStorage.keyName="value";
讀取數據
localStorage.getItem( key ); sessionStorage.getItem(keyName); //獲取指定key的本地存儲的值 //或者 var keyName=sessionStorage.key;
刪除單個數據
localStorage.removeItem( key ); sessionStorage.removeItem( key );
刪除全部數據
localStorage.clear( ); sessionStorage.clear( );
獲取索引的key
localStorage.key( index ); sessionStorage.key( index );監聽storage事件
可以通過監聽 window 對象的 storage 事件并指定其事件處理函數,當頁面中對 localStorage 或 sessionStorage 進行修改時,則會觸發對應的處理函數
window.addEventListener("storage",function(e){ console.log("key="+e.key+",oldValue="+e.oldValue+",newValue="+e.newValue); })
localstorage是瀏覽器多個標簽共用的存儲空間,可以用來實現多標簽之間的通信(ps:session是會話級的存儲空間,每個標簽頁都是多帶帶的
觸發事件的時間對象(e 參數值)有幾個屬性:3.對比
key : 鍵值。
oldValue : 被修改前的值。
newValue : 被修改后的值。
url : 頁面url。
storageArea : 被修改的 storage 對象。
共同點:都是保存在瀏覽器端、且同源的,都受同源策略的制約。
區別:
cookie數據始終在同源的http請求中攜帶(即使不需要),即cookie在瀏覽器和服務器間來回傳遞,而sessionStorage和localStorage不會自動把數據發送給服務器,僅在本地保存。cookie數據還有路徑(path)的概念,可以限制cookie只屬于某個路徑下
存儲大小限制也不同,cookie數據不能超過4K,同時因為每次http請求都會攜帶cookie、所以cookie只適合保存很小的數據,如會話標識。sessionStorage和localStorage雖然也有存儲大小的限制,但比cookie大得多,可以達到5M或更大
數據有效期不同,sessionStorage:僅在當前瀏覽器窗口關閉之前有效;localStorage:始終有效,窗口或瀏覽器關閉也一直保存,因此用作持久數據;cookie:只在設置的cookie過期時間之前有效,即使窗口關閉或瀏覽器關閉
作用域不同,sessionStorage不在不同的瀏覽器窗口中共享,即使是同一個頁面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
本節參考文章:緩存(三)——數據存儲...
其他閱讀:關于Cookie、session和Web Storage
8.js事件 1.事件事件指可以被 JavaScript 偵測到的行為。即鼠標點擊、頁面或圖像載入、鼠標懸浮于頁面的某個熱點之上、在表單中選取輸入框、確認表單、鍵盤按鍵等操作。
事件通常與函數配合使用,當事件發生時函數才會執行。
事件名稱:click/mouseover/blur("不帶on")
事件處理程序(事件偵聽器):響應某個事件的函數,名稱為:onclick/onmouseove/onblur,例如
冒泡:往上
捕獲:向下
事件流指從頁面中接收事件的順序,也可理解為事件在頁面中傳播的順序。
DOM2級事件規定的事件流包括三個階段:
(1)事件捕獲階段(2)處于目標階段(3)事件冒泡階段。
當事件發生時,最先得到通知的是window,然后是document,由上至下逐級依次而入,直到真正觸發事件的那個元素(目標元素)為止,這個過程就是捕獲。
接下來,事件會從目標元素開始起泡,由下至上逐級依次傳播,直到window對象為止,這個過程就是冒泡。
所以捕獲比冒泡先執行。
希望注冊在DOM元素上的事件處理程序在捕獲階段還是在冒泡階段觸發,取決于 addEventListener() 方法的第三個參數為 true 還是 false 。
其中DOM3級事件在DOM2的基礎之上添加了更多的事件類型。
描述DOM事件捕獲的具體流程
window-->document-->html(document.documentElement)-->body(document.body)...4.DOM級別/DOM事件
DOM級別一共可以分為4個級別:DOM0級,DOM1級,DOM2級和DOM3級,
而DOM事件分為3個級別:DOM0級事件處理,DOM2級事件處理和DOM3級事件處理。
其中1級DOM標準中并沒有定義事件相關的內容,所以沒有所謂的1級DOM事件模型。
DOM0:element.onclick = function(){}
DOM2:element.addEventlistenter("click",function(){},flase)
DOM3:element.addEventlistenter("keyup",function(){},flase)
HTML事件處理程序
DOM0級事件處理程序
btn.onclick=function(){ alert("hello"); }
btn.onclick = null;//來刪除指定的事件處理程序。
如果我們嘗試給事件添加兩個事件,如:
輸出,hello again,很明顯,第一個事件函數被第二個事件函數給覆蓋掉了, 所以,DOM0級事件處理程序不能添加多個,也不能控制事件流到底是捕獲還是冒泡。
DOM2級事件處理程序
addEventListener() ---添加事件偵聽器
函數均有3個參數,
第一個參數是要處理的事件名(不帶on前綴的才是事件名)
第二個參數是作為事件處理程序的函數
第三個參數是一個boolean值,默認false表示使用冒泡機制,true表示捕獲機制。
removeEventListener() //刪除事件偵聽器
可以綁定多個事件處理程序,但是注意,如果定義了一摸一樣時監聽方法,是會發生覆蓋的,即同樣的事件和事件流機制下相同方法只會觸發一次,事件觸發的順序是添加的順序
``` // 為了兼容IE瀏覽器和標準的瀏覽器,我們需要編寫通用的方法來處理: var EventUtil = { addHandler: function (element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } }, removeHandler: function (element, type, handler) { if (element.removeEventListener()) { element.removeEventListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } } }; ```5.事件對象
事件對象是用來記錄一些事件發生時的相關信息的對象,但事件對象只有事件發生時才會產生,并且只能是事件處理函數內部訪問,在所有事件處理函數運行結束后,事件對象就被銷毀!
//currentTarget、eventPhase 一個例子:
當然,事件對象也存在一定的兼容性問題,在IE8及以前本版之中,通過設置屬性注冊事件處理程序時,調用的時候并未傳遞事件對象,需要通過全局對象window.event來獲取。解決方法如下:
function getEvent(event) { event = event || window.event; }
在IE瀏覽器上面是event事件是沒有preventDefault()這個屬性的,所以在IE上,我們需要設置的屬性是returnValue
window.event.returnValue=false
stopPropagation()也是,所以需要設置cancelBubble,cancelBubble是IE事件對象的一個屬性,設置這個屬性為true能阻止事件進一步傳播。
event.cancelBubble=true
常見屬性 | 解析 |
---|---|
event.preventDefault() | 阻止默認行為 |
event.stopPropagation() | 阻止冒泡。使用了stopPropagation()之后,事件就不能進一步傳播了,同時如果是同一個div上有捕獲和冒泡兩種事件監聽,在捕獲階段傳播阻止后冒泡階段事件監聽也不會觸發。 |
event.stopImmediatePropagation() | 使用了stopImmediatePropagation()之后,綁定的后續事件監聽都會忽略。 |
event.currentTarget | 當前綁定的事件 |
event.target | 事件代理時 點擊的元素 |
關于捕獲和冒泡:理解 addEventListener、捕獲和冒泡
6.自定義事件jq
// 添加一個適當的事件監聽器 $("#foo").on("custom", function(event, param1, param2) { alert(param1 + " " + param2); }); $("#foo").trigger("custom", ["Custom", "Event"]);
原生Event:
var eve = new Event("custome") element.addEventListenter("custome",function(){ console.log("custome") }) element.dispatchEvent(eve)
原生CustomEvent
// 添加一個適當的事件監聽器 obj.addEventListener("custom", function(e) { console.log(JSON.stringify(e.detail)); }) // 創建并分發事件 var event = new CustomEvent("custom", {"detail":{"Custom":true}}); obj.dispatchEvent(event)
本節參考文章:一個能拖動,能調整大小,能更新bind值的vue指令-vuedragx
7.事件委托(代理)事件委托就是利用事件冒泡,只指定一個事件處理程序,就可以管理某一類型的所有事件。
案例一:
解決方法:事件委托
案例二:
點擊“添加”按鈕添加一個按鈕,點擊添加的按鈕移除這個按鈕
添加繪畫散步靜坐
document.getElementById("wrap").addEventListener("click", function(e){ var target = e.target; while(target !== this){ var type = target.dataset.type; if(type == "btn"){ var feat = target.dataset.feat; switch(feat){ case "add": this.innerHTML += "靜坐" return; case "delete": target.parentNode.removeChild(target); return; } } target = target.parentNode; } }, false);
適合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。
推薦閱讀:JavaScript 事件委托詳解
本節參考文章:前端小知識--JavaScript事件流
9.link / @import兩者都是外部引用 CSS 的方式,但是存在一定的區別:
link是XHTML標簽,除了能夠加載CSS,還可以定義RSS等其他事務;而@import屬于CSS范疇,只可以加載CSS。
link引用CSS時,在頁面載入時同時加載;@import需要頁面完全載入以后再加載。
link是XHTML標簽,無兼容問題;@import則是在CSS2.1提出的,低版本的瀏覽器不支持。
link方式的樣式的權重 高于@import的權重.
link支持使用Javascript控制DOM改變樣式;而@import不支持。
本節參考文章:前端面試題-url、href、src
10.異步編程的實現方式1.回調函數
優點:簡單、容易理解
缺點:不利于維護,代碼耦合高
2.事件監聽(采用時間驅動模式,取決于某個事件是否發生):
優點:容易理解,可以綁定多個事件,每個事件可以指定多個回調函數
缺點:事件驅動型,流程不夠清晰
3.發布/訂閱(觀察者模式)
類似于事件監聽,但是可以通過‘消息中心’,了解現在有多少發布者,多少訂閱者
4.Promise對象
優點:可以利用then方法,進行鏈式寫法;可以書寫錯誤時的回調函數;
缺點:編寫和理解,相對比較難
5.Generator函數
優點:函數體內外的數據交換、錯誤處理機制
缺點:流程管理不方便
6.async函數
優點:內置執行器、更好的語義、更廣的適用性、返回的是Promise、結構清晰。
缺點:錯誤處理機制
document.write只能重繪整個頁面
innerHTML可以重繪頁面的一部分
isPrototypeOf() 與 instanceof 運算符不同。
在表達式 "object instanceof AFunction"中,object 的原型鏈是針對 AFunction.prototype 進行檢查的,而不是針對 AFunction 本身。
isPrototypeOf() 方法允許你檢查一個對象是否存在于另一個對象的原型鏈上。
function Foo() {} function Bar() {} function Baz() {} Bar.prototype = Object.create(Foo.prototype); Baz.prototype = Object.create(Bar.prototype); var baz = new Baz(); //isPrototypeOf console.log(Baz.prototype.isPrototypeOf(baz)); // true console.log(Bar.prototype.isPrototypeOf(baz)); // true console.log(Foo.prototype.isPrototypeOf(baz)); // true console.log(Object.prototype.isPrototypeOf(baz)); // true if (Foo.prototype.isPrototypeOf(baz)) { // do something safe } //instanceof console.log(baz instanceof Baz); // true console.log(baz instanceof Bar); // true console.log(baz instanceof Foo); // true console.log(baz instanceof Object); // true
var obj1 = { name: "esw" } var obj2 = Object.create(obj1) // isPrototypeOf()方法 Object.prototype.isPrototypeOf(obj1) // true obj1.isPrototypeOf(obj2) // true Object.prototype.isPrototypeOf(obj2) // true13.constructor、__proto__與prototype
在javascript中我們每創建一個對象,該對象都會獲得一個__proto__屬性(該屬性是個對象),該屬性指向創建該對象的構造函數的原型即prototype,同時__proto__對象有一個constructor屬性指向該構造函數。這里我們需要注意的是只有函數才有prototype,每個對象(函數也是對象)都有__proto__,Object本身是個構造函數。舉例來說:
var obj = new Object() // 也可以使用對象字面量創建,但使用Object.create()情況會不一樣 // Object本身是個構造函數 Object instanceof Function // true obj.__proto__ === Object.prototype // true obj.__proto__.constructor === Object // true // 我們一般習慣這樣寫 obj.constructor === Object // true
當我們訪問obj.constructor的時候,obj本身是沒有constructor屬性的,但屬性訪問會沿著__proto__向上查找,即在obj.__proto__里面尋找constructor屬性,如果找到了就返回值,如果未找到則繼續向上查找直到obj.__proto__.__proto__...(__proto__) === null為止,沒有找到則返回undefined。這樣由__proto__構成的一條查找屬性的線稱為‘原型鏈’。
本節參考文章:重新認識javascript對象(三)——原型及原型鏈、一篇文章帶你進一步了解object屬性
14.淺拷貝/深拷貝1.淺拷貝只能復制值類型的屬性。對于引用類型的屬性,復制前后的兩個對象指向同一內存地址,操作其中一個對象的引用類型屬性,另一個對象也會相應發生改變;也就是說只有改變值類型的屬性兩個對象才不會相互影響。
2.深拷貝不僅可以復制值類型的屬性,還可以復制引用類型的屬性,無論兩個對象怎么改變都不會相互影響。
淺復制
var obj = { a : 1, b: { c: 2 } } // 淺復制 function lowerClone(obj){ var newObj=obj.constructor === Array ? [] : {}; for(var i in obj){ newObj[i]=obj[i] } return newObj; } var objClone = lowerClone(obj) objClone.a // 1 obj.a // 1 objClone.a = 100 // 改變復制對象的值類型屬性,值類型屬性的值不變 obj.a // 1 objClone.b.c = 200 // 改變復制對象的引用類型的屬性,引用類型的屬性值改變 obj.b.c //200
深復制
var obj = { a : 1, b: { c: 2 } } function deepClone(obj){ if( typeof obj != "object"){ return obj; } var newObj=obj.constructor === Array ? [] : {}; for(var i in obj){ newObj[i]=deepClone(obj[i]); } return newObj; } var objClone = deepClone(obj) objClone.a // 1 obj.a // 1 objClone.a = 100 // 改變復制對象的值類型屬性,值類型屬性的值不變 obj.a // 1 objClone.b.c = 200 // 改變復制對象的引用類型的屬性,引用類型的屬性值不變 obj.b.c // 2
本節參考文章:javascript淺復制與深復制
15.apply/call/bindapply 、 call 、bind 三者都是用來改變函數的this對象的指向的;
apply 、 call 、bind 三者第一個參數都是this要指向的對象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后續參數傳參;
bind是返回對應函數,便于稍后調用;apply 、call 則是立即調用 。
call apply 的區別是他們指定參數的方式不同。
案例
function fn(a,b){ console.log(this); console.log(a); console.log(b); } // bind(this,args...) bf = fn.bind("Bind this",10); // 沒有任何輸出,也就是說沒有執行這個函數 bf(); // "Bind this",10,undefined bf(20);// “Bind this”,10,20 // 原函數不受影響 fn(1,2); //window, 1,2 bf2 = fn.bind("Bind this",1,2); bf2(); // "Bind this",1,2 // call(this,args...) fn.call("Call this",1) // "Call this",1,undefined fn.call("Call this",1,2) // "Call this",1,2 // apply(this,[args]) fn.apply("Apply this",[1]) // "Apply this",1,undefined fn.apply("Apply this",[1,2]) // "Apply this",1,2
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100983.html
摘要:前端基本功常見概念一點這里前端基本功常見概念二點這里前端基本功常見概念三點這里什么是原型鏈當一個引用類型繼承另一個引用類型的屬性和方法時候就會產生一個原型鏈。函數式編程是聲明式而不是命令式,并且應用程序狀態通過純函數流轉。 前端基本功-常見概念(一) 點這里前端基本功-常見概念(二) 點這里前端基本功-常見概念(三) 點這里 1.什么是原型鏈 當一個引用類型繼承另一個引用類型的屬性和方...
摘要:前端基本功常見概念一點這里前端基本功常見概念二點這里前端基本功常見概念三點這里是更完美的,不是全局變量,具有塊級函數作用域,大多數情況不會發生變量提升。 前端基本功-常見概念(一) 點這里前端基本功-常見概念(二) 點這里前端基本功-常見概念(三) 點這里 1.let、const/var let 是更完美的var,不是全局變量,具有塊級函數作用域,大多數情況不會發生變量提升。cons...
摘要:前端基本功常見概念一點這里前端基本功常見概念二點這里前端基本功常見概念三點這里是更完美的,不是全局變量,具有塊級函數作用域,大多數情況不會發生變量提升。 前端基本功-常見概念(一) 點這里前端基本功-常見概念(二) 點這里前端基本功-常見概念(三) 點這里 1.let、const/var let 是更完美的var,不是全局變量,具有塊級函數作用域,大多數情況不會發生變量提升。cons...
摘要:一般來說,聲明式編程關注于發生了啥,而命令式則同時關注與咋發生的。聲明式編程可以較好地解決這個問題,剛才提到的比較麻煩的元素選擇這個動作可以交托給框架或者庫區處理,這樣就能讓開發者專注于發生了啥,這里推薦一波與。 本文翻譯自FreeCodeCamp的from-zero-to-front-end-hero-part。 繼續譯者的廢話,這篇文章是前端攻略-從路人甲到英雄無敵的下半部分,在...
閱讀 2336·2021-11-23 09:51
閱讀 1137·2021-11-22 13:52
閱讀 3611·2021-11-10 11:35
閱讀 1187·2021-10-25 09:47
閱讀 2994·2021-09-07 09:58
閱讀 1059·2019-08-30 15:54
閱讀 2817·2019-08-29 14:21
閱讀 3025·2019-08-29 12:20