南平網(wǎng)站開發(fā)客戶引流推廣方案
前言
?📫 大家好,我是南木元元,熱愛技術(shù)和分享,歡迎大家交流,一起學習進步!
?🍅?個人主頁:南木元元
今天來分享一下10個常見的JavaScript手寫功能。
目錄
1.實現(xiàn)new
2.call、apply、bind
實現(xiàn)call
實現(xiàn)apply
實現(xiàn)bind
3.防抖和節(jié)流
防抖
節(jié)流
4.實現(xiàn)instanceof
5.實現(xiàn)Ajax
6.深拷貝和淺拷貝
淺拷貝
深拷貝
7.函數(shù)柯里化
參數(shù)定長的柯里化
參數(shù)不定長的柯里化
8.數(shù)組扁平化
9.數(shù)組去重
10.手寫類型判斷函數(shù)
?結(jié)語
1.實現(xiàn)new
(1)首先創(chuàng)建一個新的空對象。
(2)設(shè)置原型,將對象的原型設(shè)置為函數(shù)的prototype對象。
(3)讓函數(shù)的this指向這個對象,執(zhí)行構(gòu)造函數(shù)的代碼。
(4)判斷函數(shù)的返回值類型,如果是值類型,返回創(chuàng)建的對象。如果是引用類型,就返回這個引用類型的對象。
function myNew(constructor, ...args) {// 如果不是一個函數(shù),就報錯if (typeof constructor !== "function") {throw "myNew function the first param must be a function";}// 基于原型鏈 創(chuàng)建一個新對象,繼承構(gòu)造函數(shù)constructor的原型對象上的屬性let newObj = Object.create(constructor.prototype);// 將newObj作為this,執(zhí)行 constructor ,傳入?yún)?shù)let res = constructor.apply(newObj, args);// 判斷函數(shù)的執(zhí)行結(jié)果是否是對象,typeof null 也是'object'所以要排除nulllet isObject = typeof res === "newObject" && res !== null;// 判斷函數(shù)的執(zhí)行結(jié)果是否是函數(shù)let isFunction = typeof res === "function";// 如果函數(shù)的執(zhí)行結(jié)果是一個對象或函數(shù), 則返回執(zhí)行的結(jié)果, 否則, 返回新創(chuàng)建的對象return isObject || isFunction ? res : newObj;
}// 用法
function Person(name, age) {this.name = name;this.age = age;// 如果構(gòu)造函數(shù)內(nèi)部,return 一個引用類型的對象,則整個構(gòu)造函數(shù)失效,而是返回這個引用類型的對象
}
Person.prototype.say = function() {console.log(this.age);
};
let p1 = myNew(Person, "poety", 18);
console.log(p1.name); //poety
console.log(p1); //Person {name: 'poety', age: 18}
p1.say(); //18
測試結(jié)果:?
2.call、apply、bind
實現(xiàn)call
思路:接受傳入的context上下文,如果不傳默認為window,將被調(diào)用的方法設(shè)置為上下文的屬性,使用上下文對象來調(diào)用這個方法,刪除新增屬性,返回結(jié)果。
//寫在函數(shù)的原型上
Function.prototype.myCall = function (context) {// 如果要調(diào)用的方法不是一個函數(shù),則報錯if (typeof this !== "function") {throw new Error("Type error");}// 判斷 context 是否傳入,如果沒有傳就設(shè)置為 windowcontext = context || window;// 獲取參數(shù),[...arguments]把類數(shù)組轉(zhuǎn)為數(shù)組let args = [...arguments].slice(1);let result = null;// 將被調(diào)用的方法設(shè)置為context的屬性,this即為要調(diào)用的方法context.fn = this;// 執(zhí)行要被調(diào)用的方法result = context.fn(...args);// 刪除手動增加的屬性方法delete context.fn;// 將執(zhí)行結(jié)果返回return result;
};//測試
function f(a,b){console.log(a+b)console.log(this.name)
}
let obj={name:1
}
f.myCall(obj,1,2) // 3,1
測試結(jié)果:
實現(xiàn)apply
除了傳參方式是數(shù)組,其它與call沒區(qū)別。
Function.prototype.myApply = function (context) {if (typeof this !== "function") {throw new Error("Type error");}let result = null;context = context || window;// 與上面代碼相比,我們使用 Symbol 來保證屬性唯一,也就是保證不會重寫用戶自己原來定義在 context 中的同名屬性const fnSymbol = Symbol();context[fnSymbol] = this;// 執(zhí)行要被調(diào)用的方法,處理參數(shù)和 call 有區(qū)別,判斷是否傳了參數(shù)數(shù)組if (arguments[1]) {//傳了參數(shù)數(shù)組result = context[fnSymbol](...arguments[1]);} else {//沒傳參數(shù)數(shù)組result = context[fnSymbol]();}delete context[fnSymbol];return result;
};//測試
function f(a,b){console.log(a,b)console.log(this.name)
}
let obj={name:'張三'
}
f.myApply(obj,[1,2]) //1 2,張三
測試結(jié)果:
實現(xiàn)bind
bind返回的是一個函數(shù),需要判斷函數(shù)作為構(gòu)造函數(shù)的情況,當作為構(gòu)造函數(shù)時,this 指向?qū)嵗?#xff0c;不會被任何方式改變 this,所以要忽略傳入的context上下文。
bind可以分開傳遞參數(shù),所以需要將參數(shù)拼接。
如果綁定的是構(gòu)造函數(shù),還需要繼承構(gòu)造函數(shù)原型上的屬性和方法,保證不丟失。
Function.prototype.myBind = function (context) {// 判斷調(diào)用對象是否為函數(shù)if (typeof this !== "function") {throw new Error("Type error");}// 獲取參數(shù)const args = [...arguments].slice(1);const fn = this; // 保存this的值,代表調(diào)用bind的函數(shù)//返回一個函數(shù),此函數(shù)可以被作為構(gòu)造函數(shù)調(diào)用,也可以作為普通函數(shù)調(diào)用const Fn = function () {// 根據(jù)調(diào)用方式,傳入不同綁定值// 當作為構(gòu)造函數(shù)時,this 指向?qū)嵗?#xff0c;不會被任何方式改變 this,要忽略傳入的context上下文return fn.apply(this instanceof Fn ? this : context,// bind可以分開傳遞參數(shù)(如f.bind(obj, 1)(2)),所以需要將參數(shù)拼接,這里使用apply,參數(shù)拼接成一個數(shù)組args.concat(...arguments)//當前的這個 arguments 是指 Fn 的參數(shù),也可以用剩余參數(shù)的方式);};//對于構(gòu)造函數(shù),要保證原函數(shù)的原型對象上的屬性不能丟失Fn.prototype = Object.create(fn.prototype);return Fn;
};// 1.先測試作為構(gòu)造函數(shù)調(diào)用
function Person(name, age) {console.log(name);console.log(age);console.log(this); //構(gòu)造函數(shù)this指向?qū)嵗龑ο?}
// 構(gòu)造函數(shù)原型的方法
Person.prototype.say = function () {console.log("say");
};
var obj = {name: "cc",age: 18,
};
var bindFun = Person.myBind(obj, "cxx");
var a = new bindFun(10);
// cxx
// 10
// Person {}
a.say(); // say// 2.再測試作為普通函數(shù)調(diào)用
function normalFun(name, age) {console.log(name);console.log(age);console.log(this); // 普通函數(shù)this指向綁定bind的第一個參數(shù) 也就是例子中的obj
}
var obj = {name: "aa",age: 18,
};
var bindNormalFun = normalFun.myBind(obj, "cc");
bindNormalFun(12);
// cc
// 12
// { name: 'aa', age: 18 }
測試結(jié)果:?
3.防抖和節(jié)流
防抖
防抖是指在事件被觸發(fā) n 秒后再執(zhí)行回調(diào),如果在這 n 秒內(nèi)事件又被觸發(fā),則重新計時??梢允褂迷谝恍c擊請求的事件上,避免因為用戶的多次點擊向后端發(fā)送多次請求。
//fn是需要防抖的函數(shù),delay是等待時間
function debounce(fn, delay = 500) {let timer = null;// 這里返回的函數(shù)是每次用戶實際調(diào)用的防抖函數(shù)return function(...args) { //...args是es6的剩余參數(shù)語法,將多余的參數(shù)放入數(shù)組,用來代替arguments對象// 如果已經(jīng)設(shè)定過定時器了就清空上一次的定時器if(timer) {clearTimeout(timer); }// 開始一個新的定時器,延遲執(zhí)行用戶傳入的方法;注:定時器的返回值是一個數(shù)值,作為定時器的編號,可以傳入clearTimeout來取消定時器timer = setTimeout(() => { //這里必須是箭頭函數(shù),不然this指向window,要讓this就指向fn的調(diào)用者fn.apply(this, args); }, delay) }
}
節(jié)流
節(jié)流就是一定時間內(nèi)只執(zhí)行一次事件,即使重復(fù)觸發(fā),也只有一次生效??梢允褂迷诒O(jiān)聽滾動scroll事件上,通過事件節(jié)流來降低事件調(diào)用的頻率。
- 定時器版本
function throttle(fn, delay = 500) {let timer = null;return function(...args) {// 當前有任務(wù)了,直接返回if(timer) {return;}timer = setTimeout(() => {fn.apply(this, args);//執(zhí)行完后,需重置定時器,不然timer一直有值,無法開啟下一個定時器timer = null; }, delay)}
}
- 時間戳版本
// 節(jié)流
function throttle(fn, delay = 500) {let prev = Date.now();// 上一次執(zhí)行該函數(shù)的時間return function(...args) {let now = Date.now();//返回從UTC到當前時間的毫秒數(shù)// 如果差值大于等于設(shè)置的等待時間就執(zhí)行函數(shù)if (now - prev >= delay) {fn.apply(this, args);prev = Date.now();}};
}
詳細可以去看我的這篇文章——前端性能優(yōu)化之防抖&節(jié)流
4.實現(xiàn)instanceof
instanceof用于檢測構(gòu)造函數(shù)的prototype
屬性是否出現(xiàn)在某個實例對象的原型鏈上。
function myInstanceof(instance, constructor) {//如果不是對象,或者是null,直接返回falseif (typeof instance !== "object" || instance === null) {return false;}// 獲取對象的原型let proto = Object.getPrototypeOf(instance);// 獲取構(gòu)造函數(shù)的 prototype 對象let prototype = constructor.prototype;// 判斷構(gòu)造函數(shù)的 prototype對象是否在對象的原型鏈上while (true) {// 到達原型鏈終點null,說明沒找到if (!proto) {return false;}if (proto === prototype) {return true;}// 如果沒有找到,就繼續(xù)從其原型上找proto = Object.getPrototypeOf(proto);}
}//測試
let Fn = function () { }
let p1 = new Fn()
console.log(myInstanceof(p1, Fn));//true
console.log(myInstanceof([], Fn));//false
console.log(myInstanceof([], Array)) // true
console.log(myInstanceof(function(){}, Function)) // true
測試結(jié)果:?
5.實現(xiàn)Ajax
(1)創(chuàng)建一個 XMLHttpRequest 對象。
(2)在這個對象上使用 open 方法創(chuàng)建一個 HTTP 請求(參數(shù)為請求方法、請求地址、是否異步和用戶的認證信息)。
(3)通過send方法來向服務(wù)器發(fā)起請求(post請求可以入?yún)?shù)作為發(fā)送的數(shù)據(jù)體)。
(4)監(jiān)聽請求成功后的狀態(tài)變化:根據(jù)狀態(tài)碼進行相應(yīng)的處理。onreadystatechange設(shè)置監(jiān)聽函數(shù),當對象的readyState變?yōu)?的時候,代表服務(wù)器返回的數(shù)據(jù)接收完成,這個時候可以通過判斷請求的狀態(tài),如果狀態(tài)是200則代表成功,404或500代表失敗。
function ajax(url) {//1.創(chuàng)建XMLHttpRequest對象const xhr = new XMLHttpRequest();//2.使用open方法創(chuàng)建一個GET請求xhr.open('GET',url);//xhr.open('GET',url,true);//true代表異步,已完成事務(wù)的通知可供事件監(jiān)聽器使用;如果為false,send() 方法直到收到答復(fù)前不會返回//3.發(fā)送請求xhr.send(); //4.監(jiān)聽請求成功后的狀態(tài)變化(readyState改變時觸發(fā)):根據(jù)狀態(tài)碼(0~5)進行相應(yīng)的處理xhr.onreadystatechange = function () {//readyState為4代表服務(wù)器返回的數(shù)據(jù)接收完成if (xhr.readyState == 4) { //請求的狀態(tài)為200或304代表成功if(xhr.status == 200 || xhr.status == 304) {//this.response代表返回的數(shù)據(jù)handle(this.response);} else {//this.statusText代表返回的文本信息console.error(this.statusText);}}};
}
使用Promise封裝AJAX:
function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest()xhr.open('get', url)xhr.send() xhr.onreadystatechange = () => {if (xhr.readyState == 4) {if (xhr.status == 200 || xhr.status == 304) {resolve(this.response)} else {reject(new Error(this.statusText));}}}})
}
//使用
let url = '/data.json'
ajax(url).then(res => console.log(res)).catch(reason => console.log(reason))
6.深拷貝和淺拷貝
淺拷貝
淺拷貝是創(chuàng)建一個新對象,這個對象有著原始對象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值,如果屬性是引用類型,拷貝的就是內(nèi)存地址 ,所以如果其中一個對象改變了這個地址,就會影響到另一個對象。
//實現(xiàn)淺拷貝
function shallowCopy (obj){// 只拷貝對象,基本類型或null直接返回if(typeof obj !== 'object' || obj === null) {return obj;}// 判斷是新建一個數(shù)組還是對象let newObj = Array.isArray(obj) ? []: {};//for…in會遍歷對象的整個原型鏈,如果只考慮對象本身的屬性,需要搭配hasOwnPropertyfor(let key in obj ){//hasOwnProperty判斷是否是對象自身屬性,會忽略從原型鏈上繼承的屬性if(obj.hasOwnProperty(key)){newObj[key] = obj[key];//只拷貝對象本身的屬性}}return newObj;
}//測試
var obj ={name:'張三',age:8,pal:['王五','王六','王七']
}
let obj2 = shallowCopy(obj);
obj2.name = '李四'
obj2.pal[0] = '王麻子'
console.log(obj); //{age: 8, name: "張三", pal: ['王麻子', '王六', '王七']}
console.log(obj2); //{age: 8, name: "李四", pal: ['王麻子', '王六', '王七']}
測試結(jié)果:?
深拷貝
深拷貝是將一個對象從內(nèi)存中完整的拷貝一份出來,從堆內(nèi)存中開辟一個新的區(qū)域存放新對象,且修改新對象不會影響原對象。
function deepCopy (obj, map = new WeakMap()){// 基本類型或null直接返回if(typeof obj !== 'object' || obj === null) {return obj;}// 判斷是新建一個數(shù)組還是對象let newObj = Array.isArray(obj) ? []: {};//利用map解決循環(huán)引用if (map.has(obj)) {return map.get(obj);}map.set(obj, newObj);//將當前對象作為key,克隆對象作為valuefor(let key in obj ){ if(obj.hasOwnProperty(key)){newObj[key] = deepCopy(obj[key], map); //遞歸}}return newObj
}// 測試
let obj1 = {name : '南木元元',arr : [1,[2,3],4],
};
let obj2=deepCopy(obj1)
obj2.name = "阿元";
obj2.arr[1] = [5,6,7] ; // 新對象跟原對象不共享內(nèi)存console.log('obj1',obj1) // obj1 { name: '南木元元', arr: [ 1, [ 2, 3 ], 4 ] }
console.log('obj2',obj2) // obj2 { name: '阿元', arr: [ 1, [ 5, 6, 7 ], 4 ] }
測試結(jié)果:
7.函數(shù)柯里化
函數(shù)柯里化指的是一種將使用多個參數(shù)的一個函數(shù)轉(zhuǎn)換成一系列使用一個參數(shù)的函數(shù)的技術(shù)。
作用:可以參數(shù)復(fù)用(公共的參數(shù)已經(jīng)通過柯里化預(yù)置了)和延遲執(zhí)行(柯里化時只是返回一個預(yù)置參數(shù)的新函數(shù),并沒有立刻執(zhí)行,在滿足條件后才會執(zhí)行)。
參數(shù)定長的柯里化
思路:通過函數(shù)的length屬性獲取函數(shù)的形參個數(shù)?????,形參的個數(shù)就是所需參數(shù)的個數(shù)。維護一個數(shù)組,當數(shù)組的長度與函數(shù)接收參數(shù)的個數(shù)一致,再執(zhí)行該函數(shù)。
// 實現(xiàn)函數(shù)柯里化
function curry(fn) {// 返回一個新函數(shù)return function curried(...args) {if (args.length >= fn.length) {return fn.apply(this, args); // 如果參數(shù)夠了,就執(zhí)行原函數(shù),返回結(jié)果} else {//返回一個新函數(shù),繼續(xù)遞歸去進行柯里化,利用閉包,將當前已經(jīng)傳入的參數(shù)保存下來return function (...args2) {//遞歸調(diào)用 curried 函數(shù)return curried.apply(this, [...args, ...args2]); //新函數(shù)調(diào)用時會繼續(xù)傳參,拼接參數(shù)};}};
}// 測試
function sum(a, b, c) {return a + b + c;
}
var curried = curry(sum);
console.log(curried(1, 2, 3)); //6
console.log(curried(1, 2)(3)); //6
console.log(curried(1)(2, 3)); //6
console.log(curried(1)(2)(3)); //6
測試結(jié)果:
參數(shù)不定長的柯里化
題目:如何實現(xiàn)一個方法,使計算結(jié)果能夠滿足如下預(yù)期。
add(1, 2, 3) // 6
add(1) // 1
add(1)(2) // 3
add(1, 2)(3) // 6
add(1)(2)(3) // 6
add(1)(2)(3)(4) // 10
思路:利用閉包和遞歸,如果參數(shù)為空,則判斷遞歸結(jié)束,求和,返回結(jié)果。
function addCurry() {// 利用閉包的特性收集所有參數(shù)值let arr = [...arguments] //返回函數(shù)return function fn() {// 如果參數(shù)為空,則判斷遞歸結(jié)束,即傳入一個()執(zhí)行函數(shù)if(arguments.length === 0) { return arr.reduce((a, b) => a + b) // 求和} else {arr.push(...arguments)return fn //遞歸}}
}// 測試
console.log(addCurry(1)()); //1
console.log(addCurry(1)(2)()); //3
console.log(addCurry(1)(2)(3)()); //6
console.log(addCurry(1, 2)(3)()); //6
console.log(addCurry(1, 2, 3)()); //6
上述寫法,總是要以空括號()結(jié)尾,于是再改進為隱式轉(zhuǎn)換.toString
寫法,原理:當用 Function的值做計算的時候,會調(diào)用toString做隱式轉(zhuǎn)換。注意一些舊版本的瀏覽器隱式轉(zhuǎn)換會默認執(zhí)行,新版本不行了??梢岳秒[式轉(zhuǎn)換或者alert。
function addCurry() {let arr = [...arguments]// 利用閉包的特性收集所有參數(shù)值var fn = function() {arr.push(...arguments);return fn;//遞歸};// 利用 toString 隱式轉(zhuǎn)換,轉(zhuǎn)換的時候再返回結(jié)果fn.toString = function () {return arr.reduce(function (a, b) {return a + b;});}return fn;
}//測試
console.log(addCurry(1)(2) == 3) //true 利用隱式轉(zhuǎn)換,自動調(diào)用toString方法得到柯里化的結(jié)果
//alert(addCurry(1)(2)(3))//6 alert參數(shù)只能是字符串,如果其他類型的值,會轉(zhuǎn)換成字符串,會調(diào)用toString方法
console.log(addCurry(1).toString());//1 手動調(diào)用toString
console.log(addCurry(1, 2)(3).toString());//6
console.log(addCurry(1, 2)(3)(4)(5).toString());//15
測試結(jié)果:
8.數(shù)組扁平化
數(shù)組扁平化其實就是將多維數(shù)組轉(zhuǎn)為一維數(shù)組。
(1)ES6中的flat
const arr = [1,[2,[3,[4,5]]],6]
// arr.flat([depth]) flat的參數(shù)代表的是需要展開幾層,如果是Infinity的話,就是不管嵌套幾層,全部都展開
console.log(arr.flat(Infinity)) //[1,2,3,4,5,6]
(2)遞歸
let arr = [1, [2, [3, 4]]];
function flatten(arr) {let result = [];for (let i = 0; i < arr.length; i++) {//如果當前元素還是一個數(shù)組if (Array.isArray(arr[i])) {result = result.concat(flatten(arr[i]));//遞歸拼接} else {result.push(arr[i]);}}return result;
}
console.log(flatten(arr)); // [1, 2, 3, 4]
(3)reduce函數(shù)迭代
從上面普通的遞歸函數(shù)中可以看出,其實就是對數(shù)組的每一項進行處理,那么其實也可以用reduce來實現(xiàn)數(shù)組的拼接,從而簡化第一種方法的代碼。
let arr = [1, [2, [3, 4]]];
function flatten(arr) {return arr.reduce((total, cur) => {return total.concat(Array.isArray(cur) ? flatten(cur) : cur);}, []); //傳遞初始值空數(shù)組[],就會從數(shù)組索引為 0 的元素開始執(zhí)行
}
console.log(flatten(arr)); // [1, 2, 3, 4]
(4)split和toString
數(shù)組的toString
方法可以把數(shù)組直接轉(zhuǎn)換成逗號分隔的字符串。如[1, [2, [3, 4]]] => "1,2,3,4"
let arr = [1, [2, [3, 4]]];
function flatten(arr) {//先把數(shù)組直接轉(zhuǎn)換成逗號分隔的字符串,然后再用 split 方法把字符串重新轉(zhuǎn)換為數(shù)組return arr.toString().split(",").map(Number);
}
console.log(flatten(arr)); // [ 1, 2, 3, 4 ]
9.數(shù)組去重
(1)利用Set。new一個Set,參數(shù)為需要去重的數(shù)組,Set 會自動刪除重復(fù)的元素,再Array.from將 Set 轉(zhuǎn)為數(shù)組返回。
const arr = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
console.log([...new Set(arr)]); //[ 1, 2, 3, 5, 9, 8 ]
console.log(Array.from(new Set(arr))); //[ 1, 2, 3, 5, 9, 8 ]
(2)利用數(shù)組的filter() + indexOf去重。利用filter方法,返回arr.indexOf(num)等于index的值。原理就是indexOf會返回最先找到的數(shù)字的索引。
function unique(arr) {return arr.filter((item, index, array) => {return array.indexOf(item) === index;});
}
const arr = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
console.log(unique(arr)); // [1, 2, 3, 5, 9, 8]
(3)利用map。新建一個數(shù)組和map,如果當前值在map中沒有出現(xiàn)過,就加入數(shù)組,最后返回數(shù)組。
const unique = (arr) => {const map = new Map();const res = [];for (let item of arr) {if (!map.has(item)) {map.set(item, true);res.push(item);}}return res;
}
const arr = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
console.log(unique(arr)); // [1, 2, 3, 5, 9, 8]
10.手寫類型判斷函數(shù)
思路:如果是null,直接返回String(null);基本類型和函數(shù),直接使用typeof;
其它引用類型,使用Object.prototype.toString.call。
function getType(value) {// 判斷數(shù)據(jù)是 null 的情況if (value === null) {return String(value);}// 判斷數(shù)據(jù)是基本數(shù)據(jù)類型的情況和函數(shù)的情況,使用typeofif (typeof value !== "object") {return typeof value;} else {// 判斷數(shù)據(jù)是引用類型的情況,設(shè)當前類型為datelet valueClass = Object.prototype.toString.call(value); //"[object Date]"type = valueClass.split(" ")[1].split(""); //[ 'D', 'a', 't', 'e', ']' ] 截取類型并轉(zhuǎn)換為數(shù)組type.pop(); //[ 'D', 'a', 't', 'e' ],去掉數(shù)組最后的右括號"]"return type.join("").toLowerCase(); //[ 'D', 'a', 't', 'e' ] => "Date" => "date" 數(shù)組轉(zhuǎn)小寫字符串}
}// 測試
console.info(getType(null)); // null
console.info(getType(undefined)); // undefined
console.info(getType(100)); // number
console.info(getType("abc")); // string
console.info(getType(true)); // boolean
console.info(getType(Symbol())); // symbol
console.info(getType({})); // object
console.info(getType([])); // array
console.info(getType(() => {})); // function
console.info(getType(new Date())); // date
console.info(getType(new RegExp(""))); // regexp
console.info(getType(new Map())); // map
console.info(getType(new Set())); // set
console.info(getType(new WeakMap())); // weakmap
console.info(getType(new WeakSet())); // weakset
console.info(getType(new Error())); // error
console.info(getType(new Promise(() => {}))); // promise
測試結(jié)果:
結(jié)語
本文總結(jié)了前端常見的一些手寫功能,你是不是全都掌握了呢,歡迎在評論區(qū)交流。
🔥如果此文對你有幫助的話,歡迎💗關(guān)注、👍點贊、?收藏、??評論,支持一下博主~?????