由浅入深,66条JavaScript面试知识点(六)
...

作者:Jake Zhang

转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061

目录

由浅入深,66条JavaScript面试知识点(一)

由浅入深,66条JavaScript面试知识点(二)

由浅入深,66条JavaScript面试知识点(三)

由浅入深,66条JavaScript面试知识点(四)

由浅入深,66条JavaScript面试知识点(五)

由浅入深,66条JavaScript面试知识点(六)本篇

由浅入深,66条JavaScript面试知识点(七)

小编建议小伙们从第一篇开始,按照顺序来看,更清晰明了。

63. instanceof的原理是什么,如何实现

instanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。

实现 instanceof:

首先获取类型的原型 然后获得对象的原型 然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null function myInstanceof(left, right) { let prototype = right.prototype left = left.__proto__ while (true) { if (left === null || left === undefined) return false if (prototype === left) return true left = left.__proto__ } }

64. js 的节流与防抖

函数防抖 是指在时间被触发 n 秒后再执行回调,如果在这 n 秒内时间又被触发,则重新计时。这可以使用在一些点击请求的时间上,避免因为用户的多次点击向后端发送多次请求。

函数节流 是指规定一个单位时间,在这个单位时间内,只能有一次触发时间的回调函数执行,如果在同一个单位时间内某时间被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。

// 函数防抖的实现 function debounce(fn, wait) { var timer = null; return function() { var context = this, args = arguments; // 如果此时存在定时器的话,则取消之前的定时器重新记时 if (timer) { clearTimeout(timer); timer = null; } // 设置定时器,使事件间隔指定事件后执行 timer = setTimeout(() => { fn.apply(context, args); }, wait); }; } // 函数节流的实现; function throttle(fn, delay) { var preTime = Date.now(); return function() { var context = this, args = arguments, nowTime = Date.now(); // 如果两次时间间隔超过了指定时间,则执行函数。 if (nowTime - preTime >= delay) { preTime = Date.now(); return fn.apply(context, args); } }; }

详细资料可以参考:

《轻松理解 JS 函数节流和函数防抖》https://juejin.im/post/5a35ed25f265da431d3cc1b1

《JavaScript 事件节流和事件防抖》https://juejin.im/post/5aa60b0e518825556b6c6d1a

《JS 的防抖与节流》https://juejin.im/entry/5b1d2d54f265da6e2545bfa4

65. 什么是设计模式?

1. 概念

设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。

2. 设计原则

S – Single Responsibility Principle 单一职责原则 一个程序只做好一件事 如果功能过于复杂就拆分开,每个部分保持独立 O – OpenClosed Principle 开放/封闭原则 对扩展开放,对修改封闭 增加需求时,扩展新代码,而非修改已有代码 L – Liskov Substitution Principle 里氏替换原则 人类能覆盖父类 父类能出现的地方子类就能出现 I – Interface Segregation Principle 接口隔离原则 保持接口的单一独立 类似单一职责原则,这里更关注接口 D – Dependency Inversion Principle 依赖倒转原则 面向接口编程,依赖于抽象而不依赖于具 使用方只关注接口而不关注具体类的实现

3. 设计模式的类型

结构型模式(Structural Patterns): 通过识别系统中组件间的简单关系来简化系统的设计。 创建型模式(Creational Patterns): 处理对象的创建,根据实际情况使用合适的方式创建对象。常规的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。 行为型模式(Behavioral Patterns): 用于识别对象之间常见的交互模式并加以实现,如此,增加了这些交互的灵活性。

66. 9种前端常见的设计模式

1. 外观模式(Facade Pattern)

外观模式是最常见的设计模式之一,它为子系统中的一组接口提供一个统一的高层接口,使子系统更容易使用。简而言之外观设计模式就是把多个子系统中复杂逻辑进行抽象,从而提供一个更统一、更简洁、更易用的API。很多我们常用的框架和库基本都遵循了外观设计模式,比如JQuery就把复杂的原生DOM操作进行了抽象和封装,并消除了浏览器之间的兼容问题,从而提供了一个更高级更易用的版本。其实在平时工作中我们也会经常用到外观模式进行开发,只是我们不自知而已。

