面試中遇到的平常沒注意的問題
1. for循環(huán)中l(wèi)et 聲明的變量i 和 var 聲明的變量i 有什么區(qū)別
- let 聲明的是塊級作用域;var 聲明的是在全局范圍內(nèi)都有效
- let 聲明的變量只在本輪循環(huán)有效;var 聲明的全局范圍有效,全局只有這一個i值,所以每次循環(huán)變量i的值都會發(fā)生改變
實例代碼
var a = []
for (let i=0; i<10;i++){
a[i] = function(){
console.log(i)
}
}
console.log(a[6]()) // 6
var a = []
for (var i=0; i<10; i++) {
a[i] = function() {
console.log(i)
}
}
console.log(a[6]()) // 10
for循環(huán)還有一個特別的地方, 就是設(shè)置循環(huán)變量的部分是一個父作用域,循環(huán)體內(nèi)部是一個單獨的子作用域
例
for(let i = 0;i?<3;i++){
let i = 'abc'
console.log(i)
}
// abc
// abc
// abc
2.如何才能實現(xiàn)深復制(深拷貝)
使用遞歸的方式進行深拷貝
1 第一種遞歸方式,復制目標對象是函數(shù)參數(shù)
// 深拷貝函數(shù) 復制目標對象
function deepCopy(targetObject) {
let newObj = {}
if(typeof targetObject !== 'object'){
return targetObject
} else {
for(let property in targetObject){
newObj[property] = deepCopy(targetObject[property])
}
}
return newObj
}
2 序列化反序列化 (一句代碼搞定,裝逼爽歪歪)
let newObject = JSON.parse(JSON.stringify(targetObject))
3.實現(xiàn)一個function sum 達到以下目的
sum(1,2,3,4,5).valueOf() // 15
sum(1,2,3,4)(5).valueOf() // 15
sum(1,2,3)(4)(5).valueOf() // 15
sum(1,2)(3)(4)(5).valueOf() // 15
sum(1,2)(3,4)(5).valueOf() // 15
- 這道題考的是 函數(shù) 的柯里化
- valueOf 涉及到的是 js 的隱式轉(zhuǎn)換
柯里化:(Currying) 又稱為部分求值,是把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的函數(shù),并且返回一個新的函數(shù)的技術(shù),新函數(shù)接受余下參數(shù)并返回運算結(jié)果。
1.接收單一參數(shù),因為要攜帶不少信息,因此常常以回調(diào)函數(shù)的理由來解決
2.將部分參數(shù)通過回調(diào)函數(shù)等方式傳入函數(shù)中
3.返回一個新函數(shù),用于處理所有的想要傳入的參數(shù)
概念我晚點查閱一下專業(yè)的書籍來補充,包括柯里化的好處這些
由淺入深的柯里化
// 普通的add函數(shù)
function add(x, y){
return x + y
}
// 柯里化后
function curryingAdd(x){
return function(y){
return x+y
}
}
add(1, 2) // 3
curryingAdd(1)(2) // 3
接著,補充幾個隱式轉(zhuǎn)換的小問題
function fn(){
return 20
}
console.log(fn + 10)
// 輸出什么
function fn(){
return 20
}10
function fn() {
return 20
}
fn.toString = function() {
return 10
}
console.log(fn + 10)
// 輸出什么
20
function fn() {
return 20
}
fn.toString = function() {
return 10
}
fn.valueOf = function() {
return 5
}
console.log(fn + 10)
// 輸出什么
15
這里是JavaScript 中隱式轉(zhuǎn)換的知識點
其中 valueOf 的優(yōu)先級比 toString 要高
開始解這個題
function add() {
// 第一次執(zhí)行時,定義一個數(shù)組專門用來存儲所有的參數(shù)
var _args = Array.prototype.slice.call(arguments)
// 在內(nèi)部聲明一個函數(shù),利用閉包特性保存_args并收集所有的參數(shù)
var _adder = function() {
_args.push(...arguments);
return _adder
}
// 利用toString隱式轉(zhuǎn)換的特性,當最后執(zhí)行時隱式轉(zhuǎn)換,并計算最終的值返回(valueOf 也可以,優(yōu)先級比toString高)
_adder.toString = function() {
return _args.reduce(function(a,b){
return a+b;
})
}
_adder.valueOf = function() {
return _args.reduce(function(a,b){
return a+b;
})
}
return _adder
}
4.如何自己實現(xiàn)雙向綁定
使用 Object.defineProperty
html 部分
<html>
<head></head>
<body>
<input type='text' id='test-input' />
<span id='test-label'></span>
<script>
(function(){
const obj = {}
const _input = document.getElementById('test-input')
const _label = document.getElementById('test-label')
Object.defineProperty(obj,'val', {
get: function(){
return obj
},
set: function(newValue){
_input.value = newValue;
_label.innerHTML = newValue
}
})
// 監(jiān)聽input的改值事件
_input.addEventListener('keyup', function(e){
obj.val = e.target.value
})
})()
</script>
</body>
</html>
5. react 中的key的作用,和為什么最好不要使用索引index作為key
和react的diff算法有關(guān)
- 當使用React,在某一個時間點,可以認為render()函數(shù)是在創(chuàng)建React元素樹
- 在下一個狀態(tài)或?qū)傩愿聲r,render()函數(shù)將返回一個不同的React元素樹。
- React需要算出如何高效更新UI以匹配最新的樹
- react 基于兩點假設(shè),實現(xiàn)了一個啟發(fā)的O(n)算法
- 兩個不同類型的元素將產(chǎn)生不同的樹
- 開發(fā)者可以使用Key屬性來提示那些子元素貫穿不同渲染是穩(wěn)定的
所以使用key屬性是為了區(qū)分相同類型的元素,避免重新渲染所有元素樹來使用的
- 當子節(jié)點有key時,react使用Key來匹配原始樹的子節(jié)點和隨后樹的子節(jié)點
- 這個key在兄弟元素中必須是惟一的
- 如果使用索引來作為Key, 如果元素順序不變,效果不錯,但是重新排序就會很慢
6.實現(xiàn)一個閉包,每調(diào)用一次就自增加1
const addSelf = (function(){
var num = 0;
return function(){
return num++
}
})()
6.盒子模型描述
標準盒子模型: 盒子的總寬度/總高度 = margin+padding+border+width/height
IE盒子模型: 盒子的總寬度/總高度 = marging+width/height
7. ajax 的事件是
ajaxStart()——————用于為ajax請求的ajaxStart事件綁定處理函數(shù)
ajaxStop()——————用于為ajax請求ajaxStop事件綁定處理函數(shù)
ajaxSend()——————用于設(shè)置當Ajax請求即將被發(fā)送時執(zhí)行的回調(diào)函數(shù)
ajaxComplete()——————用于設(shè)置當Ajax請求完成(無論成功或失敗)時執(zhí)行的回調(diào)函數(shù)
ajaxSuccess()——————用于設(shè)置當Ajax請求成功完成時執(zhí)行的回調(diào)函數(shù)
ajaxError()———————用于設(shè)置當Ajax請求失敗時執(zhí)行的回調(diào)函數(shù)
8. JavaScript 異步編程,如何解決回調(diào)地獄
- 拆解function
- 事件發(fā)布/監(jiān)聽模式
- Promise
- generator 函數(shù)
- async/await
- 拆解function // 缺點:缺少通用性
// 將各步拆解為單個的function
function getData(count){
get(`/sampleget?count=${count}`, data=>{
console.log(data)
})
}
function queryDB(kw){
db.find(`select * from sample where kw = ${kw}`, (err, res)=>{
getData(res.length)
})
}
function readFile(filepath){
fs.readFile(filepath, 'utf-8',(err, content)=>{
let keyword = content.substring(0, 5);
queryDB(keyword)
})
}
readFile('./sample.txt')
- 事件發(fā)布/監(jiān)聽模式 // event.emitter 一方面監(jiān)聽事件,事件發(fā)生時,進行相應回調(diào)操作; 另一方面,當某些操作完成后,通過發(fā)布事件觸發(fā)回調(diào)
const events = require('events')
const eventEmitter = new events.EventEmitter();
// 訂閱
eventEmitter.on('db',(err, kw)=>{
db.find(`select * from sample where kw = ${kw}`, (err, res)=>{
// 發(fā)布
eventEmitter.emit('get', res.length);
})
})
// 訂閱
eventEmitter.on('get',(err,count)=>{
get(`/sampleget?count=${count}`, data=>{
console.log(data);
})
})
fs.readFile('./sample.txt', 'utf-8', (err, content)=>{
let keyword = content.substring(0, 5);
// 發(fā)布
eventEmitter.emit('db', keyword)
})
- Promise 鏈式調(diào)用
// 借助原生promise來改造需要用到的方法, 以 fs.readFile為例
// 對其他一些異步方法也可以參照這種方法進行改造
const readFile = function(filepath){
let resolve;
let reject;
let promise = new Promise((_resolve, _reject)=>{
resolve = _resolve;
reject = _reject;
})
let deferred = {
resolve,
reject,
promise
};
fs.readFile(filepath, 'utf-8', function(err, ...args){
if(err){
deferred.reject(err)
} else {
deferred.resolve(...args)
}
})
return deferred.promise
}
使用中就變成了
readFile('./sample.txt').then(content=>{
let keyword = content.substring(0, 5);
return queryDB(keyword)
}).then(res => {
return getData(res.length)
}).then(data => {
console.log(data)
}).catch(err => {
console.log(err);
})
- generator 函數(shù) // 在function關(guān)鍵字后面添加*即可 通過 yield暫停; 通過next方法來恢復yield 后面的代碼執(zhí)行
- async/await 函數(shù);相當于generator函數(shù)的語法糖,在async函數(shù)中可以使用await語句,await后一般是一個Promise對象
8. 隱式轉(zhuǎn)換類問題 下面代碼輸出什么
console.log(1+"2"+"2") // 122
console.log(1+ +"2"+"2") // 32 這種隱式轉(zhuǎn)換只有+和-才能轉(zhuǎn),* 和 / 皆要報錯
console.log('A'-'B'+ 2) // NaN
console.log('A'-'B'+'2') // NaN2 字母和數(shù)字字符串的轉(zhuǎn)換也只能用 +
9.以下js表達式返回false的是; 這個只有輸出看,考隱式轉(zhuǎn)換
1 == true;
''== false;
false == null; // false undefined == false; false == undefined 也是false
null==undefined;
9.關(guān)于屏幕尺寸標準
col-xs-* < 768 手機
col-sm-* >= 768 平板
col-md-* >= 992 桌面
col-lg-* >= 1200 大屏幕桌面
10. var a = b = 3 ===> var a = 3; b = 3; 變量聲明提升是將var a;提升到最上面,賦值不會
11 什么是事件委托,事件委托的好處是什么?
事件委托就是利用事件冒泡機器指定一個事件處理程序,來管理某一類型的所有事件。
即: 利用冒泡的原理,給父元素綁定事件,用來監(jiān)聽子元素的冒泡事件,并找到是哪個子元素的事件
好處:只在內(nèi)存中開辟了一塊空間,避免對每個子元素添加事件監(jiān)聽器,節(jié)省資源同時減少了dom操作,提高性能
對于新添加的元素也會有之前的事件
12 用setTimeout來模擬setInterval();有什么區(qū)別
function simulateInterval(){
setTimeout(function(){
simulateInterval();
}, 1000)
}
setTimeout(simulateInterval, 1000)
區(qū)別是setTimeout是調(diào)用自身函數(shù)
13 垂直居中的方式
常用的
flex布局
父元素{
display: flex;
justify-content: center;
align-items: center;
}
萬能居中
父元素{
position: relative;
}
子元素{
position: absoluted;
top: 50%;
left:50%;
transform: translate(-50%,-50%)
}
確定子元素高度的居中
父元素{
position: 除了static之外的定位
}
子元素{
position: absoluted;
top:0;
left:0;
right:0;
bottom:0;
margin:auto;
overflow:auto;
width: XX px;
height: XX px;
}
14 js中的new()到底做了什么?
- 創(chuàng)建了一個新的對象實例
- 將構(gòu)造函數(shù)的作用域賦給了新對象實例(使this指向了這個新的對象實例)
- 調(diào)用構(gòu)造函數(shù)(為新對象添加屬性和方法)
- 返回新對象
15 js垃圾回收機制,它的存在有什么必要性(刪除使用過不再使用的變量,釋放內(nèi)存),具體的垃圾回收方法有哪幾種?各自的特點是什么?
垃圾收集器會定期(周期性)找出那些不在繼續(xù)使用的變量,然后釋放其內(nèi)存
垃圾回收的方法有兩種
1.標記清除法: 在函數(shù)聲明一個變量的時候,就將這個變量標記為“進入環(huán)境”。而當變量離開環(huán)境時,則將其標記為“離開環(huán)境”。 垃圾回收器在運行時會給存儲在內(nèi)存中的所有變量都加上標記, 然后會去掉環(huán)境中的變量以及被環(huán)境中變量引用的的變量的標記。 在此之后再被標記的變量將被視為準備刪除的變量,因為環(huán)境中的變量已經(jīng)無法訪問到這些變量了。最后,垃圾回收器完成內(nèi)存清除工作,銷毀那些帶標記的值并回收他們所占用的內(nèi)存空間
function test(){ var a = 10; // 被標記,進入環(huán)境 var b = 20; // 被標記, 進入環(huán)境 } test(); // 執(zhí)行完畢之后, a、b又被標記離開環(huán)境,被回收2.引用計數(shù)法
引用計數(shù)的含義是跟蹤記錄每個值被引用的次數(shù)。當生命了一個變量并將一個引用類型值賦給該變量時,則這個值的引用次數(shù)就是1.如果同一個值又被賦給另一個變量,則該值的引用次數(shù)加1.相反,如果包含對這個值引用的變量又取得了另外一個值,則這個值的引用次數(shù)減1.當這個值的引用次數(shù)變成0時,則說明沒有辦法再范文這個值了,因而可以將其占用的內(nèi)存空間回收回來。這樣,當垃圾回收器下次再運行時,它就會釋放那些引用次數(shù)為0的值所占用的內(nèi)存。當遇到循環(huán)引用的時候,函數(shù)的引用次數(shù)就不會為0,所以不會被垃圾回收器回收內(nèi)存,會造成內(nèi)存泄露
function test(){ var a = {}; // a的引用次數(shù)為0 var b = a; // a的引用次數(shù)加1, 為1 var c = a; // a 的引用次數(shù)再加1, 為2 var b = {} // a的引用次數(shù)減1, 為1 }
16 TCP與UDP區(qū)別總結(jié)
- TCP面向連接(如電話要先撥號建立連接); UDP是無連接的,即發(fā)送數(shù)據(jù)之前不需要建立連接
- TCP提供可靠的服務。也就是說,通過TCP連接傳送的數(shù)據(jù),無差錯,不丟失,不重復,且按需到達;UDP盡最大努力交付,即不保證可靠交付
- UDP具有較好的實時性,工作效率比TCP高,適用于對高速傳輸和實時性較高的通信或廣播通信。
- 每一條TCP連接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通信
- TCP對系統(tǒng)資源要求較多,UDP對系統(tǒng)資源要求較少