便利蜂2019秋季(11月)web前端筆試題
前端技能試題v2.6
一、單選題(總共8分,每題2分)
1、以下不屬於HTTP緩存控制協議頭是( )
A. Cache-Control B. Content-Type C.Etag D.Vary
考點:web緩存只http技術
答案:B
分析:已經系統了解了web緩存策略,總結了筆記,可以點此查看。Content-Type是響應頭告訴瀏覽器返回的資源的MIME類型。
2、cookie特性下列說法正確的是( )
A. cookie沒有長度限制,可以無限存儲
B. 瀏覽器可以獲取任一域名的cookie
C. cookie不需要手動處理,請求時會自動攜帶
D. 瀏覽器請求image時不會攜帶cookie
考點:web緩存只本地存儲技術中的 cookie
答案:D
分析:cookie的個小、條數都有限制。不同瀏覽器表現也不一值。
大概都給4kb空間,每個域名50條左右。超出這閥門后,將會被忽略
cookie獲取有嚴格限制,不同域名(domain)下無法取的,同域名不同路徑下(pach)下也不能取得。必須是同域名,和同路徑(以及父子路徑)
只要設置了cookie,瀏覽器每次請求均會自己攜帶。所以c對,d不對
3、cors中不屬於簡單請求的請求類型是()
A. GET B. HEAD C.POST D.DELETE
考點:跨域技術之cors
答案:C
分析:CORS即跨來源資源共享,通俗說就是我們所熟知的跨域請求。
眾所周知,在以前,跨域可以采用代理、JSONP等方式,而在Modern瀏覽器面前,這些終將成為過去式,因為有了CORS。
CORS可以分成兩種:簡單請求、復雜請求。一個簡單的請求大致如下
// HTTP方法是下列之一
HEAD
GET
POST
// HTTP頭信息不超出以下幾種字段
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type,但僅能是下列之一
application/x-www-form-urlencoded
multipart/form-data
text/plain
4、關於https以下描述錯誤的是()
A. http是超文本傳輸協議,信息是明文傳輸。https則是具有安全性的ssl/tsl加密傳輸協議
B. https一直使用非對稱加密協議來保證數據安全
C. http默認使用80端口,https默認使用443端口來進行通信
D. https公鑰默認使用CA證書進行簽名方式防止被修改
考點:網絡傳輸協議之https
答案:D
分析:考察https相關知識點。https證書需要自己申請,默認沒有
二、多選題(總分20分,每題4分)
1、已知
// finally方法的回調函數不接受任何參數,所以data參數無意義,永遠是undefined fn.finally((data)=>{ console.log(data); });
請選擇正確的fn,保證打印輸出為‘我愛中國’()
A.
fn = new Promise((res,rej)=>{ setTimeout(()=>{ res('我愛中國'); }); // 第一步: // promise一旦狀態改變就不會再變,所以上邊定時器沒有意義,將只會執行rej, // 加了個return是防止構造函數內之后的代碼執行,這里return后邊沒代碼,所以沒有意義,干擾視覺 return rej('我愛中國'); }).then( data => { return Promise.resolve(data); }, data => { // 第二步: // 這里拋出的異常,又沒有catch處理,所以會報錯,此函數內程序終止,但是不影響外邊的(比如finally) throw 'I LOVE CHINA'; } ) //第三步執行題目代碼finally // 最終執行結果:先打印undefined,再報錯
B.
//沒有then處理狀態改變,所以resolve也無意義,最后執行題目的finally fn = Promise.resolve('我愛中國'); // 最終執行結果:打印undefined
C.
//觸發了reject狀態, //但是then卻沒有第二個回調(其實連then都沒有),所以會走到catch,並將值傳入catch的回調內 fn = Promise.reject('我愛中國').catch((data)=>{//所以data是'我愛中國' Promise.resolve(data);//但是這里又定義了個promise對象,最后也沒人使用,所以這個也沒有意義 }) //接着走題目的finally //最終執行結果:打印undefined
D.
//沒有then的第二個參數,也沒有catch,所以只會執行finall,最終打印undefined fn = Promise.reject('我愛中國');
答案:這題是個坑,沒有一個對的選項
分析:
/* Promise有三種狀態,pending(進行中)、fulfilled(已成功,也叫做resolve)和rejected(已失敗) 其狀態一旦改變,就不會再變 */ let pm = new Promise((resolve,reject)=>{ }); /* Promise有3個實例方法then、catch、finally then:狀態變更會執行此函數,此函數傳入兩個回調(非必須全傳) */ pm .then(()=>{ //狀態為resolve的回調 },()=>{ //狀態為rejected的回調 }) .catch((e)=>{ //用於補貨構造及其3個實例函數的異常,需要注意的是如果then中沒有第二個參數也會走到catch }) .finally(()=>{ //不管promise最后的狀態,在執行完then或catch指定的回調后,都會執行finally方法指定的回調 })
考點:es6的Promise
2、以下JSX組件使用方式寫法錯誤的是()
A. <MyComponents.DataPicker color='blue'/>
B. <hello toWhat='world'/>
C. <components[props.storyType] store={props.story}/>
D. <Greeting {...props}/>
答案:B C
A屬於點表示法,比如MyComponents是一個對象,里邊有很多導出jsx元素的方法
const MyComponents = { DataPicker:function(props){return '<div></div>'} }
B選項不對,jsx明確規定,為區分與html元素。react的元素都必須以大寫字母開頭
C想考察在運行時選擇,但是這種寫法是錯誤的。如果你的確想通過表達式來確定 React 元素的類型,請先將其賦值給大寫開頭的變量。這種情況一般會在你想通過屬性值條件渲染組件時出現。要解決這個問題,我們需要先將類型賦值給大寫開頭的變量就可以了。參考這里
const SpecificStory = components[props.storyType];
<SpecificStory story={props.story}>;
3、以下生明周期中,不可以setState() 的是()
A. render() //不能寫,會引起死循環
B. componentWillReceiveProps()
C. componentWillUpdate()// 不能使用,會引起死循環
D. componentDidMount() //可以,但是會警告。因為會引起重繪,但是此函數只執行一次。推薦在構造或者用定時器包裹setState
答案:A C
4、React三大原則是()
A. 單一數據源
B. State是只讀的
C.單項數據流
D. 使用純函數來執行修改
答案:A B D
分析:C是其核心概念
三、簡答題(總分52分)
1、有如下代碼
const MENU = { 'FE':'前端開發工程師', 'DEV':'后端開發工程師', 'QA':'測試開發工程師' }; function getTestScore(...args){ //第一步:“數組的擴展運算符,將一個個參數反轉為數組 const [userInfo = {}, type = 'FE'] = args; //第二步:數組的解構賦值,解構賦值時的默認值。 const { userId } = userInfo; //第三步:解構userInfo對象里邊的userId屬性 consle.log(`我的名字叫${userId},職位是${MENU[type]}`); // 第四步:打印 };
請根據要求,分析答案。(如有異常,填寫報錯並指出報錯代碼段)
(1)getTestScore(); //我的名字叫undefined,職位是前端開發工程師
(2)getTestScore('DEV');//我的名字叫undefined,職位是前端開發工程師
(3)getTestScore(null,'QA');//Cannot destructure property `userId` of 'undefined' or 'null'
考點:es6 變量的解構運算符、es6的擴展運算符
答案:已在題目后標出
分析:一段針對一個分析,下邊是具體
沒有任何參數,所以第二步解構的無果,均用默認值。 所以userInfo解構的userId為undefined,type為FE
只有DEV一個參數,這個參數將會被映射到userInfo上,而type依然是默認值。因為UserInfo被解構為字符串DEV所以會再被隱式轉換為對象去解構userId,為undefined。type為FE
第三種情況,null不能被解構,直接掛了
2、有如下代碼
class a { name='a'; count=1; getName(){ return 'a'+1; } getCount = () => { return this.count; } } class b extends a { name='b'; count=2; getName(){ return this.name+this.count; } getCount = () => { return this.count; } } var c = new a(); var d = new b();
請寫出函數返回值(10分)
(1)c.getName();// 'a1'
Js中,字符串與任意數據類型用加號連接,都表示字符串的拼接。JS中+、+= 運算符既是算術運算符,也是字符串的連接符
(2)d.getCount();//2
這一題沒有難度,估計是靠我class基本語法
(3)delete d.getName; d.getName();//b2
delete操作只會在自身的成員(成員包含屬性和方法)上起作用,
如果你試圖刪除的成員不存在,那么delete將不會起任何作用,但仍會返回true。不會報錯
class中變量屬性在自身成員內,但函數是定義在原型上的
綜上,刪除對象d不存在的getName,其實無意義。 根據原型知識,d會查找到原型上的getName執行:b2
(4)d.__proto__.getName(); // NaN
es6的繼承,與如下寫的還是有區別的

function E(){ this.ename = 'e'; this.name = 'e'; } E.prototype.getName=function(){ console.log(this) console.log(this.name) } function F(){ this.name = 'f' } F.prototype = new E() F.prototype.getName=function(){ console.log(this) console.log(this.name) } var f = new F();
es6的繼承,其實就是讓父類的原型 = 子類的實例對象,但是跟如上傳統寫法又有區別,具體表現在
如果父類有額外的(跟子類屬性名稱不同的)成員會將其屬性添加到子類的成員屬性上。傳統不會融合到子類上
子類b的原型對象 = a的實例。但是 這個a的實例不包含成員屬性。傳統不會 不包含子類的成員屬性
所以,d.__proto__其實就是一個不包含成員屬性的a類的實例對象,
然后a的實例對象去調用getName方法,該方法里找不到成員屬性name和count,就去a類的原型,發現依然沒有,然后在向a的原型對象的原型對象上object找,依然沒有
最后就是undefined+undefined,最后是NaN
(5) d.__proto__.getCount(); //報錯找不到這個方法
es6中的靜態方法,並不會被轉義到function的原型上,而是被定義到成員屬性上。
因為用了extend,所以雖然getCount成為 成員屬性,但是通過類繼承方式隱式創建的a實例,並無如上說的特性a的實例中並無成員屬性,自然沒有getCount,原型鏈也沒有
3、有如下代碼,請填寫執行效果(10分)
// 第一步:將異步代碼放入宏任務的event table之中, // 等時間到了,推入宏任務回調到event queue中, // 等待第二輪開始的時候首先執行宏任務中的event queue中 setTimeout(()=>{ //第六步:(第二輪事件循環開始)打印1 console.log(1); }); var data = {}; for (var i = 0; i < 10; i++) { data[i] = function(){ //永遠會是10,因為i是全局變量,for循環完畢,i是10 // 用let聲明i或者用下邊注釋的方法利用閉包來保存變量就可以輸出理想的值 console.log(i); }; }; // data[i] = ( function(i){ // return function(){ // console.log(i); // } // })(i) // 第二步:將異步代碼放入微任務的event table之中, // 等執行完畢,推入微任務回調到event queue中, // 等待第一輪同步代碼執行完畢的時候,就去執行異步棧的微任務event queue中,即本輪event loop循環就執行 var p = new Promise((res,rej)=>{ console.log(2); //第三步:(第一輪事件循環)同步代碼打印2 res(3); }); //第五步:(第一輪事件循環)同步代碼執行完畢,開始執行異步之微任務event queue p.then(data=>{ console.log(data); }) //第四步:(第一輪事件循環)同步代碼打印10和undefined,其中undefined是函數的默認返回值 console.log(data['8']()); //打印順序:2 10 undefined 3 1
4、請指出以下代碼存在的問題(12分);
const fetchDataList =()=>{ return new Promise((resolve,reject)=>{ setTimeout(()=>{ let result = [ {id:20190101,name:'張三'}, {id:20190102,name:'李四'}, ] resolve(result) },2000) }) } import React from 'react'; class Page extends React.Component{ constructor(props){ super(props); this.state = { dataList:[] }; } componentDidMount(){ setInterval(()=>{ this.fresh() },1000); } async fresh(){ const dataList = await fetchDataList(); console.log(dataList) this.setState({dataList}); } render(){ return(<div> {this.state.dataList.forEach(item => ( <div> <p>編號:{item.id}</p> <p>名字:{item.name}</p> </div> ))} </div>) } } export default Page;
答案:
a、jsx中的循環調使用了forEach。要知道react推薦用map是有原因的,說一下兩者差別
map和forEach均屬於Array的實例方法。數組直接調用即可
兩者參數均是一個回調函數,且回調函數的參數一致 (item,index,arr)
不同的是map有返回值,而forEach沒有返回值:
map通過回調函數內的return作為依據,返回一個新組合的數組,不會改變原數組
forEach則會回調內改變原來的數組,即使回調內有return也不會讓其有返回值,
forEach如果前邊有變量妄想接收它的返回值,那將是undefined(js規定方法都有返回值,如果沒有,系統默認返回undefined表示沒有返回值)
所以,如上的循環並不會返回新的數組,返回的是undefined,自然也不會被遍歷出內容
b、遍歷的時候,需要給頂層元素一個key,作為react同等元素diff規則依據,如果沒有,這個diff性能將會耗費性能,默認會給警告
index.js:1375 Warning: Each child in a list should have a unique "key" prop.
c、那個定時器去獲取最新數據的地方也有2個問題。
首先setInterva不會自動銷毀,會一直執行下去,在組件卸載的時候,將其clear掉
其次,每隔2s執行一次請求來更新數據也有問題,setIntervel具有累積效應,
如果某個操作特別耗時,超過了setInterval的時間間隔,排在后面的操作會被累積起來,然后在很短的時間內連續觸發,這可能或造成性能問題
這個地方可以單獨做一個講解,先不改這個地方
我覺得,類似於這樣實時更新數據的,可以用遞歸的方式成功之后,再次請求,或者websocket
修改完所有bug后的代碼

import React from 'react'; let i = 20190102; const fetchDataList =()=>{ //模擬后端接口 return new Promise((resolve,reject)=>{ setTimeout(()=>{ i++ let result = [ {id:20190101,name:'張三'}, {id:i,name:'李四'}, ] resolve(result) },4000) }) } class Page extends React.Component{ constructor(props){ super(props); this.mySetInterval = null; this.state = { dataList:[] }; } componentDidMount(){ this.mySetInterval = setInterval(()=>{ this.fresh() },1000); } componentWillUnmount(){ // 如果沒有這個操作將會報錯:我們不能在組件銷毀后設置state,防止出現內存泄漏的情況 //關於react中切換路由時報以上錯誤,實際的原因是因為在組件掛載(mounted)之后進行了異步操作, // 比如ajax請求或者設置了定時器等,而你在callback中進行了setState操作。 //當你切換路由時,組件已經被卸載(unmounted)了,此時異步操作中callback還在執行,因此setState沒有得到值。 this.setState = ()=>{ return false; } //清除掉定時器 this.mySetInterval&&clearInterval(this.mySetInterval) } async fresh(){ const dataList = await fetchDataList(); this.setState({dataList}); } render(){ return(<div> <div> {this.state.dataList.map(item => ( <div key={item.id}> <p>編號:{item.id}</p> <p>名字:{item.name}</p> </div> ))} <div>你好</div> </div> </div>) } } export default Page;
5、請寫出下面代碼運行后console輸出結果(mobx為非poxy實現版本) (13分)
這一題,需要知道mobx,就不提出來了,考試這個實際意義不大,
如果考生沒有接觸過mobx就無從下手。mobx是類似於redux的狀態管理庫,但是沒有redux出名
四、編碼題(總分20分)
請寫出發設計模式之布訂閱者(bunsub)模式的代碼,注意類似於線程安全和邊界問題。
這個之前做過總結,點此查看。