兼容浏览器事件绑定 let addMyEvent = function (el, ev, fn) { if (el.addEventListener) { el.addEventListener(ev, fn, false) } else if (el.attachEvent) { el.attachEvent('on' + ev, fn) } else { el['on' + ev] = fn } }; 封装接口 let myEvent = { // ... stop: e => { e.stopPropagation(); e.preventDefault(); } };

场景

设计初期,应该要有意识地将不同的两个层分离,比如经典的三层结构,在数据访问层和业务逻辑层、业务逻辑层和表示层之间创建外观Facade 在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,增加外观Facade可以提供一个简单的接口,减少他们之间的依赖。 在维护一个遗留的大型系统时,可能这个系统已经很难维护了,这时候使用外观Facade也是非常合适的,为新系统开发一个外观Facade类,为设计粗糙和高度复杂的遗留代码提供比较清晰的接口,让新系统和Facade对象交互,Facade与遗留代码交互所有的复杂工作。

优点

减少系统相互依赖。 提高灵活性。 提高了安全性

缺点

不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。

2. 代理模式(Proxy Pattern)

是为一个对象提供一个代用品或占位符,以便控制对它的访问

假设当A 在心情好的时候收到花,小明表白成功的几率有60%,而当A 在心情差的时候收到花,小明表白的成功率无限趋近于0。小明跟A 刚刚认识两天,还无法辨别A 什么时候心情好。如果不合时宜地把花送给A,花被直接扔掉的可能性很大,这束花可是小明吃了7 天泡面换来的。但是A 的朋友B 却很了解A,所以小明只管把花交给B,B 会监听A 的心情变化,然后选择A 心情好的时候把花转交给A,代码如下:

let Flower = function() {} let xiaoming = { sendFlower: function(target) { let flower = new Flower() target.receiveFlower(flower) } } let B = { receiveFlower: function(flower) { A.listenGoodMood(function() { A.receiveFlower(flower) }) } } let A = { receiveFlower: function(flower) { console.log('收到花'+ flower) }, listenGoodMood: function(fn) { setTimeout(function() { fn() }, 1000) } } xiaoming.sendFlower(B)

场景

HTML元 素事件代理 <ul id="ul"> <li>1</li> <li>2</li> <li>3</li> </ul> <script> let ul = document.querySelector('#ul'); ul.addEventListener('click', event => { console.log(event.target); }); </script> ES6 的 proxy 阮一峰Proxy jQuery.proxy()方法

优点

代理模式能将代理对象与被调用对象分离,降低了系统的耦合度。代理模式在客户端和目标对象之间起到一个中介作用,这样可以起到保护目标对象的作用 代理对象可以扩展目标对象的功能;通过修改代理对象就可以了,符合开闭原则;

缺点

处理请求速度可能有差别,非直接访问存在开销

3. 工厂模式(Factory Pattern)

工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便创建的时候指定自己的对象类型。

class Product { constructor(name) { this.name = name } init() { console.log('init') } fun() { console.log('fun') } } class Factory { create(name) { return new Product(name) } } // use let factory = new Factory() let p = factory.create('p1') p.init() p.fun()

场景

如果你不想让某个子系统与较大的那个对象之间形成强耦合,而是想运行时从许多子系统中进行挑选的话,那么工厂模式是一个理想的选择 将new操作简单封装,遇到new的时候就应该考虑是否用工厂模式; 需要依赖具体环境创建不同实例,这些实例都有相同的行为,这时候我们可以使用工厂模式,简化实现的过程,同时也可以减少每种对象所需的代码量,有利于消除对象间的耦合,提供更大的灵活性

优点

创建对象的过程可能很复杂,但我们只需要关心创建结果。 构造函数和创建者分离, 符合“开闭原则” 一个调用者想创建一个对象,只要知道其名称就可以了。 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。

缺点

添加新产品时,需要编写新的具体产品类,一定程度上增加了系统的复杂度 考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度

什么时候不用

当被应用到错误的问题类型上时,这一模式会给应用程序引入大量不必要的复杂性.除非为创建对象提供一个接口是我们编写的库或者框架的一个设计上目标,否则我会建议使用明确的构造器,以避免不必要的开销。 由于对象的创建过程被高效的抽象在一个接口后面的事实,这也会给依赖于这个过程可能会有多复杂的单元测试带来问题。

4. 单例模式(Singleton Pattern)

顾名思义,单例模式中Class的实例个数最多为1。当需要一个对象去贯穿整个系统执行某些任务时,单例模式就派上了用场。而除此之外的场景尽量避免单例模式的使用,因为单例模式会引入全局状态,而一个健康的系统应该避免引入过多的全局状态。

实现单例模式需要解决以下几个问题:

如何确定Class只有一个实例? 如何简便的访问Class的唯一实例? Class如何控制实例化的过程? 如何将Class的实例个数限制为1?

我们一般通过实现以下两点来解决上述问题:

隐藏Class的构造函数,避免多次实例化 通过暴露一个 getInstance() 方法来创建/获取唯一实例

Javascript中单例模式可以通过以下方式实现:

// 单例构造器 const FooServiceSingleton = (function () { // 隐藏的Class的构造函数 function FooService() {} // 未初始化的单例对象 let fooService; return { // 创建/获取单例对象的函数 getInstance: function () { if (!fooService) { fooService = new FooService(); } return fooService; } } })();

实现的关键点有:

使用 IIFE创建局部作用域并即时执行; getInstance()为一个 闭包 ,使用闭包保存局部作用域中的单例对象并返回。

我们可以验证下单例对象是否创建成功:

const fooService1 = FooServiceSingleton.getInstance(); const fooService2 = FooServiceSingleton.getInstance(); console.log(fooService1 === fooService2); // true

场景例子

定义命名空间和实现分支型方法 登录框 vuex 和 redux中的store

优点

划分命名空间,减少全局变量 增强模块性,把自己的代码组织在一个全局变量名下,放在单一位置,便于维护 且只会实例化一次。简化了代码的调试和维护

缺点

由于单例模式提供的是一种单点访问,所以它有可能导致模块间的强耦合 从而不利于单元测试。无法单独测试一个调用了来自单例的方法的类,而只能把它与那个单例作为一 个单元一起测试。

推荐JavaScript学习相关文章

《为什么 setTimeout 有最小时延 4ms ?》

《如何处理 Node.js 中出现的未捕获异常?》

《Angular v10.0.0 正式发布,不再支持 IE9/10》

《基于 Docker 的 SSR 持续开发集成环境实践》

《细聊图解webpack 指南手册》

《一文带你彻底搞懂 NPM 知识点“进阶篇”》

《细聊webpack性能优化面面观》

《JS实现各种日期操作方法汇总》

《“实践”细聊前端性能优化总结》

《“实践”浏览器中的画中画(Picture-in-Picture)模式及其 API》

《“多图”一文带你彻底搞懂 Web Workers (上)》

《“多图”一文带你彻底搞懂 Web Workers (中)》

《深入细聊前端下载总结“干货”》

《细品西瓜播放器功能分析(上)“实践”》

《细品西瓜播放器功能分析(下)“实践”》

《细聊50道JavaScript基础面试题“附答案”》

《webpack4主流程源码解说以及动手实现一个简单的webpack(上)》

《webpack4主流程源码解说以及动手实现一个简单的webpack(下)》

《细聊前端架构师的视野》

《细聊应用场景再谈防抖和节流“进阶篇”》

《前端埋点统一接入方案实践》

《细聊微内核架构在前端的应用“干货”》

《一种高性能的Tree组件实现方案“干货”》

《进击的JAMStack》

《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)上》

