2021-04-25

前端工程师进阶之旅-手撕代码【前端常用方法以及面试常见题】

前端工程师进阶之旅-手撕代码

主要包括一些工作中常用的方法,面试常问到的方法。还有一些不太了解,趁机深入了解的知识点。

废话少说,直接干代码就完事了。

数据类型判断

使用 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)];}//数组去重 filterfunction unique(arr) { return arr.filter((item, index, array) => { return array.indexOf(item) === index; });}// 数组去重 forEachfunction 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;}//somefunction 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 zhijianconsole.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(); // 开启

函数柯里化

//把多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术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)); //2console.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);








原文转载:http://www.shaoqun.com/a/706734.html

跨境电商:https://www.ikjzd.com/

旺店通:https://www.ikjzd.com/w/2390

tiki:https://www.ikjzd.com/w/2053


前端工程师进阶之旅-手撕代码主要包括一些工作中常用的方法,面试常问到的方法。还有一些不太了解,趁机深入了解的知识点。废话少说,直接干代码就完事了。数据类型判断使用Object.prototype.toString实现。functionmyTypeof(data){returnObject.prototype.toString.call(data).slice(8,-1).toLowerCase()
燕文物流:https://www.ikjzd.com/w/2229
usps:https://www.ikjzd.com/w/513
livingsocial:https://www.ikjzd.com/w/714.html
流量少?发货急?出单难?这是你误解的Shopee!:https://www.ikjzd.com/home/102399
深度干货 | CPC低成本强关联,BestSeller最新打法:https://www.ikjzd.com/home/12259
通过PPC怎样才能使产品销量飞起来?:https://www.ikjzd.com/home/97923

No comments:

Post a Comment