Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
、如何判斷一個變量是數(shù)組還是字符串?
使用typeof。
2、“\==”與“===”的區(qū)別?
"=="是相等運算符,會隱式轉(zhuǎn)換。 "==="是嚴(yán)格運算符,會判斷類型。
3、如何去除字符串中的所有空格?
function trim(str){ return str.replace(/\s|\xA0/g,"");
}
4、如何獲取當(dāng)前瀏覽的URL?
window.location.href
5、怎樣添加、刪除、移動、復(fù)制、創(chuàng)建和查找節(jié)點?
1)創(chuàng)建新節(jié)點
createDocumentFragment() //創(chuàng)建一個DOM片段
createElement() //創(chuàng)建一個具體的元素
createTextNode() //創(chuàng)建一個文本節(jié)點
2)添加、移除、替換、插入
appendChild() //添加
removeChild() //移除
replaceChild() //替換
insertBefore() //插入
3)查找
getElementsByTagName() //通過標(biāo)簽名稱
getElementsByName() //通過元素的Name屬性的值
getElementById() //通過元素Id,唯一性
實現(xiàn)一個函數(shù)clone,可以對JavaScript中的5種主要的數(shù)據(jù)類型(包括Number、String、Object、Array、Boolean)進(jìn)行值復(fù)制。
6、什么是閉包?
閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。
7、JS中const、let、var之間的區(qū)別
1)const定義的變量不可以修改,而且必須初始化。
2)var定義的變量可以修改,如果不初始化會輸出undefined,不會報錯。
3)let是塊級作用域,函數(shù)內(nèi)部使用let定義后,對函數(shù)外部無影響。
8、什么叫同源策略?有什么用?
同源策略是指域名,協(xié)議,端口相同,只有同源的腳本才會被執(zhí)行。
9、什么是Ajax? 適用于哪些應(yīng)用場景?
Ajax是一種在無需重新加載整個網(wǎng)頁的情況下,能夠更新部分網(wǎng)頁的技術(shù)。 如:無刷新判斷驗證碼是否輸入正確等。
10、Ajax請求時get和post有什么區(qū)別?
1)get參數(shù)放在url后面,post放在http body里面 2)有大小限制,get受url長度限制,post受內(nèi)存限制 3)安全問題,get明文傳輸不儂好 4)應(yīng)用不同,get用于獲取數(shù)據(jù),post提交數(shù)據(jù)
11、談?wù)凧sonp的原理?
動態(tài)創(chuàng)建script標(biāo)簽,回調(diào)函數(shù)
12、split()和join()函數(shù)的區(qū)別?
前者是切割成數(shù)組的形式,后者是將數(shù)組轉(zhuǎn)換成字符串
13、寫出3個使用this的典型應(yīng)用。
1)事件:如onclick this->發(fā)生事件的對象 2)構(gòu)造函數(shù):this->new 出來的object 3)call/apply:改變this
14、怎么使用JS改變元素的class?
object.ClassName = xxxx
15、普通事件與綁定事件有什么區(qū)別?
普通事件只支持單個事件,而事件綁定可以添加多個事件
16、請描述js里事件的三個階段。
捕獲、處于目標(biāo)階段、冒泡階段(IE8以下只支持冒泡)
17、數(shù)組方法pop() push() unshift() shift()都有什么用?
push():尾部添加 pop():尾部刪除 unshift():頭部添加 shift():頭部刪除
18、IE和DOM事件流的區(qū)別是什么?
1)執(zhí)行順序不一樣 2)參數(shù)不一樣 3)事件加不加on 4)this指向問題
19、call和apply有什么區(qū)別?
call與apply的區(qū)別就是參數(shù)寫法不同:
a.call(b,arg1,arg2,...) a.apply(b,[arg1,arg2,...])
20、什么是事件委托?
讓利用事件冒泡的原理,讓自己的所觸發(fā)的事件,讓他的父元素代替執(zhí)行!
21、JS中的本地對象,內(nèi)置對象和宿主對象分別有哪些?
本地對象:array obj regExp等可以new實例化的 內(nèi)置對象:Math等不可以實例化的 宿主對象:document window等瀏覽器自帶的
22、document load 和 document ready 的區(qū)別是什么?
load必須等到頁面內(nèi)包括圖片的所有元素加載完畢后才能執(zhí)行。 ready是DOM結(jié)構(gòu)繪制完畢后就執(zhí)行,不必等到加載完畢。
23、你怎么理解WebPack和Grunt和Gulp?
Webpack可以看做是模塊打包器,把你的代碼轉(zhuǎn)換成合適的格式供瀏覽器使用;
常用webpack構(gòu)建本地服務(wù)器,可以讓瀏覽器監(jiān)聽你代碼的修改,自動刷新現(xiàn)實后的結(jié)果;
Gulp/Grunt是一種能夠優(yōu)化前端開發(fā)的流程工具,而WebPack是一種模塊化的解決方案;
WebPack有4個配置選項,打包速度越快,負(fù)面作用就越大,會不利于調(diào)試,文件的執(zhí)行效率也有一定的影響;開發(fā)階段使用:eval-source-map:使用eval打包源文件模塊,在同一個文件中生成干凈的完整的source map。這個選項可以在不影響構(gòu)建速度的前提下生成完整的sourcemap,但是對打包后輸出的JS文件的執(zhí)行具有性能和安全的隱患。在開發(fā)階段這是一個非常好的選項,在生產(chǎn)階段則一定不要啟用這個選項;
babel是一個編譯js的平臺,把es6/es7轉(zhuǎn)換成瀏覽器支持的es5提供瀏覽器使用;
24、JS有哪幾種數(shù)據(jù)類型?
String,Number,Boolean,Null,Undefined;Object,Array,Function(引用類型)
25、什么是ES?ES和JS有什么聯(lián)系?
要講清楚這個問題,需要回顧歷史。1996年11月,JavaScript的創(chuàng)造者Netscape公司,決定將JavaScript提交給國際標(biāo)準(zhǔn)化組織ECMA,希望這種語言能夠成為國際標(biāo)準(zhǔn)。次年,ECMA發(fā)布262號標(biāo)準(zhǔn)文件(ECMA-262)的第一版,規(guī)定了瀏覽器腳本語言的標(biāo)準(zhǔn),并將這種語言稱為ECMAScript,這個版本就是1.0版。
該標(biāo)準(zhǔn)從一開始就是針對JavaScript語言制定的,但是之所以不叫JavaScript,有兩個原因。一是商標(biāo),Java是Sun公司的商標(biāo),根據(jù)授權(quán)協(xié)議,只有Netscape公司可以合法地使用JavaScript這個名字,且JavaScript本身也已經(jīng)被Netscape公司注冊為商標(biāo)。二是想體現(xiàn)這門語言的制定者是ECMA,不是Netscape,這樣有利于保證這門語言的開放性和中立性。
因此,ECMAScript和JavaScript的關(guān)系是,前者是后者的規(guī)格,后者是前者的一種實現(xiàn)(另外的ECMAScript方言還有Jscript和ActionScript)。在日常場合,這兩個詞是可以互換的。
26、ES6有哪些新特性?
箭頭操作符;對class的支持(constructor構(gòu)造函數(shù));不定參數(shù)...x;let和const關(guān)鍵字;for of遍歷;模塊的支持import;promise異步函數(shù)的處理模式(pending等待中;resolve返回成功,reject返回失敗);
27、typeof都會返回什么數(shù)據(jù)類型?
Object number function boolean underfind
28、請例舉3種強(qiáng)制類型轉(zhuǎn)換和2種隱式類型轉(zhuǎn)換。
29、截取字符串javastack的java?
30、如何規(guī)避javascript多人開發(fā)函數(shù)重名問題?
31、檢測一個變量是否是String類型有哪幾種方式?
1)typeof(obj) == 'string' 2)obj.constructor == String;
32、請說出至少三種減低頁面加載時間的方法。
1)壓縮css、js文件 2)合并js、css文件,減少http請求 3)外部js、css文件放在最底下 4)減少dom操作,盡可能用變量替代不必要的dom操作
33、請詳細(xì)解釋Ajax的工作原理。
1)創(chuàng)建ajax對象(XMLHttpRequest/ActiveXObject(Microsoft.XMLHttp)) 2)判斷數(shù)據(jù)傳輸方式(GET/POST) 3)打開鏈接 open() 4)發(fā)送 send() 5)當(dāng)ajax對象完成第四步(onreadystatechange)數(shù)據(jù)接收完成,判斷http響應(yīng)狀態(tài)(status)200-300之間或者304(緩存)執(zhí)行回調(diào)函數(shù)
34、JS中有哪幾種函數(shù)?
具名函數(shù)(命名函數(shù))和匿名函數(shù)。
35、請至少寫出3種創(chuàng)建函數(shù)的方式。
1)聲明函數(shù)
function fn1(){}
2)創(chuàng)建匿名函數(shù)表達(dá)式
var fn1 = function (){}
3)創(chuàng)建具名函數(shù)表達(dá)式
var fn1 = function javastack(){};
36、什么是跨域?解決跨域的方法有哪些?
由于瀏覽器同源策略,凡是發(fā)送請求url的協(xié)議、域名、端口三者之間任意一個與當(dāng)前頁面地址不同即為跨域。
解決方案:JSONP、CORS、代理、服務(wù)器實現(xiàn)等。
參考答案
防抖
觸發(fā)高頻事件后n秒內(nèi)函數(shù)只會執(zhí)行一次,如果n秒內(nèi)高頻事件再次被觸發(fā),則重新計算時間
每次觸發(fā)事件時都取消之前的延時調(diào)用方法
function debounce(fn) {
let timeout = null; // 創(chuàng)建一個標(biāo)記用來存放定時器的返回值
return function () {
clearTimeout(timeout); // 每當(dāng)用戶輸入的時候把前一個 setTimeout clear 掉
timeout = setTimeout(() => { // 然后又創(chuàng)建一個新的 setTimeout, 這樣就能保證輸入字符后的 interval 間隔內(nèi)如果還有字符輸入的話,就不會執(zhí)行 fn 函數(shù)
fn.apply(this, arguments);
}, 500);
};
}
function sayHi() {
console.log('防抖成功');
}
var inp = document.getElementById('inp');
inp.addEventListener('input', debounce(sayHi)); // 防抖
節(jié)流
高頻事件觸發(fā),但在n秒內(nèi)只會執(zhí)行一次,所以節(jié)流會稀釋函數(shù)的執(zhí)行頻率
每次觸發(fā)事件時都判斷當(dāng)前是否有等待執(zhí)行的延時函數(shù)
function throttle(fn) {
let canRun = true; // 通過閉包保存一個標(biāo)記
return function () {
if (!canRun) return; // 在函數(shù)開頭判斷標(biāo)記是否為true,不為true則return
canRun = false; // 立即設(shè)置為false
setTimeout(() => { // 將外部傳入的函數(shù)的執(zhí)行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout執(zhí)行完畢后再把標(biāo)記設(shè)置為true(關(guān)鍵)表示可以執(zhí)行下一次循環(huán)了。當(dāng)定時器沒有執(zhí)行的時候標(biāo)記永遠(yuǎn)是false,在開頭被return掉
canRun = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));
誤區(qū):我們經(jīng)常說get請求參數(shù)的大小存在限制,而post請求的參數(shù)大小是無限制的。
參考答案
實際上HTTP 協(xié)議從未規(guī)定 GET/POST 的請求長度限制是多少。對get請求參數(shù)的限制是來源與瀏覽器或web服務(wù)器,瀏覽器或web服務(wù)器限制了url的長度。為了明確這個概念,我們必須再次強(qiáng)調(diào)下面幾點:
補(bǔ)充補(bǔ)充一個get和post在緩存方面的區(qū)別:
可從IIFE、AMD、CMD、CommonJS、UMD、webpack(require.ensure)、ES Module、<script type="module"> 這幾個角度考慮。
參考答案
模塊化主要是用來抽離公共代碼,隔離作用域,避免變量沖突等。
IIFE:使用自執(zhí)行函數(shù)來編寫模塊化,特點:在一個單獨的函數(shù)作用域中執(zhí)行代碼,避免變量沖突。
(function(){
return {
data:[]
}
})()
AMD:使用requireJS 來編寫模塊化,特點:依賴必須提前聲明好。
define('./index.js',function(code){
// code 就是index.js 返回的內(nèi)容
})
CMD:使用seaJS 來編寫模塊化,特點:支持動態(tài)引入依賴文件。
define(function(require, exports, module) {
var indexCode = require('./index.js');
})
CommonJS:nodejs 中自帶的模塊化。
var fs = require('fs');
UMD:兼容AMD,CommonJS 模塊化語法。
webpack(require.ensure):webpack 2.x 版本中的代碼分割。
ES Modules:ES6 引入的模塊化,支持import 來引入另一個 js 。
import a from 'a';
參考答案
輸入 npm install 命令并敲下回車后,會經(jīng)歷如下幾個階段(以 npm 5.5.1 為例):
參考答案
ES5的繼承時通過prototype或構(gòu)造函數(shù)機(jī)制來實現(xiàn)。ES5的繼承實質(zhì)上是先創(chuàng)建子類的實例對象,然后再將父類的方法添加到this上(Parent.apply(this))。
ES6的繼承機(jī)制完全不同,實質(zhì)上是先創(chuàng)建父類的實例對象this(所以必須先調(diào)用父類的super()方法),然后再用子類的構(gòu)造函數(shù)修改this。
具體的:ES6通過class關(guān)鍵字定義類,里面有構(gòu)造方法,類之間通過extends關(guān)鍵字實現(xiàn)繼承。子類必須在constructor方法中調(diào)用super方法,否則新建實例報錯。因為子類沒有自己的this對象,而是繼承了父類的this對象,然后對其進(jìn)行加工。如果不調(diào)用super方法,子類得不到this對象。
ps:super關(guān)鍵字指代父類的實例,即父類的this對象。在子類構(gòu)造函數(shù)中,調(diào)用super后,才可使用this關(guān)鍵字,否則報錯。
參考答案
參考答案
**因為js是單線程的,瀏覽器遇到setTimeout或者setInterval會先執(zhí)行完當(dāng)前的代碼塊,在此之前會把定時器推入瀏覽器的待執(zhí)行事件隊列里面,等到瀏覽器執(zhí)行完當(dāng)前代碼之后會看一下事件隊列里面有沒有任務(wù),有的話才執(zhí)行定時器的代碼。**所以即使把定時器的時間設(shè)置為0還是會先執(zhí)行當(dāng)前的一些代碼。
function test(){
var aa = 0;
var testSet = setInterval(function(){
aa++;
console.log(123);
if(aa<10){
clearInterval(testSet);
}
},20);
var testSet1 = setTimeout(function(){
console.log(321)
},1000);
for(var i=0;i<10;i++){
console.log('test');
}
}
test()
輸出結(jié)果:
test //10次
undefined
123
321
參考答案
輸出:[1, NaN, NaN]
var new_array = arr.map(function callback(currentValue[, index[, array]]) { // Return element for new_array }[, thisArg])這個callback一共可以接收三個參數(shù),其中第一個參數(shù)代表當(dāng)前被處理的元素,而第二個參數(shù)代表該元素的索引。
參考答案
Doctype聲明于文檔最前面,告訴瀏覽器以何種方式來渲染頁面,這里有兩種模式,嚴(yán)格模式和混雜模式。
參考答案
fetch發(fā)送post請求的時候,總是發(fā)送2次,第一次狀態(tài)碼是204,第二次才成功?
原因很簡單,因為你用fetch的post請求的時候,導(dǎo)致fetch 第一次發(fā)送了一個Options請求,詢問服務(wù)器是否支持修改的請求頭,如果服務(wù)器支持,則在第二次中發(fā)送真正的請求。
history對象
Navigator對象
參考答案
共同點:都是保存在瀏覽器端,并且是同源的
補(bǔ)充說明一下cookie的作用:
參考答案
XSS(跨站腳本攻擊)是指攻擊者在返回的HTML中嵌入javascript腳本,為了減輕這些攻擊,需要在HTTP頭部配上,set-cookie:
結(jié)果應(yīng)該是這樣的:Set-Cookie=.....
參考答案
其中一個主要的區(qū)別在于瀏覽器的event loop 和nodejs的event loop 在處理異步事件的順序是不同的,nodejs中有micro event;其中Promise屬于micro event 該異步事件的處理順序就和瀏覽器不同.nodejs V11.0以上 這兩者之間的順序就相同了.
function test () {
console.log('start')
setTimeout(() => {
console.log('children2')
Promise.resolve().then(() => {console.log('children2-1')})
}, 0)
setTimeout(() => {
console.log('children3')
Promise.resolve().then(() => {console.log('children3-1')})
}, 0)
Promise.resolve().then(() => {console.log('children1')})
console.log('end')
}
test()
// 以上代碼在node11以下版本的執(zhí)行結(jié)果(先執(zhí)行所有的宏任務(wù),再執(zhí)行微任務(wù))
// start
// end
// children1
// children2
// children3
// children2-1
// children3-1
// 以上代碼在node11及瀏覽器的執(zhí)行結(jié)果(順序執(zhí)行宏任務(wù)和微任務(wù))
// start
// end
// children1
// children2
// children2-1
// children3
// children3-1
參考答案
https協(xié)議由 http + ssl 協(xié)議構(gòu)成,具體的鏈接過程可參考SSL或TLS握手的概述
中間人攻擊過程如下:
防范方法:
參考答案
(1). 減少HTTP請求數(shù)
這條策略基本上所有前端人都知道,而且也是最重要最有效的。都說要減少HTTP請求,那請求多了到底會怎么樣呢?首先,每個請求都是有成本的,既包 含時間成本也包含資源成本。一個完整的請求都需要經(jīng)過DNS尋址、與服務(wù)器建立連接、發(fā)送數(shù)據(jù)、等待服務(wù)器響應(yīng)、接收數(shù)據(jù)這樣一個“漫長”而復(fù)雜的過程。時間成本就是用戶需要看到或者“感受”到這個資源是必須要等待這個過程結(jié)束的,資源上由于每個請求都需要攜帶數(shù)據(jù),因此每個請求都需要占用帶寬。
另外,由于瀏覽器進(jìn)行并發(fā)請求的請求數(shù)是有上限的,因此請求數(shù)多了以后,瀏覽器需要分批進(jìn)行請求,因此會增加用戶的等待時間,會給 用戶造成站點速度慢這樣一個印象,即使可能用戶能看到的第一屏的資源都已經(jīng)請求完了,但是瀏覽器的進(jìn)度條會一直存在。減少HTTP請求數(shù)的主要途徑包括:
(2). 從設(shè)計實現(xiàn)層面簡化頁面
如果你的頁面像百度首頁一樣簡單,那么接下來的規(guī)則基本上都用不著了。保持頁面簡潔、減少資源的使用時最直接的。如果不是這樣,你的頁面需要華麗的皮膚,則繼續(xù)閱讀下面的內(nèi)容。
(3). 合理設(shè)置HTTP緩存
緩存的力量是強(qiáng)大的,恰當(dāng)?shù)木彺嬖O(shè)置可以大大的減少HTTP請求。以有啊首頁為例,當(dāng)瀏覽器沒有緩存的時候訪問一共會發(fā)出78個請求,共600多K 數(shù)據(jù)(如圖1.1),而當(dāng)?shù)诙卧L問即瀏覽器已緩存之后訪問則僅有10個請求,共20多K數(shù)據(jù)(如圖1.2)。(這里需要說明的是,如果直接F5刷新頁面 的話效果是不一樣的,這種情況下請求數(shù)還是一樣,不過被緩存資源的請求服務(wù)器是304響應(yīng),只有Header沒有Body,可以節(jié)省帶寬)
怎樣才算合理設(shè)置?原則很簡單,能緩存越多越好,能緩存越久越好。例如,很少變化的圖片資源可以直接通過HTTP Header中的Expires設(shè)置一個很長的過期頭;變化不頻繁而又可能會變的資源可以使用Last-Modifed來做請求驗證。盡可能的讓資源能夠 在緩存中待得更久。
(4). 資源合并與壓縮
如果可以的話,盡可能的將外部的腳本、樣式進(jìn)行合并,多個合為一個。另外,CSS、Javascript、Image都可以用相應(yīng)的工具進(jìn)行壓縮,壓縮后往往能省下不少空間。
(5). CSS Sprites
合并CSS圖片,減少請求數(shù)的又一個好辦法。
(6). Inline Images
使用data: URL scheme的方式將圖片嵌入到頁面或CSS中,如果不考慮資源管理上的問題的話,不失為一個好辦法。如果是嵌入頁面的話換來的是增大了頁面的體積,而且無法利用瀏覽器緩存。使用在CSS中的圖片則更為理想一些。
(7). Lazy Load Images
這條策略實際上并不一定能減少HTTP請求數(shù),但是卻能在某些條件下或者頁面剛加載時減少HTTP請求數(shù)。對于圖片而言,在頁面剛加載的時候可以只 加載第一屏,當(dāng)用戶繼續(xù)往后滾屏的時候才加載后續(xù)的圖片。這樣一來,假如用戶只對第一屏的內(nèi)容感興趣時,那剩余的圖片請求就都節(jié)省了。有啊首頁曾經(jīng)的做法 是在加載的時候把第一屏之后的圖片地址緩存在Textarea標(biāo)簽中,待用戶往下滾屏的時候才“惰性”加載。
參考答案
重繪(Repaint)和回流(Reflow)
重繪和回流是渲染步驟中的一小節(jié),但是這兩個步驟對于性能影響很大。
回流必定會發(fā)生重繪,重繪不一定會引發(fā)回流。回流所需的成本比重繪高的多,改變深層次的節(jié)點很可能導(dǎo)致父節(jié)點的一系列回流。
所以以下幾個動作可能會導(dǎo)致性能問題:
很多人不知道的是,重繪和回流其實和 Event loop 有關(guān)。
減少重繪和回流
參考答案
vue和react都是采用diff算法來對比新舊虛擬節(jié)點,從而更新節(jié)點。在vue的diff函數(shù)中(建議先了解一下diff算法過程)。在交叉對比中,當(dāng)新節(jié)點跟舊節(jié)點頭尾交叉對比沒有結(jié)果時,會根據(jù)新節(jié)點的key去對比舊節(jié)點數(shù)組中的key,從而找到相應(yīng)舊節(jié)點(這里對應(yīng)的是一個key => index 的map映射)。如果沒找到就認(rèn)為是一個新增節(jié)點。而如果沒有key,那么就會采用遍歷查找的方式去找到對應(yīng)的舊節(jié)點。一種一個map映射,另一種是遍歷查找。相比而言。map映射的速度更快。vue部分源碼如下:
// vue項目 src/core/vdom/patch.js -488行
// 以下是為了閱讀性進(jìn)行格式化后的代碼
// oldCh 是一個舊虛擬節(jié)點數(shù)組
if (isUndef(oldKeyToIdx)) {
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
}
if(isDef(newStartVnode.key)) {
// map 方式獲取
idxInOld = oldKeyToIdx[newStartVnode.key]
} else {
// 遍歷方式獲取
idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
}
創(chuàng)建map函數(shù)
function createKeyToOldIdx (children, beginIdx, endIdx) {
let i, key
const map = {}
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key
if (isDef(key)) map[key] = i
}
return map
}
遍歷尋找
// sameVnode 是對比新舊節(jié)點是否相同的函數(shù)
function findIdxInOld (node, oldCh, start, end) {
for (let i = start; i < end; i++) {
const c = oldCh[i]
if (isDef(c) && sameVnode(node, c)) return i
}
}
參考答案
在React中,如果是由React引發(fā)的事件處理(比如通過onClick引發(fā)的事件處理),調(diào)用setState不會同步更新this.state,除此之外的setState調(diào)用會同步執(zhí)行this.state。所謂“除此之外”,指的是繞過React通過addEventListener直接添加的事件處理函數(shù),還有通過setTimeout/setInterval產(chǎn)生的異步調(diào)用。
**原因:**在React的setState函數(shù)實現(xiàn)中,會根據(jù)一個變量isBatchingUpdates判斷是直接更新this.state還是放到隊列中回頭再說,而isBatchingUpdates默認(rèn)是false,也就表示setState會同步更新this.state,但是,有一個函數(shù)batchedUpdates,這個函數(shù)會把isBatchingUpdates修改為true,而當(dāng)React在調(diào)用事件處理函數(shù)之前就會調(diào)用這個batchedUpdates,造成的后果,就是由React控制的事件處理過程setState不會同步更新this.state。
class Example extends React.Component {
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 1 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 2 次 log
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 3 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 4 次 log
}, 0);
}
render() {
return null;
}
};
1、第一次和第二次都是在 react 自身生命周期內(nèi),觸發(fā)時 isBatchingUpdates 為 true,所以并不會直接執(zhí)行更新 state,而是加入了 dirtyComponents,所以打印時獲取的都是更新前的狀態(tài) 0。
2、兩次 setState 時,獲取到 this.state.val 都是 0,所以執(zhí)行時都是將 0 設(shè)置成 1,在 react 內(nèi)部會被合并掉,只執(zhí)行一次。設(shè)置完成后 state.val 值為 1。
3、setTimeout 中的代碼,觸發(fā)時 isBatchingUpdates 為 false,所以能夠直接進(jìn)行更新,所以連著輸出 2,3。
輸出: 0 0 2 3
參考答案
虛擬dom相當(dāng)于在js和真實dom中間加了一個緩存,利用dom diff算法避免了沒有必要的dom操作,從而提高性能。
具體實現(xiàn)步驟如下:
用 JavaScript 對象結(jié)構(gòu)表示 DOM 樹的結(jié)構(gòu);然后用這個樹構(gòu)建一個真正的 DOM 樹,插到文檔當(dāng)中
當(dāng)狀態(tài)變更的時候,重新構(gòu)造一棵新的對象樹。然后用新的樹和舊的樹進(jìn)行比較,記錄兩棵樹差異
把2所記錄的差異應(yīng)用到步驟1所構(gòu)建的真正的DOM樹上,視圖就更新了。
參考答案
結(jié)構(gòu):display:none: 會讓元素完全從渲染樹中消失,渲染的時候不占據(jù)任何空間, 不能點擊, visibility: hidden:不會讓元素從渲染樹消失,渲染元素繼續(xù)占據(jù)空間,只是內(nèi)容不可見,不能點擊 opacity: 0: 不會讓元素從渲染樹消失,渲染元素繼續(xù)占據(jù)空間,只是內(nèi)容不可見,可以點擊
繼承:display: none:是非繼承屬性,子孫節(jié)點消失由于元素從渲染樹消失造成,通過修改子孫節(jié)點屬性無法顯示。visibility: hidden:是繼承屬性,子孫節(jié)點消失由于繼承了hidden,通過設(shè)置visibility: visible;可以讓子孫節(jié)點顯式。
性能:displaynone : 修改元素會造成文檔回流,讀屏器不會讀取display: none元素內(nèi)容,性能消耗較大 visibility:hidden: 修改元素只會造成本元素的重繪,性能消耗較少讀屏器讀取visibility: hidden元素內(nèi)容 opacity: 0 :修改元素會造成重繪,性能消耗較少
聯(lián)系:它們都能讓元素不可見
參考答案
常用的一般為三種.clearfix, clear:both,overflow:hidden;
比較好是 .clearfix,偽元素萬金油版本,后兩者有局限性.
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
<!--
為毛沒有 zoom ,_height 這些,IE6,7這類需要 csshack 不再我們考慮之內(nèi)了
.clearfix 還有另外一種寫法,
-->
.clearfix:before, .clearfix:after {
content:"";
display:table;
}
.clearfix:after{
clear:both;
overflow:hidden;
}
.clearfix{
zoom:1;
}
<!--
用display:table 是為了避免外邊距margin重疊導(dǎo)致的margin塌陷,
內(nèi)部元素默認(rèn)會成為 table-cell 單元格的形式
-->
clear:both:若是用在同一個容器內(nèi)相鄰元素上,那是賊好的,有時候在容器外就有些問題了, 比如相鄰容器的包裹層元素塌陷
overflow:hidden:這種若是用在同個容器內(nèi),可以形成 BFC避免浮動造成的元素塌陷
參考答案
概念:將多個小圖片拼接到一個圖片中。通過 background-position 和元素尺寸調(diào)節(jié)需要顯示的背景圖案。
優(yōu)點:
缺點:
參考答案
參考答案
block元素特點:
1.處于常規(guī)流中時,如果width沒有設(shè)置,會自動填充滿父容器 2.可以應(yīng)用margin/padding 3.在沒有設(shè)置高度的情況下會擴(kuò)展高度以包含常規(guī)流中的子元素 4.處于常規(guī)流中時布局時在前后元素位置之間(獨占一個水平空間) 5.忽略vertical-align
inline元素特點
1.水平方向上根據(jù)direction依次布局
2.不會在元素前后進(jìn)行換行
3.受white-space控制
4.margin/padding在豎直方向上無效,水平方向上有效
5.width/height屬性對非替換行內(nèi)元素?zé)o效,寬度由元素內(nèi)容決定
6.非替換行內(nèi)元素的行框高由line-height確定,替換行內(nèi)元素的行框高由height,margin,padding,border決定 7.浮動或絕對定位時會轉(zhuǎn)換為block8.vertical-align屬性生效
參考答案
/**
* 在標(biāo)準(zhǔn)瀏覽器下使用
* 1 content內(nèi)容為空格用于修復(fù)opera下文檔中出現(xiàn)
* contenteditable屬性時在清理浮動元素上下的空白
* 2 使用display使用table而不是block:可以防止容器和
* 子元素top-margin折疊,這樣能使清理效果與BFC,IE6/7
* zoom: 1;一致
**/
.clearfix:before,
.clearfix:after {
content: " "; /* 1 */
display: table; /* 2 */
}
.clearfix:after {
clear: both;
}
/**
* IE 6/7下使用
* 通過觸發(fā)hasLayout實現(xiàn)包含浮動
**/
.clearfix {
*zoom: 1;
}
參考答案
GIF:
JPEG:
PNG:
參考答案
參考答案
參考答案
七種數(shù)據(jù)類型
(ES6之前)其中5種為基本類型:string,number,boolean,null,undefined,
ES6出來的Symbol也是原始數(shù)據(jù)類型 ,表示獨一無二的值
Object為引用類型(范圍挺大),也包括數(shù)組、函數(shù),
參考答案
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
輸出結(jié)果是:
1
2
4
3
promise構(gòu)造函數(shù)是同步執(zhí)行的,then方法是異步執(zhí)行的
Promise new的時候會立即執(zhí)行里面的代碼 then是微任務(wù) 會在本次任務(wù)執(zhí)行完的時候執(zhí)行 setTimeout是宏任務(wù) 會在下次任務(wù)執(zhí)行的時候執(zhí)行
參考答案
工廠模式
簡單的工廠模式可以理解為解決多個相似的問題;
function CreatePerson(name,age,sex) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sex = sex;
obj.sayName = function(){
return this.name;
}
return obj;
}
var p1 = new CreatePerson("longen",'28','男');
var p2 = new CreatePerson("tugenhua",'27','女');
console.log(p1.name); // longen
console.log(p1.age); // 28
console.log(p1.sex); // 男
console.log(p1.sayName()); // longen
console.log(p2.name); // tugenhua
console.log(p2.age); // 27
console.log(p2.sex); // 女
console.log(p2.sayName()); // tugenhua
單例模式
只能被實例化(構(gòu)造函數(shù)給實例添加屬性與方法)一次
// 單體模式
var Singleton = function(name){
this.name = name;
};
Singleton.prototype.getName = function(){
return this.name;
}
// 獲取實例對象
var getInstance = (function() {
var instance = null;
return function(name) {
if(!instance) {//相當(dāng)于一個一次性閥門,只能實例化一次
instance = new Singleton(name);
}
return instance;
}
})();
// 測試單體模式的實例,所以a===b
var a = getInstance("aa");
var b = getInstance("bb");
沙箱模式
將一些函數(shù)放到自執(zhí)行函數(shù)里面,但要用閉包暴露接口,用變量接收暴露的接口,再調(diào)用里面的值,否則無法使用里面的值
let sandboxModel=(function(){
function sayName(){};
function sayAge(){};
return{
sayName:sayName,
sayAge:sayAge
}
})()
發(fā)布者訂閱模式
就例如如我們關(guān)注了某一個公眾號,然后他對應(yīng)的有新的消息就會給你推送,
//發(fā)布者與訂閱模式
var shoeObj = {}; // 定義發(fā)布者
shoeObj.list = []; // 緩存列表 存放訂閱者回調(diào)函數(shù)
// 增加訂閱者
shoeObj.listen = function(fn) {
shoeObj.list.push(fn); // 訂閱消息添加到緩存列表
}
// 發(fā)布消息
shoeObj.trigger = function() {
for (var i = 0, fn; fn = this.list[i++];) {
fn.apply(this, arguments);//第一個參數(shù)只是改變fn的this,
}
}
// 小紅訂閱如下消息
shoeObj.listen(function(color, size) {
console.log("顏色是:" + color);
console.log("尺碼是:" + size);
});
// 小花訂閱如下消息
shoeObj.listen(function(color, size) {
console.log("再次打印顏色是:" + color);
console.log("再次打印尺碼是:" + size);
});
shoeObj.trigger("紅色", 40);
shoeObj.trigger("黑色", 42);
代碼實現(xiàn)邏輯是用數(shù)組存貯訂閱者, 發(fā)布者回調(diào)函數(shù)里面通知的方式是遍歷訂閱者數(shù)組,并將發(fā)布者內(nèi)容傳入訂閱者數(shù)組
參考答案
1.字面量
let obj={'name':'張三'}
2.Object構(gòu)造函數(shù)創(chuàng)建
let Obj=new Object()
Obj.name='張三'
3.使用工廠模式創(chuàng)建對象
function createPerson(name){
var o = new Object();
o.name = name;
};
return o;
}
var person1 = createPerson('張三');
4.使用構(gòu)造函數(shù)創(chuàng)建對象
function Person(name){
this.name = name;
}
var person1 = new Person('張三');
參考答案
HTML中與javascript交互是通過事件驅(qū)動來實現(xiàn)的,例如鼠標(biāo)點擊事件onclick、頁面的滾動事件onscroll等等,可以向文檔或者文檔中的元素添加事件偵聽器來預(yù)訂事件。想要知道這些事件是在什么時候進(jìn)行調(diào)用的,就需要了解一下“事件流”的概念。
什么是事件流:事件流描述的是從頁面中接收事件的順序,DOM2級事件流包括下面幾個階段。
addEventListener:addEventListener是DOM2 級事件新增的指定事件處理程序的操作,這個方法接收3個參數(shù):要處理的事件名、作為事件處理程序的函數(shù)和一個布爾值。最后這個布爾值參數(shù)如果是true,表示在捕獲階段調(diào)用事件處理程序;如果是false,表示在冒泡階段調(diào)用事件處理程序。
IE只支持事件冒泡。
參考答案
獲取一個對象的原型,在chrome中可以通過__proto__的形式,或者在ES6中可以通過Object.getPrototypeOf的形式。
那么Function.proto是什么么?也就是說Function由什么對象繼承而來,我們來做如下判別。
Function.__proto__==Object.prototype //false
Function.__proto__==Function.prototype//true
我們發(fā)現(xiàn)Function的原型也是Function。
我們用圖可以來明確這個關(guān)系:
參考答案
這里來舉個栗子,以Object為例,我們常用的Object便是一個構(gòu)造函數(shù),因此我們可以通過它構(gòu)建實例。
// 實例
const instance = new Object()
則此時, 實例為instance, 構(gòu)造函數(shù)為Object,我們知道,構(gòu)造函數(shù)擁有一個prototype的屬性指向原型,因此原型為:
// 原型
const prototype = Object.prototype
這里我們可以來看出三者的關(guān)系:
實例.__proto__ === 原型
原型.constructor === 構(gòu)造函數(shù)
構(gòu)造函數(shù).prototype === 原型
// 這條線其實是是基于原型進(jìn)行獲取的,可以理解成一條基于原型的映射線
// 例如:
// const o = new Object()
// o.constructor === Object --> true
// o.__proto__ = null;
// o.constructor === Object --> false
實例.constructor === 構(gòu)造函數(shù)
參考答案
在 JS 中,繼承通常指的便是 原型鏈繼承,也就是通過指定原型,并可以通過原型鏈繼承原型上的屬性或者方法。
參考答案
在函數(shù)式編程中,函數(shù)是一等公民。那么函數(shù)柯里化是怎樣的呢?
函數(shù)柯里化指的是將能夠接收多個參數(shù)的函數(shù)轉(zhuǎn)化為接收單一參數(shù)的函數(shù),并且返回接收余下參數(shù)且返回結(jié)果的新函數(shù)的技術(shù)。
函數(shù)柯里化的主要作用和特點就是參數(shù)復(fù)用、提前返回和延遲執(zhí)行。
在一個函數(shù)中,首先填充幾個參數(shù),然后再返回一個新的函數(shù)的技術(shù),稱為函數(shù)的柯里化。通常可用于在不侵入函數(shù)的前提下,為函數(shù) 預(yù)置通用參數(shù),供多次重復(fù)調(diào)用。
const add = function add(x) {
return function (y) {
return x + y
}
}
const add1 = add(1)
add1(2) === 3
add1(20) === 21
參考答案
call 和 apply 都是為了解決改變 this 的指向。作用都是相同的,只是傳參的方式不同。
除了第一個參數(shù)外,call 可以接收一個參數(shù)列表,apply 只接受一個參數(shù)數(shù)組。
let a = {
value: 1
}
function getValue(name, age) {
console.log(name)
console.log(age)
console.log(this.value)
}
getValue.call(a, 'yck', '24')
getValue.apply(a, ['yck', '24'])
bind和其他兩個方法作用也是一致的,只是該方法會返回一個函數(shù)。并且我們可以通過 bind實現(xiàn)柯里化。
(下面是對這三個方法的擴(kuò)展介紹)
如何實現(xiàn)一個 bind 函數(shù)
對于實現(xiàn)以下幾個函數(shù),可以從幾個方面思考
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var _this = this
var args = [...arguments].slice(1)
// 返回一個函數(shù)
return function F() {
// 因為返回了一個函數(shù),我們可以 new F(),所以需要判斷
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}
如何實現(xiàn)一個call函數(shù)
Function.prototype.myCall = function (context) {
var context = context || window
// 給 context 添加一個屬性
// getValue.call(a, 'yck', '24') => a.fn = getValue
context.fn = this
// 將 context 后面的參數(shù)取出來
var args = [...arguments].slice(1)
// getValue.call(a, 'yck', '24') => a.fn('yck', '24')
var result = context.fn(...args)
// 刪除 fn
delete context.fn
return result
}
如何實現(xiàn)一個apply函數(shù)
Function.prototype.myApply = function (context) {
var context = context || window
context.fn = this
var result
// 需要判斷是否存儲第二個參數(shù)
// 如果存在,就將第二個參數(shù)展開
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
參考答案
function a() {
return () => {
return () => {
console.log(this)
}
}
}
console.log(a()()())
箭頭函數(shù)其實是沒有 this的,這個函數(shù)中的 this只取決于他外面的第一個不是箭頭函數(shù)的函數(shù)的 this。在這個例子中,因為調(diào)用 a符合前面代碼中的第一個情況,所以 this是 window。并且 this一旦綁定了上下文,就不會被任何代碼改變。
function sayHi() {
console.log(name);
console.log(age);
var name = "Lydia";
let age = 21;
}
sayHi();
參考答案
在函數(shù)中,我們首先使用var關(guān)鍵字聲明了name變量。這意味著變量在創(chuàng)建階段會被提升(JavaScript會在創(chuàng)建變量創(chuàng)建階段為其分配內(nèi)存空間),默認(rèn)值為undefined,直到我們實際執(zhí)行到使用該變量的行。我們還沒有為name變量賦值,所以它仍然保持undefined的值。
使用let關(guān)鍵字(和const)聲明的變量也會存在變量提升,但與var不同,初始化沒有被提升。在我們聲明(初始化)它們之前,它們是不可訪問的。這被稱為“暫時死區(qū)”。當(dāng)我們在聲明變量之前嘗試訪問變量時,JavaScript會拋出一個ReferenceError。
關(guān)于let的是否存在變量提升,我們何以用下面的例子來驗證:
let name = 'ConardLi'
{
console.log(name) // Uncaught ReferenceError: name is not defined
let name = 'code秘密花園'
}
let變量如果不存在變量提升,console.log(name)就會輸出ConardLi,結(jié)果卻拋出了ReferenceError,那么這很好的說明了,let也存在變量提升,但是它存在一個“暫時死區(qū)”,在變量未初始化或賦值前不允許訪問。
變量的賦值可以分為三個階段:
關(guān)于let、var和function:
var a = 10;
(function () {
console.log(a)
a = 5
console.log(window.a)
var a = 20;
console.log(a)
})()
依次輸出:undefined -> 10 -> 20
在立即執(zhí)行函數(shù)中,var a = 20; 語句定義了一個局部變量 a,由于js的變量聲明提升機(jī)制,局部變量a的聲明會被提升至立即執(zhí)行函數(shù)的函數(shù)體最上方,且由于這樣的提升并不包括賦值,因此第一條打印語句會打印undefined,最后一條語句會打印20。
由于變量聲明提升,a = 5; 這條語句執(zhí)行時,局部的變量a已經(jīng)聲明,因此它產(chǎn)生的效果是對局部的變量a賦值,此時window.a 依舊是最開始賦值的10,
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor;
}
constructor({ newColor = "green" } = {}) {
this.newColor = newColor;
}
}
const freddie = new Chameleon({ newColor: "purple" });
freddie.colorChange("orange");
答案: D
colorChange方法是靜態(tài)的。靜態(tài)方法僅在創(chuàng)建它們的構(gòu)造函數(shù)中存在,并且不能傳遞給任何子級。由于freddie是一個子級對象,函數(shù)不會傳遞,所以在freddie實例上不存在freddie方法:拋出TypeError。
var a = ?;
if(a == 1 && a == 2 && a == 3){
conso.log(1);
}
參考答案
因為==會進(jìn)行隱式類型轉(zhuǎn)換 所以我們重寫toString方法就可以了
var a = {
i: 1,
toString() {
return a.i++;
}
}
if( a == 1 && a == 2 && a == 3 ) {
console.log(1);
}
var obj = {
'2': 3,
'3': 4,
'length': 2,
'splice': Array.prototype.splice,
'push': Array.prototype.push
}
obj.push(1)
obj.push(2)
console.log(obj)
參考答案
1.使用第一次push,obj對象的push方法設(shè)置 obj[2]=1;obj.length+=12.使用第二次push,obj對象的push方法設(shè)置 obj[3]=2;obj.length+=13.使用console.log輸出的時候,因為obj具有 length 屬性和 splice 方法,故將其作為數(shù)組進(jìn)行打印 4.打印時因為數(shù)組未設(shè)置下標(biāo)為 0 1 處的值,故打印為empty,主動 obj[0] 獲取為 undefined
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x)
console.log(b.x)
參考答案
undefined {n:2}
首先,a和b同時引用了{(lán)n:2}對象,接著執(zhí)行到a.x = a = {n:2}語句,盡管賦值是從右到左的沒錯,但是.的優(yōu)先級比=要高,所以這里首先執(zhí)行a.x,相當(dāng)于為a(或者b)所指向的{n:1}對象新增了一個屬性x,即此時對象將變?yōu)閧n:1;x:undefined}。之后按正常情況,從右到左進(jìn)行賦值,此時執(zhí)行a ={n:2}的時候,a的引用改變,指向了新對象{n:2},而b依然指向的是舊對象。之后執(zhí)行a.x = {n:2}的時候,并不會重新解析一遍a,而是沿用最初解析a.x時候的a,也即舊對象,故此時舊對象的x的值為{n:2},舊對象為 {n:1;x:{n:2}},它被b引用著。后面輸出a.x的時候,又要解析a了,此時的a是指向新對象的a,而這個新對象是沒有x屬性的,故訪問時輸出undefined;而訪問b.x的時候,將輸出舊對象的x的值,即{n:2}。
function checkAge(data) {
if (data === { age: 18 }) {
console.log("You are an adult!");
} else if (data == { age: 18 }) {
console.log("You are still an adult.");
} else {
console.log(`Hmm.. You don't have an age I guess`);
}
}
checkAge({ age: 18 });
參考答案
Hmm.. You don't have an age I guess
在比較相等性,原始類型通過它們的值進(jìn)行比較,而對象通過它們的引用進(jìn)行比較。JavaScript檢查對象是否具有對內(nèi)存中相同位置的引用。
我們作為參數(shù)傳遞的對象和我們用于檢查相等性的對象在內(nèi)存中位于不同位置,所以它們的引用是不同的。
這就是為什么{ age: 18 } === { age: 18 }和 { age: 18 } == { age: 18 }返回 false的原因。
const obj = { 1: "a", 2: "b", 3: "c" };
const set = new Set([1, 2, 3, 4, 5]);
obj.hasOwnProperty("1");
obj.hasOwnProperty(1);
set.has("1");
set.has(1);
參考答案
true` `true` `false` `true
所有對象鍵(不包括Symbols)都會被存儲為字符串,即使你沒有給定字符串類型的鍵。這就是為什么obj.hasOwnProperty('1')也返回true。
上面的說法不適用于Set。在我們的Set中沒有“1”:set.has('1')返回false。它有數(shù)字類型1,set.has(1)返回true。
// example 1
var a={}, b='123', c=123;
a[b]='b';
a[c]='c';
console.log(a[b]);
---------------------
// example 2
var a={}, b=Symbol('123'), c=Symbol('123');
a[b]='b';
a[c]='c';
console.log(a[b]);
---------------------
// example 3
var a={}, b={key:'123'}, c={key:'456'};
a[b]='b';
a[c]='c';
console.log(a[b]);
參考答案
這題考察的是對象的鍵名的轉(zhuǎn)換。
// example 1
var a={}, b='123', c=123;
a[b]='b';
// c 的鍵名會被轉(zhuǎn)換成字符串'123',這里會把 b 覆蓋掉。
a[c]='c';
// 輸出 c
console.log(a[b]);
// example 2
var a={}, b=Symbol('123'), c=Symbol('123');
// b 是 Symbol 類型,不需要轉(zhuǎn)換。
a[b]='b';
// c 是 Symbol 類型,不需要轉(zhuǎn)換。任何一個 Symbol 類型的值都是不相等的,所以不會覆蓋掉 b。
a[c]='c';
// 輸出 b
console.log(a[b]);
// example 3
var a={}, b={key:'123'}, c={key:'456'};
// b 不是字符串也不是 Symbol 類型,需要轉(zhuǎn)換成字符串。
// 對象類型會調(diào)用 toString 方法轉(zhuǎn)換成字符串 [object Object]。
a[b]='b';
// c 不是字符串也不是 Symbol 類型,需要轉(zhuǎn)換成字符串。
// 對象類型會調(diào)用 toString 方法轉(zhuǎn)換成字符串 [object Object]。這里會把 b 覆蓋掉。
a[c]='c';
// 輸出 c
console.log(a[b]);
(() => {
let x, y;
try {
throw new Error();
} catch (x) {
(x = 1), (y = 2);
console.log(x);
}
console.log(x);
console.log(y);
})();
參考答案
1` `undefined` `2
catch塊接收參數(shù)x。當(dāng)我們傳遞參數(shù)時,這與變量的x不同。這個變量x是屬于catch作用域的。
之后,我們將這個塊級作用域的變量設(shè)置為1,并設(shè)置變量y的值。現(xiàn)在,我們打印塊級作用域的變量x,它等于1。
在catch塊之外,x仍然是undefined,而y是2。當(dāng)我們想在catch塊之外的console.log(x)時,它返回undefined,而y返回2。
function Foo() {
Foo.a = function() {
console.log(1)
}
this.a = function() {
console.log(2)
}
}
Foo.prototype.a = function() {
console.log(3)
}
Foo.a = function() {
console.log(4)
}
Foo.a();
let obj = new Foo();
obj.a();
Foo.a();
參考答案
輸出順序是 4 2 1
function Foo() {
Foo.a = function() {
console.log(1)
}
this.a = function() {
console.log(2)
}
}
// 以上只是 Foo 的構(gòu)建方法,沒有產(chǎn)生實例,此刻也沒有執(zhí)行
Foo.prototype.a = function() {
console.log(3)
}
// 現(xiàn)在在 Foo 上掛載了原型方法 a ,方法輸出值為 3
Foo.a = function() {
console.log(4)
}
// 現(xiàn)在在 Foo 上掛載了直接方法 a ,輸出值為 4
Foo.a();
// 立刻執(zhí)行了 Foo 上的 a 方法,也就是剛剛定義的,所以
// # 輸出 4
let obj = new Foo();
/* 這里調(diào)用了 Foo 的構(gòu)建方法。Foo 的構(gòu)建方法主要做了兩件事:
1. 將全局的 Foo 上的直接方法 a 替換為一個輸出 1 的方法。
2. 在新對象上掛載直接方法 a ,輸出值為 2。
*/
obj.a();
// 因為有直接方法 a ,不需要去訪問原型鏈,所以使用的是構(gòu)建方法里所定義的 this.a,
// # 輸出 2
Foo.a();
// 構(gòu)建方法里已經(jīng)替換了全局 Foo 上的 a 方法,所以
// # 輸出 1
作者:靜觀流葉
原文地址:https://mp.weixin.qq.com/s/PTSaytcf3xgOp6C9l3Pvjw
<input type="button" />
<input type="submit" />
<input type="rest" />
<button />
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。