JS相關
文章目錄
- 寫在前面的話:
- 1. ES5和ES6繼承方式區別
- 2. Generator瞭解
- 3. 手寫Promise實現
- 4. Promise優缺點
- 5. 觀察者模式
- 6. 手寫實現bind
- 7. 手寫實現4種繼承
- 8. css菊花圖
- 9. http狀態碼
- 10.
Object.create
實現(原型式繼承,特點:實例的proto指向構造函數本身) - 11. async和await:
- 12. 算法和數據結構:
- 13. 封裝JSONP
- 14. 手動實現
map
(filter
以及forEach
也類似) - 15. js實現
checkbox
全選以及反選 - 16. 對原型鏈的理解?
prototype
上都有哪些屬性 - 17. 爲什麼使用繼承
- 18.
setTimeout
時間延遲爲何不準 - 19. 事件循環述,宏任務和微任務有什麼區別?
- 20.
let ,const ,var
作用域 - 21. 節流和防抖
- 22. 實現一個
sleep
函數 - 23. 閉包
- 24.
Immutable.js
- 25. js實現
instanceof
- 27. ES6的模塊引入和
CommonJs
區別 - 28. 嚴格模式
- 29.
typescript
缺點 - 30. 構造函數實現原理
- 31.
for in
和
for of
區別
- 32. JS實現併發控制:
- 33.
ajax
和axios
、fetch
的區別 - 34.
promise.finally
實現
寫在前面的話:
本篇整理了一些關於js常見的面試題,如果各位大佬有補充的,評論區留言,或者私信我,後續會補充。
1. ES5和ES6繼承方式區別
- ES5定義類以函數形式, 以
prototype
來實現繼承 - ES6以class形式定義類, 以
extend
形式繼承
2. Generator瞭解
ES6 提供的一種異步編程解決方案, Generator
函數是一個狀態機,封裝了多個內部狀態。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
調用後返回指向內部狀態的指針, 調用next()纔會移向下一個狀態, 參數:
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
3. 手寫Promise實現
var myPromise = new Promise((resolve, reject) => {
// 需要執行的代碼
...
if (/* 異步執行成功 */) {
resolve(value)
} else if (/* 異步執行失敗 */) {
reject(error)
}
})
myPromise.then((value) => {
// 成功後調用, 使用value值
}, (error) => {
// 失敗後調用, 獲取錯誤信息error
})
4. Promise優缺點
優點: 解決回調地獄, 對異步任務寫法更標準化與簡潔化
缺點: 首先,無法取消Promise,一旦新建它就會立即執行,無法中途取消; 其次,如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部; 第三,當處於pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成).
極簡版promise封裝:
function promise () {
this.msg = '' // 存放value和error
this.status = 'pending'
var that = this
var process = arguments[0]
process (function () {
that.status = 'fulfilled'
that.msg = arguments[0]
}, function () {
that.status = 'rejected'
that.msg = arguments[0]
})
return this
}
promise.prototype.then = function () {
if (this.status === 'fulfilled') {
arguments[0](this.msg)
} else if (this.status === 'rejected' && arguments[1]) {
arguments[1](this.msg)
}
}
5. 觀察者模式
又稱: 發佈-訂閱模式, 舉例子說明.
實現: 發佈者管理訂閱者隊列, 並有新消息推送功能. 訂閱者僅關注更新就行
6. 手寫實現bind
Function.prototype.bind = function () {
// 保存原函數
var self = this
// 取出第一個參數作爲上下文, 相當於[].shift.call(arguments)
var context = Array.prototype.shift.call(arguments)
// 取剩餘的參數作爲arg; 因爲arguments是僞數組, 所以要轉化爲數組才能使用數組方法
var arg = Array.prototype.slice.call(arguments)
// 返回一個新函數
return function () {
// 綁定上下文並傳參
self.apply(context, Array.prototype.concat.call(arg, Array.prototype.slice.call(arguments)))
}
}
7. 手寫實現4種繼承
function Father () {}
function Child () {}
// 1\. 原型繼承
Child.prototype = new Father()
// 2\. 構造繼承
function Child (name) {
Father.call(this, name)
}
// 3\. 組合繼承
function Child (name) {
Father.call(this, name)
}
Child.prototype = new Father()
// 4\. 寄生繼承
function cloneObj (o) {
var clone = object.create(o)
clone.sayName = ...
return clone
}
// 5\. 寄生組合繼承
// 6\. ES6 class extend繼承
8. css菊花圖
四個小圓點一直旋轉
// 父標籤
animation: antRotate 1.2s infinite linear;
// 子標籤
animation: antSpin 1s infinite linear;
@keyframe antSpin {
to {
opacity: 1
}
}
@keyframe antRotate {
to {
transform: rotate(405)
}
}
// animation-delay: 逐個延遲0.4s
9. http狀態碼
- 1**: 服務器收到請求, 需請求者進一步操作
- 2**: 請求成功
- 3**: 重定向, 資源被轉移到其他URL了
- 4**: 客戶端錯誤, 請求語法錯誤或沒有找到相應資源
- 5**: 服務端錯誤, server error
- 304: Not Modified. 指定日期後未修改, 不返回資源
10. Object.create
實現(原型式繼承,特點:實例的proto指向構造函數本身)
11. async和await:
Generator
函數的語法糖,將*改成async
,將yield
換成await
。- 是對
Generator
函數的改進, 返回promise
。 - 異步寫法同步化,遇到
await
先返回,執行完異步再執行接下來的. - 內置執行器, 無需
next()
12. 算法和數據結構:
- 算法:
解決具體問題所需要的解決方法。執行效率最快的最優算法。時間複雜度。輸入,輸出,有窮性,確定性,可行性。冒泡排序,二叉樹遍歷,最長迴文,二分查找,指針,鏈表等,堆棧,隊列等。力扣,codewar,算法導論。 - 數據結構:
- 邏輯結構:集合、線性、樹形、圖形結構
- 物理結構:順序、鏈式存儲結構
13. 封裝JSONP
function jsonp({ url, params, callback }) { //接收參數配置
return new Promise(( resolve, reject) => { // es6 promise
let script = document . createElement( 'script' ); //創建script標籤
window [callback] = function(data) {
document. body. removeChild(script); //移除script標籤
}
var params = { ..params, callback };
Let arr = [];
for (let key in params) {
arr. push( ) ${key} = !${params [key]} );
}
script.src =、${url}?${arr.jion('&')} //拼接url地址
document . body . appendChild(script); // 將創建好的sCr ipt標籤添加到body下面
})
}
function jsonp ({url, param, callback}) {
return new Promise((resolve, reject) => {
var script = document.createElement('script')
window.callback = function (data) {
resolve(data)
document.body.removeChild('script')
}
var param = {...param, callback}
var arr = []
for (let key in param) {
arr.push(`${key}=${param[key]}`)
}
script.src = `${url}?${arr.join('&')}`
document.body.appendChild(script)
})
}
14. 手動實現map
(filter
以及forEach
也類似)
// for循環實現
Array.prototype.myMap = function () {
var arr = this
var [fn, thisValue] = Array.prototype.slice.call(arguments)
var result = []
for (var i = 0; i < arr.length; i++) {
result.push(fn.call(thisValue, arr[i], i, arr))
}
return result
}
var arr0 = [1, 2, 3]
console.log(arr0.myMap(v => v + 1))
// forEach實現(reduce類似)
Array.prototype.myMap = function (fn, thisValue) {
var result = []
this.forEach((v, i, arr) => {
result.push(fn.call(thisValue, v, i, arr))
})
return result
}
var arr0 = [1, 2, 3]
console.log(arr0.myMap(v => v + 1))
15. js實現checkbox
全選以及反選
<body>
<button id="other">反選</button>
<input type="checkbox" id="all" />全選
<input type="checkbox" class="check" />1
<input type="checkbox" class="check" />2
<input type="checkbox" class="check" />3
<script>
var checkbox = document.getElementsByClassName('check')
var checkAll = document.getElementById('all')
var checkOther = document.getElementById('other')
checkAll.onclick = function() {
var flag = true
for (var i = 0; i < checkbox.length; i++) {
if (!checkbox[i].checked) flag = false
}
if (flag) {
for (var i = 0; i < checkbox.length; i++) {
checkbox[i].checked = false
}
} else {
for (var i = 0; i < checkbox.length; i++) {
checkbox[i].checked = true
}
}
}
checkOther.onclick = function() {
for (var i = 0; i < checkbox.length; i++) {
checkbox[i].checked = !checkbox[i].checked
}
}
</script>
</body>
16. 對原型鏈的理解?prototype
上都有哪些屬性
- 在js裏,繼承機制是原型繼承。繼承的起點是 對象的原型(
Object prototype
)。 - 一切皆爲對象,只要是對象,就會有 proto 屬性,該屬性存儲了指向其構造的指針。
Object prototype
也是對象,其proto
指向null
。- 對象分爲兩種:函數對象和普通對象,只有函數對象擁有『原型』對象(prototype)。
prototype
的本質是普通對象。Function prototype
比較特殊,是沒有prototype
的函數對象。- new操作得到的對象是普通對象。
- 當調取一個對象的屬性時,會先在本身查找,若無,就根據
proto
找到構造原型,若無,繼續往上找。最後會到達頂層Object prototype
,它的proto
指向null
,均無結果則返回undefined
,結束。 - 由
proto
串起的路徑就是『原型鏈』。 - 通過
prototype
可以給所有子類共享屬性
17. 爲什麼使用繼承
通常在一般的項目裏不需要,因爲應用簡單,但你要用純js做一些複雜的工具或框架系統就要用到了,比如webgis、或者js框架如jquery、ext什麼的,不然一個幾千行代碼的框架不用繼承得寫幾萬行,甚至還無法維護。
18. setTimeout
時間延遲爲何不準
單線程, 先執行同步主線程, 再執行異步任務隊列
19. 事件循環述,宏任務和微任務有什麼區別?
- 先主線程後異步任務隊列
- 先微任務再宏任務
20. let ,const ,var
作用域
參考博客let,const,var 區別
21. 節流和防抖
- 函數節流是指一定時間內js方法只跑一次。比如人的眨眼睛,就是一定時間內眨一次。這是函數節流最形象的解釋。
// 函數節流 滾動條滾動
var canRun = true;
document.getElementById("throttle").onscroll = function(){
if(!canRun){
// 判斷是否已空閒,如果在執行中,則直接return
return;
}
canRun = false;
setTimeout(function(){
console.log("函數節流");
canRun = true;
}, 300);
};
- 函數防抖是指頻繁觸發的情況下,只有足夠的空閒時間,才執行代碼一次。比如生活中的坐公交,就是一定時間內,如果有人陸續刷卡上車,司機就不會開車。只有別人沒刷卡了,司機纔開車。
// 函數防抖
var timer = false;
document.getElementById("debounce").onscroll = function(){
clearTimeout(timer); // 清除未執行的代碼,重置回初始化狀態
timer = setTimeout(function(){
console.log("函數防抖");
}, 300);
};
22. 實現一個sleep
函數
// 這種實現方式是利用一個僞死循環阻塞主線程。因爲JS是單線程的。所以通過這種方式可以實現真正意義上的sleep()。
function sleep(delay) {
var start = (new Date()).getTime();
while ((new Date()).getTime() - start < delay) {
continue;
}
}
function test() {
console.log('111');
sleep(2000);
console.log('222');
}
test()
23. 閉包
- 概念: 內層函數能夠訪問外層函數作用域的變量
- 缺點: 引起內存泄漏(釋放內存)
- 作用:
- 使用閉包修正打印值
- 實現柯里化
- 實現
node commonJs
模塊化, 實現私有變量 - 保持變量與函數活性, 可延遲迴收和執行
24. Immutable.js
Facebook出品, 倡導數據的不可變性, 用的最多就是List
和Map
.
25. js實現instanceof
// 檢測l的原型鏈(__proto__)上是否有r.prototype,若有返回true,否則false
function myInstanceof (l, r) {
var R = r.prototype
while (l.__proto__) {
if (l.__proto__ === R) return true
}
return false
}
27. ES6的模塊引入和CommonJs
區別
28. 嚴格模式
// 嚴格模式下, 隱式綁定丟失後this不會指向window, 而是指向undefined
'use strict'
var a = 2
var obj = {
a: 1,
b: function() {
// console.log(this.a)
console.log(this)
}
}
var c = obj.b
c() // undefined
29. typescript
缺點
- 並不是嚴格意義的js的超集, 與js不完全兼容, 會報錯
- 更多的限制, 是一種桎梏
- 有些js第三方庫沒有dts, 有問題
30. 構造函數實現原理
- 構造函數中沒有顯示的創建
Object
對象, 實際上後臺自動創建了 - 直接給
this
對象賦值屬性和方法,this
即指向創建的對象 - 沒有
return
返回值, 後臺自動返回了該對象
// 模擬構造函數實現
var Book = function(name) {
this.name = name;
};
//正常用法
var java = new Book(‘Master Java’);
//使用代碼模擬,在非IE瀏覽器中測試,IE瀏覽器不支持
var python = {};
python.__proto__ = Book.prototype;
Book.call(python, 'Master Python');
31. for in 和 for of區別
和 for of區別
for in
遍歷數組會遍歷到數組原型上的屬性和方法, 更適合遍歷對象forEach
不支持break, continue, return
等- 使用
for of
可以成功遍歷數組的值, 而不是索引, 不會遍歷原型 for in
可以遍歷到myObject
的原型方法method,
如果不想遍歷原型方法和屬性的話,可以在循環內部判斷一下,hasOwnPropery
方法可以判斷某屬性是否是該對象的實例屬性
32. JS實現併發控制:
使用消息隊列以及setInterval或promise
進行入隊和出隊
33. ajax
和axios
、fetch
的區別
關於這個問題,引用一下這篇文章ajax,axios,fetch的區別
34. promise.finally
實現
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};