前端大廠面試一點總結

WEB前端含光發表於2020-06-10

一、前言

大家好!今天來跟大家分享一下衝刺大廠必會的手寫程式碼篇,為了整個篇幅看起來更加舒適一些,今天這篇是蒐集的手寫程式碼,下一篇將會總結大廠的高頻問答篇。請大家耐心等待ing…

本篇適用人群:正在處於面試階段的你,或者正準備跳槽,或者平時工作中用到以下程式碼的…,都歡迎大家來給捧場。

本篇的程式碼都做了詳細的註釋,方便大家閱讀與理解,如果仍有不理解的可以在評論區討論一下。

開始讓我們的遨遊在程式碼的世界裡吧~~~

二、程式碼篇

  1. URL處理

實現效果:將位址列中的URL中問號後面的內容變為物件的形式:http://www.baidu.com/search?name=li&age=18#student

=>

{HASH: “student”, name: “li”, age: “18”}

1.1 使用陣列中的方法實現

let str = ‘http://www.baidu.com/search?name=li&age=18#student’;
function urlQuery(str) {
// 獲取?和#的索引
let askIndex = str.indexOf(’?’),
polIndex = str.indexOf(’#’),
askText = ‘’,
polText = ‘’;
//儲存最後我們想要的結果
let obj = {};
// 把# ?後面的內容給polText 和 askText
polIndex != -1 ? polText = str.substring(polIndex + 1) : null;
askIndex != -1 ? askText = str.substring(askIndex + 1, polIndex) : null;
//儲存到物件中
polText ? obj[‘HASH’] = polText : null;
if (askText) {
askText.split(’&’).forEach((item, index) => {
item = item.split(’=’);
obj[item[0]]=item[1];
});
}
return obj;
}
console.log(urlQuery(str)); //=> {HASH: “student”, name: “li”, age: “18”}
複製程式碼
1.2 正則的實現

如果對下面案例中的正則不熟悉,可以檢視這個正則篇來學習一下:正則學習

let str = ‘http://www.baidu.com/search?name=li&age=18#student’;
function urlQuery(str) {
let obj = {};
//正則匹配=兩邊的
str.replace(/([=?&#]+)=([=?&#]+)/g,(,group1,group2)=>{
obj[group1]=group2;
});
//正則匹配#後邊的
str.replace(/#([^=?&#]+)/g,(
,group1)=>{
obj[‘HASH’]=group1;
});
return obj;
}
console.log(urlQuery(str)); //=> {name: “li”, age: “18”, HASH: “student”}
複製程式碼
2. 陣列去重

實現效果:把陣列中的重複項去掉。例如: [1,2,3,4,2,1] => [1,2,3,4]

2.1 時間複雜度為O(n^2)的實現

let ary = [1, 2, 2, 3, 4, 2, 1, 3, 1, 3];
function unique(ary) {
// 雙重for迴圈拿當前項和後面的每一項進行對比
for (let i = 0; i < ary.length; i++) {
let item = ary[i];
for (let j = i + 1; j < ary.length; j++) {
if (item == ary[j]) {
//如果當前項和後面的這一項相等,那麼末尾一項賦值給後面的這一項,並且長度減一,
ary[j] = ary[ary.length - 1];
ary.length–;
j–;
}
}
}
return ary;
}
console.log(unique(ary));
複製程式碼
2.2 時間複雜度為O(n)的實現

let ary = [1, 2, 2, 3, 4, 2, 1, 3, 1, 3];
function unique(ary) {
let obj = {};
for (let i = 0; i < ary.length; i++) {
let item = ary[i];
if (obj[item] !== undefined) {
ary[i] = ary[ary.length - 1];
ary.length–;
//解決陣列塌陷
i–;
}
obj[item] = item;
}
return ary;
}
console.log(unique(ary));
複製程式碼
2.3 使用ES6的Set實現

Set 是ES6中一種沒有重複項的資料結構: 阮一峰老師ES6語法的講解

let ary = [1, 2, 2, 3, 4, 2, 1, 3, 1, 3];
ary = Array.from(new Set(ary));
console.log(ary);
複製程式碼
3. 內建new的實現

function Fn(x,y){
this.x=x;
this.y=y;
}
function _new(func){
let params=Array.from(arguments).slice(1);
let obj=Object.create(func.prototype);
let result=func.apply(obj,params);
//如果類中有return並且是引入資料型別,那麼返回類中的return
if(result!null&&(typeof result=‘object’||typeof result===‘function’)){
return result;
}
return obj;
}
let f1=_new(Fn,10,20);
複製程式碼
4. 什麼情況下輸出’ok’

var a = ?;
if (a == 1 && a == 2 && a == 3) {
console.log(‘ok’);
}
複製程式碼
//方案一
var a = {
n:0,
toString:function(){
return ++this.n;
}
}

//方案二
var a = [1,2,3];
a.toString = a.shift;

//方案三
var i = 0;
Object.defineProperty(window,‘a’,{
get(){
//只有獲取a的值,就一定會觸發get方法執行
return ++i;
}
})
複製程式碼
5. 內建call

Function.prototype.changeThis = function changeThis(context,…args){
if(context == null){
//:null和undefined在兩個等號的時候相等,因為if後面,只要傳遞進來是undefined或者null都可以
context = window;
}
if(typeof context !
‘object’ && typeof context !== ‘function’){
context = new context.constructor(context);
}
let uniqueKey = $${new Date().getTime()};
context[uniqueKey] = this;
let result = contextuniqueKey;
delete context[uniqueKey];
return result;
}
複製程式碼
6. 內建apply

Function.prototype.myApply = function (context) {
// 如果沒有傳或傳的值為空物件 context指向window
if (context == null) {
context = window
}
let fn = mySymbol(context)
context[fn] = this //給context新增一個方法 指向this
// 處理引數 去除第一個引數this 其它傳入fn函式
let arg = […arguments].slice(1) //[…xxx]把類陣列變成陣列,arguments為啥不是陣列自行搜尋 slice返回一個新陣列
contextfn //執行fn
delete context[fn] //刪除方法
}

複製程式碼
7. 內建bind

Function.prototype.bind = function (context) {
//返回一個繫結this的函式,我們需要在此儲存this
let self = this
// 可以支援柯里化傳參,儲存引數
let arg = […arguments].slice(1)
// 返回一個函式
return function () {
//同樣因為支援柯里化形式傳參我們需要再次獲取儲存引數
let newArg = […arguments]
console.log(newArg)
// 返回函式繫結this,傳入兩次儲存的引數
//考慮返回函式有返回值做了return
return self.apply(context, arg.concat(newArg))
}
}

複製程式碼
8. 防抖

函式的防抖debounce:不是某個事件觸發就去執行函式,而是在指定的時間間隔內,執行一次,減少函式執行的次數。(在一定時間只能只能執行一次)

/*

  • @Parma
  • func要執行的函式
  • wait間隔等待的時間
  • immediate在開始邊界還是結束邊界觸發執行(true在開始邊界)
  • @return
  • 可被呼叫的函式
  • by 不要情緒 on 2020/05/24
    */
    function debounce(func, wait, immediate) {
    let result = null,
    timeout = null;
    return function (…args) {
    let context = this,
    now = immediate && !timeout;
    clearTimeout(timeout); //重點:在設定新的定時器之前,要把之前設定的定時器都清除,因為防抖的目的是等待時間內,只執行一次
    timeout = setTimeout(() => {
    if (!immediate) result = func.call(context, …args);
    clearTimeout(timeout);
    timeout = null;
    }, wait);
    if (now) result = func.call(context, …args);
    }
    }
    複製程式碼
  1. 節流

函式的節流(throttle):為了縮減執行的頻率,但不像防抖一樣,一定時間內只能執行一次,而是一定時間內能執行多次

/*

  • throttle:函式節流是為了縮減執行頻率,當達到了一定的時間間隔就會執行一次
  • @params
  •  func:需要執行的函式
    
  •  wait:設定的間隔時間
    
  • @return
  •  返回可被呼叫的函式
    
  • by 不要情緒 on 2019/08/20
    */
    let throttle = function (func, wait) {
    let timeout = null,
    result = null,
    previous = 0; //=>上次執行時間點
    return function (…args) {
    let now = new Date,
    context = this;
    //=>remaining小於等於0,表示上次執行至此所間隔時間已經超過一個時間間隔
    let remaining = wait - (now - previous);
    if (remaining <= 0) {
    clearTimeout(timeout);
    previous = now;
    timeout = null;
    result = func.apply(context, args);
    } else if (!timeout) {
    timeout = setTimeout(() => {
    previous = new Date;
    timeout = null;
    result = func.apply(context, args);
    }, remaining);
    }
    return result;
    };
    };

複製程式碼
10. 深克隆

不僅把第一級克隆一份給新的陣列,如果原始陣列中存在多級,那麼是把每一級都克隆一份賦值給新陣列的每一個級別

function cloneDeep(obj) {
// 傳遞進來的如果不是物件,則無需處理,直接返回原始的值即可(一般Symbol和Function也不會進行處理的)
if (obj === null) return null;
if (typeof obj !== “object”) return obj;

// 過濾掉特殊的物件(正則物件或者日期物件):直接使用原始值建立當前類的一個新的例項即可,這樣克隆後的是新的例項,但是值和之前一樣
if (obj instanceof RegExp) return new RegExp(obj);
if (obj instanceof Date) return new Date(obj);

// 如果傳遞的是陣列或者物件,我們需要建立一個新的陣列或者物件,用來儲存原始的資料
// obj.constructor 獲取當前值的構造器(Array/Object)
let cloneObj = new obj.constructor;
for (let key in obj) {
// 迴圈原始資料中的每一項,把每一項賦值給新的物件
if (!obj.hasOwnProperty(key)) break;
cloneObj[key] = cloneDeep(obj[key]);
}
return cloneObj;
}

複製程式碼

image
11. 深比較

function _assignDeep(obj1, obj2) {
// 先把OBJ1中的每一項深度克隆一份賦值給新的物件
let obj = _cloneDeep(obj1);

// 再拿OBJ2替換OBJ中的每一項
for (let key in obj2) {
if (!obj2.hasOwnProperty(key)) break;
let v2 = obj2[key],
v1 = obj[key];
// 如果OBJ2遍歷的當前項是個物件,並且對應的OBJ這項也是一個物件,此時不能直接替換,需要把兩個物件重新合併一下,合併後的最新結果賦值給新物件中的這一項
if (typeof v1 === “object” && typeof v2 === “object”) {
obj[key] = _assignDeep(v1, v2);
continue;
}
obj[key] = v2;
}

return obj;
}
複製程式碼
12. 驗證手機號

規則:

11 位

第一位是數字 1

第二位是數字 3-9 中的任意一位

let reg = /^1[3-9]\d{9}$/;
console.log(reg.test(‘13245678945’)); //true
console.log(reg.test(‘1324567895’)); //false
console.log(reg.test(‘12245678945’)); //false
複製程式碼
13. 驗證是否是有效數字

規則:

開頭可以有+ -

整數位:

如果是一位數可以是 0-9 任意數;

如果是多位數,首位不可以是 0;

小數位:如果有小數位,那麼小數位後面至少有一位數字,也可以沒有小數位

let reg = /1?(\d|[1-9]\d+)(.\d+)?$/;
console.log(reg.test(‘0.2’)); //true
console.log(reg.test(‘02.1’)); //false
console.log(reg.test(‘20.’)); //false
複製程式碼
14. 驗證密碼

規則:

6-16 位組成

必須由數字字母組成

let reg = /^(?=.\d)(?=.[a-z])(?=.*[A-Z])[\d(a-z)(A-Z)]{6,16}$/;
複製程式碼
15. 驗證真實姓名

規則:

必須是漢字

名字長度 2-10 位

可能有譯名:·漢字

let reg = /2{2,10}(·[\u4E00-\u9FA5]{2,10})?$/;
複製程式碼
16. 驗證郵箱

規則: 郵箱的名字以‘數字字母下劃線-.’幾部分組成,但是-/.不能連續出現也不能作為開頭 \w+((-\w+)|(.\w+))
;

@ 後面可以加數字字母,可以出現多位 @[A-Za-z0-9]+ ;

.com/.cn 等域名 .[A-Za-z0-9]+

let reg = /^\w+((-\w+)|(.\w+))@[A-Za-z0-9]+((.|-)[A-Za-z0-9]+).[A-Za-z0-9]+$/

複製程式碼
17. 驗證身份證號

let reg =/^([1-9]\d{5})((19|20)\d{2})(0[1-9]|10|11|12)(0[1-9]|[1-2]\d|30|31)\d{3}(\d|x)$/i;
複製程式碼
18. 時間格式字串

月日不足十位補零

換成年月日的格式

let time = ‘2020-5-27’;
String.prototype.formatTime = function formatTime(template) {
let arr = this.match(/\d+/g).map(item => {
return item.length < 2 ? ‘0’ + item : item;
});
template = template || ‘{0}年{1}月{2}日 {3}時{4}分{5}秒’;
return template.replace(/{(\d+)}/g, (_, group) => {
return arr[group] || “00”;
});
};
console.log(time.formatTime()); //=> 2020年05月27日 00時00分00秒
複製程式碼
19. 字串中出現次數最多的字元以及次數

let str = “hello”;
let ary = […new Set(str.split(’’))];
let max = 0;
let code = ‘’; for (let i = 0; i < ary.length; i++) {
//建立正則匹配字元
let reg = new RegExp(ary[i], ‘g’);
//利用match找出對應字元在中字串中出現的地方,取匹配的返回陣列的長度,即是對應字串出現的次數
let val = str.match(reg).length; //更新出現次數最高的字元與次數
if (val > max) {
max = val;
code = ary[i];
} else if (val === max) {
//處理不同字元出現次數相同的情況
code = ${code}、${ary[i]};
}
}
console.log(出現次數最多的字元是:${code},次數為:${max}); //=> 出現次數最多的字元是:l,次數為:2

複製程式碼
20. 千分符

String.prototype.millimeter = function millimeter() {
return this.replace(/\d{1,3}(?=(\d{3})+$)/g, value => {
return value + ‘,’;
});
};
let str = “2312345638”;
str = str.millimeter();
console.log(str); //=>“2,312,345,638”
複製程式碼
21. 繼承(一)原型繼承

function Parent() {
this.x = 100;
}
Parent.prototype.getX = function getX() {
return this.x;
};

function Child() {
this.y = 200;
}
Child.prototype = new Parent; //=>原型繼承
Child.prototype.getY = function getY() {
return this.y;
};
let c1 = new Child;
console.log(c1);
複製程式碼
22. 繼承(二)call繼承

function Parent() {
this.x = 100;
}
Parent.prototype.getX = function getX() {
return this.x;
};
function Child() {
// 在子類建構函式中,把父類當做普通方法執行(沒有父類例項,父類原型上的那些東西也就和它沒關係了)
// this -> Child的例項c1
Parent.call(this); // this.x=100 相當於強制給c1這個例項設定一個私有的屬性x,屬性值100,相當於讓子類的例項繼承了父類的私有的屬性,並且也變為了子類私有的屬性 “拷貝式”
this.y = 200;
}
Child.prototype.getY = function getY() {
return this.y;
};
let c1 = new Child;
console.log(c1);

複製程式碼
23. 繼承(三)組合式繼承

function Parent() {
this.x = 100;
}
Parent.prototype.getX = function getX() {
return this.x;
};
function Child() {
Parent.call(this);//call
this.y = 200;
}
Child.prototype = Object.create(Parent.prototype);//原型繼承
Child.prototype.constructor = Child;
Child.prototype.getY = function getY() {
return this.y;
};

let c1 = new Child;
console.log(c1);

複製程式碼
24. 陣列扁平化

[1, 2, [3], [4, 5, [6, [7, 8, 9]]]] => [1,2,3,4,5,6,7,8,9,]
var arr = [1, 2, [3], [4, 5, [6, [7, 8, 9]]]];
function flatten(arr) {
return arr.reduce((res, next) => {
return res.concat(Array.isArray(next) ? flatten(next) : next);
}, []);
}
console.log(flatten(arr));
複製程式碼
25. 改造程式碼,輸出0-9

閉包的解決方案

for (var i = 0; i< 10; i++){
setTimeout(() => {
console.log(i);
}, 1000)
}
複製程式碼
for (let i = 0; i< 10; i++){
setTimeout(() => {
console.log(i);
}, 1000)
}
複製程式碼
26. 氣泡排序

var ary = [3, 1, 5, 2];
function bubble(ary) {
// 比的輪數,最後一輪不用,前面幾輪比完之後,最後那個肯定就是最小的
for (var i = 0; i < ary.length - 1; i++) {
//兩兩進行比較
for (var j = 0; j < ary.length - 1 - i; j++) {
if (ary[j] > ary[j + 1]) {
//解構賦值
[ary[j], ary[j + 1]] = [ary[j + 1], ary[j]]
}
}
}
return ary;
}
var res = bubble(ary);
console.log(res);
複製程式碼
27. 快速排序

function quickSort(ary){
if(ary.length<1){
return ary;
}
var centerIndex=Math.floor(ary.length/2);
// 拿到中間項的同時,把中間項從陣列中刪除掉
var centerValue=ary.splice(centerIndex,1)[0];
// 新建兩個陣列:leftAry,rightAry;把ary中剩餘的項,給中間項做對比,如果大項就放到右陣列,小項就放到左陣列.
var leftAry=[],rightAry=[];
for(var i=0;i<ary.length;i++){
if(ary[i]<centerValue){
leftAry.push(ary[i]);
}else{
rightAry.push(ary[i]);
}
}
return quickSort(leftAry).concat(centerValue,quickSort(rightAry));
}
var ary=[12,15,14,13,16,11];
var res=quickSort(ary);
console.log(res);
複製程式碼
28. 插入排序

var ary = [34, 56, 12, 66, 12];
function insertSort(ary) {
//最終排序好的陣列盒子
var newAry = [];
//拿出的第一項放進去,此時盒子中只有一項,不用個比較
newAry.push(ary[0]);
// 依次拿出原陣列中的每一項進行插入
for (var i = 1; i < ary.length; i++) {
var getItem = ary[i];
// 在插入的時候需要跟新陣列中的每一項進行比較(從右向左)
for (var j = newAry.length - 1; j >= 0; j–) {
var newItemAry = newAry[j];
if (getItem >= newItemAry) {
// 如果拿出的項比某項大或者相等,就放到此項的後面
newAry.splice(j + 1, 0, getItem);
// 插入完畢,不用再繼續比較停止迴圈;
break;
}
if (j == 0) {
//如果都已經比到第一項了,還沒滿足條件,說明這個就是最小項,我們之間插入到陣列的最前面
newAry.unshift(getItem);
}
}

}
return newAry;
}
var res = insertSort(ary);
console.log(res);
複製程式碼
29. 倒數計時案例

let box = document.querySelector(’#box’),
content = document.querySelector(’#content’),
timer = null;
/*

  • 每次頁面重新整理,必須從伺服器拿到時間,才可以進行下面的事情
  • 基於ajax非同步去請求,請求成功才做
  • 所以需要放在回撥函式裡面
  • 為了防止回撥地獄問題,可以使用promise
    */

//獲取伺服器的時間
function getServerTime() {
return new Promise(resolve => {
let xhr = new XMLHttpRequest;
xhr.open(‘get’, ‘./data.json?_=’ + Math.random());
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && /^(2|3)\d{2}$/.test(xhr.status)) {
let time = xhr.getResponseHeader(‘date’);
time = new Date(time);
resolve(time);
}
}
xhr.send(null);
});
}

//根據伺服器時間計算倒數計時
function computed(time) {
let target = new Date(‘2020/05/13 18:59:00’),
spanTime = target - time;
if (spanTime <= 0) {
//已經到底搶購的時間點了
box.innerHTML = ‘開始搶購吧!’
clearInterval(timer);
return;
}
//計算出毫秒差中包含多少小時、多少分鐘、多少秒
let hour = Math.floor(spanTime / (60 * 60 * 1000));
spanTime = spanTime - hour * 60 * 60 * 1000;
let minutes = Math.floor(spanTime / (60 * 1000));
spanTime = spanTime - minutes * 60 * 1000;
let seconds = Math.floor(spanTime / 1000);
hour < 10 ? hour = ‘0’ + hour : null;
minutes < 10 ? minutes = ‘0’ + minutes : null;
seconds < 10 ? seconds = ‘0’ + seconds : null;
content.innerHTML = ${hour}:${minutes}:${seconds};
}

getServerTime().then(time => {
//獲取到伺服器的時間後
computed(time);
//每隔1秒,累加1
timer = setInterval(() => {
time = new Date(time.getTime() + 1000);
computed(time);
}, 1000);
});

複製程式碼
30. 隨機驗證碼

//隨機驗證碼:數字和字母組合,(四個數字和字母)
/*
=> 1先把驗證碼準備好
=> 2隨機獲取相應的索引值
=> 3獲取元素
*/
var code = document.getElementById(“code”);
var btn = document.getElementById(“btn”);
code.innerHTML = getCode();
btn.onclick = function () {
code.innerHTML = getCode();
}
function getCode() {
var str = “qwertyuiopasdfghjklzxcvbnm” + “QWERTYUIOPASDFGHJKLZXCVBNM” + “0123456789”;
var result = “”;
//==> 索引值的範圍:0—61
while (result.length < 4) {
var index = Math.round(Math.random() * 61);
var item = str[index];
if (result.indexOf(item) == -1) {
result += item;
}
}
return result;
}
複製程式碼
31. 選項卡實現

  • css
  • js
  • html
  • css樣式
  • js行為
  • HTML結構
複製程式碼 * { margin: 0; padding: 0; list-style: none; }

#app {
width: 500px;
margin: 20px auto;
}
.list {
overflow: hidden;
position: relative;
top: 1px;
}
.list li {
width: 100px;
float: left;
border: 1px solid #333;
text-align: center;
margin-right: 10px;
height: 50px;
line-height: 50px;
cursor: pointer;
}
.list li.active {
background-color: crimson;
border-bottom-color: crimson;
}
.con li {
width: 100%;
border: 1px solid #333;
height: 300px;
line-height: 300px;
text-align: center;
background-color: crimson;
display: none;
}
.con li.active {
display: block;
}
複製程式碼
let list = document.querySelector(’.list’),
lists = list.querySelectorAll(‘li’),
con = document.querySelector(’.con’),
cons = con.querySelectorAll(‘li’);
for (let i = 0; i < lists.length; i++) {
lists[i].onclick = function () {
click(i);
};
}
function click(index) {
for (let i = 0; i < lists.length; i++) {
lists[i].className = ‘’;
cons[i].className = ‘’;
}
lists[index].className = ‘active’;
cons[index].className = ‘active’;
}
複製程式碼

有更多想了解的可以,
一、搜尋QQ群,前端學習交流群:1093606290

二、https://jq.qq.com/?_wv=1027&k=MlDBtuEG

三、


  1. +- ↩︎

  2. \u4E00-\u9FA5 ↩︎

相關文章