《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)中》

《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)下》

《一文带你搞懂 babel-plugin-import 插件(上)“源码解析”》

《一文带你搞懂 babel-plugin-import 插件(下)“源码解析”》

《JavaScript常用API合集汇总“值得收藏”》

《推荐10个常用的图片处理小帮手(上)“值得收藏”》

《推荐10个常用的图片处理小帮手(下)“值得收藏”》

《JavaScript 中ES6代理的实际用例》

《12 个实用的前端开发技巧总结》

《一文带你搞懂搭建企业级的 npm 私有仓库》

《教你如何使用内联框架元素 IFrames 的沙箱属性提高安全性?》

《细说前端开发UI公共组件的新认识“实践”》

《细说DOM API中append和appendChild的三个不同点》

《细品淘系大佬讲前端新人如何上王者“干货”》

《一文带你彻底解决背景跟随弹窗滚动问题“干货”》

《推荐常用的5款代码比较工具“值得收藏”》

《Node.js实现将文字与图片合成技巧》

《爱奇艺云剪辑Web端的技术实现》

《我再也不敢说我会写前端 Button组件“实践”》

《NodeX Component - 滴滴集团 Node.js 生态组件体系“实践”》

《Node Buffers 完整指南》

《推荐18个webpack精美插件“干货”》

《前端开发需要了解常用7种JavaScript设计模式》

