1 Javascript 基礎(chǔ)
1.1 手寫Object.create
解析:Object.create()
方法創(chuàng)建一個(gè)新對象门怪,使用現(xiàn)有的對象來提供新創(chuàng)建的對象的proto。
解決方案:將傳入對象作為原型票髓。
function create(obj) {
const A = function() {};
A.prototype = obj;
return new A();
}
1.2 手寫instanceof
解析:instanceof
運(yùn)算符 用于檢測構(gòu)造函數(shù)的 prototype
屬性是否出現(xiàn)在某個(gè)實(shí)例對象的原型鏈上攀涵。
解決方案:
- 獲取類型的原型
- 獲取對象的原型
- 然后遞歸循環(huán)對象的原型是否為類型原型,直到null(原型鏈終端)炬称,如果不同,則為false涡拘,相同為true
function NewInstanceOf(source, target) {
if (typeof source !== 'object') {
return false;
}
// 這里也可以使用while循環(huán)方式
if (source.__proto__ !== target.prototype) {
if (source.__proto__ === null) {
return false;
}
return NewInstanceOf(source.__proto__, target);
}
return true;
}
1.3 手寫new操作符號
解析:new
運(yùn)算符 創(chuàng)建一個(gè)用戶定義的對象類型的實(shí)例或具有構(gòu)造函數(shù)的內(nèi)置對象的實(shí)例玲躯。
new會(huì)經(jīng)過以下的過程:
- 首先會(huì)創(chuàng)建一個(gè)新的對象
- 設(shè)置對象原型為函數(shù)的prototype原型
- 設(shè)置函數(shù)this指向這個(gè)對象,執(zhí)行構(gòu)造函數(shù)代碼(設(shè)置函數(shù)函數(shù)屬性為對象屬性)
- 判斷當(dāng)前返回類型鳄乏,如果是值類型跷车,則返回創(chuàng)建對象,如果是引用類型橱野,就返回這個(gè)引用類型對象朽缴。
const newFunc = (obj, ...args) => {
let newObj = null;
if (typeof obj !== 'function') {
throw ('type error');
}
newObj = Object.create(obj.prototype);
const result = obj.apply(newObj, args);
if (result && (typeof result === 'function' || typeof result === 'function')) {
return result;
}
return newObj;
}
1.4 手寫防抖函數(shù)
解析:指在事件被觸發(fā) n 秒后再執(zhí)行回調(diào),如果在這 n 秒內(nèi)事件又被觸發(fā)水援,則重新計(jì)時(shí)密强。
這可以使用在一些點(diǎn)擊請求的事件上茅郎,避免因?yàn)橛脩舻亩啻吸c(diǎn)擊向后端發(fā)送多次請求。
function debounce(fn, time) {
let timer = null;
return function (...args) {
!!timer && window.clearTimeout(timer);
timer = setTimeout(() => fn(...args), time);
}
}
1.5 手寫節(jié)流函數(shù)
解析:規(guī)定一個(gè)單位時(shí)間或渤,在這個(gè)單位時(shí)間內(nèi)系冗,只能有一次觸發(fā)事件的回調(diào)函數(shù)執(zhí)行,如果在同一個(gè)單位時(shí)間內(nèi)某事件被觸發(fā)多次薪鹦,只有一次能生效掌敬。
節(jié)流可以使用在 scroll 函數(shù)的事件監(jiān)聽上,通過事件節(jié)流來降低事件調(diào)用的頻率池磁。
function throttle(fn, time) {
let timer = null;
return function (...args) {
if (timer) {
return;
}
timer = setTimeout(() => {
window.clearTimeout(timer);
timer = null;
fn(...args);
}, time);
}
}
1.6 手寫判斷類型函數(shù)
解決方案:主要是根據(jù)為typeof為object做區(qū)分奔害,使用Object.prototype.toString獲對象類型信息。
function computedType(arg) {
if (null === arg) {
return 'null';
}
if (typeof arg === 'object') {
const str = Object.prototype.toString.call(arg);
return str.replace(/\]/, '').split(' ')[1];
}
return typeof arg;
}
1.7 手寫call函數(shù)
解決方案:
- 判斷調(diào)用對象是否為函數(shù)地熄,即使我們是定義在函數(shù)原型上面华临,但是可能出現(xiàn)call等調(diào)用方式
- 判斷上下文傳入對象是否存在,否則离斩,返回window
- 需要將函數(shù)作為上下文對象的屬性银舱,使用上下文對象調(diào)用這個(gè)方法,保存結(jié)果返回
- 刪除剛才上下文函數(shù)的新增屬性跛梗,返回結(jié)果
Function.prototype.newCall = function(context = window, ...args) {
if (typeof this !== 'function') {
throw('type error');
}
context._fn = this;
const result = context._fn(...args);
delete context._fn;
return result;
}
1.8 手寫apply函數(shù)
解決反感:實(shí)現(xiàn)和call類似寻馏,只是參數(shù)需要簡單處理下。
Function.prototype.newApply = function(context = window, args) {
if (typeof this !== 'function') {
throw('type error');
}
context._fn = this;
const result = context._fn(...args);
delete context._fn;
return result;
}
1.9 手寫bind函數(shù)
解析:bind需要返回一個(gè)函數(shù)核偿。其參數(shù)除了上下文執(zhí)行對象诚欠,還有函數(shù)執(zhí)行上下文的參數(shù)。
Function.prototype.newBind = function(context = window, ...args) {
if (typeof this !== 'function') {
throw('type error');
}
context.fn = this;
return function (...newArgs) {
const result = context.fn(...args, ...newArgs);
delete context.fn;
return result;
}
}
1.10 函數(shù)柯里化
解析:只傳遞給函數(shù)一部分參數(shù)來調(diào)用它漾岳,讓它返回一個(gè)函數(shù)去處理剩下的參數(shù)轰绵。
function curry(fn, ...args) {
// 這里重點(diǎn)在于:
// 1.使用fn.length來獲取函數(shù)參數(shù)長度
// 2.當(dāng)少于長度的時(shí)候,返回閉包函數(shù)形式尼荆,這個(gè)函數(shù)可以累積參數(shù)長度
return args.length === fn.length
? fn(...args)
: (...newArgs) => curry(fn, ...args, ...newArgs);
}
2 數(shù)據(jù)轉(zhuǎn)換
2.1 交換a b值左腔,不能使用臨時(shí)變量
解決方案:利用兩數(shù)相減,兩數(shù)相加捅儒,計(jì)算差值液样。
a = a + b;
b = a - b;
a = a - b;
另外還能利用js高級語法來進(jìn)行交換。
[a, b] = [b, a];
2.2 實(shí)現(xiàn)數(shù)據(jù)亂序輸出
解決方案:從0開始遍歷巧还,產(chǎn)生一個(gè)隨機(jī)數(shù)鞭莽,隨機(jī)數(shù)不為小于當(dāng)前數(shù),將隨機(jī)數(shù)和當(dāng)前值進(jìn)行交換麸祷,直至數(shù)組末端澎怒。
function renderRandomArray(arr = []) {
for(let i = 0; i < arr.length; i++) {
const random = i + Math.round(Math.random() * (arr.length - i - 1));
[arr[i], arr[random]] = [arr[random], arr[i]];
}
return arr;
}
2.3 數(shù)組扁平化
解決方案:遞歸
function flatten(arr) {
let result = [];
for(let i = 0; i < arr.length; i++) {
if(Array.isArray(arr[i])) {
result = result.concat(flatten(arr[i]));
} else {
result.push(arr[i]);
}
}
return result;
}
2.4 數(shù)據(jù)去重
解決方式:map
function uniqueArray(arr) {
let map = {};
let newArr = [];
for (const i of arr) {
if (!map[i]) {
map[i] = true;
newArr.push(i);
}
}
return newArr;
}
2.5 將js對象轉(zhuǎn)化為樹結(jié)果
// 轉(zhuǎn)換前:
source = [
{ id: 1, parentId: 0, name: 'rank1' },
{ id: 2, parentId: 1, name: 'rank2' },
{ id: 3, parentId: 2, name: 'rank3' }
]
// 轉(zhuǎn)換為:
tree = [{
id: 1,
parentId: 0,
name: 'rank1',
children: [{
id: 2,
parentId: 1,
name: 'rank2',
children: [{ id: 3, parentId: 1, name: 'rank3' }]
}]
}]
解決方案:
- 建立一個(gè)map,將數(shù)組每一項(xiàng)的id作為key阶牍,項(xiàng)值作為value喷面。
- 設(shè)置臨時(shí)變量result星瘾,遍歷數(shù)組,判斷是否有父節(jié)點(diǎn)乖酬,如果有死相,將父節(jié)點(diǎn)取出,設(shè)置父節(jié)點(diǎn)的children為該項(xiàng)咬像,如果沒有算撮,push到result中县昂。
- 最后返回result肮柜。
function JsonToTree(arr) {
if (!Array.isArray(arr)) {
return arr;
}
let result = [];
let map = new Map();
for (const item of arr) {
map.set(item.id, item);
}
for(const item of arr) {
if (map.get(item.parentId)) {
let parent = map.get(item.parentId);
parent.children = item;
map.set(item.parentId, parent);
}
else {
result.push(item);
}
}
return result;
}
3 場景問題
3.1 循環(huán)打印紅黃綠
問題:紅燈 3s 亮一次,綠燈 1s 亮一次倒彰,黃燈 2s 亮一次审洞;如何讓三個(gè)燈不斷交替重復(fù)亮燈?
function flash(type, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(type);
resolve();
}, time);
});
}
function run() {
return flash('red', 3000)
.then(() => flash('green', 1000))
.then(() => flash('yellow', 2000))
.then(() => run());
}
run();
3.2 判斷對象是否存在循環(huán)引用
解決方式:通過map的方式保存遞歸的對象待讳,判斷是否有循環(huán)引用芒澜。
function isCycle(obj, tem = {}) {
for(let o in obj) {
const v = obj[o];
if (typeof v === 'object') {
if (tem[v]) {
return true;
}
tem[v] = true;
if (isCycle(v, tem)) {
return true;
}
}
}
return false;
}