1. call和apply的區別是什麼,哪個性能更好一些
fn.call(obj, 10, 20, 30)
fn.apply(obj, [10, 20, 30])
call性能要比apply性能好一點,(尤其是傳遞給函數的參數超過三個的時候),所以後期研發的時候,可以使用call多一點
let arr = [10, 20, 30]
obj = {}
function fn(x, y, z) {}
fn.apply(obj, arr)
fn.call(obj, ...arr) // 基於ES6的展開運算符也可以實現把數組中的每一項一次傳遞給函數
實現性能測試:任何代碼性能測試讀書和測試環境有關係的,例如cpu、內存、GPU等電腦當前性能不會有相同的情況,不同瀏覽器也會導致性能上的不同
// console.time 可以測試出一段程序執行的時間
// console.profile()在火狐瀏覽器中安裝firebug,可以更精準的獲取到每個步驟所消耗的時間
console.time('A')
for(let i = 0; i< 100000; i++) {
}
console.timeEnd('A')
2. 實現(5).add(3).minus(2) ,使其輸出結果爲: 6
~function() {
// 每一個方法執行完,都要返回number這個類型的實例,這樣纔可以用繼續調取number類型原型中的方法
function check(n) {
n = Number(n);
return isNaN(n)? 0 : n;
}
function add(n){
n = check(n)
return this + n
}
function minus(n) {
n = check(n)
return this -n
}
Number.prototype.add = add;
Number.prototype.minus = minus
}()
3. 箭頭函數與普通函數(function)的區別是什麼?構造函數(function)可以使用new生成實力,那麼箭頭函數可以用嗎?
箭頭函數和普通函數的區別:
- 箭頭函數語法比普通函數更加簡潔(ES6中每一種函數都可以使用形參賦默認值和剩餘運算符)
- 箭頭函數沒有自己的this,它裏面出現的this是繼承函數所處的上下文中的this(使用call/apply等任何方式都無法改變this的指向)
let fn = x => x + 1
// 普通函數
let obj = {
name: 'lanfeng'
}
function fn1() {
console.log(this)
}
fn1.call(obj) // {name: "lanfeng"}
// 箭頭函數
let fn2 = () => {
console.log(this)
}
fn2.call(obj) //Window {parent: Window, opener: null, top: Window, length: 3, frames: Window, …}
document.body.onclick = () => {
//=> this: window不是當前操作的body了
}
// 回調函數: 把伊尼戈函數B作爲實參傳遞給另外一個函數A,函數A在執行的時候,可以把傳遞進來的函數去執行(執行N次,可傳值)
function each(arr, callback) {
for(let i = 0; i < arr.length; i++) {
let item = arr[i],
index = i;
// 接收回調函數返回的結果,如果是false,則結束循環
let flag = callback.call(arr,item, index)
if(flag === false) {
break;
}
}
}
each([10, 20, 30, 40], function(item, index){
// this:原始操作數組
})
- 箭頭函數中沒有arguments(類數組),只能基於...arg獲取傳遞參數集合(數組)
let fn = (...arg) => {
console.log(arguments) //Uncaught ReferenceError: arguments is not defined
console.log(arg) // [10, 20, 30n]
}
fn(10, 20, 30)
- 箭頭函數不能被new執行(因爲箭頭函數沒有this,也沒有prototype)
4. 如何把一個字符串的大小寫取反(大寫變小寫,小寫變大寫)
let str = "lanfengQIUqiu前端"
str = str.replace(/[a-zA-Z]/g, content => {
// content:每一次正則匹配的結果
// 驗證是否爲大寫字母:把字母轉換爲大寫和之前是否一樣,之前是大寫的:在ASII表中找到大寫字母的取值範圍進行判斷
return content.toUpperCase() === content ? content.toLowerCase() : content.toUpperCase()
})
console.log(str) // LANFENGqiuQIU前端
5. 實現一個字符串匹配算法,從字符串s中,查找是否存在字符串T,若存在返回所在位置,不存在返回-1(如果並不能基於indexOf/includes等內置的方法,你會如何處理呢)
循環原始字符串中的每一項,讓每一項從當前位置向後截取T.length個字符,然後和T進行比較,
如果不一樣,繼續循環,如果一樣返回當前索引即可(循環結束)
(function() {
/**
* 循環原始字符串中的每一項,讓每一項從當前位置向後截取T.length個字符,然後和T進行比較,
如果不一樣,繼續循環,如果一樣返回當前索引即可(循環結束)
**/
function myIndexOf(T) {
let lenT = T.length,
lenS = this.length,
res = -1
if(lenT > lenS) return -1
for(let i = 0; i < lenS- lenT + 1; i++) {
let char = this[i];
if(this.substr(i,lenT) === T) {
res = i;
break;
}
}
return res
}
String.prototype.myIndexOf = myIndexOf
})();
let S = 'zhufengpeixun',
T = 'pei'
console.log(S.myIndexOf(T))
正則處理
(function() {
/**
* 正則處理:exec方法
**/
function myIndexOf(T) {
let reg = new RegExp(T),
res = reg.exec(this);
return res === null ? -1 : res.index;
}
String.prototype.myIndexOf = myIndexOf
})();
let S = 'zhufengpeixun',
T = 'pei'
console.log(S.myIndexOf(T))
5. 在輸入框中如何判斷輸入的是一個正確的網址,例如:用戶輸入一個字符串,驗證是否符合URL網址格式
let str = "https://lanfeng.blog.csdn.net/"
let reg = /^((http|https|ftp):\/\/)?(([\w-]+\.)+[a-z0-9]+)((\/[^/]*)+)?(\?[^#]+)?(#.+)?$/i
// 1. 協議: // http/https/ftp
// 2. 域名
// 3. 請求路徑
// 4. 問號傳參
console.log(reg.exec(str))
6.
function Foo() {
Foo.a = function() {
console.log(1)
}
this.a = function() {
console.log(2)
}
}
// 把Foo當做類,在原型上設置實例公有的屬性方法 => 實例.a()
Foo.prototype.a = function() {
console.log(3)
}
// 把Foo當做普通對象設置私有屬性方法 => Foo.a()
Foo.a = function() {
console.log(4)
}
Foo.a(); //4
let obj = new Foo(); // obj可以調取原型上的方法 Foo.a
obj.a(); //2 //私有屬性中有a
Foo.a(); //1
7. 編寫代碼實現圖片懶加載
- 前端性能優化的重要方案,通過圖片或者數據的延遲加載,我們可以加快頁面渲染的速度,讓第一次打開頁面的速度變快,只有滑動到某個區域,我們才加載真實的圖片,這樣也可以節省加載的流量
- 處理方案:把所有需要延遲加載的圖片用一個盒子包起來,設置寬高和默認佔位圖;開始讓所有的img的src爲空,把真實圖片地址放到img的自定義屬性上,讓img隱藏;等到所有其他資源都加載完之後,我們再開始加載圖片;對於很多圖片,需要當頁面滾動的時候,當前圖片區域完全顯示出來之後再加載真實圖片
8. 編寫一條正則,用來驗證此規則:一個6~16位的字符串,必須同時包含有大小寫字母和數字
let reg = /^(?!^[a-zA-Z]+$)(?!^[0-9]+$)(?!^[a-z0-9]+$)(?!^[A-Z0-9]+$)[a-zA-Z0-9]{6,16}/
9. 1-10位:數字、字母、下劃線組成字符串,必須有_
let reg = /(?!^[a-zA-Z0-9]+$)^\w{1, 10}$/
10. 實現一個$attr(name, value)遍歷,屬性爲name,值爲value的元素集合,例如:
let ary = $attr('class', 'box') // 獲取頁面中所有class爲box的元素
function $attr(property, value) {
let elements = document.getElementsByTagName('*'),
arr = [];
[].forEach.call(elements, item => {
// 存儲的是當前元素property對應的屬性值
let itemValue = item.getAttribute(property);
if(property === 'class') {
// 樣式類屬性名要特殊處理
new RegExp('\\b' + value +'\\b').test(itemValue) ? arr.push(item) ? null
return;
}
if(itemValue === value) {
// 獲取的值和傳遞的值校驗成功:當前就是我們想要的
arr.push(item)
}
})
return arr
}
10. 英文字母漢字組成的字符串,用正則給英文單詞前後加空格
let str = 'no作no死,你能你can, 不能no嗶嗶',
reg = /\b[a-z]+\b/ig;
str = str.replace(reg, value => {
return ' ' + value + ' ';
}).trim();
console.log(str) // no 作 no 死,你能你 can , 不能 no 嗶嗶
11. 編寫一個程序,將數組扁平化,並去除其中重複部分數據,最終得到一個升序且不重複的數組
let arr = [[1,2,3],[3,4,5,5],[6,7,8,9,[11, 12,[12,13,[14]]]],10]
arr = arr.flat(Infinity)
//使用ES6中推廣的Array.prototype.flat處理
console.log(arr)
[...new Set(arr)]
arr = Array.from(new Set(arr).sort((a,b) => z-b))
//把數組變成字符串(數組tostring之後,不管數組多少級,最後都會變爲以逗號分隔的字符串),直接扁平化了
arr = arr.toString().split(',').map(item => {
return Number(item)
})
//JSON.stringify(arr)也可以扁平化數組
arr = JSON.stringify(arr).replace(/(\[|\])/g, '').split(',').map(item => Number(item))
//遞歸處理
~function () {
function myFlat() {
let result = [],
_this = this;
// 循環arr中的每一項,把不是數組的存儲到新數組中
let fn = (arr) => {
for(let i = 0; i < arr.length; i++) {
let item = arr[i];
if(Array.isArray(item)) {
fn(item);
continue;
}
result.push(item)
}
};
fn(_this);
return result;
}
Array.prototype.myFlat = myFlat
}();
arr = arr.myFlat()
11. 有關構造函數
function Dog(name) {
this.name = name;
}
Dog.prototype.bark = function() {
console.log('wangwang');
}
Dog.prototype.sayName = function() {
console.log('my name is' + this.name)
}
//基於內置的new關鍵詞,可以創建Dog的一個實例sanmao,實例可以調取原型上的屬性和方法,實現一個_new方法,也能模擬出內置new後的結果
// Fn當前要new的類
// arg後期需要給構造函數傳遞的參數信息
function _new(Fn, ...arg) {
//let obj = {}
//obj.__proto__ = Fn.prototype //創建一個空對象,讓他的原型鏈指向Fn.prototype
let obj = Object.create(Fn.prototype)
Fn.call(obj, ...arg)
return obj
}
/**
* let sanmao = new Dog('三毛');
* 1. 像普通函數執行一樣,形成一個私有的作用域
* + 形參賦值
* + 變量提升
* 2. 默認創建一個對象,讓函數中的this執行這個對象,這個對象就是當前類的一個實例
* 3. 代碼執行
* 4. 默認把創建的對象返回
**/
let sanmao = _new(Dog,'三毛');
sanmao.bark(); // wangwang
sanmao.sayName(); //"my name is 三毛"
console.log(sanmao instanceof Dog) // true
12. 有關數組合並和排序
let ary1 = ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2'];
let ary2 = ['A', 'B', 'C', 'D']
// 合併後的數組爲:['A1', 'A2','A', 'B1', 'B2','B', 'C1', 'C2','C', 'D1', 'D2', 'D'];
ary2 = ary2.map(item => item + '嵐峯')
let arr = ary1.concat(ary2)
arr = arr.sort((a,b) => a.localeCompare(b)).map(item =>{
return item.replace('嵐峯', '')
})
console.log(arr)
let ary1 = ['D1', 'D2','A1', 'A2', 'B1', 'B2', 'C1', 'C2'];
let ary2 = ['B','A', 'C', 'D']
// 合併後的數組爲:['D1', 'D2', 'D', 'A1', 'A2','A', 'B1', 'B2','B', 'C1', 'C2','C'];
let n = 0;
for(let i = 0; i< ary2.length; i++) {
let item2 = ary2[i];
for(let k = 0; k< ary1.length; k++) {
let item1 = ary1[k];
if(item1.includes(item2)) {
n = k;
continue;
}
break;
}
ary1.splice(n+1, 0, item2)
}
console.log(arr)
13. 定時器是異步編程:每一輪循環設置定時器,無需等定時器觸發執行,繼續下一輪循環(定時器觸發的時候,循環已經結束)
for(var i = 0; i < 10; i++) {
(function(i){
setTimeout(() => {
console.log(i)
}, 1000)
})(i)
}
//第二種
for(var i = 0; i < 10; i++) {
setTimeout(((i) => {
return () => {
console.log(i)
}
})(i), 1000)
}
//第三種 可以基於bind預先處理機制:在循環的時候就把每次執行函數需要輸出的結果,預先傳給函數即可
for(var i = 0; i< 10; i++) {
var fn = function(i) {
console.log(i)
};
setTimeout(fn.bind(null, i), 1000)
}
//第四種,var變爲let
for(let i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i)
}, 1000)
}
14. 有關變量
var b = 10;
(function() {
b = 20;
console.log(b) //20
})()
console.log(b) // 20
15.
/**
* == 進行比較的時候,如果左右兩邊數據類型不一樣,則先轉換爲相同的數據類型,然後再進行比較
* 1. {} == {} 兩個對象進行比較,比較的是堆內存的地址
* 2. null == undefined 相等的, null === undefined 不相等
* 3. NaN和誰都不相等 NaN == NaN 不相等
* 4. [12] == '12' 對象和字符串相比較,是把對象toString轉換爲字符串後再進行比較的
* 5. 剩餘所有情況在進行比較的時候,都是轉換爲數字(前提數據類型不一樣)
對象轉數字:先轉換爲字符串,再轉化爲數字
字符串轉數字:只要出現一個非數字字符,結果就是NaN
undefined轉數字NaN
[12] == true =>false
[] == false => 0== 0 => true
[] == 1 => 0 == 1 false
**/
// 1. 對象和數字比較:先把對象.toString()變爲字符串,然後再轉換爲數字
var a = {
n: 0,
toString: function () {
return ++this.n
}
};
// a.toString(); // 此時調取的就不再是Object.prototype.toString了,調取的是自己私有的
if(a == 1 && a ==2 & a==3) {
console.log(1)
}
// 2. shift: 刪除數組的第一項,把刪除的內容返回,原有數組改變
var a = [1,2,3]
a.toString = a.shift
if(a == 1 && a == 2 && a==3) {
console.log('Ok')
}
// 3.
Object.defineProperty(window, 'a', {
get: function() {
this.value ? this.value++ : this.value = 1
return this.value
}
});
if(a == 1 && a == 2 && a==3) {
console.log('Ok')
}
16. 數組push方法原理運用
/**
* Array.prototype.push = function(val) {
this[this.length] = val
//this.length在原來的基礎上加1
return this.length;
}
**/
let obj = {
2:3, //=>1
3: 4, //=> 2
length: 2, // => 3 => 4
push: Array.prototype.push
}
obj.push(1) // this.obj => obj[obj.length] = 1 => obj[2] = 1 => obj.length = 3
obj.push(2) // this.obj => obj[obj.length] = 2 => obj[3] = 2 => obj.length = 4
console.log(obj)
17. 數組當中三大經典排序算法: 冒泡排序
/**
* 冒泡排序思想
* 讓數組中的當前項項和後一項進行比較,如果當前項比後一項大,則兩項交換位置(讓大的靠後)即可
**/
let ary = [12, 8, 24, 16, 1]
/**
* bubble:實現冒泡排序
* ary[Array] 需要排序的數組
* @return
[Array] 排序h後的新數組
**/
function bubble(ary) {
//外層循環i控制比較的輪數
for(let i = 0; i<ary.length; i++) {
// 裏面循環控制每一輪比較的次數
for(let j = 0; j< ary.length-1-i; j++) {
if(ary[j]> ary[j+1]) {
temp = ary[j];
ary[j] = ary[j+1];
ary[j+1] = temp
}
}
}
return ary
}
let ary = [12, 8, 24, 16, 1]
ary = bubble(ary)
console.log(ary)
18. 數組當中三大經典排序算法: 插入排序
/**
* insert:實現插入排序
* ary[Array] 需要排序的數組
* @return
[Array] 排序h後的新數組
**/
function insert(ary) {
// 1. 準備一個新數組,用來存儲抓到手裏的牌,
let handle = [];
handle.push(ary[0])
for(let i = 1; i<ary.length; i++) {
// A是新抓的牌
let A = ary[1]
// 和handle手裏的牌依次比較(從後向前)
for(let j = handle.length - 1; j>=0; j--) {
let B = handle[j]
// 如果當前新牌A比要比較的B大,把A放到B的後面
if(A>B) {
handle.splice(j+1, 0 , A)
breadk;
}
// 已經比到第一項,我們把新牌放到手中最前面即可
if(j===0) {
handle.unshift(A)
}
}
}
return handle
}
let ary = [12, 8, 24, 16, 1]
ary = insert(ary)
console.log(ary)
19. 數組當中三大經典排序算法: 快速排序
/**
* quick:實現快速排序
* ary[Array] 需要排序的數組
* @return
[Array] 排序h後的新數組
**/
function quick(ary) {
//結束遞歸(當ary中小於等於一項,則不用處理)
if(ary.length <=1) {
return aryy
}
// 1. 找到數組的中間項,在你原有的數組中把它拆除
let middleIndex = Math.floor(arry.length/2);
let middleValue = ary.splice(middleIndex, 1)[0]
// 2. 準備左右兩個數組,循環剩下數組中的每一項,比當前項曉的放到左邊數組中,反之放到右邊數組
let aryLeft = [],
aryRight = [];
for(let i = 0; i< ary.length; i++) {
let item = ary[i]
item < middleValue ? aryLeft.push(item) : aryRight.push(item);
}
// 3. 遞歸方式讓左右兩邊的數組持續這樣處理,一直到左右兩邊都排好序爲止
return quick(aryyLeft)concat(middleValue, quick(aryRight));
}
let ary = [12, 8, 24, 16, 1]
ary = quick(ary)
console.log(ary)
20.
某公司1到12月份的銷售額存在一個對象裏面
如下: {
1: 222,
2: 123,
5: 888
}
請把數據處理爲如下結構:[222, 123, null, null,888, null, null, null, null, null, null , null]
//第一種方法
let obj = {
1: 222,
2: 123,
5: 888
}
let arr = new Array(12).fill(null).map((item, index) => {
return obj[index + 1] || null
})
console.log(arr)
// 第二種方法
let obj = {
1: 222,
2: 123,
5: 888
}
obj.length = 13
let arr = Array.from(obj).slice(1).map(item => {
typeof item === 'undefined' ? null : item
})
console.log(arr)
// 第三種方法
let obj = {
1: 222,
2: 123,
5: 888
}
let arr = new Array(12).fill(null)
Object.keys(obj).forEach(item => {
arr[item-1] = obj[item];
})
console.log(arr)
21. 給定兩個數組,寫一個方法來計算它們的交集
// 第一種方法
let num1 = [1,2,2,1]
let num2 = [2,2]
//輸出結果[2, 2]
let arr = [];
for(let i = 0; i< num1.length; i++) {
let item1 = num1[i]
for(let k = 0; k< num2.length; k++) {
let item2 = num2[k]
if(item1 === item2) {
arr.push(item1)
break;
}
}
}
console.log(arr)
// 第二種方法
let num1 = [1,2,2,1]
let num2 = [2,2]
let arr = [];
num1.forEach(item => {
num2.includes(item) ? arr.push(item) : null
})
console.log(arr)
22.
實現一個add函數,滿足以下功能
add(1); // 1
add(1)(2); // 3
add(1)(2)(3) // 6
add(1)(2)(3)(4); //10
add(1)(2, 3); // 6
add(1, 2)(3); //6
add(1,2,3); // 6
函數柯里化:預先處理的思想(利用閉包的機制)
let obj = {name: 'lanfeng'}
function fn(...arg) {
console.log(this, arg)
}
// 點擊的時候fn中的this => obj arg=> [100, 200, 事件對象]
document.body.onclick = fn.bind(obj, 100, 200);
// 執行bind方法,會返回一個匿名函數,當事件觸發,匿名函數執行,我們仔處理fn即可
document.body.onclick = function(ev) {
//ev事件對象:給每個元素的某個事件綁定方法,當事件觸發會執行這個方法,並且把當前事件的相關信息傳遞給這個函數“事件對象”
}
function currying(fn, length) {
length = length || fn.length;
return function(...args) {
if(args.length >= length) {
return fn(...args);
}
return currying(fn.bind(null, ...args), length-args.length);
}
}
function add(n1, n2, n3) {
return n1 + n2 + n3;
}
add = currying(add, 3)
console.log(add(1)(2)(3))