《浅谈浏览器架构、单线程js、事件循环、消息队列、宏任务和微任务》

《了不起的 Webpack HMR 学习指南(上)“含源码讲解”》

《了不起的 Webpack HMR 学习指南(下)“含源码讲解”》

《10个打开了我新世界大门的 WebAPI(上)“实践”》

《10个打开了我新世界大门的 WebAPI(中)“实践”》

《10个打开了我新世界大门的 WebAPI(下)“实践”》

《“图文”ESLint 在中大型团队的应用实践》

《Deno是代码的浏览器,你认同吗?》

《前端存储除了 localStorage 还有啥?》

《Javascript 多线程编程​的前世今生》

《微前端方案 qiankun(实践及总结)》

《“图文”V8 垃圾回收原来这么简单?》

《Webpack 5模块联邦引发微前端的革命?》

《基于 Web 端的人脸识别身份验证“实践”》

《“前端进阶”高性能渲染十万条数据(时间分片)》

《“前端进阶”高性能渲染十万条数据(虚拟列表)》

《图解 Promise 实现原理(一):基础实现》

《图解 Promise 实现原理(二):Promise 链式调用》

《图解 Promise 实现原理(三):Promise 原型方法实现》

《图解 Promise 实现原理(四):Promise 静态方法实现》

《实践教你从零构建前端 Lint 工作流“干货”》

《高性能多级多选级联组件开发“JS篇”》

《深入浅出讲解Node.js CLI 工具最佳实战》

《延迟加载图像以提高Web网站性能的五种方法“实践”》

《比较 JavaScript 对象的四种方式“实践”》

《使用Service Worker让你的 Web 应用如虎添翼(上)“干货”》

《使用Service Worker让你的 Web 应用如虎添翼(中)“干货”》

《使用Service Worker让你的 Web 应用如虎添翼(下)“干货”》

《前端如何一次性处理10万条数据“进阶篇”》

《推荐三款正则可视化工具“JS篇”》

《如何让用户选择是否离开当前页面?“JS篇”》

《JavaScript开发人员更喜欢Deno的五大原因》

《仅用18行JavaScript实现一个倒数计时器》

《图文细说JavaScript 的运行机制》

《一个轻量级 JavaScript 全文搜索库,轻松实现站内离线搜索》

《推荐Web程序员常用的15个源代码编辑器》

《10个实用的JS技巧“值得收藏”》

《细品269个JavaScript小函数,让你少加班熬夜(一)“值得收藏”》

《细品269个JavaScript小函数,让你少加班熬夜(二)“值得收藏”》

《细品269个JavaScript小函数,让你少加班熬夜(三)“值得收藏”》

《细品269个JavaScript小函数,让你少加班熬夜(四)“值得收藏”》

《细品269个JavaScript小函数,让你少加班熬夜(五)“值得收藏”》

《细品269个JavaScript小函数,让你少加班熬夜(六)“值得收藏”》

《深入JavaScript教你内存泄漏如何防范》

《手把手教你7个有趣的JavaScript 项目-上“附源码”》

《手把手教你7个有趣的JavaScript 项目-下“附源码”》

《JavaScript 使用 mediaDevices API 访问摄像头自拍》

《手把手教你前端代码如何做错误上报“JS篇”》

《一文让你彻底搞懂移动前端和Web 前端区别在哪里》

《63个JavaScript 正则大礼包“值得收藏”》

《提高你的 JavaScript 技能10 个问答题》

《JavaScript图表库的5个首选》

《一文彻底搞懂JavaScript 中Object.freeze与Object.seal的用法》

《可视化的 JS:动态图演示 - 事件循环 Event Loop的过程》

《教你如何用动态规划和贪心算法实现前端瀑布流布局“实践”》

《可视化的 js:动态图演示 Promises & Async/Await 的过程》

《原生JS封装拖动验证滑块你会吗?“实践”》

《如何实现高性能的在线 PDF 预览》

《细说使用字体库加密数据-仿58同城》

《Node.js要完了吗?》

《Pug 3.0.0正式发布,不再支持 Node.js 6/8》

《纯JS手写轮播图(代码逻辑清晰,通俗易懂)》

《JavaScript 20 年 中文版之创立标准》

《值得收藏的前端常用60余种工具方法“JS篇”》

《箭头函数和常规函数之间的 5 个区别》

