JavaScript面試題大全之基礎(chǔ)面試題(附答案)

介紹JavaScript的數(shù)據(jù)類型
基本數(shù)據(jù)類型與引用類型在存儲上有什么區(qū)別?
判斷 js 類型的方式
但判斷 typeof(null) 時值為 'object'; 判斷數(shù)組和對象時值均為 'object'
function A() {}let a = new A();a instanceof A //true,因為 Object.getPrototypeOf(a) === A.prototype;
3. Object.prototype.toString.call()
常用于判斷瀏覽器內(nèi)置對象,對于所有基本的數(shù)據(jù)類型都能進(jìn)行判斷,即使是 null 和 undefined
Object.prototype.toString.call(null)//"[object Null]"Object.prototype.toString.call(undefined)//"[object Undefined]"Object.prototype.toString.call(Object)//"[object Function]"
4. Array.isArray()
用于判斷是否為數(shù)組。
typeof運(yùn)算符和instanceof運(yùn)算符以及isPrototypeOf()方法的區(qū)別
typeof是一個運(yùn)算符,用于檢測數(shù)據(jù)的類型,比如基本數(shù)據(jù)類型null、undefined、string、number、boolean,以及引用數(shù)據(jù)類型object、function,但是對于正則表達(dá)式、日期、數(shù)組這些引用數(shù)據(jù)類型,它會全部識別為object
instanceof同樣也是一個運(yùn)算符,它就能很好識別數(shù)據(jù)具體是哪一種引用類型。它與isPrototypeOf的區(qū)別就是它是用來檢測構(gòu)造函數(shù)的原型是否存在于指定對象的原型鏈當(dāng)中;
而isPrototypeOf是用來檢測調(diào)用此方法的對象是否存在于指定對象的原型鏈中,所以本質(zhì)上就是檢測目標(biāo)不同。
NaN 是什么
NaN 即非數(shù)值(Not a Number),NaN 屬性用于引用特殊的非數(shù)字值,該屬性指定的并不是不合法的數(shù)字。
NaN 屬性 與 Number.Nan 屬性相同。
提示: 請使用 isNaN() 來判斷一個值是否是數(shù)字。原因是 NaN 與所有值都不相等,包括它自己。
js中本地對象?內(nèi)置對象?宿主對象?
本地對象:ECMA-262 把本地對象(native object)定義為“獨(dú)立于宿主環(huán)境的 ECMAScript 實現(xiàn)提供的對象”。
包含了:Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError。
內(nèi)置對象:ECMA-262 把內(nèi)置對象(built-in object)定義為“由 ECMAScript 實現(xiàn)提供的、獨(dú)立于宿主環(huán)境的所有對象,在 ECMAScript 程序開始執(zhí)行時出現(xiàn)”。這意味著開發(fā)者不必明確實例化內(nèi)置對象,它已被實例化了。包含了:Gobal 和 Math。
宿主對象:由ECMAScript實現(xiàn)的宿主環(huán)境提供的對象,可以理解為:瀏覽器提供的對象。所有的BOM和DOM都是宿主對象。
棧和堆的區(qū)別?
棧(stack):由編譯器自動分配釋放,存放函數(shù)的參數(shù)值,局部變量等;
堆(heap):一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時可能由操作系統(tǒng)釋放。
描述以下變量的區(qū)別:null,undefined或undeclared
null 表示"沒有對象",即該處不應(yīng)該有值,轉(zhuǎn)為數(shù)值時為0。典型用法是:
(1) 作為函數(shù)的參數(shù),表示該函數(shù)的參數(shù)不是對象。
(2) 作為對象原型鏈的終點(diǎn)。
undefined 表示"缺少值",就是此處應(yīng)該有一個值,但是還沒有定義,轉(zhuǎn)為數(shù)值時為NaN。典型用法是:
(1)變量被聲明了,但沒有賦值時,就等于undefined。
(2) 調(diào)用函數(shù)時,應(yīng)該提供的參數(shù)沒有提供,該參數(shù)等于undefined。
(3)對象沒有賦值的屬性,該屬性的值為undefined。
(4)函數(shù)沒有返回值時,默認(rèn)返回undefined。
undeclared :js語法錯誤,沒有申明直接使用,js無法找到對應(yīng)的上下文。
for..in 和 object.keys的區(qū)別
Object.keys不會遍歷繼承的原型屬性
for...in 會遍歷繼承的原型屬性
JS中的匿名函數(shù)是什么?
匿名函數(shù):就是沒有函數(shù)名的函數(shù),如:
(function(x, y){alert(x + y);})(2, 3);
這里創(chuàng)建了一個匿名函數(shù)(在第一個括號內(nèi)),第二個括號用于調(diào)用該匿名函數(shù),并傳入?yún)?shù)。
解釋 JS 中的函數(shù)提升
JS允許將聲明移動到頂部的默認(rèn)行為稱為提升。JS中創(chuàng)建函數(shù)的兩種方法是函數(shù)聲明和函數(shù)表達(dá)式。
函數(shù)聲明
具有特定參數(shù)的函數(shù)稱為函數(shù)聲明,在JS中創(chuàng)建變量稱為聲明。如:
hoisted(); // logs "foo"function hoisted() {console.log('foo');}
函數(shù)表達(dá)式
當(dāng)使用表達(dá)式創(chuàng)建函數(shù)時,稱為函數(shù)表達(dá)式。如:
notHoisted(); // TypeError: notHoisted is not a functionvar notHoisted = function() {console.log('bar');};
Js隱式轉(zhuǎn)換介紹
在js中,當(dāng)運(yùn)算符在運(yùn)算時,如果兩邊數(shù)據(jù)不統(tǒng)一,CPU就無法計算,這時我們編譯器會自動將運(yùn)算符兩邊的數(shù)據(jù)做一個數(shù)據(jù)類型轉(zhuǎn)換,轉(zhuǎn)成一樣的數(shù)據(jù)類型再計算
這種無需程序員手動轉(zhuǎn)換,而由編譯器自動轉(zhuǎn)換的方式就稱為隱式轉(zhuǎn)換
例如1 > "0"這行代碼在js中并不會報錯,編譯器在運(yùn)算符時會先把右邊的"0"轉(zhuǎn)成數(shù)字0`然后在比較大小
隱式轉(zhuǎn)換規(guī)則:
1. 轉(zhuǎn)成string類型: +(字符串連接符) 2..轉(zhuǎn)成number類型:++/--(自增自減運(yùn)算符) + - * / %(算術(shù)運(yùn)算符) > < >= <= == != === !=== (關(guān)系運(yùn)算符)
2. 轉(zhuǎn)成boolean類型:!(邏輯非運(yùn)算符)
例子:
console.log([] == []) // falseconsole.log([] == ![]) // trueconsole.log([] !== []) // trueconsole.log(NaN != NaN) // trueconsole.log(null == undefined) // trueconsole.log(null === undefined) // falseconsole.log(1 == true) // trueconsole.log(null > 0) // falseconsole.log(true + 1) // 2console.log(undefined + 1) // NaNconsole.log({} + 1) // [object Object]1console.log([] + {}) // [object Object]console.log([2,3] + [1,2]) // 2,31,2
Object.is()與原來的比較操作符"==="、"==” 的區(qū)別?
(1)兩等號判等,會在比較時進(jìn)行類型轉(zhuǎn)換;
(2)三等號判等(判斷嚴(yán)格),比較時不進(jìn)行隱式類型轉(zhuǎn)換,(類 型不同則會返回false);
(3)Object.is 在三等號判等的基礎(chǔ)上特別處理了NaN、-0和+0,保證-0和+0不再相同,但Object.is(NaN, NaN)會返回true。Object.is應(yīng)被認(rèn)為有其特殊的用途,而不能用它認(rèn)為它比其它的相等對比更寬松或嚴(yán)格。
JS 中 == 和 === 區(qū)別是什么?
1、對于string,number等基礎(chǔ)類型,==和===有區(qū)別
1)不同類型間比較,==之比較“轉(zhuǎn)化成同一類型后的值”看“值”是否相等,===如果類型不同,其結(jié)果就是不等。
2)同類型比較,直接進(jìn)行“值”比較,兩者結(jié)果一樣。
2、對于Array,Object等高級類型,==和===沒有區(qū)別
進(jìn)行“指針地址”比較。
3、基礎(chǔ)類型與高級類型,==和===有區(qū)別
1)對于==,將高級轉(zhuǎn)化為基礎(chǔ)類型,進(jìn)行“值”比較。
2)因為類型不同,===結(jié)果為false。
ES5 和 ES6 分別幾種方式聲明變量
ES5 有倆種:var 和 function
ES6 有六種:增加四種,let、const、class 和 import
注意:let、const、class聲明的全局變量再也不會和全局對象的屬性掛鉤。
什么是事件代理/事件委托?
事件代理/事件委托是利用事件冒泡的特性,將本應(yīng)該綁定在多個元素上的事件綁定在他們的祖先元素上,尤其在動態(tài)添加子元素的時候,可以非常方便的提高程序性能,減小內(nèi)存空間。
什么是事件冒泡?什么是事件捕獲?
冒泡型事件:事件按照從最特定的事件目標(biāo)到最不特定的事件目標(biāo)(document對象)的順序觸發(fā)。
捕獲型事件:事件從最不精確的對象(document 對象)開始觸發(fā),然后到最精確(也可以在窗口級別捕獲事件,不過必須由開發(fā)人員特別指定)。
在添加事件時用addEventListener(event,fn,useCapture)方法,基中第3個參數(shù)useCapture是一個Boolean值,用來設(shè)置事件是在事件捕獲時執(zhí)行,還是事件冒泡時執(zhí)行。
注意:IE瀏覽器用attachEvent()方法,此方法沒有相關(guān)設(shè)置,不過IE的事件模型默認(rèn)是在事件冒泡時執(zhí)行的,也就是在useCapture等于false的時候執(zhí)行,所以把在處理事件時把useCapture設(shè)置為false是比較安全,也實現(xiàn)兼容瀏覽器的效果。
如何阻止事件冒泡?
w3c的方法是e.stopPropagation(),IE則是使用e.cancelBubble = true。例如:
window.event.cancelBubble = true;e.stopPropagation();
return false也可以阻止冒泡。
如何阻止默認(rèn)事件?
w3c的方法是e.preventDefault(),IE則是使用e.returnValue = false,比如:
function stopDefault( e ) {if ( e && e.preventDefault )e.preventDefault(); //IE中阻止函數(shù)器默認(rèn)動作的方式elsewindow.event.returnValue = false;}
return false也能阻止默認(rèn)行為。
DOM 事件有哪些階段?談?wù)剬κ录淼睦斫?/span>
分為三大階段:捕獲階段--目標(biāo)階段--冒泡階段
事件代理簡單說就是:事件不直接綁定到某元素上,而是綁定到該元素的父元素上,進(jìn)行觸發(fā)事件操作時(例如'click'),再通過條件判斷,執(zhí)行事件觸發(fā)后的語句(例如'alert(e.target.innerhtml)')
好處:(1)使代碼更簡潔;(2)節(jié)省內(nèi)存開銷
如何用原生js給一個按鈕綁定兩個onclick事件?
使用addEventListener的方式來綁定多個事件。例如
var btn = document.getElementById('btn')btn.addEventListener('click', fn1)btn.addEventListener('click', fn2)function fn1 () {console.log('我是方法1')}function fn2 () {console.log('我是方法2')}
響應(yīng)事件有哪些?
onclick鼠標(biāo)點(diǎn)擊某個對象;onfocus獲取焦點(diǎn);onblur失去焦點(diǎn);onmousedown鼠標(biāo)被按下等,常用的如下:
鼠標(biāo)單擊事件( onclick )
鼠標(biāo)經(jīng)過事件( onmouseover )
鼠標(biāo)移開事件( onmouseout )
光標(biāo)聚焦事件( onfocus )
失焦事件( onblur )
內(nèi)容選中事件( onselect )
文本框內(nèi)容改變事件( onchange )
加載事件( onload )
卸載事件( onunload )
閉包的概念?優(yōu)缺點(diǎn)?使用場景?
閉包的概念:閉包就是能讀取其他函數(shù)內(nèi)部變量的函數(shù)。
避免全局變量的污染
希望一個變量長期存儲在內(nèi)存中(緩存變量)
缺點(diǎn):
內(nèi)存泄露(消耗)
常駐內(nèi)存,增加內(nèi)存使用量
使用場景:封裝功能時(需要使用私有的屬性和方法),函數(shù)防抖、函數(shù)節(jié)流、函數(shù)柯里化、給元素偽數(shù)組添加事件需要使用元素的索引值。
造成內(nèi)存泄露的原因
意外的全局變量(在函數(shù)內(nèi)部沒有使用var進(jìn)行聲明的變量)
console.log
閉包
對象的循環(huán)引用
未清除的計時器
DOM泄露(獲取到DOM節(jié)點(diǎn)之后,將DOM節(jié)點(diǎn)刪除,但是沒有手動釋放變量,拿對應(yīng)的DOM節(jié)點(diǎn)在變量中還可以訪問到,就會造成泄露)
new操作符具體干了什么呢?
1) 創(chuàng)建一個空對象,并且 this 變量引用該對象,同時還繼承了該函數(shù)的原型。
2) 屬性和方法被加入到 this 引用的對象中。
3) 新創(chuàng)建的對象由 this 所引用,并且最后隱式的返回 this 。
javascript中this的指向
this永遠(yuǎn)指向函數(shù)運(yùn)行時所在的對象,而不是函數(shù)被創(chuàng)建時所在的對象。
普通的函數(shù)調(diào)用,函數(shù)被誰調(diào)用,this就是誰。
構(gòu)造函數(shù)的話,如果不用new操作符而直接調(diào)用,那即this指向window。用new操作符生成對象實例后,this就指向了新生成的對象。
匿名函數(shù)或不處于任何對象中的函數(shù)指向window 。
如果是call,apply等,指定的this是誰,就是誰。
談?wù)則his的理解
1) this總是指向函數(shù)的直接調(diào)用者(而非間接調(diào)用者)
2) 如果有new關(guān)鍵字,this指向new出來的那個對象
3) 在事件中,this指向目標(biāo)元素,特殊的是IE的attachEvent中的this總是指向全局對象window。
eval是做什么的?
它的功能是把對應(yīng)的字符串解析成JS代碼并運(yùn)行;應(yīng)該避免使用eval,不安全,非常耗性能(2次,一次解析成js語句,一次執(zhí)行)。
call,apply,bind的區(qū)別
call apply bind都可以改變函數(shù)調(diào)用的this指向。
函數(shù).call(對象,arg1,arg2....)函數(shù).apply(對象,[arg1,arg2,...])var ss=函數(shù).bind(對象,arg1,arg2,....)
1.第一個參數(shù)都是指定函數(shù)內(nèi)部中this的指向(函數(shù)執(zhí)行時所在的作用域),然后根據(jù)指定的作用域,調(diào)用該函數(shù)。
2.都可以在函數(shù)調(diào)用時傳遞參數(shù)。call,bind方法需要直接傳入,而apply方法需要以數(shù)組的形式傳入。
3.call,apply方法是在調(diào)用之后立即執(zhí)行函數(shù),而bind方法沒有立即執(zhí)行,需要將函數(shù)再執(zhí)行一遍。
4.改變this對象的指向問題不僅有call,apply,bind方法,也可以使用that變量來固定this的指向。
Javascript作用鏈域
作用域鏈的原理和原型鏈很類似,如果這個變量在自己的作用域中沒有,那么它會尋找父級的,直到最頂層。
注意:JS沒有塊級作用域,若要形成塊級作用域,可通過(function(){})();立即執(zhí)行的形式實現(xiàn)。
Javascript原型鏈
首先理解三個概念:
prototype:原型對象,每個函數(shù)都有一個 prototype 屬性,再通過 new 命令實例對象時,該屬性會成為該實例的原型對象。
constructor:構(gòu)造函數(shù)。指向原型對象的 constructor
__proto__:實例對象的原型
在javascript中,實例對象與原型之間的鏈接,叫做原型鏈。Javascript解析引擎在讀取一個Object的屬性的值時,會沿著原型鏈向上尋找,如果最終沒有找到,則該屬性值為undefined;如果最終找到該屬性的值,則返回結(jié)果。
繼承(6種方式)以及優(yōu)缺點(diǎn)
1.原型鏈繼承
2.構(gòu)造函數(shù)繼承
3.組合繼承(原型鏈繼承+構(gòu)造函數(shù)繼承)
4.原型式繼承
5.寄生繼承
6.組合寄生繼承
代碼示例:
//借助構(gòu)造函數(shù)實現(xiàn)繼承:缺點(diǎn)是父構(gòu)造函數(shù)的原型鏈繼承不了,若要全部繼承除非將所有屬性和方法定義在構(gòu)造函數(shù)中function Parent1 () {this.name = 'parent1';}function Child1 () {//這么做的好處是定義在父構(gòu)造函數(shù)中的引用類型的屬性,對于子構(gòu)造函數(shù)的每個實例來說是獨(dú)立的//并且在子構(gòu)造函數(shù)實例化時,可以給父構(gòu)造函數(shù)傳參Parent.call(this);this.type = 'child1';}//借助原型鏈實現(xiàn)繼承:缺點(diǎn)是繼承的引用類型屬性是共享的,子構(gòu)造函數(shù)的實例更改會影響其他實例上的這個屬性,比如 play 屬性function Parent2 () {this.name = 'parent2';this.play = [1, 2, 3];}function Child2 () {this.type = 'Child2';}Child2.prototype = new Parent2();//組合方式:缺點(diǎn)是會執(zhí)行兩次父構(gòu)造函數(shù)function Child3 () {//執(zhí)行第一次Parent2.call(this);this.type = 'Child3';}Child3.prototype = new Parent2(); //執(zhí)行第二次//組合優(yōu)化1,不需要再將定義在父構(gòu)造函數(shù)中的屬性和方法再繼承一次,只需要繼承原型鏈上的Child3.prototype = Parent2.prototype;//缺點(diǎn)是無法區(qū)分一個實例是子函構(gòu)造函數(shù)實例化的還是父構(gòu)造函數(shù)實例化的let s1 = new Child3();//s1.constructor 指向了 Parent2,而不是 Child3,因為 Child3 原型對象的屬性 constructor 繼承了 Parent2 原型對象上的//如果你強(qiáng)行執(zhí)行 Child3.prototype.constructor = Child3 的話,也會將 Parent2.prototype.constructor 改成 Child3//組合優(yōu)化2,通過 Object.create() 創(chuàng)建一個中間對象,將兩個原型對象區(qū)別開來,并且繼承父構(gòu)造函數(shù)的原型鏈Child3.prototype = Object.create(Parent2.prototype);Child3.prototype.constructor = Child3;//即 Child3.prototype.__proto__ === Parent2.prototype 為 true//如此 Child3.prototype 和 Parent2.prototype 被隔離開,是兩個對象,不會相互影響//這種方式為理想繼承方式
請解釋變量聲明提升
通過var聲明的變量會被提升至作用域的頂端。不僅僅是變量,函數(shù)聲明也一樣會被提升。
當(dāng)同一作用域內(nèi)同時出現(xiàn)變量和函數(shù)聲明提升時,變量仍然在函數(shù)前面。
函數(shù)聲明與函數(shù)表達(dá)式的區(qū)別?
在Javscript中,解析器在向執(zhí)行環(huán)境中加載數(shù)據(jù)時,對函數(shù)聲明和函數(shù)表達(dá)式并非是一視同仁的,解析器會率先讀取函數(shù)聲明,并使其在執(zhí)行任何代碼之前可用(可以訪問),至于函數(shù)表達(dá)式,則必須等到解析器執(zhí)行到它所在的代碼行,才會真正被解析執(zhí)行。
什么是window對象? 什么是document對象?
window對象代表瀏覽器中打開的一個窗口。document對象代表整個html文檔。實際上,document對象是window對象的一個屬性。
document.onload和document.ready兩個事件的區(qū)別
頁面加載完成有兩種事件,一是ready,表示文檔結(jié)構(gòu)已經(jīng)加載完成(不包含圖片等非文字媒體文件),二是onload,指示頁面包含圖片等文件在內(nèi)的所有元素都加載完成。
Js的淺拷貝和深拷貝
淺拷貝只復(fù)制指向某個對象的指針,而不復(fù)制對象本身,新舊對象還是共享同一塊內(nèi)存。但深拷貝會另外創(chuàng)造一個一模一樣的對象,新對象跟原對象不共享內(nèi)存,修改新對象不會改到原對象。
淺拷貝
// 第一層為深拷貝
Object.assign()
Array.prototype.slice()
擴(kuò)展運(yùn)算符 ...
深拷貝
JSON.parse(JSON.stringify())
遞歸函數(shù)
function cloneObject(obj) {
var newObj = {} //如果不是引用類型,直接返回
if (typeof obj !== 'object') {
return obj
}
//如果是引用類型,遍歷屬性
else {
for (var attr in obj) {
//如果某個屬性還是引用類型,遞歸調(diào)用
newObj[attr] = cloneObject(obj[attr])
}
}
return newObj
}JavaScript里arguments究竟是什么?
Javascrip中國每個函數(shù)都會有一個Arguments對象實例arguments,它引用著函數(shù)的實參,可以用數(shù)組下標(biāo)的方式"[]"引用arguments的元素。arguments.length為函數(shù)實參個數(shù),arguments.callee引用函數(shù)自身。
在函數(shù)代碼中,使用特殊對象arguments,開發(fā)者無需明確指出參數(shù)名,通過使用下標(biāo)就可以訪問相應(yīng)的參數(shù)。
function test() {
var s = "";
for (var i = 0; i < arguments.length; i++) {
alert(arguments[i]);
s += arguments[i] + ",";
}
return s;
}
test("name", "age");//name,age
arguments雖然有一些數(shù)組的性質(zhì),但其并非真正的數(shù)組,只是一個類數(shù)組對象。其并沒有數(shù)組的很多方法,不能像真正的數(shù)組那樣調(diào)用.jion(),.concat(),.pop()等方法。
什么是"use strict";?使用它的好處和壞處分別是什么?
在代碼中出現(xiàn)表達(dá)式-"use strict"; 意味著代碼按照嚴(yán)格模式解析,這種模式使得Javascript在更嚴(yán)格的條件下運(yùn)行。
好處:
消除Javascript語法的一些不合理、不嚴(yán)謹(jǐn)之處,減少一些怪異行為;
消除代碼運(yùn)行的一些不安全之處,保證代碼運(yùn)行的安全;
提高編譯器效率,增加運(yùn)行速度;
為未來新版本的Javascript做好鋪墊。
壞處:
同樣的代碼,在"嚴(yán)格模式"中,可能會有不一樣的運(yùn)行結(jié)果;
一些在"正常模式"下可以運(yùn)行的語句,在"嚴(yán)格模式"下將不能運(yùn)行。
什么是跨域?有什么方法解決跨域帶來的問題?
跨域需要針對瀏覽器的同源策略來理解,同源策略指的是請求必須是同一個端口,同一個協(xié)議,同一個域名,不同源的客戶端腳本在沒有明確授權(quán)的情況下,不能讀寫對方資源。
受瀏覽器同源策略的影響,不是同源的腳本不能操作其他源下面的對象。想要操作另一個源下的對象是就需要跨域。
常用解決方案:
跨域資源共享(CORS)
nginx代理跨域
nodejs中間件代理跨域
jsonp跨域
解釋什么是Json
(1)JSON 是一種輕量級的數(shù)據(jù)交換格式。
(2)JSON 獨(dú)立于語言和平臺,JSON 解析器和 JSON 庫支持許多不同的編程語言。
(3)JSON的語法表示三種類型值,簡單值(字符串,數(shù)值,布爾值,null),數(shù)組,對象
解釋jsonp的原理,以及為什么不是真正的ajax
Json是通過動態(tài)創(chuàng)建script標(biāo)簽,回調(diào)函數(shù)的方式實現(xiàn)的,而Ajax是頁面無刷新請求數(shù)據(jù)操作。
原生Js的實現(xiàn):
(function (window,document) {
"use strict";
var jsonp = function (url,data,callback) {
// 1.將傳入的data數(shù)據(jù)轉(zhuǎn)化為url字符串形式
// {id:1,name:'fly63'} => id=1&name=fly63
var dataString = url.indexof('?') == -1? '?': '&';
for(var key in data){
dataString += key + '=' + data[key] + '&';
};
// 2 處理url中的回調(diào)函數(shù)
// cbFuncName回調(diào)函數(shù)的名字 :my_json_cb_名字的前綴 + 隨機(jī)數(shù)(把小數(shù)點(diǎn)去掉)
var cbFuncName = 'my_json_cb_' + Math.random().toString().replace('.','');
dataString += 'callback=' + cbFuncName;
// 3.創(chuàng)建一個script標(biāo)簽并插入到頁面中
var scriptEle = document.createElement('script');
scriptEle.src = url + dataString;
// 4.掛載回調(diào)函數(shù)
window[cbFuncName] = function (data) {
callback(data);
// 處理完回調(diào)函數(shù)的數(shù)據(jù)之后,刪除jsonp的script標(biāo)簽
document.body.removeChild(scriptEle);
}
// 5.append到頁面中
document.body.appendChild(scriptEle);
}
window.$jsonp = jsonp;// 因為jsonp是一個私有函數(shù)外部不能調(diào)用,所有jsonp函數(shù)作文window對象的一個方法,供外部調(diào)用
})(window,document)談?wù)凜ookie的弊端?
(1)Cookie數(shù)量和長度的限制。每個domain最多只能有20條cookie,每個cookie長度不能超過4KB,否則會被截掉。
(2)安全性問題。如果cookie被 人攔截了,那人就可以取得所有的session信息。即使加密也與事無補(bǔ),因為攔截者并不需要知道cookie的意義,他只要原樣轉(zhuǎn)發(fā)cookie就可以達(dá)到目的了。
(3)有些狀態(tài)不可能保存在客戶端。例如,為了防止重復(fù)提交表單,我們需要在服務(wù)器端保存一個計數(shù)器。如果我們把這個計數(shù)器保存在客戶端,那么它起不到任何作用。
什么是Cookie 隔離?(或者:請求資源的時候不要帶cookie怎么做)
通過使用多個非主要域名來請求靜態(tài)文件,如果靜態(tài)文件都放在主域名下,那靜態(tài)文件請求的時候帶有的cookie的數(shù)據(jù)提交給server是非常浪費(fèi)的,還不如隔離開。因為cookie有域的限制,因此不能跨域提交請求,故使用非主要域名的時候,請求頭中就不會帶有cookie數(shù)據(jù),這樣可以降低請求頭的大小,降低請求時間,從而達(dá)到降低整體請求延時的目的。同時這種方式不會將cookie傳入server,也減少了server對cookie的處理分析環(huán)節(jié),提高了server的http請求的解析速度。
描述一下cookies,sessionStorage和localStorage的區(qū)別?
sessionStorage和localStorage是HTML5 Web Storage API提供的,可以方便的在web請求之間保存數(shù)據(jù)。有了本地數(shù)據(jù),就可以避免數(shù)據(jù)在瀏覽器和服務(wù)器間不必要地來回傳遞。sessionStorage、localStorage、cookie都是在瀏覽器端存儲的數(shù)據(jù),其中sessionStorage的概念很特別,引入了一個“瀏覽器窗口”的概念。sessionStorage是在同源的同窗口(或tab)中,始終存在的數(shù)據(jù)。也就是說只要這個瀏覽器窗口沒有關(guān)閉,即使刷新頁面或進(jìn)入同源另一頁面,數(shù)據(jù)仍然存在。關(guān)閉窗口后,sessionStorage即被銷毀。同時“獨(dú)立”打開的不同窗口,即使是同一頁面,sessionStorage對象也是不同的cookies會發(fā)送到服務(wù)器端。其余兩個不會。Microsoft指出InternetExplorer8增加cookie限制為每個域名50個,但I(xiàn)E7似乎也允許每個域名50個cookie。
Firefox每個域名cookie限制為50個。
Opera每個域名cookie限制為30個。
Firefox和Safari允許cookie多達(dá)4097個字節(jié),包括名(name)、值(value)和等號。
Opera允許cookie多達(dá)4096個字節(jié),包括:名(name)、值(value)和等號。
InternetExplorer允許cookie多達(dá)4095個字節(jié),包括:名(name)、值(value)和等號。
localstorage不能手動刪除的時候,什么時候過期
除非被清除,否則永久保存 clear()可清楚
sessionStorage 僅在當(dāng)前會話下有效,關(guān)閉頁面或瀏覽器后被清除
什么是函數(shù)柯里化
函數(shù)柯里化是把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。
柯里化的目的是,減少代碼冗余,以及增加代碼的可讀性。下面看一個與柯里化有關(guān)的經(jīng)典例子:
// 實現(xiàn)一個add方法,使計算結(jié)果能夠滿足類似如下的預(yù)期:
// add(1)(2)(3) = 6;
// add(1, 2, 3)(4) = 10;
// add(1)(2)(3)(4)(5) = 15;
var add_currying=function(...rest){
var sum=0;
for(let item of rest){
sum+=item;
}
var add_back = (...rest) => {
for(let item of rest){
sum+=item;
}
return add_back;
};
add_back.toString = () => sum;
return add_back;
}
console.log(add_currying(1,2,3)); //6
console.log(add_currying(1,2)(3,4)); //10
console.log(add_currying(1,2)(3)(4,5)); //15
console.log(add_currying(1)(2)(3)(4)(5)(6)); //21
//打印出來會自動使用toString,即使是寫var a=add_currying(1,2,3)也會自動調(diào)用此方法(默認(rèn)將函數(shù)語句以字符串打出)
//而為了打印出我們想要的結(jié)果我們就需要自己重寫toString方法
//如果不用es6的三點(diǎn)運(yùn)算符就只能使用以前的Array.prototype.slice.call(arguments)方法js如何處理防抖和節(jié)流?
在進(jìn)行窗口的resize、scroll,輸入框內(nèi)容校驗等操作時,如果事件處理函數(shù)調(diào)用的頻率無限制,會加重瀏覽器的負(fù)擔(dān),導(dǎo)致用戶體驗非常糟糕。
此時我們可以采用debounce(防抖)和throttle(節(jié)流)的方式來減少調(diào)用頻率,同時又不影響實際效果。
函數(shù)防抖(debounce):
當(dāng)持續(xù)觸發(fā)事件時,一定時間段內(nèi)沒有再觸發(fā)事件,事件處理函數(shù)才會執(zhí)行一次,如果設(shè)定的時間到來之前,又一次觸發(fā)了事件,就重新開始延時。
如下,持續(xù)觸發(fā)scroll事件時,并不執(zhí)行handle函數(shù),當(dāng)1000毫秒內(nèi)沒有觸發(fā)scroll事件時,才會延時觸發(fā)scroll事件。
function debounce(fn, wait) {
var timeout = null;
return function() {
if(timeout !== null) clearTimeout(timeout);
timeout = setTimeout(fn, wait);
}
}
// 處理函數(shù)
function handle() {
console.log(Math.random());
}
// 滾動事件
window.addEventListener('scroll', debounce(handle, 1000)); 函數(shù)節(jié)流函數(shù)節(jié)流(throttle):
當(dāng)持續(xù)觸發(fā)事件時,保證一定時間段內(nèi)只調(diào)用一次事件處理函數(shù)。
節(jié)流通俗解釋就比如我們水龍頭放水,閥門一打開,水嘩嘩的往下流,秉著勤儉節(jié)約的優(yōu)良傳統(tǒng)美德,我們要把水龍頭關(guān)小點(diǎn),最好是如我們心意按照一定規(guī)律在某個時間間隔內(nèi)一滴一滴的往下滴。
如下,持續(xù)觸發(fā)scroll事件時,并不立即執(zhí)行handle函數(shù),每隔1000毫秒才會執(zhí)行一次handle函數(shù)。
var throttle =function(func, delay) {var prev = Date.now();return function() {var context = this;var args = arguments;var now = Date.now();if (now - prev >= delay) {func.apply(context, args);prev = Date.now();}}}function handle() {console.log(Math.random());}window.addEventListener('scroll', throttle(handle, 1000));
JS里垃圾回收機(jī)制是什么,常用的是哪種,怎么處理的?
JS的垃圾回收機(jī)制是為了以防內(nèi)存泄漏,內(nèi)存泄漏的含義就是當(dāng)已經(jīng)不需要某塊內(nèi)存時這塊內(nèi)存還存在著,垃圾回收機(jī)制就是間歇的不定期的尋找到不再使用的變量,并釋放掉它們所指向的內(nèi)存。
JS中最常見的垃圾回收方式是標(biāo)記清除。
工作原理:是當(dāng)變量進(jìn)入環(huán)境時,將這個變量標(biāo)記為“進(jìn)入環(huán)境”。當(dāng)變量離開環(huán)境時,則將其標(biāo)記為“離開環(huán)境”。標(biāo)記“離開環(huán)境”的就回收內(nèi)存。
工作流程:
垃圾回收器,在運(yùn)行的時候會給存儲在內(nèi)存中的所有變量都加上標(biāo)記。
去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標(biāo)記。
再被加上標(biāo)記的會被視為準(zhǔn)備刪除的變量。
垃圾回收器完成內(nèi)存清除工作,銷毀那些帶標(biāo)記的值并回收他們所占用的內(nèi)存空間。
那些操作會造成內(nèi)存泄漏
全局變量、閉包、DOM清空或刪除時,事件未清除、子元素存在引用
什么是虛擬DOM
文檔對象模型或 DOM 定義了一個接口,該接口允許 JavaScript 之類的語言訪問和操作 HTML 文檔。元素由樹中的節(jié)點(diǎn)表示,并且接口允許我們操縱它們。但是此接口需要付出代價,大量非常頻繁的 DOM 操作會使頁面速度變慢。
vue 通過在內(nèi)存中實現(xiàn)文檔結(jié)構(gòu)的虛擬表示來解決此問題,其中虛擬節(jié)點(diǎn)(VNode)表示 DOM 樹中的節(jié)點(diǎn)。當(dāng)需要操縱時,可以在虛擬 DOM的 內(nèi)存中執(zhí)行計算和操作,而不是在真實 DOM 上進(jìn)行操縱。這自然會更快,并且允許虛擬 DOM 算法計算出最優(yōu)化的方式來更新實際 DOM 結(jié)構(gòu)。
一旦計算出,就將其應(yīng)用于實際的 DOM 樹,這就提高了性能,這就是為什么基于虛擬 DOM 的框架(例如 vue 和 react)如此突出的原因。
Ajax使用
所謂異步,就是向服務(wù)器發(fā)送請求的時候,我們不必等待結(jié)果,而是可以同時做其他的事情,等到有了結(jié)果它自己會根據(jù)設(shè)定進(jìn)行后續(xù)操作,與此同時,頁面是不會發(fā)生整頁刷新的,提高了用戶體驗。
創(chuàng)建Ajax的過程:
1) 創(chuàng)建XMLHttpRequest對象(異步調(diào)用對象)
var xhr = new XMLHttpRequest();
2) 創(chuàng)建新的Http請求(方法、URL、是否異步)
xhr.open(‘get’,’example.php’,false);
3) 設(shè)置響應(yīng)HTTP請求狀態(tài)變化的函數(shù)。
onreadystatechange事件中readyState屬性等于4。響應(yīng)的HTTP狀態(tài)為200(OK)或者304(Not Modified)。
4) 發(fā)送http請求
xhr.send(data);
5) 獲取異步調(diào)用返回的數(shù)據(jù)
注意:
1) 頁面初次加載時,盡量在web服務(wù)器一次性輸出所有相關(guān)的數(shù)據(jù),只在頁面加載完成之后,用戶進(jìn)行操作時采用ajax進(jìn)行交互。
2) 同步ajax在IE上會產(chǎn)生頁面假死的問題。所以建議采用異步ajax。
3) 盡量減少ajax請求次數(shù)
4) ajax安全問題,對于敏感數(shù)據(jù)在服務(wù)器端處理,避免在客戶端處理過濾。對于關(guān)鍵業(yè)務(wù)邏輯代碼也必須放在服務(wù)器端處理。
Ajax的原生寫法
function ajax() {
//創(chuàng)建一個 XHR 對象
let oAjax = window.XMLHttpRequest ? (new XMLHttpRequest()) : (new window.ActiveXobject('Microsoft.XMLHTTP'));
//返回一個函數(shù),這是函數(shù)柯里化操作,不用每次調(diào)用 ajax 都判斷瀏覽器環(huán)境
//但是會占用更多的內(nèi)存,因為總是會保存外部函數(shù)的作用域
return function(url, fnSucc, fnFaild) {
//只要 XHR 對象的 readyState 屬性的值發(fā)生改變,就觸發(fā)這個事件
oAjax.onreadystatechange = function() {
// readyState 屬性是 0-4 的值,當(dāng)為 4 時,表示已經(jīng)接收到全部響應(yīng)數(shù)據(jù),并可以在客戶端使用
if (oAjax.readyState === 4) {
//響應(yīng)的 HTTP 狀態(tài)
let s = oAjax.status;
if (s === 200 || s === 206 || s === 304) {
//將響應(yīng)主體被返回的文本作為參數(shù)傳給這個函數(shù),并執(zhí)行這個函數(shù)
if (fnSucc) fnSucc(oAjax.responseText);
} else {
if (fnFaild) fnFaild(oAjax.status);
}
}
};
//啟動一個請求,準(zhǔn)備發(fā)送
oAjax.open('GET', url, true);
//發(fā)送請求
oAjax.send(null);
}
}ajax請求的時候get 和post方式的區(qū)別
最直觀的區(qū)別就是GET把參數(shù)包含在URL中,POST通過request body傳遞參數(shù)。
GET在瀏覽器回退時是無害的,而POST會再次提交請求。
GET產(chǎn)生的URL地址可以被Bookmark,而POST不可以。
GET請求會被瀏覽器主動cache,而POST不會,除非手動設(shè)置。
GET請求只能進(jìn)行url編碼,而POST支持多種編碼方式。
GET請求參數(shù)會被完整保留在瀏覽器歷史記錄里,而POST中的參數(shù)不會被保留。
GET請求在URL中傳送的參數(shù)是有長度限制的,而POST么有。
對參數(shù)的數(shù)據(jù)類型,GET只接受ASCII字符,而POST沒有限制。
GET比POST更不安全,因為參數(shù)直接暴露在URL上,所以不能用來傳遞敏感信息。
GET參數(shù)通過URL傳遞,POST放在Request body中。
Javascript中,執(zhí)行時對象查找時,永遠(yuǎn)不會去查找原型的函數(shù)?
Object.hasOwnProperty(proName):是用來判斷一個對象是否有你給出名稱的屬性。不過需要注意的是,此方法無法檢查該對象的原型鏈中是否具有該屬性,該屬性必須是對象本身的一個成員。
JS延遲加載的方式有哪些?
JS的延遲加載有助與提高頁面的加載速度。
defer和async、動態(tài)創(chuàng)建DOM方式(用得最多)、按需異步載入JS
defer:延遲腳本。立即下載,但延遲執(zhí)行(延遲到整個頁面都解析完畢后再運(yùn)行),按照腳本出現(xiàn)的先后順序執(zhí)行。
async:異步腳本。下載完立即執(zhí)行,但不保證按照腳本出現(xiàn)的先后順序執(zhí)行。
同步和異步的區(qū)別?
同步的概念在操作系統(tǒng)中:不同進(jìn)程協(xié)同完成某項工作而先后次序調(diào)整(通過阻塞、喚醒等方式),同步強(qiáng)調(diào)的是順序性,誰先誰后。異步不存在順序性。
同步:瀏覽器訪問服務(wù)器,用戶看到頁面刷新,重新發(fā)請求,等請求完,頁面刷新,新內(nèi)容出現(xiàn),用戶看到新內(nèi)容之后進(jìn)行下一步操作。
異步:瀏覽器訪問服務(wù)器請求,用戶正常操作,瀏覽器在后端進(jìn)行請求。等請求完,頁面不刷新,新內(nèi)容也會出現(xiàn),用戶看到新內(nèi)容。
頁面編碼和被請求的資源編碼如果不一致如何處理?
若請求的資源編碼,如外引js文件編碼與頁面編碼不同??筛鶕?jù)外引資源編碼方式定義為 charset="utf-8"或"gbk"。
比如:http://www.fly63.com/a.html 中嵌入了一個http://www.fly63.com/test.js
a.html 的編碼是gbk或gb2312的。 而引入的js編碼為utf-8的 ,那就需要在引入的時候
<script src="http://www.fly63.com/test.js" charset="utf-8"></script>
模塊化開發(fā)怎么做?
模塊化開發(fā)指的是在解決某一個復(fù)雜問題或者一系列問題時,依照一種分類的思維把問題進(jìn)行系統(tǒng)性的分解。
模塊化是一種將復(fù)雜系統(tǒng)分解為代碼結(jié)構(gòu)更合理,可維護(hù)性更高的可管理的模塊方式。對于軟件行業(yè):系統(tǒng)被分解為一組高內(nèi)聚,低耦合的模塊。
(1)定義封裝的模塊
(2)定義新模塊對其他模塊的依賴
(3)可對其他模塊的引入支持。在JavaScript中出現(xiàn)了一些非傳統(tǒng)模塊開發(fā)方式的規(guī)范。 CommonJS的模塊規(guī)范,AMD(Asynchronous Module Definition),CMD(Common Module Definition)等。AMD是異步模塊定義,所有的模塊將被異步加載,模塊加載不影響后邊語句運(yùn)行。
AMD與CMD規(guī)范區(qū)別?
AMD 是 RequireJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。CMD 是 SeaJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。區(qū)別:
1) 對于依賴的模塊,AMD 是提前執(zhí)行,CMD 是延遲執(zhí)行。不過 RequireJS 從 2.0 開始,也改成可以延遲執(zhí)行(根據(jù)寫法不同,處理方式不同)。
2) CMD 推崇依賴就近,AMD 推崇依賴前置。
3) AMD 的 API 默認(rèn)是一個當(dāng)多個用,CMD 的 API 嚴(yán)格區(qū)分,推崇職責(zé)單一。
解釋頁面的回流與重繪
當(dāng)render tree中的一部分(或全部)因為元素的規(guī)模尺寸,布局,隱藏等改變而需要重新構(gòu)建。這就稱為回流(reflow)。
每個頁面至少需要一次回流,就是在頁面第一次加載的時候,這時候是一定會發(fā)生回流的,因為要構(gòu)建render tree。
在回流的時候,瀏覽器會使渲染樹中受到影響的部分失效,并重新構(gòu)造這部分渲染樹,完成回流后,瀏覽器會重新繪制受影響的部分到屏幕中,該過程成為重繪。
當(dāng)render tree中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀,風(fēng)格,而不會影響布局的,比如background-color。則就叫稱為重繪。
區(qū)別:
回流必將引起重繪,而重繪不一定會引起回流。比如:只有顏色改變的時候就只會發(fā)生重繪而不會引起回流
當(dāng)頁面布局和幾何屬性改變時就需要回流,比如:添加或者刪除可見的DOM元素,元素位置改變,元素尺寸改變——邊距、填充、邊框、寬度和高度,內(nèi)容改變
瀏覽器的滾動距離
可視區(qū)域距離頁面頂部的距離
var scrollTop=document.documentElement.scrollTop||document.body.scrollTop
可視區(qū)的大小
(1)innerXXX(不兼容ie)
window.innerHeight 可視區(qū)高度,包含滾動條寬度
window.innerWidth 可視區(qū)寬度,包含滾動條寬度
(2)document.documentElement.clientXXX(兼容ie)
document.documentElement.clientWidth可視區(qū)寬度,不包含滾動條寬度
document.documentElement.clientHeight可視區(qū)高度,不包含滾動條寬度
節(jié)點(diǎn)的種類有幾種,分別是什么?
(1)元素節(jié)點(diǎn):nodeType ===1;
(2)文本節(jié)點(diǎn):nodeType ===3;
(3)屬性節(jié)點(diǎn):nodeType ===2;
innerHTML和outerHTML的區(qū)別
innerHTML(元素內(nèi)包含的內(nèi)容)
outerHTML(自己以及元素內(nèi)的內(nèi)容)
document.write和innerHTML的區(qū)別
document.write 將內(nèi)容寫入頁面,清空替換掉原來的內(nèi)容,會導(dǎo)致重繪
document.innerHTML 將內(nèi)容寫入某個Dom節(jié)點(diǎn),不會重繪
offsetWidth offsetHeight和clientWidth clientHeight的區(qū)別
(1)offsetWidth (content寬度+padding寬度+border寬度)
(2)offsetHeight(content高度+padding高度+border高度)
(3)clientWidth(content寬度+padding寬度)
(4)clientHeight(content高度+padding高度)
DOM操作
(1)創(chuàng)建新節(jié)點(diǎn)
createDocumentFragment() //創(chuàng)建一個DOM片段
createElement() //創(chuàng)建一個具體的元素
createTextNode() //創(chuàng)建一個文本節(jié)點(diǎn)
(2)添加、移除、替換、插入
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有的子節(jié)點(diǎn)前插入一個新的子節(jié)點(diǎn)
(3)查找
getElementsByTagName() //通過標(biāo)簽名稱
getElementsByName() //通過元素的Name屬性的值(IE容錯能力較強(qiáng),會得到一個數(shù)組,其中包括id等于name值的)
getElementById() //通過元素Id,唯一性
BOM 和 DOM 的關(guān)系
BOM全稱Browser Object Model,即瀏覽器對象模型,主要處理瀏覽器窗口和框架。
DOM全稱Document Object Model,即文檔對象模型,是 HTML 和XML 的應(yīng)用程序接口(API),遵循W3C 的標(biāo)準(zhǔn),所有瀏覽器公共遵守的標(biāo)準(zhǔn)。
JS是通過訪問BOM(Browser Object Model)對象來訪問、控制、修改客戶端(瀏覽器),由于BOM的window包含了document,window對象的屬性和方法是直接可以使用而且被感知的,因此可以直接使用window對象的document屬性,通過document屬性就可以訪問、檢索、修改XHTML文檔內(nèi)容與結(jié)構(gòu)。因為document對象又是DOM的根節(jié)點(diǎn)。
可以說,BOM包含了DOM(對象),瀏覽器提供出來給予訪問的是BOM對象,從BOM對象再訪問到DOM對象,從而js可以操作瀏覽器以及瀏覽器讀取到的文檔。
數(shù)組對象有哪些原生方法,列舉一下
pop、push、shift、unshift、splice、reverse、sort、concat、join、slice、toString、indexOf、lastIndexOf、reduce、reduceRight、forEach、map、filter、every、some
pop:刪除并返回數(shù)組最后一個元素(改變原數(shù)組);
push:返回添加完成后的數(shù)組的長度(改變原數(shù)組);
shift:移除并返回數(shù)組的第一個元素(改變原數(shù)組);
unshift:在數(shù)組頭部插入一個元素
slice:slice(下標(biāo),個數(shù))返回裁剪后的數(shù)組(不改變原數(shù)組);
splice:插入,刪除或替換數(shù)組的元素
concat:合并數(shù)組返回組合數(shù)組(不改變原數(shù)組);
join:將數(shù)組用標(biāo)識符鏈接成字符串返回拼接好的字符串(不改變原數(shù)組);
reverse:翻轉(zhuǎn)數(shù)組(改變原數(shù)組);
toString:將數(shù)組轉(zhuǎn)換成一個字符串;
split:把字符串分割開,以數(shù)組方式儲存;
forEach:主要用于遍歷數(shù)組;
every:主要用于檢查數(shù)組中每個元素是否符合函數(shù)的條件,如果其中有一個不符合,則返回false;
indexOf:主要用于在數(shù)組中查找元素,并把元素的位置返回來。
字符串方法
charAt():根據(jù)下標(biāo)找到對應(yīng)值
charCodeAt():通過下標(biāo)值找到對應(yīng)字符Unicode編碼
indexOf():通過字符查找對應(yīng)下標(biāo)(首次出現(xiàn))
lastIndexOf():通過字符找最后一次出現(xiàn)的下標(biāo)值
slice():截取字符串,2個參數(shù),(起始位置,結(jié)束位置)
split():把字符串按分隔符分割成數(shù)組
substring():截取字符串,(起始位置,結(jié)束位置)
substr():截取指定位置和長度的字符串,(起始位置,長度)
toLowerCase():字符串轉(zhuǎn)為小寫
toUpperCase():字符串轉(zhuǎn)成大寫
trim():去掉字符串前后所有空格
JS中的Array.splice()和Array.slice()方法有什么區(qū)別
話不多說,來看第一個例子:
var arr=[0,1,2,3,4,5,6,7,8,9];//設(shè)置一個數(shù)組console.log(arr.slice(2,7));//2,3,4,5,6console.log(arr.splice(2,7));//2,3,4,5,6,7,8//由此我們簡單推測數(shù)量兩個函數(shù)參數(shù)的意義,slice(start,end)第一個參數(shù)表示開始位置,第二個表示截取到的位置(不包含該位置)splice(start,length)第一個參數(shù)開始位置,第二個參數(shù)截取長度
接著看第二個:
var x=y=[0,1,2,3,4,5,6,7,8,9]console.log(x.slice(2,5));//2,3,4console.log(x);[0,1,2,3,4,5,6,7,8,9]原數(shù)組并未改變//接下來用同樣方式測試spliceconsole.log(y.splice(2,5));//2,3,4,5,6console.log(y);//[0,1,7,8,9]顯示原數(shù)組中的數(shù)值被剔除掉了
slice和splice雖然都是對于數(shù)組對象進(jìn)行截取,但是二者還是存在明顯區(qū)別,函數(shù)參數(shù)上slice和splice第一個參數(shù)都是截取開始位置,slice第二個參數(shù)是截取的結(jié)束位置(不包含),而splice第二個參數(shù)(表示這個從開始位置截取的長度),slice不會對原數(shù)組產(chǎn)生變化,而splice會直接剔除原數(shù)組中的截取數(shù)據(jù)!
如何在JS中動態(tài)添加/刪除對象的屬性?
咱們可以使用object.property_name = value向?qū)ο筇砑訉傩?,delete object.property_name 用于刪除屬性。
例如:
let user = new Object();// adding a propertyuser.name='Anil';user.age =25;console.log(user);delete user.age;console.log(user);
圖片的懶加載和預(yù)加載
預(yù)加載:常用的是new Image();,設(shè)置其src來實現(xiàn)預(yù)載,再使用onload方法回調(diào)預(yù)載完成事件。
function loadImage(url, callback){var img = new Image(); //創(chuàng)建一個Image對象,實現(xiàn)圖片預(yù)下載img.src = url;if (img.complete){// 如果圖片已經(jīng)存在于瀏覽器緩存,直接調(diào)用回調(diào)函數(shù)callback.call(img);return; // 直接返回,不用再處理onload事件}img.onload = function (){//圖片下載完畢時異步調(diào)用callback函數(shù)。callback.call(img);//將回調(diào)函數(shù)的this替換為Image對象 ,如果你直接用img.width的時候,圖片還沒有完全下載下來};}
懶加載:主要目的是作為服務(wù)器前端的優(yōu)化,緩解服務(wù)器前端壓力,一次性請求次數(shù)減少或延遲請求。實現(xiàn)方式:
1.第一種是純粹的延遲加載,使用setTimeOut、setInterval進(jìn)行加載延遲.
2.第二種是條件加載,符合某些條件,或觸發(fā)了某些事件才開始異步下載。
3.第三種是可視區(qū)加載,即僅加載用戶可以看到的區(qū)域,這個主要由監(jiān)控滾動條來實現(xiàn),一般會在距用戶看到某圖片前一定距離遍開始加載,這樣能保證用戶拉下時正好能看到圖片。
什么是回調(diào)
回調(diào)函數(shù)是作為參數(shù)或選項傳遞給某個方法的普通JS函數(shù)。它是一個函數(shù),在另一個函數(shù)完成執(zhí)行后執(zhí)行,因此稱為回調(diào)。
在JS中,函數(shù)是對象,因此,函數(shù)可以接受函數(shù)作為參數(shù),并且可以由其他函數(shù)返回。
JS中的宿主對象與原生對象有何不同?
宿主對象:這些是運(yùn)行環(huán)境提供的對象。這意味著它們在不同的環(huán)境下是不同的。例如,瀏覽器包含像windows這樣的對象,但是Node.js環(huán)境提供像Node List這樣的對象。
原生對象:這些是JS中的內(nèi)置對象。它們也被稱為全局對象,因為如果使用JS,內(nèi)置對象不受是運(yùn)行環(huán)境影響。
漸進(jìn)增強(qiáng)與優(yōu)雅降級
漸進(jìn)增強(qiáng):針對低版本瀏覽器進(jìn)行構(gòu)建頁面,保證最基本的功能,然后再針對高級瀏覽器進(jìn)行效果、交互等改進(jìn),達(dá)到更好的用戶體驗。
優(yōu)雅降級:一開始就構(gòu)建完整的功能,然后再針對低版本瀏覽器進(jìn)行兼容。
Web Worker和Web Socket?
web socket:在一個單獨(dú)的持久連接上提供全雙工、雙向的通信。使用自定義的協(xié)議(ws://、wss://),同源策略對web socket不適用。
web worker:運(yùn)行在后臺的JavaScript,不影響頁面的性能。
創(chuàng)建worker:var worker = new Worker(url);
向worker發(fā)送數(shù)據(jù):worker.postMessage(data);
接收worker返回的數(shù)據(jù):worker.onmessage
終止一個worker的執(zhí)行:worker.terminate();
瀏覽器的線程
JS引擎線程:解釋執(zhí)行JS代碼、用戶輸入、網(wǎng)絡(luò)請求等
GUI線程(渲染線程):繪制用戶界面、與JS主線程互斥
HTTP網(wǎng)絡(luò)請求線程:處理用戶的GET、POST等請求,等拿到返回結(jié)果后,將回調(diào)函數(shù)推入事件隊列
定時器觸發(fā)線程:setTimeout、setInterval等待時間結(jié)束后,將執(zhí)行函數(shù)推入事件隊列中
事件處理線程:將click、mouse、input等交互事件發(fā)生后,將事件處理函數(shù)推入事件隊列中
js 執(zhí)行機(jī)制、事件循環(huán)
JavaScript 語言的一大特點(diǎn)就是單線程,同一個時間只能做一件事。單線程就意味著,所有任務(wù)需要排隊,前一個任務(wù)結(jié)束,才會執(zhí)行后一個任務(wù)。如果前一個任務(wù)耗時很長,后一個任務(wù)就不得不一直等著。
JavaScript 語言的設(shè)計者意識到這個問題,將所有任務(wù)分成兩種,一種是同步任務(wù)(synchronous),另一種是異步任務(wù)(asynchronous),在所有同步任務(wù)執(zhí)行完之前,任何的異步任務(wù)是不會執(zhí)行的。
當(dāng)我們打開網(wǎng)站時,網(wǎng)頁的渲染過程就是一大堆同步任務(wù),比如頁面骨架和頁面元素的渲染。而像加載圖片音樂之類占用資源大耗時久的任務(wù),就是異步任務(wù)。
JS 異步有一個機(jī)制,就是遇到宏任務(wù),先執(zhí)行宏任務(wù),將宏任務(wù)放入 Event Queue,然后再執(zhí)行微任務(wù),將微任務(wù)放入 Event Queue,但是,這兩個 Queue 不是一個 Queue。
當(dāng)你往外拿的時候先從微任務(wù)里拿這個回調(diào)函數(shù),然后再從宏任務(wù)的 Queue 拿宏任務(wù)的回調(diào)函數(shù)。
宏任務(wù):整體代碼 script,setTimeout,setInterval
微任務(wù):Promise,process.nextTick
JS為什么要區(qū)分微任務(wù)和宏任務(wù)
(1)js是單線程的,但是分同步異步
(2)微任務(wù)和宏任務(wù)皆為異步任務(wù),它們都屬于一個隊列
(3)宏任務(wù)一般是:script,setTimeout,setInterval、setImmediate
(4)微任務(wù):原生Promise
(5)遇到微任務(wù),先執(zhí)行微任務(wù),執(zhí)行完后如果沒有微任務(wù),就執(zhí)行下一個宏任務(wù),如果有微任務(wù),就按順序一個一個執(zhí)行微任務(wù)
減少頁面加載時間的方法
優(yōu)化圖片
圖像格式的選擇(GIF:提供的顏色較少,可用在一些對顏色要求不高的地方)
優(yōu)化css(壓縮合并css,如margin-top,margin-left...)
網(wǎng)址后加斜杠(如www.campr.com/目錄,會判斷這個“目錄是什么文件類型,或者是目錄。)
標(biāo)明高度和寬度(如果瀏覽器沒有找到這兩個參數(shù),它需要一邊下載圖片一邊計算大小,如果圖片很多,瀏覽器需要不斷地調(diào)整頁面。這不但影響速度,也影響瀏覽體驗。當(dāng)瀏覽器知道了高度和寬度參數(shù)后,即使圖片暫時無法顯示,頁面上也會騰出圖片的空位,然后繼續(xù)加載后面的內(nèi)容。從而加載時間快了,瀏覽體驗也更好了。)
減少http請求(合并文件,合并圖片)。
線程與進(jìn)程的區(qū)別
一個程序至少有一個進(jìn)程,一個進(jìn)程至少有一個線程。線程的劃分尺度小于進(jìn)程,使得多線程程序的并發(fā)性高。
另外,進(jìn)程在執(zhí)行過程中擁有獨(dú)立的內(nèi)存單元,而多個線程共享內(nèi)存,從而極大地提高了程序的運(yùn)行效率。
線程在執(zhí)行過程中與進(jìn)程還是有區(qū)別的。每個獨(dú)立的線程有一個程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口。
但是線程不能夠獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個線程執(zhí)行控制。
從邏輯角度來看,多線程的意義在于一個應(yīng)用程序中,有多個執(zhí)行部分可以同時執(zhí)行。但操作系統(tǒng)并沒有將多個線程看做多個獨(dú)立的應(yīng)用,來實現(xiàn)進(jìn)程的調(diào)度和管理以及資源分配。這就是進(jìn)程和線程的重要區(qū)別。
說說你對語義化的理解?
1.去掉或樣式丟失的時候能讓頁面呈現(xiàn)清晰的結(jié)構(gòu):html本身是沒有表現(xiàn)的,我們看到例如<h1>是粗體,字體大小2em,加粗;<strong>是加粗的,不要認(rèn)為這是html的表現(xiàn),這些其實html默認(rèn)的css樣式在起作用。
所以去掉或樣式丟失的時候能讓頁面呈現(xiàn)清晰的結(jié)構(gòu)不是語義化的HTML結(jié)構(gòu)的優(yōu)點(diǎn),但是瀏覽器都有有默認(rèn)樣式,默認(rèn)樣式的目的也是為了更好的表達(dá)html的語義,可以說瀏覽器的默認(rèn)樣式和語義化的HTML結(jié)構(gòu)是不可分割的。
2.屏幕閱讀器(如果訪客有視障)會完全根據(jù)你的標(biāo)記來“讀”你的網(wǎng)頁。
3.PDA、手機(jī)等設(shè)備可能無法像普通電腦的瀏覽器一樣來渲染網(wǎng)頁(通常是因為這些設(shè)備對CSS的支持較弱)。
4.有利于SEO:和搜索引擎建立良好溝通,有助于爬蟲抓取更多的有效信息:爬蟲依賴于標(biāo)簽來確定上下文和各個關(guān)鍵字的權(quán)重。
5.便于團(tuán)隊開發(fā)和維護(hù),語義化更具可讀性,是下一步吧網(wǎng)頁的重要動向,遵循W3C標(biāo)準(zhǔn)的團(tuán)隊都遵循這個標(biāo)準(zhǔn),可以減少差異化。
為什么利用多個域名來提供網(wǎng)站資源會更有效?
1.CDN緩存更方便
2.突破瀏覽器并發(fā)限制(一般每個域名建立的鏈接不超過6個)
3.Cookieless,節(jié)省帶寬,尤其是上行帶寬一般比下行要慢
4.對于UGC的內(nèi)容和主站隔離,防止不必要的安全問題(上傳js竊取主站cookie之類的)。正是這個原因要求用戶內(nèi)容的域名必須不是自己主站的子域名,而是一個完全獨(dú)立的第三方域名。
5.數(shù)據(jù)做了劃分,甚至切到了不同的物理集群,通過子域名來分流比較省事。這個可能被用的不多。
PS:關(guān)于Cookie的問題,帶寬是次要的,安全隔離才是主要的。關(guān)于多域名,也不是越多越好,雖然服務(wù)器端可以做泛解釋,瀏覽器做dns解釋也是耗時間的,而且太多域名,如果要走h(yuǎn)ttps的話,還有要多買證書和部署的問題。
你如何組織,優(yōu)化自己的代碼?
對內(nèi):模塊模式;對外:繼承
代碼重用
避免全局變量(命名空間,封閉空間,模塊化mvc…)
拆分函數(shù)避免函數(shù)過于臃腫
注釋
前端模塊化和組件化
模塊化:可復(fù)用,側(cè)重的功能的封裝,主要是針對Javascript代碼,隔離、組織復(fù)制的javascript代碼,將它封裝成一個個具有特定功能的的模塊。
組件化:可復(fù)用,更多關(guān)注的UI部分,頁面的每個部件,比如頭部,彈出框甚至確認(rèn)按鈕都可以成為一個組件,每個組件有獨(dú)立的HTML、css、js代碼。
http的cache機(jī)制及作用?
定義:瀏覽器緩存(Browser Caching)是為了加速瀏覽,瀏覽器在用戶磁盤上對最近請求過的文檔進(jìn)行存儲,當(dāng)訪問者再次請求這個頁面時,瀏覽器就可以從本地磁盤顯示文檔,這樣就可以加速頁面的閱覽。
cache的作用:
1、減少延遲,讓你的網(wǎng)站更快,提高用戶體驗。
2、避免網(wǎng)絡(luò)擁塞,減少請求量,減少輸出帶寬。
實現(xiàn)手段:
Cache-Control中的max-age是實現(xiàn)內(nèi)容cache的主要手段,共有3種常用策略:max-age和Last-Modified(If-Modified-Since)的組合、僅max-age、max-age和ETag的組合。
對于強(qiáng)制緩存,服務(wù)器通知瀏覽器一個緩存時間,在緩存時間內(nèi),下次請求,直接用緩存,不在時間內(nèi),執(zhí)行比較緩存策略。
對于比較緩存,將緩存信息中的Etag和Last-Modified通過請求發(fā)送給服務(wù)器,由服務(wù)器校驗,返回304狀態(tài)碼時,瀏覽器直接使用緩存。
HTTP 常見的狀態(tài)碼
400 客戶端請求有語法錯誤,不能被服務(wù)器所理解
403 服務(wù)器收到請求但是拒絕提供服務(wù)|
200 客戶端請求成功|
404 請求資源不存在 eg:輸入錯誤的URL |
500 服務(wù)器發(fā)生不可預(yù)期的錯誤 |
503 服務(wù)器當(dāng)前不能處理客戶端請求,一段時間后可能恢復(fù)正常
JS 中的主要有哪幾類錯誤
JS有三類的錯誤:
加載時錯誤:加載web頁面時出現(xiàn)的錯誤(如語法錯誤)稱為加載時錯誤,它會動態(tài)生成錯誤。
運(yùn)行時錯誤:由于濫用html語言中的命令而導(dǎo)致的錯誤。
邏輯錯誤:這些錯誤是由于對具有不同操作的函數(shù)執(zhí)行了錯誤的邏輯而導(dǎo)致的
列出JS中的一些設(shè)計模式
設(shè)計模式是軟件設(shè)計中常見問題的通用可重用解決方案,以下是一些設(shè)計模式是:
創(chuàng)建模式:該模式抽象了對象實例化過程。
結(jié)構(gòu)型模式:這些模式處理不同的類和對象以提供新功能。
行為模式:也稱發(fā)布-訂閱模式,定義了一個被觀察者和多個觀察者的、一對多的對象關(guān)系。
并行設(shè)計模式:這些模式處理多線程編程范例。
架構(gòu)設(shè)計模式:這些模式用于處理架構(gòu)設(shè)計。
es6的新特性
const let
模板字符串
箭頭函數(shù)
函數(shù)的參數(shù)默認(rèn)值
對象和數(shù)組解構(gòu)
for...of 和 for...in
解釋一下什么是箭頭函數(shù)?
箭頭函數(shù)是在es6或更高版本中編寫函數(shù)表達(dá)式的簡明方法。箭頭函數(shù)不能用作構(gòu)造函數(shù),也不支持this,arguments,super或new.target關(guān)鍵字,它最適合非方法函數(shù)。
通常,箭頭函數(shù)看起來像 const function_name =()=> {}。
const greet=()=>{console.log('hello');}greet();
普通函數(shù)和箭頭函數(shù)的區(qū)別
1、普通函數(shù)
可以通過bind、call、apply改變this指向
可以使用new
2、箭頭函數(shù)
本身沒有this指向,
它的this在定義的時候繼承自外層第一個普通函數(shù)的this
被繼承的普通函數(shù)的this指向改變,箭頭函數(shù)的this指向會跟著改變
箭頭函數(shù)外層沒有普通函數(shù)時,this指向window
不能通過bind、call、apply改變this指向
使用new調(diào)用箭頭函數(shù)會報錯,因為箭頭函數(shù)沒有constructor
解釋一下什么是 promise ?
promise是js中的一個對象,用于生成可能在將來產(chǎn)生結(jié)果的值。 值可以是已解析的值,也可以是說明為什么未解析該值的原因。
promise 可以有三種狀態(tài):
pending:初始狀態(tài),既不是成功也不是失敗
fulfilled:意味著操作完全成功
rejected:意味著操作失敗
一個等待狀態(tài)的promise對象能夠成功后返回一個值,也能失敗后帶回一個錯誤,當(dāng)這兩種情況發(fā)生的時候,處理函數(shù)會排隊執(zhí)行通過then方法會被調(diào)用。
resolve和reject分別是兩個函數(shù),當(dāng)在回調(diào)中調(diào)用時,會改變promise實例的狀態(tài),resolve改變狀態(tài)為成功,reject為失敗。
手寫promise
function Promise(exector) {let _this = this;//status表示一種狀態(tài)let status = “pending“;let value = undefined;let reason = undefined;//成功function resolve(value) {if (status == “pending“) {_this.value = value;_this.status = “resolve“;}}//執(zhí)行失敗function reject(value) {if (status == “pending“) {_this.value = value;_this.status = “reject“}}//異步操作try {exector(resolve, reject)} catch (e) {reject(e)}//測試thenPromise.prototype.then = function(reject, resolve) {let _this = this;if (this.status == “resolve“) {reject(_this.value)}if (this.status == “reject“) {resolve(_this.reason)}}}//new Promise測試let promise = new Promise((reject,resolve)=>{resolve(“return resolve“);});promise.then(data => {console.log(`success${data}`);}, err => {console.log(`err${data}`);})
promise、async有什么區(qū)別
1、什么是Async/Await
async/await是寫異步代碼的新方式,使用的方式看起來像同步
async/await是基于Promise實現(xiàn)的,它不能用于普通的回調(diào)函數(shù)。
2、什么是promise
為了解決異步嵌套而產(chǎn)生,讓代碼更易于理解
區(qū)別:async/await讓代碼更像同步,進(jìn)一步優(yōu)化了代碼
介紹各種異步方案
(1)回調(diào)函數(shù)(異步回調(diào))
回調(diào)是一個函數(shù)被作為一個參數(shù)傳遞到另一個函數(shù)里,在那個函數(shù)執(zhí)行完后再執(zhí)行。
(2)promise
promise對象是commonJS工作組提出的一種規(guī)范,一種模式,目的是為了異步編程提供統(tǒng)一接口。
(3)async/await
(4)事件監(jiān)聽
采用事件驅(qū)動模式。任務(wù)的執(zhí)行不取決于代碼的順序,而取決于某個事件是否發(fā)生。
(5)發(fā)布/訂閱
module.exports 和 exports 之間有什么區(qū)別?
module和exports是Node.js給每個js文件內(nèi)置的兩個對象。可以通過console.log(module)和console.log(exports)打印出來。如果你在main.js中寫入下面兩行,然后運(yùn)行$ node main.js:
console.log(exports);//輸出:{}console.log(module);//輸出:Module {..., exports: {}, ...} (注:...代表省略了其他一些屬性)
從打印咱們可以看出,module.exports和exports一開始都是一個空對象{},實際上,這兩個對象指向同一塊內(nèi)存。這也就是說module.exports和exports是等價的(有個前提:不去改變它們指向的內(nèi)存地址)。
例如:exports.age = 18和module.export.age = 18,這兩種寫法是一致的(都相當(dāng)于給最初的空對象{}添加了一個屬性,通過require得到的就是{age: 18})。
import 和 exports 是什么?
import和exports 幫助咱們編寫模塊化的JS代碼。使用import和exports,咱們可以將代碼分割成多個文件。import只允許獲取文件的某些特定變量或方法??梢詫?dǎo)入模塊導(dǎo)出的方法或變量。
//index.jsimport name,age from './person';console.log(name);console.log(age);//person.jslet name ='Sharad', occupation='developer', age =26;export { name, age};
學(xué)習(xí)更多技能
請點(diǎn)擊下方公眾號
![]()

