前面咱們已經梳理了前端面試中css的相關內容,同時也對面試中常問的本地存儲 、緩存作了一個詳細的介紹,有須要的可自行查看以前的文章,文章連接以下:
前端常見面試-css篇
前端常見面試-存儲/緩存篇
固然,對於前端開發來講,js那就是每個前端小可愛都須要必備掌握的技能,不管如今多火爆的mv*框架,都是基於基礎的js來進行的。好了,話很少說,直接進入主題javascript
答:js中的數據類型主要分爲兩種:基礎類型和引用類型,其中基礎類型包括:字符串(String)、數字(Number)、布爾(Boolean)、對空(Null)、未定義(Undefined)、Symbol(es6中引入的);引用類型包括:對象(Object)、數組(Array)、函數(Function)css
答:js中申明變量的方式主要有三種,分別爲:var、let、const
1)var:定義的變量能夠修改,若是不初始化會輸出undefined,不會報錯
2)let:是塊級做用域,函數內部使用let定義後,對函數外部無影響
3)const:定義變量不能夠修改,並且必須初始化,可是若是定義的是對象,則對象的屬性能夠修改(緣由是引用的是對象的地址,地址不可更改,可是地址對應的內容能夠修改)
不過對於非嚴格模式下來講,沒有使用這三者方式定義的變量會自動的掛載到window對象下,成爲一個全局變量。html
答:在ES6以前,JavaScript沒有塊級做用域(一對花括號{}即爲一個塊級做用域),只有全局做用域和函數做用域。變量提高即將變量聲明提高到它所在做用域的最開始的部分。js中建立函數有兩種方式:函數聲明式和函數表達式申明。只有函數聲明才存在函數提高前端
答:事件冒泡(event bubbling),事件最開始時由觸發的那個元素身上發生,而後沿着DOM樹向上傳播,直到document對象。java
1)事件冒泡容許多個操做被集中處理(把事件處理器添加到一個父級元素上,避免把事件處理器添加到多個子級元素上),它還可讓你在對象層的不一樣級別捕獲事件
2)讓不一樣的對象同時捕獲同一事件,並調用本身的專屬處理程序作本身的事情es6
1)使用e.stopPropagation()
2)根據當前觸發的元素來進行判斷阻止:e.target==e.currentTarget面試
●事件捕獲其實有三種方式,事件冒泡只是其中的一種:(1)IE從裏到外(inside→outside)的冒泡型事件。(2)Netscape4.0從外到裏(outside→inside)的捕獲型事件。(3)DOM事件流,先從外到裏,再從裏到外回到原點(outside→inside→outside)的事件捕獲方法(彷佛對象將觸發兩次事件處理,這有什麼做用?鄙人不懂!)。正則表達式
●不是全部的事件都能冒泡。如下事件不冒泡:blur、focus、load、unload。編程
●事件捕獲方式在不一樣瀏覽器,甚至同種瀏覽器的不一樣版本中是有所區別的。如Netscape4.0採用捕獲型事件解決方案,其它多數瀏覽器則支持冒泡型事件解決方案,另外DOM事件流還支持文本節點事件冒泡。segmentfault
●事件捕獲到達頂層的目標在不一樣瀏覽器或不一樣瀏覽器版本也是有區別的。在IE6中HTML是接收事件冒泡的,另外大部分瀏覽器將冒泡延續到window對象,即……body→documen→window。
●阻止冒泡並不能阻止對象默認行爲。好比submit按鈕被點擊後會提交表單數據,這種行爲無須咱們寫程序定製。
答:js默認狀況是非嚴格模式,若是須要使用嚴格模式,可使用關鍵字:'use strict',對於嚴格模式和非嚴格模式有以下區別:
1) 嚴格模式下, delete運算符後跟隨非法標識符(即delete 不存在的標識符),會拋出語法錯誤; 非嚴格模式下,會靜默失敗並返回false
2)嚴格模式中,對象中定義同名屬性會拋出語法錯誤; 非嚴格模式不會報錯
3)嚴格模式中,函數形參存在同名的,拋出錯誤; 非嚴格模式不會
4)嚴格模式不容許八進制整數(如:023)
5)嚴格模式中,arguments對象是傳入函數內實參列表的靜態副本;非嚴格模式下,arguments對象裏的元素和對應的實參是指向同一個值的引用
6)嚴格模式中 eval和arguments當作關鍵字,它們不能被賦值和用做變量聲明
7)嚴格模式會限制對調用棧的檢測能力,訪問arguments.callee.caller會拋出異常
8)嚴格模式下變量必須先聲明,直接給變量賦值,不會隱式建立全局變量,不能用with
9)嚴格模式中 call、apply傳入null、undefined保持原樣不被轉換爲window
1)apply 、 call 、bind 三者都是用來改變函數的this對象的指向的;
2)apply 、 call 、bind 三者第一個參數都是this要指向的對象,也就是想指定的上下文(函數的每次調用都會擁有一個特殊值——本次調用的上下文(context)——這就是this關鍵字的值。);
3)apply 、 call 、bind 三者均可以利用後續參數傳參;
4)bind 是返回對應函數,便於稍後調用;apply 、call 則是當即調用
5)apply也能夠有多個參數,可是不一樣的是,第二個參數必須是一個數組
6)call的的多個參數須要依次羅列傳入
答:通常狀況下:this的指向在函數定義的時候是肯定不了的,只有函數執行的時候才能肯定this到底指向誰,實際上this的最終指向的是那個調用它的對象。可是,在幾個特殊的狀況下this的指向會有所變化,具體以下:
1)狀況1:若是一個函數中有this,可是它沒有被上一級的對象所調用,那麼this指向的就是window(非嚴格模式下)
2)狀況2:若是一個函數中有this,這個函數有被上一級的對象所調用,那麼this指向的就是上一級的對象。
3)狀況3:若是一個函數中有this,這個函數中包含多個對象,儘管這個函數是被最外層的對象所調用,this指向的也只是它上一級的對象
1)在嚴格版中的默認的this再也不是window,而是undefined。
2)new操做符會改變函數this的指向問題,雖然咱們上面講解過了,可是並無深刻的討論這個問題,網上也不多說,因此在這裏有必要說一下。
function fn(){ this.num = 1; } var a = new fn(); console.log(a.num); //1
答:其實Javascript並無重載函數的功能,可是Arguments對象可以模擬重載。Javascrip中每一個函數都會有一個Arguments對象實例arguments,它引用着函數的實參,能夠用數組下標的方式"[]"引用arguments的元素。其中arguments.length爲函數實參個數,arguments.callee引用函數自身。
固然,對於arguments來講,其只是一個類數組對象,不能直接使用數組對象封裝的內置方法,其轉換爲數組的方法爲: Array.prototype.slice.call(arguments)
答:二者的具體區別以下:
1)箭頭函數與function定義函數的寫法
function demo(){console.log('demo')} var demo=()=>{consoloe.log('demo')}
2) this指向的區別:使用function定義的函數,this的指向隨着調用環境的變化而變化的,而箭頭函數中的this指向是固定不變的,一直指向的是定義函數的環境。(箭頭函數自己沒有this對象)
3) 構造函數的區別:function是能夠定義構造函數的,而箭頭函數是不行的(箭頭函數自己沒有this對象)
4)變量提高的區別:因爲js的內存機制,function的級別最高,而用箭頭函數定義函數的時候,須要var(let const定義的時候更沒必要說)關鍵詞,而var所定義的變量不能獲得變量提高,故箭頭函數必定要定義於調用以前!
對某種結構進行解析,而後將解析出來的值賦值給相應的變量,常見的有數組、對象、字符串、函數的解構賦值等
1.數組解構賦值:1)模式匹配解構賦值,2)省略解構賦值,3)含剩餘參數的解構賦值(省略號模式),4)set解構賦值:先執行new Set()進行數據去重 而後進行解構賦值,5)迭代器解構賦值
2. 對象解構賦值:1)解構的屬性名稱必須保持一致,2)解構的屬性名稱能夠直接使用冒號進行設置新的別名,設置爲新的別名後,原屬性名稱不存在 只能進行別名調用
3.字符串解構賦值
⚠️:解構賦值時 只有當變量嚴格等於(===)的時候纔會進行賦值爲默認值 其餘狀況下均直接賦值爲變量的值
總結:解構成對象,只要等號右邊的值不是對象或者數組,就先將其轉換爲對象。因爲undefined和null沒法轉換爲對象,所以直接對它們解構賦值會直接報錯;解構成數組,等號右邊必須爲了迭代的對象
1. let定義變量,const定義常量(若是常量爲對象 對象的屬性能夠修改),做用域爲塊級做用域,無變量提高 必須先定義纔可使用
2. 函數擴展:增長函數參數的默認值定義,箭頭函數,箭頭函數無this和arguments,可是其有refuse,其this指向箭頭函數最近的一個函數,若是沒有最近的函數則尋找到window對象
3. 對象擴展:屬性名爲變量名時直接能夠寫屬性名,Object.keys():獲取對象的全部屬性名和方法名組成一個數組,Object.assign():合併對象 有對象屬性名相同時,後者覆蓋前者;
4. for of循環;
5. import和export:export能夠對外暴露一個module,import引入export暴露的module
6. promise:異步解決方案,其包含三個狀態,分別爲:rejected:失敗、resolveed:已完成、pending:進行中。promise構造函數中包括一個參數和一個帶有resolve和reject的回調,對於實例化的promise能夠經過promise.then來接受resolve和reject
7. 解構賦值
8. Set數據結構:一個類數組格式的數據結構,無重複值。size爲長度,add()添加數據 返回Set自己,delete()刪除一個數據 返還boolean值表示是否刪除成功,has()查找某個元素 返回boolean是否找到,clear()清除全部的元素 無返回值
9. 模板字符串,使用反引號進行模板字符串 內部可使用${}來書寫變量
答:對於二者來講,其都是定義在必定的時間後進行相應函數的觸發操做,可是兩者也有明顯的區別,具體以下:
1)setTimeout(fn,t),超時調用,超過期間t,就執行fn;setInterval(fn,t),間歇調用,調用週期t,執行fn。
2) 兩者調用後,均返回一個數值ID,此ID是計劃執行代碼的惟一標識符,能夠經過它來取消還沒有執行的調用。 clearTimeout(id)
是取消setTimeout的調用,clearInterval(id)
是取消setInterval的調用。取消間歇調用的重要性要遠遠高於取消超時調用,由於在不加干涉的狀況下,間歇調用將會一直執行到頁面卸載。
3) setTimeout(fn,t)中t告訴JS再過多久把當前任務添加到隊列中。並非執行的到setTimeout就添加任務。若是隊列是空,那麼添加的代碼會當即執行;若是隊列不空,那麼它就要等前面的代碼執行完了之後在執行。二對於setInterval(fn,t),在間歇調用時,會出現一些意外:
setInterval(function () { func(i++); }, 100)
a、若是函數的執行時間小於100毫秒的時候,那麼下一個循環調用將在100毫秒前完成;
b、當func的執行時間多於100毫秒,在觸發下一個func函數時(200ms處),上一個func尚未執行完,那麼此時第二個func會在隊列(event loop)中等待,直到第一個函數執行完,第二個func當即執行
c、當第一個函數的執行時間特別長,以至於在執行過程當中出發了多個func時,就會致使第3個func及之後的函數被忽視。由於任務隊列中不會有兩個及以上同一個定時器的回調函數
所以,若是須要每次函數在相同的間隔下完成指定的內容,則須要使用setTimeout,若是隻是爲了每隔必定範圍的時間運行指定的內容,則使用setInterval
詳情瞭解:setTimeout和setInterval的詳解
一、首先要理解js的數據類型,js基本類型主要包括String、Number、Boolean、Null、Undefined、Symbol:直接將值存在堆內存中,直接進行值賦值;引用類型包括:object、function、array,引用類型分爲引用地址和具體值,引用地址存在堆內存中,具體值存在隊內存中
二、深拷貝和淺拷貝:經常使用的淺拷貝引用的方法:Object.assign()、Array.prototype.slice()、Array.prototype.concat();經常使用深拷貝:JSON.stringify()、JSON.parse()
function deepCopy(obj) { if (obj instanceof Object) { //當拷貝的是對象時 let tmp = {} for(let key in obj) { tmp[i] = deepCopy(key) } return tmp } else if (obj instanceof Array) { //當拷貝的是數組時 let tmp = [] for (let i = 0;i < obj.length;i++) { tmp.push(deepCopy(obj[i])) } return tmp } else if (obj instanceof Function) { //當拷貝的是函數時 return new Function('return '+ obj.toString()) } else { return obj //當拷貝的是基本數據類型 }
參考地址:深度拷貝詳解
答:遞歸就是在函數體內重複的調用函數自己,達到循環計算的邏輯。可是,因爲遞歸不斷的調用函數自己,引用函數的時候會給函數引用開闢一個獨立的內存空間直到因函數引用結束纔會釋放,所以待必定量後的遞歸就容易出現堆棧溢出。所以,爲了優化遞歸的堆棧溢出,就提出了尾遞歸的概念:即:在函數的最後一步進行函數的引用,而不是在代碼的最後一行進行函數的引用。具體原理以下:
1. 堆棧:後進先出的原則。想象一下桌子上放一堆書,先放的在最底下,後放的在最頂部,拿書的時候,後放的被先拿走。即後進入堆棧的先出棧。
2. 函數的堆棧概念:js中,每次函數調用會在內存造成一個「調用記錄」, 保存着調用位置和內部變量等信息。若是函數中調用了其餘函數,則js引擎將其運行過程暫停,去執行新調用的函數,新調用函數成功後繼續返回當前函數的執行位置,繼續日後執行。執行新調用的函數過程,也是將其推入到堆棧的最頂部併爲其開闢一塊內容。新函數執行完畢後將其推出堆棧,並回收內存。由此便造成了函數的堆棧。
答:閉包是實現可重用的局部變量,且保護其不受污染的機制。具體包括:
1. 外層函數包裹受保護的變量和內層函數。
2. 內層函數專門負責操做外層函數的局部變量。
3. 將內層函數返回到外層函數外部,反覆調用。
子函數會一級一級地向上尋找全部父函數的變量。因此,父函數的全部變量,對子函數都是可見的,反之則不成立。
1. 外層函數調用了幾回,就有幾個受保護的局部變量副本。
2. 同一次外層函數調用返回的多個內層函數,共同用一個局部變量。
3. 閉包的局部變量不能釋放。
4. 儘可能不要在定時器中使用閉包。由於閉包的外層函數定義的變量不能釋放,可是定時器使用中須要釋放
1)引用的變量可能發生變化
function outer() { var result = []; for (var i = 0; i<10; i++){ result.[i] = function () { console.info(i) } } return result }
function outer() { var result = []; for (var i = 0; i<10; i++){ result.[i] = function (num) { return function() { console.info(num); // 此時訪問的num,是上層函數執行環境的num,數組有10個函數對象,每一個對象的執行環境下的number都不同 } }(i) } return result }
2)this的指向問題
var object = { name: ''object", getName: function() { return function() { console.info(this.name) } } } object.getName()() // underfined // 由於裏面的閉包函數是在window做用域下執行的,也就是說,this指向windows
3)內存泄漏
function showId() { var el = document.getElementById("app") el.onclick = function(){ aler(el.id) // 這樣會致使閉包引用外層的el,當執行完showId後,el沒法釋放 } } // 改爲下面 function showId() { var el = document.getElementById("app") var id = el.id el.onclick = function(){ aler(id) // 這樣會致使閉包引用外層的el,當執行完showId後,el沒法釋放 } el = null // 主動釋放el }
一、全部的引用對象都有__proto__屬性,其指向它的構造函數的prototype屬性,全部的函數都有prototype屬性
二、當試圖獲得一個對象的屬性時,若是對象自己不存在這個屬性,那麼就會去它的__proto__屬性中尋找
js的面向對象編程和大多數其餘語言如Java、C#的面向對象編程都不太同樣。面向對象的兩個基本概念:
1. 類:類是對象的類型模板,例如,定義Student
類來表示學生,類自己是一種類型,Student
表示學生類型,但不表示任何具體的某個學生;
2. 實例:實例是根據類建立的對象,例如,根據Student
類能夠建立出多個實例,每一個實例表示一個具體的學生,他們全都屬於Student
類型。
JavaScript不區分類和實例的概念,而是經過原型(prototype)來實現面向對象編程
// 原型對象: var Student = { name: 'Robot', height: 1.2, run: function () { console.log(this.name + ' is running...'); } }; function createStudent(name) { // 基於Student原型建立一個新對象: var s = Object.create(Student); // 初始化新對象: s.name = name; return s; } var xiaoming = createStudent('小明'); xiaoming.run(); // 小明 is running... xiaoming.__proto__ === Student; // true
答:操做數組的經常使用方法具體分爲改變原數組、不改變原數組、遍歷原數組等:
push():向數組的尾部增長一個元素,並返回新的長度;
pop():刪除一個數組中的最後的一個元素,而且返回這個元素;
shift():刪除數組的第一個元素,並返回這個元素;
unshift():向數組的開頭添加一個或更多元素,並返回新的長度;
reverse():顛倒數組中元素的順序;
splice():向/從數組中添加/刪除項目,而後返回被刪除的項目,第二個參數爲0的時候表示不刪除;
sort():對數組元素進行排序,並返回這個數組;
conpyWithin():在當前數組內部,將指定位置的成員複製到其餘位置,並返回這個數組;
fill():使用給定值,填充一個數組;
join():把數組中的全部元素經過指定的分隔符進行分隔放入一個字符串,返回生成的字符串;
slice():返回一個從開始到結束(不包括結束)選擇的數組的一部分淺拷貝到一個新數組對象,且原數組不會被修改;
concat():方法用於合併兩個或多個數組,返回一個新數組;
indexOf():在數組中能夠找到一個給定元素的第一個索引,若是不存在,則返回-1;
lastIndexOf():返回指定元素,在數組中的最後一個的索引,若是不存在則返回 -1。(從數組後面往前查找);
includes():返回一個布爾值,表示某個數組是否包含給定的值;
forEach():按升序爲數組中含有效值的每一項執行一次回調函數(沒法中途退出循環,只能用return退出本次回調,進行下一次回調。它老是返回 undefined值,即便你return了一個值。)
every():方法用於檢測數組全部元素是否都符合函數定義的條件(若是數組中檢測到有一個元素不知足,則整個表達式返回 false,且剩餘的元素不會再進行檢測。若是全部元素都知足條件,則返回 true)
some():數組中的是否有知足判斷條件的元素(若是有一個元素知足條件,則表達式返回true, 剩餘的元素不會再執行檢測。若是沒有知足條件的元素,則返回false)
filter():返回一個新數組, 其包含經過所提供函數實現的測試的全部元素
map():建立一個新數組,其結果是該數組中的每一個元素都調用一個提供的函數後返回的結果
reduce():對累加器和數組中的每一個元素(從左到右)應用一個函數,最終合併爲一個值(reduceRight()是從右到左的執行)
find()& findIndex() 根據條件找到數組成員:find()定義:用於找出第一個符合條件的數組成員,並返回該成員,若是沒有符合條件的成員,則返回undefined。findIndex()定義:返回第一個符合條件的數組成員的位置,若是全部成員都不符合條件,則返回-1。
keys()&values()&entries() 遍歷鍵名、遍歷鍵值、遍歷鍵名+鍵值:三個方法都返回一個新的 Array Iterator 對象,對象根據方法不一樣包含不一樣的值
詳細介紹:js數組中常見的操做方法
split():按照指定的格式將字符串進行拆分返回一個新的數組,第二個參數可選,表示返回的數組的最大長度
indexOf():返回字符串中一個子串第一處出現的索引(從左到右搜索)。若是沒有匹配項,返回 -1
lastIndexOf():返回字符串中一個子串最後一處出現的索引(從右到左搜索),若是沒有匹配項,返回 -1
match():直接經過字符串進行匹配,也能夠經過正則進行匹配
slice():能夠爲負數,若是起始位置爲負數,則從字符串最後一位向前找對應位數而且向後取結束位置,若是爲正整數則從前日後取起始位置到結束位置
substring():只能非負整數,截取起始結束位置同slice()函數一致
substr():從起始位置開始截取,結束位置爲第二個參數截取的字符串最大長度
trim():刪除字符串先後的空格
charAt()/charCodeAt():返回指定位置的字符或其字符編碼值
replace():替換對應的內容,可直接替換也可以使用正則表達式替換
答:雙等因而非嚴格等於,只須要在值上相等便可爲真;三等因而嚴格等於,必須在值和值的類型都一致的狀況下才返回真
答:事件委託就是利用了js的事件冒泡,經過事件冒泡來對多個事件進行委託觸發。
一、「事件冒泡」:事件開始由最具體的元素接受,而後逐級向上傳播
二、「事件捕捉」:事件由最不具體的節點先接收,而後逐級向下,一直到最具體的
三、「DOM事件流」:三個階段:事件捕捉,目標階段,事件冒泡
1)null是一個表示"無"的對象,轉爲數值時爲0;undefined是一個表示"無"的原始值,轉爲數值時爲NaN。
2)當聲明的變量還未被初始化時,變量的默認值爲undefined。 null用來表示還沒有存在的對象
3)undefined表示"缺乏值",就是此處應該有一個值,可是尚未定義。典型用法是:
(1)變量被聲明瞭,但沒有賦值時,就等於undefined。
(2)調用函數時,應該提供的參數沒有提供,該參數等於undefined。
(3)對象沒有賦值的屬性,該屬性的值爲undefined。
(4)函數沒有返回值時,默認返回undefined。
4)null表示"沒有對象",即該處不該該有值。典型用法是:
(1) 做爲函數的參數,表示該函數的參數不是對象。
(2) 做爲對象原型鏈的終點。
1.建立一個空對象
2.由this變量引用該對象
3.該對象繼承該函數的原型
4.把屬性和方法加入到this引用的對象中
5.新建立的對象由this引用,最後隱式地返回this。
過程以下:
var obj={}; obj.__proto__=Base.prototype; Base.call(obj);
html文件是自上而下的執行方式,但引入的css和javascript的順序有所不一樣,css引入執行加載時,程序仍然往下執行,而執行到<script>腳本是則中斷線程,待該script腳本執行結束以後程序才繼續往下執行。
因此,大部分網上討論是將script腳本放在<body>以後,那樣dom的生成就不會由於長時間執行script腳本而延遲阻塞,加快了頁面的加載速度。
但又不能將全部的script放在body以後,由於有一些頁面的效果的實現,是須要預先動態的加載一些js腳本。因此這些腳本應該放在<body>以前。
其次,不能將須要訪問dom元素的js放在body以前,由於此時尚未開始生成dom,因此在body以前的訪問dom元素的js會出錯,或者無效
script放置位置的原則「頁面效果實現類的js應該放在body以前,動做,交互,事件驅動,須要訪問dom屬性的js均可以放在body以後
答:同源策略限制了一個源(origin)中加載文本或腳本與來自其它源(origin)中資源的交互方式。同源指的是協議、域名、端口相同,同源策略是一種安全協議。
答:在Javscript中,解析器在向執行環境中加載數據時,對函數聲明和函數表達式並不是是一視同仁的,解析器會率先讀取函數聲明,並使其在執行任何代碼以前可用(能夠訪問),至於函數表達式,則必須等到解析器執行到它所在的代碼行,纔會真正被解析執行