《通过发布/订阅的设计模式搞懂 Node.js 核心模块 Events》

《“前端篇”不再为正则烦恼》

《“速围”Node.js V14.3.0 发布支持顶级 Await 和 REPL 增强功能》

《深入细品浏览器原理“流程图”》

《JavaScript 已进入第三个时代,未来将何去何从?》

《前端上传前预览文件 image、text、json、video、audio“实践”》

《深入细品 EventLoop 和浏览器渲染、帧动画、空闲回调的关系》

《推荐13个有用的JavaScript数组技巧“值得收藏”》

《前端必备基础知识:window.location 详解》

《不要再依赖CommonJS了》

《犀牛书作者:最该忘记的JavaScript特性》

《36个工作中常用的JavaScript函数片段“值得收藏”》

《Node + H5 实现大文件分片上传、断点续传》

《一文了解文件上传全过程(1.8w字深度解析)“前端进阶必备”》

《【实践总结】关于小程序挣脱枷锁实现批量上传》

《手把手教你前端的各种文件上传攻略和大文件断点续传》

《字节跳动面试官:请你实现一个大文件上传和断点续传》

《谈谈前端关于文件上传下载那些事【实践】》

《手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件》

《最全的 JavaScript 模块化方案和工具》

《“前端进阶”JS中的内存管理》

《JavaScript正则深入以及10个非常有意思的正则实战》

《前端面试者经常忽视的一道JavaScript 面试题》

《一行JS代码实现一个简单的模板字符串替换“实践”》

《JS代码是如何被压缩的“前端高级进阶”》

《前端开发规范:命名规范、html规范、css规范、js规范》

《【规范篇】前端团队代码规范最佳实践》

《100个原生JavaScript代码片段知识点详细汇总【实践】》

《关于前端174道 JavaScript知识点汇总(一)》

《关于前端174道 JavaScript知识点汇总(二)》

《关于前端174道 JavaScript知识点汇总(三)》

《几个非常有意思的javascript知识点总结【实践】》

《都2020年了,你还不会JavaScript 装饰器?》

《JavaScript实现图片合成下载》

《70个JavaScript知识点详细总结(上)【实践】》

《70个JavaScript知识点详细总结(下)【实践】》

《开源了一个 JavaScript 版敏感词过滤库》

《送你 43 道 JavaScript 面试题》

《3个很棒的小众JavaScript库,你值得拥有》

《手把手教你深入巩固JavaScript知识体系【思维导图】》

《推荐7个很棒的JavaScript产品步骤引导库》

《Echa哥教你彻底弄懂 JavaScript 执行机制》

《一个合格的中级前端工程师需要掌握的 28 个 JavaScript 技巧》

《深入解析高频项目中运用到的知识点汇总【JS篇】》

《JavaScript 工具函数大全【新】》

《从JavaScript中看设计模式(总结)》

《身份证号码的正则表达式及验证详解(JavaScript,Regex)》

《浏览器中实现JavaScript计时器的4种创新方式》

《Three.js 动效方案》

《手把手教你常用的59个JS类方法》

《127个常用的JS代码片段,每段代码花30秒就能看懂-【上】》

《深入浅出讲解 js 深拷贝 vs 浅拷贝》

《手把手教你JS开发H5游戏【消灭星星】》

《深入浅出讲解JS中this/apply/call/bind巧妙用法【实践】》

《手把手教你全方位解读JS中this真正含义【实践】》

《书到用时方恨少,一大波JS开发工具函数来了》

《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》

《手把手教你JS 异步编程六种方案【实践】》

《让你减少加班的15条高效JS技巧知识点汇总【实践】》

《手把手教你JS开发H5游戏【黄金矿工】》

《手把手教你JS实现监控浏览器上下左右滚动》

《JS 经典实例知识点整理汇总【实践】》

《2.6万字JS干货分享,带你领略前端魅力【基础篇】》

《2.6万字JS干货分享,带你领略前端魅力【实践篇】》

《简单几步让你的 JS 写得更漂亮》

《恭喜你获得治疗JS this的详细药方》

《谈谈前端关于文件上传下载那些事【实践】》

《面试中教你绕过关于 JavaScript 作用域的 5 个坑》

《Jquery插件(常用的插件库)》

《【JS】如何防止重复发送ajax请求》

《JavaScript+Canvas实现自定义画板》

《Continuation 在 JS 中的应用“前端篇”》

作者:Jake Zhang

转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061