摘要:線程的劃分尺度小于進(jìn)程,使得多線程程序的并發(fā)性高。線程在執(zhí)行過(guò)程中與進(jìn)程還是有區(qū)別的。每個(gè)獨(dú)立的線程有一個(gè)程序運(yùn)行的入口順序執(zhí)行序列和程序的出口。從邏輯角度來(lái)看,多線程的意義在于一個(gè)應(yīng)用程序中,有多個(gè)執(zhí)行部分可以同時(shí)執(zhí)行。
前言
本文講解 56 道 JavaScript 和 ES6+ 面試題的內(nèi)容。
復(fù)習(xí)前端面試的知識(shí),是為了鞏固前端的基礎(chǔ)知識(shí),最重要的還是平時(shí)的積累!
注意:文章的題與題之間用下劃線分隔開(kāi),答案僅供參考。
前端硬核面試專題的完整版在此:前端硬核面試專題,包含:HTML + CSS + JS + ES6 + Webpack + Vue + React + Node + HTTPS + 數(shù)據(jù)結(jié)構(gòu)與算法 + Git 。
JavaScript常見(jiàn)的瀏覽器內(nèi)核有哪些 ?
Trident 內(nèi)核:IE, 360,搜狗瀏覽器 MaxThon、TT、The World,等。[又稱 MSHTML]
Gecko 內(nèi)核:火狐,F(xiàn)F,MozillaSuite / SeaMonkey 等
Presto 內(nèi)核:Opera7 及以上。[Opera 內(nèi)核原為:Presto,現(xiàn)為:Blink]
Webkit 內(nèi)核:Safari,Chrome 等。 [ Chrome 的:Blink(WebKit 的分支)]
mouseenter 和 mouseover 的區(qū)別?
不論鼠標(biāo)指針穿過(guò)被選元素或其子元素,都會(huì)觸發(fā) mouseover 事件,對(duì)應(yīng) mouseout。
只有在鼠標(biāo)指針穿過(guò)被選元素時(shí),才會(huì)觸發(fā) mouseenter 事件,對(duì)應(yīng) mouseleave。
用正則表達(dá)式匹配字符串,以字母開(kāi)頭,后面是數(shù)字、字符串或者下劃線,長(zhǎng)度為 9 - 20?
var re=new RegExp("^[a-zA-Z][a-zA-Z0-9_]{9,20}$");
手機(jī)號(hào)碼校驗(yàn)
function checkPhone(){ var phone = document.getElementById("phone").value; if(!(/^1(3|4|5|7|8)d{9}$/.test(phone))){ alert("手機(jī)號(hào)碼有誤,請(qǐng)重填"); return false; } }
^1(3|4|5|7|8)d{9}$,表示以 1 開(kāi)頭,第二位可能是 3/4/5/7/8 等的任意一個(gè),在加上后面的 d 表示數(shù)字 [0-9] 的 9 位,總共加起來(lái) 11 位結(jié)束。
手機(jī)號(hào)碼格式驗(yàn)證方法(正則表達(dá)式驗(yàn)證)支持最新電信 199, 移動(dòng) 198, 聯(lián)通 166
// 手機(jī)號(hào)碼校驗(yàn)規(guī)則 let valid_rule = /^(13[0-9]|14[5-9]|15[012356789]|166|17[0-8]|18[0-9]|19[8-9])[0-9]{8}$/; if ( ! valid_rule.test(phone_number)) { alert("手機(jī)號(hào)碼格式有誤"); return false; }
這樣 phone_number 就是取到的手機(jī)號(hào)碼,即可!
js 字符串兩邊截取空白的 trim 的原型方法的實(shí)現(xiàn)
js 中本身是沒(méi)有 trim 函數(shù)的。
// 刪除左右兩端的空格 function trim(str){ return str.replace(/(^s*)|(s*$)/g, ""); } // 刪除左邊的空格 /(^s*)/g // 刪除右邊的空格 /(s*$)/g
介紹一下你對(duì)瀏覽器內(nèi)核的理解 ?
內(nèi)核主要分成兩部分:渲染引擎(layout engineer 或 Rendering Engine) 和 JS 引擎。
渲染引擎
負(fù)責(zé)取得網(wǎng)頁(yè)的內(nèi)容(HTML、XML、圖像等等)、整理訊息(例如加入 CSS 等),以及計(jì)算網(wǎng)頁(yè)的顯示方式,然后會(huì)輸出至顯示器或打印機(jī)。
瀏覽器的內(nèi)核的不同對(duì)于網(wǎng)頁(yè)的語(yǔ)法解釋會(huì)有不同,所以渲染的效果也不相同。
所有網(wǎng)頁(yè)瀏覽器、電子郵件客戶端以及其它需要編輯、顯示網(wǎng)絡(luò)內(nèi)容的應(yīng)用程序都需要內(nèi)核。
JS 引擎
解析和執(zhí)行 javascript 來(lái)實(shí)現(xiàn)網(wǎng)頁(yè)的動(dòng)態(tài)效果。
最開(kāi)始渲染引擎和 JS 引擎并沒(méi)有區(qū)分的很明確,后來(lái) JS 引擎越來(lái)越獨(dú)立,內(nèi)核就傾向于只指渲染引擎。
哪些常見(jiàn)操作會(huì)造成內(nèi)存泄漏 ?
內(nèi)存泄漏指任何對(duì)象在您不再擁有或需要它之后仍然存在。
垃圾回收器定期掃描對(duì)象,并計(jì)算引用了每個(gè)對(duì)象的其他對(duì)象的數(shù)量。如果一個(gè)對(duì)象的引用數(shù)量為 0(沒(méi)有其他對(duì)象引用過(guò)該對(duì)象),或?qū)υ搶?duì)象的惟一引用是循環(huán)的,那么該對(duì)象的內(nèi)存即可回收。
setTimeout 的第一個(gè)參數(shù)使用字符串而非函數(shù)的話,會(huì)引發(fā)內(nèi)存泄漏。
閉包、控制臺(tái)日志、循環(huán)(在兩個(gè)對(duì)象彼此引用且彼此保留時(shí),就會(huì)產(chǎn)生一個(gè)循環(huán))。
線程與進(jìn)程的區(qū)別 ?
一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程。
線程的劃分尺度小于進(jìn)程,使得多線程程序的并發(fā)性高。
另外,進(jìn)程在執(zhí)行過(guò)程中擁有獨(dú)立的內(nèi)存單元,而多個(gè)線程共享內(nèi)存,從而極大地提高了程序的運(yùn)行效率。
線程在執(zhí)行過(guò)程中與進(jìn)程還是有區(qū)別的。
每個(gè)獨(dú)立的線程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口。但是線程不能夠獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線程執(zhí)行控制。
從邏輯角度來(lái)看,多線程的意義在于一個(gè)應(yīng)用程序中,有多個(gè)執(zhí)行部分可以同時(shí)執(zhí)行。
但操作系統(tǒng)并沒(méi)有將多個(gè)線程看做多個(gè)獨(dú)立的應(yīng)用,來(lái)實(shí)現(xiàn)進(jìn)程的調(diào)度和管理以及資源分配。這就是進(jìn)程和線程的重要區(qū)別。
eval() 函數(shù)有什么用 ?
eval() 函數(shù)可計(jì)算某個(gè)字符串,并執(zhí)行其中的的 JavaScript 代碼。
實(shí)現(xiàn)一個(gè)方法,使得:add(2, 5) 和 add(2)(5) 的結(jié)果都為 7
var add = function (x, r) { if (arguments.length == 1) { return function (y) { return x + y; }; } else { return x + r; } }; console.log(add(2)(5)); // 7 console.log(add(2,5)); // 7
alert(1 && 2) 和 alert(1 || 0) 的結(jié)果是 ?
alert(1 &&2 ) 的結(jié)果是 2
只要 “&&” 前面是 false,無(wú)論 “&&” 后面是 true 還是 false,結(jié)果都將返 “&&” 前面的值;
只要 “&&” 前面是 true,無(wú)論 “&&” 后面是 true 還是 false,結(jié)果都將返 “&&” 后面的值;
alert(0 || 1) 的結(jié)果是 1
只要 “||” 前面為 false,不管 “||” 后面是 true 還是 false,都返回 “||” 后面的值。
只要 “||” 前面為 true,不管 “||” 后面是 true 還是 false,都返回 “||” 前面的值。
只要記住 0 與 任何數(shù)都是 0,其他反推。
下面的輸出結(jié)果是 ?
var out = 25, inner = { out: 20, func: function () { var out = 30; return this.out; } }; console.log((inner.func, inner.func)()); console.log(inner.func()); console.log((inner.func)()); console.log((inner.func = inner.func)());
結(jié)果:25,20,20,25
代碼解析:這道題的考點(diǎn)分兩個(gè)
作用域
運(yùn)算符(賦值預(yù)算,逗號(hào)運(yùn)算)
先看第一個(gè)輸出:25,因?yàn)?( inner.func, inner.func ) 是進(jìn)行逗號(hào)運(yùn)算符,逗號(hào)運(yùn)算符就是運(yùn)算前面的 ”,“ 返回最后一個(gè),舉個(gè)栗子
var i = 0, j = 1, k = 2; console.log((i++, j++, k)) // 返回的是 k 的值 2 ,如果寫(xiě)成 k++ 的話 這里返回的就是 3 console.log(i); // 1 console.log(j); // 2 console.log(k); // 2
回到原題 ( inner.func, inner.func ) 就是返回 inner.func ,而 inner.func 只是一個(gè)匿名函數(shù)
function () { var out = 30; return this.out; }
而且這個(gè)匿名函數(shù)是屬于 window 的,則變成了
(function () { var out = 30; return this.out; })()
此刻的 this => window
所以 out 是 25。
第二和第三個(gè) console.log 的作用域都是 inner,也就是他們執(zhí)行的其實(shí)是 inner.func();
inner 作用域中是有 out 變量的,所以結(jié)果是 20。
第四個(gè) console.log 考查的是一個(gè)等號(hào)運(yùn)算 inner.func = inner.func ,其實(shí)返回的是運(yùn)算的結(jié)果,
舉個(gè)栗子
var a = 2, b = 3; console.log(a = b) // 輸出的是 3
所以 inner.func = inner.func 返回的也是一個(gè)匿名函數(shù)
function () { var out = 30; return this.out; }
此刻,道理就和第一個(gè) console.log 一樣了,輸出的結(jié)果是 25。
下面程序輸出的結(jié)果是 ?
if (!("a" in window)) { var a = 1; } alert(a);
代碼解析:如果 window 不包含屬性 a,就聲明一個(gè)變量 a,然后賦值為 1。
你可能認(rèn)為 alert 出來(lái)的結(jié)果是 1,然后實(shí)際結(jié)果是 “undefined”。
要了解為什么,需要知道 JavaScript 里的 3 個(gè)概念。
首先,所有的全局變量都是 window 的屬性,語(yǔ)句 var a = 1; 等價(jià)于 window.a = 1;
你可以用如下方式來(lái)檢測(cè)全局變量是否聲明:"變量名稱" in window。
第二,所有的變量聲明都在范圍作用域的頂部,看一下相似的例子:
alert("b" in window); var b;
此時(shí),盡管聲明是在 alert 之后,alert 彈出的依然是 true,這是因?yàn)?JavaScript 引擎首先會(huì)掃描所有的變量聲明,然后將這些變量聲明移動(dòng)到頂部,最終的代碼效果是這樣的:
var a; alert("a" in window);
這樣看起來(lái)就很容易解釋為什么 alert 結(jié)果是 true 了。
第三,你需要理解該題目的意思是,變量聲明被提前了,但變量賦值沒(méi)有,因?yàn)檫@行代碼包括了變量聲明和變量賦值。
你可以將語(yǔ)句拆分為如下代碼:
var a; //聲明 a = 1; //初始化賦值
當(dāng)變量聲明和賦值在一起用的時(shí)候,JavaScript 引擎會(huì)自動(dòng)將它分為兩部以便將變量聲明提前,
不將賦值的步驟提前,是因?yàn)樗锌赡苡绊懘a執(zhí)行出不可預(yù)期的結(jié)果。
所以,知道了這些概念以后,重新回頭看一下題目的代碼,其實(shí)就等價(jià)于:
var a; if (!("a" in window)) { a = 1; } alert(a);
這樣,題目的意思就非常清楚了:首先聲明 a,然后判斷 a 是否在存在,如果不存在就賦值為1,很明顯 a 永遠(yuǎn)在 window 里存在,這個(gè)賦值語(yǔ)句永遠(yuǎn)不會(huì)執(zhí)行,所以結(jié)果是 undefined。
提前這個(gè)詞語(yǔ)顯得有點(diǎn)迷惑了,你可以理解為:預(yù)編譯。
下面程序輸出的結(jié)果是 ?
var a = 1; var b = function a(x) { x && a(--x); }; alert(a);
這個(gè)題目看起來(lái)比實(shí)際復(fù)雜,alert 的結(jié)果是 1。
這里依然有 3 個(gè)重要的概念需要我們知道。
首先,第一個(gè)是 變量聲明在進(jìn)入執(zhí)行上下文就完成了;
第二個(gè)概念就是函數(shù)聲明也是提前的,所有的函數(shù)聲明都在執(zhí)行代碼之前都已經(jīng)完成了聲明,和變量聲明一樣。
澄清一下,函數(shù)聲明是如下這樣的代碼:
function functionName(arg1, arg2){ //函數(shù)體 }
如下不是函數(shù),而是函數(shù)表達(dá)式,相當(dāng)于變量賦值:
var functionName = function(arg1, arg2){ //函數(shù)體 };
澄清一下,函數(shù)表達(dá)式?jīng)]有提前,就相當(dāng)于平時(shí)的變量賦值。
第三需要知道的是,函數(shù)聲明會(huì)覆蓋變量聲明,但不會(huì)覆蓋變量賦值。
為了解釋這個(gè),我們來(lái)看一個(gè)例子:
function value(){ return 1; } var value; alert(typeof value); //"function"
盡管變量聲明在下面定義,但是變量 value 依然是 function,也就是說(shuō)這種情況下,函數(shù)聲明的優(yōu)先級(jí)高于變量聲明的優(yōu)先級(jí),但如果該變量 value 賦值了,那結(jié)果就完全不一樣了:
function value(){ return 1; } var value = 1; alert(typeof value); //"number"
該 value 賦值以后,變量賦值初始化就覆蓋了函數(shù)聲明。
重新回到題目,這個(gè)函數(shù)其實(shí)是一個(gè)有名函數(shù)表達(dá)式,函數(shù)表達(dá)式不像函數(shù)聲明一樣可以覆蓋變量聲明,但你可以注意到,變量 b 是包含了該函數(shù)表達(dá)式,而該函數(shù)表達(dá)式的名字是 a。不同的瀏覽器對(duì) a 這個(gè)名詞處理有點(diǎn)不一樣,在 IE 里,會(huì)將 a 認(rèn)為函數(shù)聲明,所以它被變量初始化覆蓋了,就是說(shuō)如果調(diào)用 a(–x) 的話就會(huì)出錯(cuò),而其它瀏覽器在允許在函數(shù)內(nèi)部調(diào)用 a(–x),因?yàn)檫@時(shí)候 a 在函數(shù)外面依然是數(shù)字。
基本上,IE 里調(diào)用 b(2) 的時(shí)候會(huì)出錯(cuò),但其它瀏覽器則返回 undefined。
理解上述內(nèi)容之后,該題目換成一個(gè)更準(zhǔn)確和更容易理解的代碼應(yīng)該像這樣:
var a = 1, b = function(x) { x && b(--x); }; alert(a);
這樣的話,就很清晰地知道為什么 alert 的總是 1 了。
下面程序輸出的結(jié)果是 ?
function a(x) { return x * 2; } var a; alert(a);
alert 的值是下面的函數(shù)
function a(x) { return x * 2; }
這個(gè)題目比較簡(jiǎn)單:即函數(shù)聲明和變量聲明的關(guān)系和影響,遇到同名的函數(shù)聲明,不會(huì)重新定義。
下面程序輸出的結(jié)果是 ?
function b(x, y, a) { arguments[2] = 10; alert(a); } b(1, 2, 3);
結(jié)果為 10。
活動(dòng)對(duì)象是在進(jìn)入函數(shù)上下文時(shí)刻被創(chuàng)建的,它通過(guò)函數(shù)的 arguments 屬性初始化。
三道判斷輸出的題都是經(jīng)典的題
var a = 4; function b() { a = 3; console.log(a); function a(){}; } b();
明顯輸出是 3,因?yàn)槔锩嫘薷牧?a 這個(gè)全局變量,那個(gè) function a(){} 是用來(lái)干擾的,雖然函數(shù)聲明會(huì)提升,就被 a 給覆蓋掉了,這是我的理解。
不記得具體的,就類似如下
var baz = 3; var bazz ={ baz: 2, getbaz: function() { return this.baz } } console.log(bazz.getbaz()) var g = bazz.getbaz; console.log(g()) ;
第一個(gè)輸出是 2,第二個(gè)輸出是 3。
這題考察的就是 this 的指向,函數(shù)作為對(duì)象本身屬性調(diào)用的時(shí)候,this 指向?qū)ο螅鳛槠胀ê瘮?shù)調(diào)用的時(shí)候,就指向全局了。
還有下面的題:
var arr = [1,2,3,4,5]; for(var i = 0; i < arr.length; i++){ arr[i] = function(){ alert(i) } } arr[3]();
典型的閉包,彈出 5 。
JavaScript 里有哪些數(shù)據(jù)類型
一、數(shù)據(jù)類型 ?
undefiend 沒(méi)有定義數(shù)據(jù)類型? ? ? ?
number 數(shù)值數(shù)據(jù)類型,例如 10 或者 1 或者 5.5? ? ? ?
string 字符串?dāng)?shù)據(jù)類型用來(lái)描述文本,例如 "你的姓名"? ? ? ?
boolean 布爾類型 true | false ,不是正就是反? ? ? ?
object 對(duì)象類型,復(fù)雜的一組描述信息的集合
function 函數(shù)類型
解釋清楚 null 和 undefined
null 用來(lái)表示尚未存在的對(duì)象,常用來(lái)表示函數(shù)企圖返回一個(gè)不存在的對(duì)象。? null 表示"沒(méi)有對(duì)象",即該處不應(yīng)該有值。
null 典型用法是:?
作為函數(shù)的參數(shù),表示該函數(shù)的參數(shù)不是對(duì)象。?
作為對(duì)象原型鏈的終點(diǎn)。
當(dāng)聲明的變量還未被初始化時(shí),變量的默認(rèn)值為 undefined。?undefined 表示"缺少值",就是此處應(yīng)該有一個(gè)值,但是還沒(méi)有定義。?
變量被聲明了,但沒(méi)有賦值時(shí),就等于 undefined。?
調(diào)用函數(shù)時(shí),應(yīng)該提供的參數(shù)沒(méi)有提供,該參數(shù)等于 undefined。?
對(duì)象沒(méi)有賦值的屬性,該屬性的值為 undefined。?
函數(shù)沒(méi)有返回值時(shí),默認(rèn)返回 undefined。
未定義的值和定義未賦值的為 undefined,null 是一種特殊的 object,NaN 是一種特殊的 number。
講一下 1 和 Number(1) 的區(qū)別*
1 是一個(gè)原始定義好的 number 類型;
Number(1) 是一個(gè)函數(shù)類型,是我們自己聲明的一個(gè)函數(shù)(方法)。
講一下 prototype 是什么東西,原型鏈的理解,什么時(shí)候用 prototype ?
prototype 是函數(shù)對(duì)象上面預(yù)設(shè)的對(duì)象屬性。
函數(shù)里的 this 什么含義,什么情況下,怎么用 ?
this 是 Javascript 語(yǔ)言的一個(gè)關(guān)鍵字。
它代表函數(shù)運(yùn)行時(shí),自動(dòng)生成的一個(gè)內(nèi)部對(duì)象,只能在函數(shù)內(nèi)部使用。
隨著函數(shù)使用場(chǎng)合的不同,this 的值會(huì)發(fā)生變化。
但是有一個(gè)總的原則,那就是 this 指的是,調(diào)用函數(shù)的那個(gè)對(duì)象。
情況一:純粹的函數(shù)調(diào)用?
這是函數(shù)的最通常用法,屬于全局性調(diào)用,因此 this 就代表全局對(duì)象 window。? ?
function test(){? ? this.x = 1;? ? alert(this.x);? ? }? ? test(); // 1
為了證明 this 就是全局對(duì)象,我對(duì)代碼做一些改變:? ?
var x = 1;? ? function test(){? ? alert(this.x);? ? }? ? test(); // 1? ?
運(yùn)行結(jié)果還是 1。
再變一下:? ?
var x = 1;? ? function test(){? ? this.x = 0;? ? }? ? test(); alert(x); // 0
情況二:作為對(duì)象方法的調(diào)用? ?
函數(shù)還可以作為某個(gè)對(duì)象的方法調(diào)用,這時(shí) this 就指這個(gè)上級(jí)對(duì)象。? ?
function test(){? ? alert(this.x);? ? } var x = 2? ? var o = {};? ? o.x = 1;? ? o.m = test;? ? o.m(); // 1
情況三: 作為構(gòu)造函數(shù)調(diào)用? ?
所謂構(gòu)造函數(shù),就是通過(guò)這個(gè)函數(shù)生成一個(gè)新對(duì)象(object)。這時(shí)的 this 就指這個(gè)新對(duì)象。
function Test(){? ? this.x = 1;? ? }? ? var o = new Test(); alert(o.x); // 1? ?
運(yùn)行結(jié)果為 1。為了表明這時(shí) this 不是全局對(duì)象,對(duì)代碼做一些改變:?
?
var x = 2;? ? function Test(){? ? this.x = 1;? ? }? ? var o = new Test();? ? alert(x); // 2
運(yùn)行結(jié)果為 2,表明全局變量 x 的值沒(méi)變。
情況四: apply 調(diào)用? ?
apply() 是函數(shù)對(duì)象的一個(gè)方法,它的作用是改變函數(shù)的調(diào)用對(duì)象,它的第一個(gè)參數(shù)就表示改變后的調(diào)用這個(gè)函數(shù)的對(duì)象。因此,this 指的就是這第一個(gè)參數(shù)。? ?
var x = 0;? ? function test(){? ? alert(this.x);? ? }? ? var o = {};? ? o.x = 1;? ? o.m = test;? ? o.m.apply(); // 0? ?
apply() 的參數(shù)為空時(shí),默認(rèn)調(diào)用全局對(duì)象。因此,這時(shí)的運(yùn)行結(jié)果為 0,證明 this 指的是全局對(duì)象。
? ?
如果把最后一行代碼修改為
o.m.apply(o); // 1
運(yùn)行結(jié)果就變成了 1,證明了這時(shí) this 代表的是對(duì)象 o。
apply 和 call ?什么含義,什么區(qū)別 ?什么時(shí)候用 ?
call,apply 都屬于 Function.prototype 的一個(gè)方法,它是 JavaScript 引擎內(nèi)在實(shí)現(xiàn)的,因?yàn)閷儆?Function.prototype,所以每個(gè) Function 對(duì)象實(shí)例(就是每個(gè)方法)都有 call,apply 屬性。
既然作為方法的屬性,那它們的使用就當(dāng)然是針對(duì)方法的了,這兩個(gè)方法是容易混淆的,因?yàn)樗鼈兊淖饔靡粯樱皇鞘褂梅绞讲煌?/p>
語(yǔ)法:
foo.call(this, arg1, arg2, arg3) == foo.apply(this, arguments) == this.foo(arg1, arg2, arg3);
相同點(diǎn):兩個(gè)方法產(chǎn)生的作用是完全一樣的。
不同點(diǎn):方法傳遞的參數(shù)不同。
每個(gè)函數(shù)對(duì)象會(huì)有一些方法可以去修改函數(shù)執(zhí)行時(shí)里面的 this,比較常見(jiàn)得到就是 call 和 apply,通過(guò) call 和 apply 可以重新定義函數(shù)的執(zhí)行環(huán)境,即 this 的指向。
function add(c, d) { console.log(this.a + this.b + c + d); } var o = { a: 1, b: 3 }; add.call(o, 5, 7); //1+3+5+7=16 //傳參的時(shí)候是扁平的把每個(gè)參數(shù)傳進(jìn)去 add.apply(o, [10, 20]); //1+3+10+20=34 //傳參的時(shí)候是把參數(shù)作為一個(gè)數(shù)組傳進(jìn)去 //什么時(shí)候使用 call 或者 apply function bar() { console.log(Object.prototype.toString.call(this)); // 用來(lái)調(diào)用一些無(wú)法直接調(diào)用的方法 } bar.call(7); // "[object Number]"
異步過(guò)程的構(gòu)成要素有哪些?和異步過(guò)程是怎樣的 ?
總結(jié)一下,一個(gè)異步過(guò)程通常是這樣的:
主線程發(fā)起一個(gè)異步請(qǐng)求,相應(yīng)的工作線程接收請(qǐng)求并告知主線程已收到(異步函數(shù)返回);
主線程可以繼續(xù)執(zhí)行后面的代碼,同時(shí)工作線程執(zhí)行異步任務(wù);
工作線程完成工作后,通知主線程;
主線程收到通知后,執(zhí)行一定的動(dòng)作(調(diào)用回調(diào)函數(shù))。
異步函數(shù)通常具有以下的形式:A(args..., callbackFn)。
它可以叫做異步過(guò)程的發(fā)起函數(shù),或者叫做異步任務(wù)注冊(cè)函數(shù)。
args 和 callbackFn 是這個(gè)函數(shù)的參數(shù)。
所以,從主線程的角度看,一個(gè)異步過(guò)程包括下面兩個(gè)要素:
發(fā)起函數(shù)(或叫注冊(cè)函數(shù)) A。
回調(diào)函數(shù) callbackFn。
它們都是在主線程上調(diào)用的,其中注冊(cè)函數(shù)用來(lái)發(fā)起異步過(guò)程,回調(diào)函數(shù)用來(lái)處理結(jié)果。
舉個(gè)具體的例子:
setTimeout(fn, 1000);
其中的 setTimeout 就是異步過(guò)程的發(fā)起函數(shù),fn 是回調(diào)函數(shù)。
注意:前面說(shuō)的形式 A(args..., callbackFn) 只是一種抽象的表示,并不代表回調(diào)函數(shù)一定要作為發(fā)起函數(shù)的參數(shù)。
例如:
var xhr = new XMLHttpRequest(); xhr.onreadystatechange = xxx; // 添加回調(diào)函數(shù) xhr.open("GET", url); xhr.send(); // 發(fā)起函數(shù)
發(fā)起函數(shù)和回調(diào)函數(shù)就是分離的。
說(shuō)說(shuō)消息隊(duì)列和事件循環(huán)
主線程在執(zhí)行完當(dāng)前循環(huán)中的所有代碼后,就會(huì)到消息隊(duì)列取出這條消息(也就是 message 函數(shù)),并執(zhí)行它。
完成了工作線程對(duì)主線程的通知,回調(diào)函數(shù)也就得到了執(zhí)行。
如果一開(kāi)始主線程就沒(méi)有提供回調(diào)函數(shù),AJAX 線程在收到 HTTP 響應(yīng)后,也就沒(méi)必要通知主線程,從而也沒(méi)必要往消息隊(duì)列放消息。
異步過(guò)程的回調(diào)函數(shù),一定不在當(dāng)前的這一輪事件循環(huán)中執(zhí)行。
session 與 cookie 的區(qū)別
session 保存在服務(wù)器,客戶端不知道其中的信息;
cookie 保存在客戶端,服務(wù)器能夠知道其中的信息。?
session 中保存的是對(duì)象,cookie 中保存的是字符串。 ??
session 不能區(qū)分路徑,同一個(gè)用戶在訪問(wèn)一個(gè)網(wǎng)站期間,所有的 session 在任何一個(gè)地方都可以訪問(wèn)到。
而 cookie 中如果設(shè)置了路徑參數(shù),那么同一個(gè)網(wǎng)站中不同路徑下的 cookie 互相是訪問(wèn)不到的。 ?
cookies 是干嘛的,服務(wù)器和瀏覽器之間的 cookies 是怎么傳的,httponly 的 cookies 和可讀寫(xiě)的 cookie 有什么區(qū)別,有無(wú)長(zhǎng)度限制 ?
cookies 是一些存儲(chǔ)在用戶電腦上的小文件。
它是被設(shè)計(jì)用來(lái)保存一些站點(diǎn)的用戶數(shù)據(jù),這樣能夠讓服務(wù)器為這樣的用戶定制內(nèi)容,后者頁(yè)面代碼能夠獲取到 cookie 值然后發(fā)送給服務(wù)器。
比如 cookie 中存儲(chǔ)了所在地理位置,以后每次進(jìn)入地圖就默認(rèn)定位到改地點(diǎn)即可。
請(qǐng)描述一下 cookies,sessionStorage 和 localStorage 的區(qū)別
共同點(diǎn)
都是保存在瀏覽器端,且同源的。
區(qū)別
cookie 數(shù)據(jù)始終在同源的 http 請(qǐng)求中攜帶(即使不需要),即 cookie 在瀏覽器和服務(wù)器間來(lái)回傳遞。
而 sessionStorage 和 localStorage 不會(huì)自動(dòng)把數(shù)據(jù)發(fā)給服務(wù)器,僅在本地保存。
cookie 數(shù)據(jù)還有路徑(path)的概念,可以限制 cookie 只屬于某個(gè)路徑下。
存儲(chǔ)大小限制也不同,cookie 數(shù)據(jù)不能超過(guò) 4k,同時(shí)因?yàn)槊看?http 請(qǐng)求都會(huì)攜帶 cookie,所以 cookie 只適合保存很小的數(shù)據(jù),如會(huì)話標(biāo)識(shí)。
sessionStorage 和 localStorage 雖然也有存儲(chǔ)大小的限制,但比 cookie 大得多,可以達(dá)到 5M 或更大。
數(shù)據(jù)有效期不同,sessionStorage:僅在當(dāng)前瀏覽器窗口關(guān)閉前有效,自然也就不可能持久保持;localStorage:始終有效,窗口或?yàn)g覽器關(guān)閉也一直保存,因此用作持久數(shù)據(jù);cookie 只在設(shè)置的 cookie 過(guò)期時(shí)間之前一直有效,即使窗口或?yàn)g覽器關(guān)閉。
作用域不同,sessionStorage 在不同的瀏覽器窗口中不共享,即使是同一個(gè)頁(yè)面;cookie 和 localStorage 在所有同源窗口中都是共享的。
從敲入 URL 到渲染完成的整個(gè)過(guò)程,包括 DOM 構(gòu)建的過(guò)程,說(shuō)的約詳細(xì)越好
用戶輸入 url 地址,瀏覽器根據(jù)域名尋找 IP 地址
瀏覽器向服務(wù)器發(fā)送 http 請(qǐng)求,如果服務(wù)器段返回以 301 之類的重定向,瀏覽器根據(jù)相應(yīng)頭中的 location 再次發(fā)送請(qǐng)求
服務(wù)器端接受請(qǐng)求,處理請(qǐng)求生成 html 代碼,返回給瀏覽器,這時(shí)的 html 頁(yè)面代碼可能是經(jīng)過(guò)壓縮的
瀏覽器接收服務(wù)器響應(yīng)結(jié)果,如果有壓縮則首先進(jìn)行解壓處理,緊接著就是頁(yè)面解析渲染
解析渲染該過(guò)程主要分為以下步驟:解析 HTML、構(gòu)建 DOM 樹(shù)、DOM 樹(shù)與 CSS 樣式進(jìn)行附著構(gòu)造呈現(xiàn)樹(shù)
布局
繪制
詳情:面試題之從敲入 URL 到瀏覽器渲染完成
是否了解公鑰加密和私鑰加密。如何確保表單提交里的密碼字段不被泄露。
公鑰用于對(duì)數(shù)據(jù)進(jìn)行加密,私鑰用于對(duì)數(shù)據(jù)進(jìn)行解密。
很直觀的理解:公鑰就是公開(kāi)的密鑰,其公開(kāi)了大家才能用它來(lái)加密數(shù)據(jù)。私鑰是私有的密鑰,誰(shuí)有這個(gè)密鑰才能夠解密密文。
解決方案 1:
form 在提交的過(guò)程中,對(duì)密碼字段是不進(jìn)行加密而是以明碼的形式進(jìn)行數(shù)據(jù)傳輸?shù)摹?br>如果要對(duì)數(shù)據(jù)進(jìn)行加密,你可以自己寫(xiě)一個(gè)腳本對(duì)內(nèi)容進(jìn)行編碼后傳輸,只是這個(gè)安全性也并不高。
解決方案 2:
如果想對(duì)數(shù)據(jù)進(jìn)行加密,你可以使用 HTTPS 安全傳輸協(xié)議,這個(gè)協(xié)議是由系統(tǒng)進(jìn)行密碼加密處理的,在數(shù)據(jù)傳輸中是絕對(duì)不會(huì)被攔截獲取的,只是 HTTPS 的架設(shè)會(huì)相對(duì)麻煩點(diǎn)。一些大型網(wǎng)站的登錄、銀行的在線網(wǎng)關(guān)等都是走這條路。
驗(yàn)證碼是干嘛的,是為了解決什么安全問(wèn)題。
所謂驗(yàn)證碼,就是將一串隨機(jī)產(chǎn)生的數(shù)字或符號(hào),生成一幅圖片, 圖片里加上一些干擾象素(防止OCR),由用戶肉眼識(shí)別其中的驗(yàn)證碼信息,輸入表單提交網(wǎng)站驗(yàn)證,驗(yàn)證成功后才能使用某項(xiàng)功能。
驗(yàn)證碼一般是防止批量注冊(cè)的,人眼看起來(lái)都費(fèi)勁,何況是機(jī)器。
像百度貼吧未登錄發(fā)貼要輸入驗(yàn)證碼大概是防止大規(guī)模匿名回帖的發(fā)生。
目前,不少網(wǎng)站為了防止用戶利用機(jī)器人自動(dòng)注冊(cè)、登錄、灌水,都采用了驗(yàn)證碼技術(shù)。
截取字符串 abcdefg 的 efg。
從第四位開(kāi)始截取
alert("abcdefg".substring(4)); alert ("abcdefg".slice(4))
判斷一個(gè)字符串中出現(xiàn)次數(shù)最多的字符,統(tǒng)計(jì)這個(gè)次數(shù)
步驟
將字符串轉(zhuǎn)化數(shù)組?
創(chuàng)建一個(gè)對(duì)象?
遍歷數(shù)組,判斷對(duì)象中是否存在數(shù)組中的值,如果存在值 +1,不存在賦值為 1
定義兩個(gè)變量存儲(chǔ)字符值,字符出現(xiàn)的字?jǐn)?shù)
var str = "abaasdffggghhjjkkgfddsssss3444343"; // 1.將字符串轉(zhuǎn)換成數(shù)組 var newArr = str.split(""); // 2.創(chuàng)建一個(gè)對(duì)象 var json = {}; // 3. 所有字母出現(xiàn)的次數(shù),判斷對(duì)象中是否存在數(shù)組中的值,如果存在值 +1,不存在賦值為 1 for(var i = 0; i < newArr.length; i++){ // 類似:json : { ‘a(chǎn)’: 3, ’b’: 1 } if(json[newArr[i]]){ json[newArr[i]] +=1; } else { json[newArr[i]] = 1; } } // 4 定義兩個(gè)變量存儲(chǔ)字符值,字符出現(xiàn)的字?jǐn)?shù) var num = 0 ; //次數(shù) var element = ""; //最多的項(xiàng) for(var k in json){ if(json[k] > num){ num = json[k]; element = k ; } } console.log("出現(xiàn)次數(shù):"+num +"最多的字符:"+ element);
document.write 和 innerHTML 的區(qū)別
document.write 是直接寫(xiě)入到頁(yè)面的內(nèi)容流,如果在寫(xiě)之前沒(méi)有調(diào)用 document.open, 瀏覽器會(huì)自動(dòng)調(diào)用 open。每次寫(xiě)完關(guān)閉之后重新調(diào)用該函數(shù),會(huì)導(dǎo)致頁(yè)面被重寫(xiě)。
innerHTML 則是 DOM 頁(yè)面元素的一個(gè)屬性,代表該元素的 html 內(nèi)容。你可以精確到某一個(gè)具體的元素來(lái)進(jìn)行更改。如果想修改 document 的內(nèi)容,則需要修改 document.documentElement.innerElement。
innerHTML 將內(nèi)容寫(xiě)入某個(gè) DOM 節(jié)點(diǎn),不會(huì)導(dǎo)致頁(yè)面全部重繪。
innerHTML 很多情況下都優(yōu)于 document.write,其原因在于其允許更精確的控制要刷新頁(yè)面的那一個(gè)部分。
document.write 是重寫(xiě)整個(gè) document, 寫(xiě)入內(nèi)容是字符串的 html;innerHTML 是 HTMLElement 的屬性,是一個(gè)元素的內(nèi)部 html 內(nèi)容?
JS 識(shí)別不同瀏覽器信息
function myBrowser() { var userAgent = navigator.userAgent; //取得瀏覽器的userAgent字符串 var isOpera = userAgent.indexOf("Opera") > -1; if (isOpera) { return "Opera" }; //判斷是否Opera瀏覽器 if (userAgent.indexOf("Firefox") > -1) { return "Firefox"; } //判斷是否Firefox瀏覽器 if (userAgent.indexOf("Chrome") > -1) { return "Chrome"; } //判斷是否Google瀏覽器 if (userAgent.indexOf("Safari") > -1) { return "Safari"; } //判斷是否Safari瀏覽器 if (userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera) { return "IE"; }; //判斷是否IE瀏覽器 }
JavaScript 常見(jiàn)的內(nèi)置對(duì)象
有 Object、Math、String、Array、Number、Function、Boolean、JSON 等,其中 Object 是所有對(duì)象的基類,采用了原型繼承方式。
編寫(xiě)一個(gè)方法,求一個(gè)字符串的字節(jié)長(zhǎng)度
假設(shè):一個(gè)英文字符占用一個(gè)字節(jié),一個(gè)中文字符占用兩個(gè)字節(jié)
function getBytes(str){ var len = str.length; var bytes = len; for(var i = 0; i < len; i++){ if (str.charCodeAt(i) > 255) bytes++; } return bytes; } alert(getBytes("你好,as"));
JS 組成
核心(ECMAScript) 描述了該語(yǔ)言的語(yǔ)法和基本對(duì)象
文檔對(duì)象模型(DOM) 描述了處理網(wǎng)頁(yè)內(nèi)容的方法和接口
瀏覽器對(duì)象模型(BOM) 描述了與瀏覽器進(jìn)行交互的方法和接口
new 操作符具體干了什么呢 ?
創(chuàng)建一個(gè)空對(duì)象,并且 this 變量引用該對(duì)象,同時(shí)還繼承了該函數(shù)的原型。
屬性和方法被加入到 this 引用的對(duì)象中。
新創(chuàng)建的對(duì)象由 this 所引用,并且最后隱式的返回 this 。
JSON 的了解?
JSON(JavaScript Object Notation) 是一種輕量級(jí)的數(shù)據(jù)交換格式。
它是基于 JavaScript 的一個(gè)子集。
數(shù)據(jù)格式簡(jiǎn)單,易于讀寫(xiě),占用帶寬小。
格式:采用鍵值對(duì)。例如:{ “age?: ?12?, ”name?: ?back? }
你有哪些性能優(yōu)化的方法 ?
web 前端是應(yīng)用服務(wù)器處理之前的部分,前端主要包括:HTML、CSS、javascript、image 等各種資源,針對(duì)不同的資源有不同的優(yōu)化方式。
內(nèi)容優(yōu)化
減少 HTTP 請(qǐng)求數(shù)。這條策略是最重要最有效的,因?yàn)橐粋€(gè)完整的請(qǐng)求要經(jīng)過(guò) DNS 尋址,與服務(wù)器建立連接,發(fā)送數(shù)據(jù),等待服務(wù)器響應(yīng),接收數(shù)據(jù)這樣一個(gè)消耗時(shí)間成本和資源成本的復(fù)雜的過(guò)程。
常見(jiàn)方法:合并多個(gè) CSS 文件和 js 文件,利用 CSS Sprites 整合圖像,Inline Images (使用 data:URL scheme 在實(shí)際的頁(yè)面嵌入圖像數(shù)據(jù) ),合理設(shè)置 HTTP 緩存等。
減少 DNS 查找
避免重定向
使用 Ajax 緩存
延遲加載組件,預(yù)加載組件
減少 DOM 元素?cái)?shù)量。頁(yè)面中存在大量 DOM 元素,會(huì)導(dǎo)致 javascript 遍歷 DOM 的效率變慢。
最小化 iframe 的數(shù)量。iframes 提供了一個(gè)簡(jiǎn)單的方式把一個(gè)網(wǎng)站的內(nèi)容嵌入到另一個(gè)網(wǎng)站中。但其創(chuàng)建速度比其他包括 JavaScript 和 CSS 的 DOM 元素的創(chuàng)建慢了 1-2 個(gè)數(shù)量級(jí)。
避免 404。HTTP 請(qǐng)求時(shí)間消耗是很大的,因此使用 HTTP 請(qǐng)求來(lái)獲得一個(gè)沒(méi)有用處的響應(yīng)(例如 404 沒(méi)有找到頁(yè)面)是完全沒(méi)有必要的,它只會(huì)降低用戶體驗(yàn)而不會(huì)有一點(diǎn)好處。
服務(wù)器優(yōu)化
使用內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN)。把網(wǎng)站內(nèi)容分散到多個(gè)、處于不同地域位置的服務(wù)器上可以加快下載速度。
GZIP 壓縮
設(shè)置 ETag:ETags(Entity tags,實(shí)體標(biāo)簽)是 web 服務(wù)器和瀏覽器用于判斷瀏覽器緩存中的內(nèi)容和服務(wù)器中的原始內(nèi)容是否匹配的一種機(jī)制。
提前刷新緩沖區(qū)
對(duì) Ajax 請(qǐng)求使用 GET 方法
避免空的圖像 src
Cookie 優(yōu)化
減小 Cookie 大小
針對(duì) Web 組件使用域名無(wú)關(guān)的 Cookie
CSS 優(yōu)化
將 CSS 代碼放在 HTML 頁(yè)面的頂部
避免使用 CSS 表達(dá)式
使用 < link> 來(lái)代替 @import
避免使用 Filters
javascript 優(yōu)化
將 JavaScript 腳本放在頁(yè)面的底部。
將 JavaScript 和 CSS 作為外部文件來(lái)引用。
在實(shí)際應(yīng)用中使用外部文件可以提高頁(yè)面速度,因?yàn)?JavaScript 和 CSS 文件都能在瀏覽器中產(chǎn)生緩存。
縮小 JavaScript 和 CSS
刪除重復(fù)的腳本
最小化 DOM 的訪問(wèn)。使用 JavaScript 訪問(wèn) DOM 元素比較慢。
開(kāi)發(fā)智能的事件處理程序
javascript 代碼注意:謹(jǐn)慎使用 with,避免使用 eval Function 函數(shù),減少作用域鏈查找。
圖像優(yōu)化
優(yōu)化圖片大小
通過(guò) CSS Sprites 優(yōu)化圖片
不要在 HTML 中使用縮放圖片
favicon.ico 要小而且可緩存
JS 格式化數(shù)字(每三位加逗號(hào))
從后往前取。
function?toThousands(num)?{?? ????var?num?=?(num?||?0).toString(),?result?=?"";?? ????while?(num.length?>?3)?{?? ????????result?=?","?+?num.slice(-3)?+?result;?? ????????num?=?num.slice(0,?num.length?-?3);?? ????}?? ????if?(num)?{?result?=?num?+?result;?}?? ????return?result;?? }??
合并數(shù)組
如果你需要合并兩個(gè)數(shù)組的話,可以使用?Array.concat()
var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; console.log(array1.concat(array2)); // [1,2,3,4,5,6];
然而,這個(gè)函數(shù)并不適用于合并大的數(shù)組,因?yàn)樗枰獎(jiǎng)?chuàng)建一個(gè)新的數(shù)組,而這會(huì)消耗很多內(nèi)存。
這時(shí),你可以使用?Array.push.apply(arr1, arr2)?來(lái)代替創(chuàng)建新的數(shù)組,它可以把第二個(gè)數(shù)組合并到第一個(gè)中,從而較少內(nèi)存消耗。
var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; console.log(array1.push.apply(array1, array2)); // [1, 2, 3, 4, 5, 6]
把節(jié)點(diǎn)列表 (NodeList) 轉(zhuǎn)換為數(shù)組
如果你運(yùn)行?document.querySelectorAll("p")?方法,它可能會(huì)返回一個(gè) DOM 元素的數(shù)組 — 節(jié)點(diǎn)列表對(duì)象。
但這個(gè)對(duì)象并不具有數(shù)組的全部方法,如?sort(),reduce(),?map(),filter()。
為了使用數(shù)組的那些方法,你需要把它轉(zhuǎn)換為數(shù)組。
只需使用?[].slice.call(elements)?即可實(shí)現(xiàn):
var elements = document.querySelectorAll("p"); // NodeList var arrayElements = [].slice.call(elements); // 現(xiàn)在 NodeList 是一個(gè)數(shù)組 var arrayElements = Array.from(elements); // 這是另一種轉(zhuǎn)換 NodeList 到 Array 的方法
打亂數(shù)組元素的順序
不適用?Lodash?等這些庫(kù)打亂數(shù)組元素順序,你可以使用這個(gè)技巧:
var list = [1, 2, 3]; console.log(list.sort(function() { Math.random() - 0.5 })); // [2, 1, 3]
js 的 ready 和 onload 事件的區(qū)別
onload 是等 HTML 的所有資源都加載完成后再執(zhí)行 onload 里面的內(nèi)容,所有資源包括 DOM 結(jié)構(gòu)、圖片、視頻 等資源;
ready 是當(dāng) DOM 結(jié)構(gòu)加載完成后就可以執(zhí)行了,相當(dāng)于 jQuery 中的 $(function(){ js 代碼 });
另外,onload 只能有一個(gè),ready 可以有多個(gè)。
js 的兩種回收機(jī)制
標(biāo)記清除(mark and sweep)
從語(yǔ)義上理解就比較好理解了,大概就是當(dāng)變量進(jìn)入到某個(gè)環(huán)境中的時(shí)候就把這個(gè)變量標(biāo)記一下,比如標(biāo)記為“進(jìn)入環(huán)境”,當(dāng)離開(kāi)的時(shí)候就把這個(gè)變量的標(biāo)記給清除掉,比如是“離開(kāi)環(huán)境”。而在這后面還有標(biāo)記的變量將被視為準(zhǔn)備刪除的變量。
垃圾收集器在運(yùn)行的時(shí)候會(huì)給存儲(chǔ)在內(nèi)存中的所有變量都加上標(biāo)記(可以使用任何標(biāo)記方式)。
然后,它會(huì)去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標(biāo)記。
而在此之后再被加上的標(biāo)記的變量將被視為準(zhǔn)備刪除的變量,原因是環(huán)境中的變量已經(jīng)無(wú)法訪問(wèn)到這些變量了。
最后,垃圾收集器完成內(nèi)存清除工作。銷毀那些帶標(biāo)記的值并回收它們所占用的內(nèi)存空間。
這是 javascript 最常見(jiàn)的垃圾回收方式。至于上面有說(shuō)道的標(biāo)記,到底該如何標(biāo)記 ?
好像是有很多方法,比如特殊位翻轉(zhuǎn),維護(hù)一個(gè)列表什么的。
引用計(jì)數(shù)(reference counting)
引用計(jì)數(shù)的含義是跟蹤記錄每個(gè)值被引用的次數(shù),當(dāng)聲明一個(gè)變量并將一個(gè)引用類型的值賦給該變量時(shí),這個(gè)時(shí)候的引用類型的值就會(huì)是引用次數(shù) +1 了。如果同一個(gè)值又被賦給另外一個(gè)變量,則該值的引用次數(shù)又 +1。
相反如果包含這個(gè)值的引用的變量又取得另外一個(gè)值,即被重新賦了值,那么這個(gè)值的引用就 -1 。當(dāng)這個(gè)值的引用次數(shù)編程 0 時(shí),表示沒(méi)有用到這個(gè)值,這個(gè)值也無(wú)法訪問(wèn),因此環(huán)境就會(huì)收回這個(gè)值所占用的內(nèi)存空間回收。
這樣,當(dāng)垃圾收集器下次再運(yùn)行時(shí),它就會(huì)釋放引用次數(shù)為 0 的值所占用的內(nèi)存。
三張圖搞懂 JavaScript 的原型對(duì)象與原型鏈
對(duì)于新人來(lái)說(shuō),JavaScript 的原型是一個(gè)很讓人頭疼的事情,一來(lái) prototype 容易與 proto 混淆,
一、prototype 和 proto 的區(qū)別
var a = {}; console.log(a.prototype); //undefined console.log(a.__proto__); //Object {} var b = function(){} console.log(b.prototype); //b {} console.log(b.__proto__); //function() {}
結(jié)果:
/*1、字面量方式*/ var a = {}; console.log("a.__proto__ :", a.__proto__); // Object {} console.log("a.__proto__ === a.constructor.prototype:", a.__proto__ === a.constructor.prototype); // true /*2、構(gòu)造器方式*/ var A = function(){}; var a2 = new A(); console.log("a2.__proto__:", a2.__proto__); // A {} console.log("a2.__proto__ === a2.constructor.prototype:", a2.__proto__ === a2.constructor.prototype); // true /*3、Object.create()方式*/ var a4 = { a: 1 } var a3 = Object.create(a4); console.log("a3.__proto__:", a3.__proto__); // Object {a: 1} console.log("a3.__proto__ === a3.constructor.prototype:", a3.__proto__ === a3.constructor.prototype); // false(此處即為圖1中的例外情況)
結(jié)果:
var A = function(){}; var a = new A(); console.log(a.__proto__); // A {}(即構(gòu)造器 function A 的原型對(duì)象) console.log(a.__proto__.__proto__); // Object {}(即構(gòu)造器 function Object 的原型對(duì)象) console.log(a.__proto__.__proto__.__proto__); // null
結(jié)果:
閉包的理解 ?
一、變量的作用域
要理解閉包,首先必須理解 Javascript 特殊的變量作用域。
變量的作用域無(wú)非就是兩種:全局變量和局部變量。
Javascript語(yǔ)言的特殊之處,就在于函數(shù)內(nèi)部可以直接讀取全局變量。
var n = 999; function f1(){ alert(n); } f1(); // 999
另一方面,在函數(shù)外部自然無(wú)法讀取函數(shù)內(nèi)的局部變量。
function f1(){ var n = 999; } alert(n); // error
這里有一個(gè)地方需要注意,函數(shù)內(nèi)部聲明變量的時(shí)候,一定要使用 var 命令。
如果不用的話,你實(shí)際上聲明了一個(gè)全局變量!
function f1(){ n = 999; } f1(); alert(n); // 999
二、如何從外部讀取局部變量 ?
function f1() { var n = 999; function f2() { alert(n); } return f2; } var result = f1(); result(); // 999
既然 f2 可以讀取 f1 中的局部變量,那么只要把 f2 作為返回值,我們不就可以在 f1 外部讀取它的內(nèi)部變量了嗎!
三、閉包的概念
上一節(jié)代碼中的 f2 函數(shù),就是閉包。
我的理解是,閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。
由于在 Javascript 語(yǔ)言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,因此可以把閉包簡(jiǎn)單理解成 定義在一個(gè)函數(shù)內(nèi)部的函數(shù)。
所以,在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來(lái)的一座橋梁。
四、閉包的用途
閉包可以用在許多地方。它的最大用處有兩個(gè),一個(gè)是前面提到的可以讀取函數(shù)內(nèi)部的變量,另一個(gè)就是讓這些變量的值始終保持在內(nèi)存中。
怎么來(lái)理解呢 ?請(qǐng)看下面的代碼。
function f1() { var n = 999; nAdd = function () { n += 1 } function f2() { alert(n); } return f2; } var result = f1(); result(); // 999 nAdd(); result(); // 1000
在這段代碼中,result 實(shí)際上就是閉包 f2 函數(shù)。它一共運(yùn)行了兩次,第一次的值是 999,第二次的值是 1000。這證明了,函數(shù) f1 中的局部變量 n 一直保存在內(nèi)存中,并沒(méi)有在 f1 調(diào)用后被自動(dòng)清除。
為什么會(huì)這樣呢 ?
原因就在于 f1 是 f2 的父函數(shù),而 f2 被賦給了一個(gè)全局變量,這導(dǎo)致 f2 始終在內(nèi)存中,而 f2 的存在依賴于 f1,因此 f1 也始終在內(nèi)存中,不會(huì)在調(diào)用結(jié)束后,被垃圾回收機(jī)制(garbage collection)回收。
這段代碼中另一個(gè)值得注意的地方,就是
"nAdd=function(){ n+=1 }" 這一行,首先在 nAdd 前面沒(méi)有使用 var 關(guān)鍵字,因此 nAdd 是一個(gè)全局變量,而不是局部變量。
其次,nAdd 的值是一個(gè)匿名函數(shù)(anonymous function),而這個(gè)匿名函數(shù)本身也是一個(gè)閉包,所以 nAdd 相當(dāng)于是一個(gè) setter,可以在函數(shù)外部對(duì)函數(shù)內(nèi)部的局部變量進(jìn)行操作。
五、使用閉包的注意點(diǎn)
由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會(huì)造成網(wǎng)頁(yè)的性能問(wèn)題,在 IE 中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。
閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當(dāng)作對(duì)象(object)使用,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value),這時(shí)一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。
閉包面試經(jīng)典問(wèn)題
問(wèn)題:想每次點(diǎn)擊對(duì)應(yīng)目標(biāo)時(shí)彈出對(duì)應(yīng)的數(shù)字下標(biāo) 0~4 ,但實(shí)際是無(wú)論點(diǎn)擊哪個(gè)目標(biāo)都會(huì)彈出數(shù)字 5。
function onMyLoad() { var arr = document.getElementsByTagName("p"); for (var i = 0; i < arr.length; i++) { arr[i].onclick = function () { alert(i); } } }
問(wèn)題所在:arr 中的每一項(xiàng)的 onclick 均為一個(gè)函數(shù)實(shí)例(Function 對(duì)象),這個(gè)函數(shù)實(shí)例也產(chǎn)生了一個(gè)閉包域,這個(gè)閉包域引用了外部閉包域的變量,其 function scope 的 closure 對(duì)象有個(gè)名為 i 的引用,外部閉包域的私有變量?jī)?nèi)容發(fā)生變化,內(nèi)部閉包域得到的值自然會(huì)發(fā)生改變。
解決辦法一
解決思路:增加若干個(gè)對(duì)應(yīng)的閉包域空間(這里采用的是匿名函數(shù)),專門(mén)用來(lái)存儲(chǔ)原先需要引用的內(nèi)容(下標(biāo)),不過(guò)只限于基本類型(基本類型值傳遞,對(duì)象類型引用傳遞)。
//聲明一個(gè)匿名函數(shù),若傳進(jìn)來(lái)的是基本類型則為值傳遞,故不會(huì)對(duì)實(shí)參產(chǎn)生影響, //該函數(shù)對(duì)象有一個(gè)本地私有變量 arg(形參) ,該函數(shù)的 function scope 的 closure 對(duì)象屬性有兩個(gè)引用,一個(gè)是 arr,一個(gè)是 i //盡管引用 i 的值隨外部改變 ,但本地私有變量(形參) arg 不會(huì)受影響,其值在一開(kāi)始被調(diào)用的時(shí)候就決定了 for (var i = 0; i < arr.length; i++) { (function (arg) { arr[i].onclick = function () { // onclick 函數(shù)實(shí)例的 function scope 的 closure 對(duì)象屬性有一個(gè)引用 arg, alert(arg); //只要 外部空間的 arg 不變,這里的引用值當(dāng)然不會(huì)改變 } })(i); //立刻執(zhí)行該匿名函數(shù),傳遞下標(biāo) i (實(shí)參) }
解決辦法二
解決思路:將事件綁定在新增的匿名函數(shù)返回的函數(shù)上,此時(shí)綁定的函數(shù)中的 function scope 中的 closure 對(duì)象的 引用 arg 是指向?qū)⑵浞祷氐哪涿瘮?shù)的私有變量 arg
for (var i = 0; i < arr.length; i++) { arr[i].onclick = (function (arg) { return function () { alert(arg); } })(i); }
解決辦法三
使用 ES6 新語(yǔ)法 let 關(guān)鍵字
for (var i = 0; i < arr.length; i++) { let j = i; // 創(chuàng)建一個(gè)塊級(jí)變量 arr[i].onclick = function () { alert(j); } }
JavaScript 判斷一個(gè)變量是對(duì)象還是數(shù)組 ?
typeof 都返回 object
在 JavaScript 中所有數(shù)據(jù)類型嚴(yán)格意義上都是對(duì)象,但實(shí)際使用中我們還是有類型之分,如果要判斷一個(gè)變量是數(shù)組還是對(duì)象使用 typeof 搞不定,因?yàn)樗挤祷?object。
第一,使用 typeof 加 length 屬性
數(shù)組有 length 屬性,object 沒(méi)有,而 typeof 數(shù)組與對(duì)象都返回 object,所以我們可以這么判斷
var?getDataType =?function(o){ ????if(typeof?o ==?"object"){ ????????if(?typeof?o.length ==?"number"?){ ????????????return?"Array"; ????????} else { ????????????return?"Object";??? ????????} ????} else { ????????return?"param is no object type"; ????} };
第二,使用 instanceof
利用 instanceof 判斷數(shù)據(jù)類型是對(duì)象還是數(shù)組時(shí)應(yīng)該優(yōu)先判斷 array,最后判斷 object。
var?getDataType =?function(o){ ????if(o?instanceof?Array){ ????????return?"Array" ????} else?if ( o?instanceof?Object ){ ????????return?"Object"; ????} else { ????????return?"param is no object type"; ????} };
ES5 的繼承和 ES6 的繼承有什么區(qū)別 ?
ES5 的繼承時(shí)通過(guò) prototype 或構(gòu)造函數(shù)機(jī)制來(lái)實(shí)現(xiàn)。
ES5 的繼承實(shí)質(zhì)上是先創(chuàng)建子類的實(shí)例對(duì)象,然后再將父類的方法添加到 this 上(Parent.apply(this))。
ES6 的繼承機(jī)制完全不同,實(shí)質(zhì)上是先創(chuàng)建父類的實(shí)例對(duì)象 this(所以必須先調(diào)用父類的 super()方法),然后再用子類的構(gòu)造函數(shù)修改 this。
具體的:ES6 通過(guò) class 關(guān)鍵字定義類,里面有構(gòu)造方法,類之間通過(guò) extends 關(guān)鍵字實(shí)現(xiàn)繼承。子類必須在 constructor 方法中調(diào)用 super 方法,否則新建實(shí)例報(bào)錯(cuò)。因?yàn)樽宇悰](méi)有自己的 this 對(duì)象,而是繼承了父類的 this 對(duì)象,然后對(duì)其進(jìn)行加工。如果不調(diào)用 super 方法,子類得不到 this 對(duì)象。
ps:super 關(guān)鍵字指代父類的實(shí)例,即父類的 this 對(duì)象。在子類構(gòu)造函數(shù)中,調(diào)用 super 后,才可使用 this 關(guān)鍵字,否則報(bào)錯(cuò)。
翻轉(zhuǎn)一個(gè)字符串
先將字符串轉(zhuǎn)成一個(gè)數(shù)組,然后用數(shù)組的 reverse() + join() 方法。
let a = "hello word"; let b = [...str].reverse().join(""); // drow olleh
說(shuō)說(shuō)堆和棧的區(qū)別 ?
一、堆棧空間分配區(qū)別
棧(操作系統(tǒng)):由操作系統(tǒng)自動(dòng)分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧;
堆(操作系統(tǒng)):一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時(shí)可能由 OS 回收,分配方式倒是類似于鏈表。
二、堆棧緩存方式區(qū)別
棧使用的是一級(jí)緩存, 他們通常都是被調(diào)用時(shí)處于存儲(chǔ)空間中,調(diào)用完畢立即釋放;
堆是存放在二級(jí)緩存中,生命周期由虛擬機(jī)的垃圾回收算法來(lái)決定(并不是一旦成為孤兒對(duì)象就能被回收)。所以調(diào)用這些對(duì)象的速度要相對(duì)來(lái)得低一些。
三、堆棧數(shù)據(jù)結(jié)構(gòu)區(qū)別
堆(數(shù)據(jù)結(jié)構(gòu)):堆可以被看成是一棵樹(shù),如:堆排序;
棧(數(shù)據(jù)結(jié)構(gòu)):一種先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu)。
js 經(jīng)典面試知識(shí)文章JS 是單線程,你了解其運(yùn)行機(jī)制嗎 ?
7 分鐘理解 JS 的節(jié)流、防抖及使用場(chǎng)景
JavaScript 常見(jiàn)的六種繼承方式
九種跨域方式實(shí)現(xiàn)原理(完整版)
常見(jiàn)六大 Web 安全攻防解析
一文讀懂 HTTP/2 及 HTTP/3 特性
深入理解 HTTPS 工作原理
JavaScript 中的垃圾回收和內(nèi)存泄漏
你不知道的瀏覽器頁(yè)面渲染機(jī)制
JavaScript 設(shè)計(jì)模式
深入 javascript——構(gòu)造函數(shù)和原型對(duì)象
ES6 +ES6 聲明變量的六種方法
ES5 只有兩種聲明變量的方法:var 和 function 。
ES6 除了添加 let 和 const 命令。
還有兩種聲明變量的方法:import 命令和 class 命令。
Promise 的隊(duì)列與 setTimeout 的隊(duì)列有何關(guān)聯(lián) ?
setTimeout(function(){ console.log(4) }, 0); new Promise(function(resolve){ console.log(1) for( var i = 0 ; i < 10000 ; i++ ){ i == 9999 && resolve() } console.log(2) }).then(function(){ console.log(5) }); console.log(3);
為什么結(jié)果是:1, 2, 3, 5, 4;而不是:1, 2, 3, 4, 5 ?
js 里面有宏任務(wù)(macrotask)和微任務(wù)(microtask)。
因?yàn)?setTimeout 是屬于 macrotask 的,而整個(gè) script 也是屬于一個(gè) macrotask,promise.then 回調(diào)是 microtask,執(zhí)行過(guò)程大概如下:
由于整個(gè) script 也屬于一個(gè) macrotask,由于會(huì)先執(zhí)行 macrotask 中的第一個(gè)任務(wù),再加上 promise 構(gòu)造函數(shù)因?yàn)槭峭降?,所以?huì)先打印出 1 和 2;
然后繼續(xù)同步執(zhí)行末尾的 console.log(3) 打印出 3;
此時(shí) setTimeout 被推進(jìn)到 macrotask 隊(duì)列中, promise.then 回調(diào)被推進(jìn)到 microtask 隊(duì)列中;
由于在第一步中已經(jīng)執(zhí)行完了第一個(gè) macrotask ,所以接下來(lái)會(huì)順序執(zhí)行所有的 microtask,也就是 promise.then 的回調(diào)函數(shù),從而打印出 5;
microtask 隊(duì)列中的任務(wù)已經(jīng)執(zhí)行完畢,繼續(xù)執(zhí)行剩下的 macrotask 隊(duì)列中的任務(wù),也就是 setTimeout,所以打印出 4。
ES6+ 面試知識(shí)文章那些必會(huì)用到的 ES6 精粹
最后前端硬核面試專題的完整版在此:前端硬核面試專題,包含:HTML + CSS + JS + ES6 + Webpack + Vue + React + Node + HTTPS + 數(shù)據(jù)結(jié)構(gòu)與算法 + Git 。
如果覺(jué)得本文還不錯(cuò),記得給個(gè) star , 你的 star 是我持續(xù)更新的動(dòng)力!。
聽(tīng)說(shuō)點(diǎn)收藏,不點(diǎn)贊的都是在耍流氓 -_-
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/106793.html
摘要:更是中高級(jí)面試過(guò)程中經(jīng)常會(huì)問(wèn)的技術(shù),無(wú)論你是否用過(guò),你都必須熟悉。下面我為大家準(zhǔn)備了一些常見(jiàn)的的面試題,一些是我經(jīng)常問(wèn)別人的,一些是我過(guò)去面試遇到的一些問(wèn)題,總結(jié)給大家,希望對(duì)大家能有所幫助。 想往高處走,怎么能不懂 Dubbo? Dubbo是國(guó)內(nèi)最出名的分布式服務(wù)框架,也是 Java 程序員必備的必會(huì)的框架之一。Dubbo 更是中高級(jí)面試過(guò)程中經(jīng)常會(huì)問(wèn)的技術(shù),無(wú)論你是否用過(guò),你都必須...
摘要:通過(guò)面試者的答案可以得知他都知道哪些開(kāi)發(fā)語(yǔ)言。這個(gè)問(wèn)題的答案能夠知道求職者有沒(méi)有可靠的資源,這些資源在未來(lái)開(kāi)展項(xiàng)目時(shí)可能會(huì)派上用場(chǎng)。對(duì)這個(gè)問(wèn)題的誠(chéng)實(shí)回答可以幫助面試官為求職者提供合適的團(tuán)隊(duì)。 翻譯:瘋狂的技術(shù)宅原文:https://www.indeed.com/hire/i... 不管你是面試官還是求職者,里面的思路都能讓你獲益匪淺。 你用 CSS 多久了? 解析: 這個(gè)問(wèn)題可以讓面...
摘要:引言半月刊第四期來(lái)啦,這段時(shí)間新增了道高頻面試題,今天就把最近半月匯總的面試題和部分答案發(fā)給大家,幫助大家查漏補(bǔ)缺,歡迎加群互相學(xué)習(xí)。更多更全的面試題和答案匯總在下面的項(xiàng)目中,點(diǎn)擊查看。引言 半月刊第四期來(lái)啦,這段時(shí)間 Daily-Interview-Question 新增了 14 道高頻面試題,今天就把最近半月匯總的面試題和部分答案發(fā)給大家,幫助大家查漏補(bǔ)缺,歡迎 加群 互相學(xué)習(xí)。 更多更...
摘要:作為面試官,我是如何甄別應(yīng)聘者的包裝程度語(yǔ)言和等其他語(yǔ)言的對(duì)比分析和主從復(fù)制的原理詳解和持久化的原理是什么面試中經(jīng)常被問(wèn)到的持久化與恢復(fù)實(shí)現(xiàn)故障恢復(fù)自動(dòng)化詳解哨兵技術(shù)查漏補(bǔ)缺最易錯(cuò)過(guò)的技術(shù)要點(diǎn)大掃盲意外宕機(jī)不難解決,但你真的懂?dāng)?shù)據(jù)恢復(fù)嗎每秒 作為面試官,我是如何甄別應(yīng)聘者的包裝程度Go語(yǔ)言和Java、python等其他語(yǔ)言的對(duì)比分析 Redis和MySQL Redis:主從復(fù)制的原理詳...
閱讀 4087·2021-10-09 09:43
閱讀 2933·2021-10-08 10:05
閱讀 2823·2021-09-08 10:44
閱讀 937·2019-08-30 15:52
閱讀 2899·2019-08-26 17:01
閱讀 3081·2019-08-26 13:54
閱讀 1723·2019-08-26 10:48
閱讀 879·2019-08-23 14:41