- A+
所属分类:Web前端
前端工程师进阶之旅-手撕代码
主要包括一些工作中常用的方法,面试常问到的方法。还有一些不太了解,趁机深入了解的知识点。
废话少说,直接干代码就完事了。
数据类型判断
使用 Object.prototype.toString 实现。
function myTypeof(data) { return Object.prototype.toString.call(data).slice(8, -1).toLowerCase(); }
数组去重
//new Set() 集合 //ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。 function unique(arr) { return [...new Set(arr)]; } //数组去重 filter function unique(arr) { return arr.filter((item, index, array) => { return array.indexOf(item) === index; }); } // 数组去重 forEach function unique(arr) { let res = []; arr.forEach((item) => { res.includes(item) ? "" : res.push(item); }); return res; }
数组扁平化
//使用 Infinity,可展开任意深度的嵌套数组 arr.flat(Infinity); //适用JSON转换 JSON.parse("[" + JSON.stringify(arr).replace(/[|]/g, "") + "]"); //递归 function myFlat(arr) { let res = []; arr.forEach((item) => { if (Array.isArray(item)) { res = res.concat(myFlat(item)); } else { res.push(item); } }); return res; } //some function myFlat(arr) { while (arr.some((res) => Array.isArray(res))) { arr = [].concat(...arr); } return arr; }
深拷贝深克隆
// 简单克隆 无法复制函数 var newObj = JSON.parse(JSON.stringify(obj)); // 深克隆 无法克隆特殊实例 Date等 function deepClone(target) { if (typeof target !== "object") { return target; } var result; if (Object.prototype.toString.call(target) == "[object Array]") { // 数组 result = []; } else { // 对象 result = {}; } for (var prop in target) { if (target.hasOwnProperty(prop)) { result[prop] = deepClone(target[prop]); } } return result; } //复杂版深克隆 function deepClone(target) { if (typeof target !== "object") return target; // 检测RegDate类型创建特殊实例 let constructor = target.constructor; if (/^(RegExp|Date)$/i.test(constructor.name)) { return new constructor(target); } // 判断类型 var result = Object.prototype.toString.call(target) == "[object Array]" ? [] : {}; // 迭代循环 for (var prop in target) { if (target.hasOwnProperty(prop)) { // 递归 result[prop] = deepClone(target[prop]); } } return result; }
继承方法
原型链继承:
// 原型链继承 // 问题:原型中的引用对象会被所有的实例共享,子类在实例化的时候不能给父类构造函数传参 function Father() { this.hobby = ["coding", "eat"]; } Father.prototype.skill = function () { console.log("i will javascript"); }; function Son() {} Son.prototype = new Father(); var father = new Father(); var son = new Son(); var son1 = new Son(); console.log(father.hobby); //[ 'coding', 'eat' ] father.hobby.push("play"); console.log(father.hobby, son.hobby, son1.hobby); //[ 'coding', 'eat', 'play' ] [ 'coding', 'eat' ] [ 'coding', 'eat' ] son.hobby.push("hei"); console.log(father.hobby, son.hobby, son1.hobby); //[ 'coding', 'eat', 'play' ] [ 'coding', 'eat', 'hei' ] [ 'coding', 'eat', 'hei' ] son.skill(); //i will javascript
借用构造函数实现继承
// 原型链继承 // 问题:方法需要定义在构造函数内,因此每次创建子类实例都会创建一边方法 function Father(name) { this.name = name; this.sayNmae = function () { return this.name; }; } function Son(name) { Father.call(this, name); } Son.prototype = new Father(); var father = new Father("wenbo"); var son = new Son("zhijian"); console.log(father.name, son.name); //wenbo zhijian console.log(father.sayNmae(), son.sayNmae()); //wenbo zhijian
组合继承
//组合继承,结合原型链继承和借用构造函数,使用原型链继承原型上的属性和方法,借用构造函数继承实例属性。 //即可以把方法定义在原型上实现重用,又可以让每个实例都有自己的属性 // 组合继承 function Father(name) { this.name = name; } Father.prototype.sayName = function () { return this.name; }; function Son(name, age) { Father.call(this, name); this.age = age; } Son.prototype = new Father(); Son.prototype.constructor = Son; var son = new Son("yewen", 18); console.log(son); //Son { name: 'yewen', age: 18 } console.log(son.sayName()); //yewen
寄生式组合继承
//寄生组合继承 // 组合继承会导致调用两次父类构造函数 function Father(name) { this.name = name; } Father.prototype.sayName = function () { return this.name; }; function Son(name, age) { Father.call(this, name); this.age = age; } Son.prototype = Object.create(Father.prototype); Son.prototype.constructor = Son;
class 实现继承
// calss继承 class Father { constructor(name) { this.name = name; } getName() { return this.name; } } class Son extends Father { constructor(name, age) { super(name); this.age = age; } getAge() { return this.age; } } var son = new Son("heihei", 18); console.log(son); //Son { name: 'heihei', age: 18 } console.log(son.getName(), son.getAge()); //heihei 18
事件总线(发布订阅模式)
class EventEmitter { constructor() { this.cache = {}; } //添加订阅 on(name, fn) { if (this.cache[name]) { this.cache[name].push(fn); } else { this.cache[name] = [fn]; } } //删除订阅 off(name, fn) { let tasks = this.cache[name]; if (tasks) { const index = tasks.findIndex((f) => f === fn || f.callback === fn); index >= 0 ? tasks.splice(index, 1) : ""; } } //发布事件 emit(name, once, ...args) { if (this.cache[name]) { // 创建副本 let tasks = this.cache[name].slice(); for (const fn of tasks) { fn(...args); } once ? delete this.cache[name] : ""; } } } let demo = new EventEmitter(); demo.on("wenbo", function (data) { console.log("wenbo", data); }); let fn1 = function (data) { console.log("hello:", data); }; demo.on("wenbo", fn1); demo.emit("wenbo", false, "world"); demo.off("wenbo", fn1); demo.emit("wenbo", false, "world"); //wenbo world //hello: world //wenbo world
防抖函数
function debounce(fun, wait) { var timeId = null; return function () { var _this = this; var _arg = arguments; clearTimeout(timeId); timeId = setTimeout(function () { fun.apply(_this, _arg); }, wait); }; }
节流函数
function throttle(fun, wait) { var lastTime = 0; return function () { var _this = this; var _arg = arguments; var nowTime = new Date().getTime(); if (nowTime - lastTime > wait) { fun.apply(_this, _arg); lastTime = nowTime; } }; }
图片加载优化懒加载
// 获取全部img元素 并且将类数组转化成数组 let imgList = [...document.querySelectorAll("img")]; let len = imgList.length; // 图片懒加载 function imgLazyLoad() { let count = 0; return (function () { let isLoadList = []; imgList.forEach((item, index) => { let h = item.getBoundingClientRect(); // 判断图片是否快要滚动道可视区域 if (h.top < window.innerHeight + 200) { item.src = item.dataset.src; console.log(item.dataset.src); isLoadList.push(index); count++; // 全部加载 移出scroll事件 if (len == count) { document.removeEventListener("scroll", imgLazyLoad); } } }); // 移出已经加载完成的图片 imgList = imgList.filter((img, index) => !isLoadList.includes(index)); })(); } // 节流函数 function throttle(fun, wait) { var lastTime = 0; return function () { var _this = this; var _arg = arguments; var nowTime = new Date().getTime(); if (nowTime - lastTime > wait) { fun.apply(_this, _arg); lastTime = nowTime; } }; } // 默认执行一次加载首屏图片 imgLazyLoad(); // 节流执行 document.addEventListener("scroll", throttle(imgLazyLoad, 200));
管理操作Cookie
var cookie = { //设置cookie set: function (name, value, time) { document.cookie = `${name}=${value};expires=${time};path=/`; return this; }, //获取cookie get: function (name) { var arr; var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); if ((arr = document.cookie.match(reg))) { return unescape(arr[2]); } else { return null; } }, //移出token remove: function (name) { return this.setCookie(name, "", -1); }, };
封装 myForEach 方法
// thisValue 可选参数。当执行回调函数 callback 时,用作 this 的值。 Array.prototype.myForEach = function (callback, thisValue) { var _this // 当this为空抛出异常 if (this == null) { throw new TypeError(' this is null or not defined'); } // var len = this.length // this.length >>> 0 相当于 所有非数值转换成0 ,所有大于等于 0 等数取整数部分 var len = this.length >>> 0 // callback不是函数时 抛出异常 if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); } // 判断是够有传参 thisValue if (arguments.length > 1) { _this = thisValue } // 循环遍历 for (var i = 0; i < len; i++) { // 回调函数 callback.call(_this, this[i], i, this) } }
封装 myFilter 方法
Array.prototype.myFilter = function (callback, thisValue) { var _this var arr = [] if (this == null) { throw new TypeError(' this is null or not defined'); } var len = this.length >>> 0 if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); } if (arguments.length > 1) { _this = thisValue } for (var i = 0; i < len; i++) { callback.call(_this, this[i], i, this) && arr.push(this[i]) } return arr }
封装 myMap 方法
Array.prototype.myMAp = function (callback, thisValue) { var _this var arr = [] if (this == null) { throw new TypeError(' this is null or not defined'); } var len = this.length >>> 0 if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); } if (arguments.length > 1) { _this = thisValue } for (var i = 0; i < len; i++) { arr.push(callback.call(_this, this[i], i, this)) } return arr }
封装 myEvery 方法
Array.prototype.myEvery = function (callback, thisValue) { var _this if (this == null) { throw new TypeError(' this is null or not defined'); } var len = this.length >>> 0 if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); } if (arguments.length > 1) { _this = thisValue } for (var i = 0; i < len; i++) { if (!callback.call(_this, this[i], i, this)) { return false } } return true }
封装 myReduce 方法
Array.prototype.myEvery = function (callback, initialValue) { var value = 0 console.log(value) if (this == null) { throw new TypeError(' this is null or not defined'); } var len = this.length >>> 0 if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); } if (arguments.length > 1) { value = initialValue } for (var i = 0; i < len; i++) { value = callback(value, this[i], i, this) } return value }
获取 URL 参数返回对象
function getURLParam(url) { let obj = {}; url.replace(/(?<=[?|&])(w+)=(w+)/g, function (data, key, value) { if (obj[key]) { obj[key] = [].concat(obj[key], value); } else { obj[key] = value; } }); return obj; }
HTML 字符串模板
function render(template, data) { return template.replace(/{(w+)}/g, function ($1, key) { return data[key] ? data[key] : ""; }); } let html = "我叫{name},今年{id}岁。"; let data = { name: "Yevin", age: 18, }; render(html, data); //我叫Yevin,今年18岁
利用 JSONP 实现跨域请求
function jsonp(url, callbackName) { return new Promise((resolve, reject) => { var script = document.createElement("script"); script.src = "demo.js"; document.body.appendChild(script); window[callbackName] = function (res) { //移除remove script.remove(); //返回数据 resolve(res); }; }); }
原生 JS 封装 AJAX
function Ajax(method, url, callback, data, async = true) { var xhr; //同一转换method方法为大写 method = method.toUpperCase(); // 开启XMLHTTPRequest xhr = new XMLHttpRequest(); // 监控状态变化 执行回调函数 xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.readyState == 200) { // 回调函数返回参数 callback(xhr.responseText); } }; // 判断请求方式 get post或者其他 if (method == "GET") { xhr.open("GET", url, async); } else if (method == "POST") { xhr.open("POST", url, async); // 设置请求头 xhr.setRequestHeader("Content-Type", "application/json"); // 发送参数 xhr.send(data); } }
函数柯里化
//把多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术 function curry(fun) { let fn = function (...arg) { if (arg.length == fun.length) return fun(...arg); return (...arg2) => fn(...arg, ...arg2); }; return fn; } function demo(a, b) { return a * b; } console.log(demo(1, 2)); //2 console.log(curry(demo)(1)(2)); //2
偏函数
// 偏函数,就是固定一个函数的一个或者多个参数,返回一个新的函数,这个函数用于接受剩余的参数。 function partial(fn, ...arg) { return function (...args) { return fn(...arg, ...args); }; } function demo(a, b, c) { console.log(a, b, c); // 1, 2,3 } var a = partial(demo, 1); a(2, 3);