前端JavaScript面试100问(中)

31、http 的理解 ?

HTTP 协议是超文本传输协议,是客户端浏览器或其他程序“请求”与 Web 服务器响应之间的应用层通信协议。HTTPS主要是由HTTP+SSL构建的可进行加密传输、身份认证的一种安全通信通道。

32、http 和 https 的区别 ?

  • 1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
  • 2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
  • 3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  • 4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

33、git 的常用指令有哪些 ?

git branch 分支查看git branch branch_1 增加分支git checkout branch 分支切换git merge branch_1 合并分支(合并前要切换当前分支至master)git branch -d branch_1 删除分支git remote 查看当前仓库管理的远程仓库信息git remote show origin 查看指定的远程仓库的详细信息git push --set-upstream origin branch_1 第一次将本地分支推到远程仓库git push <远程主机名> <本地分支名>:<远程分支名> 将本地分支推到远程分支git pull <远程主机名> <远程分支>:<本地分支> 将远程分支拉到本地分支git branch -d branch_0 删除本地合并后分支git brench -D branch_0 删除本地未合并分支it push origin --delete branch_0 删除远程分支git restore [filename] 进行清除工作区的改变git tag 查看标签git tag v1.0.0 打标签git push origin v1.0.0 将tag同步到远程服务器

34、平时是使用 git 指令还是图形化工具 ?

repository:git库相关操作,基本意思就是字面意思。

  • 1)资源管理器中浏览该Git库工作空间文件,省去查找路径不断点击鼠标的操作。
  • 2)启动Git bash工具(命令行工具)。
  • 3)查看当前分支文件状态,不包括未提交的信息。
  • 4)查看某个分支的文件(弹出框中可选择需要查看的版本、分支或标签),跟上一条差不多,用的比较少,可能是没有这方面的额需求。
  • 5)可视化当前分支历史、可视化所有分支历史:弹出分支操作历史,也就是gitk工具,放到
    gitk工具中介绍。
  • edit:用于操作commit时操作信息输入,只能操作文字输入部分,你没有看错。常用的快捷键大家都知道,何必要单独做成基本没啥用的。本来以为对变更的文件进行批量操作、本来以为可以对未版本跟踪的文件批量删除、本来、、、,都说了是本来。
  • Branch:新建分支(需要选择其实版本,可以根据版本号、其他分支或标签来选择)、检出分支(觉得切换分支更合适)、重命名分支、删除分支、当前分支Reset操作(会丢弃所有未提交的变更,包括工作区和索引区,当然了,有弹出框提示危险操作)。

35、Promsie.all() 使用过吗, 它是怎么使用的 ?

   promise.all()用于一个异步操作需要在几个异步操作完成后再进行时使用。promise.all()接受一个promise对象组成的数组参数,返回promise对象。当数组中所有promise都完成了,就执行当前promise对象的then方法,如果数组中有一个promise执行失败了,就执行当前promise对象的catch方法。

36、什么是三次握手和四次挥手 ?

三次握手是网络客户端跟网络服务器之间建立连接,并进行通信的过程。相当于客户端和服务器之间你来我往的3个步骤。

  • 第一次握手是建立连接,客户端发送连接请求报文,并传送规定的数据包;
  • 第二次握手是服务器端表示接收到连接请求报文,并回传规定的数据包;
  • 第三次握手是客户端接收到服务器回传的数据包后,给服务器端再次发送数据包。这样就完成了客户端跟服务器的连接和数据传送。

四次挥手表示当前这次连接请求已经结束,要断开这次连接。

  • 第一次挥手是客户端对服务器发起断开请求,
  • 第二次握手是服务器表示收到这次断开请求,
  • 第三次握手是服务器表示已经断开连接
  • 第四次握手是客户端断开连接。

37、for in 和 for of 循环的区别 ?

   `for in` 用于遍历对象的键(`key`)`for in`会遍历所有自身的和原型链上的可枚举属性。如果是数组,for in会将数组的索引(index)当做对象的key来遍历,其他的object也是一样的。
   `for of``es6`引入的语法,用于遍历 所有迭代器iterator,其中包括`HTMLCollection`,`NodeList`,`Array``Map``Set``String``TypedArray``arguments`等对象的值(`item`)

38、async/await 怎么抛出错误异常 ?

  如果可能出错的代码比较少的时候可以使用try/catch结构来了处理,如果可能出错的代码比较多的时候,可以利用async函数返回一个promise对象的原理来处理,给async修饰的函数调用后返回的promise对象,调用catch方法来处理异常。

39、 函数式编程和命令式编程的区别 ?

  • 命令式编程(过程式编程) :
   专注于”如何去做”,这样不管”做什么”,都会按照你的命令去做。解决某一问题的具体算法实现。
  • 函数式编程:把运算过程尽量写成一系列嵌套的函数调用。
     函数式编程强调没有”副作用”,意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。 所谓”副作用”,指的是函数内部与外部交互(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。

40、http 常见的响应状态码 ?

   100——客户必须继续发出请求101——客户要求服务器根据请求转换HTTP协议版本200——交易成功201——提示知道新文件的URL202——接受和处理、但处理未完成203——返回信息不确定或不完整204——请求收到,但返回信息为空205——服务器完成了请求,用户代理必须复位当前已经浏览过的文件206——服务器已经完成了部分用户的GET请求300——请求的资源可在多处得到301——删除请求数据302——在其他地址发现了请求数据303——建议客户访问其他URL或访问方式304——客户端已经执行了GET,但文件未变化305——请求的资源必须从服务器指定的地址得到306——前一版本HTTP中使用的代码,现行版本中不再使用307——申明请求的资源临时性删除400——错误请求,如语法错误401——请求授权失败402——保留有效ChargeTo头响应403——请求不允许404——没有发现文件、查询或URl405——用户在Request-Line字段定义的方法不允许406——根据用户发送的Accept拖,请求资源不可访问407——类似401,用户必须首先在代理服务器上得到授权408——客户端没有在用户指定的饿时间内完成请求409——对当前资源状态,请求不能完成410——服务器上不再有此资源且无进一步的参考地址411——服务器拒绝用户定义的Content-Length属性请求412——一个或多个请求头字段在当前请求中错误413——请求的资源大于服务器允许的大小414——请求的资源URL长于服务器允许的长度415——请求资源不支持请求项目格式416——请求中包含Range请求头字段,在当前请求资源范围内没有range指示值,请求也不包含If-Range请求头字段417——服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下一级服务器不能满足请求500——服务器产生内部错误501——服务器不支持请求的函数502——服务器暂时不可用,有时是为了防止发生系统过载503——服务器过载或暂停维修504——关口过载,服务器使用另一个关口或服务来响应用户,等待时间设定值较长505——服务器不支持或拒绝支请求头中指定的HTTP版本

41、 什么是事件流以及事件流的传播机制 ?

事件触发后,从开始找目标元素,然后执行目标元素的事件,再到离开目标元素的整个过程称之为事件流。

W3C标准浏览器事件流的传播分为3个阶段:捕获阶段、目标阶段、冒泡阶段

  • 捕获阶段指找目标元素的过程,这个找的过程,是从最大的document对象到html,再到body,。。。直到目标元素。

  • 找到目标元素后,调用执行他绑定事件时对应的处理函数,这个过程被称之为目标阶段。

  • 当目标元素的事件执行结束后,再从目标元素,到他的父元素。。。body、html再到document的过程,是冒泡阶段。

42、模块化语法 ? commonJS AMD CMD ES6 Module

  • commonJS是nodejs自带的一种模块化语法,将一个文件看做是一个模块,可以将文件中导出的时候,被另一个文件导入使用。导出使用:module.exports导出。导入使用:require函数导入。

  • AMD是社区开发的模块化语法,需要依赖require.js实现,分为定义模块,导出数据和导入模块,使用数据。AMD语法的导入是依赖前置的,也就是说,需要用到的文件需要在第一次打开页面全部加载完成,造成的后果就是首屏加载很慢,后续操作会很流畅。

  • CMD是玉伯开发的模块化语法,需要依赖sea.js实现,也分为模块定义导出,和模块导入使用数据。CMD语法可以依赖前置,也可以按需导入,缓解了AMD语法的依赖前置。

  • ES6的模块化语法,类似于commonJS的语法,分为数据导出和数据导入,导入导出更加灵活。

43、 什么是懒加载和预加载 ?

  • 懒加载:懒加载也叫延迟加载,延迟加载网络资源或符合某些条件时才加载资源。常见的就是图片延时加载。
    懒加载的意义:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。
    懒惰实现方式:

    • 1.第一种是纯粹的延迟加载,使用setTimeOut或setInterval进行加载延迟.
    • 2.第二种是条件加载,符合某些条件,或触发了某些事件才开始异步下载。
    • 3.第三种是可视区加载,即仅加载用户可以看到的区域,这个主要由监控滚动条来实现,一般会在距用户看到某图片前一定距离遍开始加载,这样能保证用户拉下时正好能看到图片。
  • 预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。

两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。预加载应用如广告弹窗等。

44、token 一般存放在哪里 ? 为什么不存放在 cookie 内 ?

token一般放在本地存储中。token的存在本身只关心请求的安全性,而不关心token本身的安全,因为token是服务器端生成的,可以理解为一种加密技术。但如果存在cookie内的话,浏览器的请求默认会自动在请求头中携带cookie,所以容易受到csrf攻击。

45、 less 和 sass 的区别 ?

  • 编译环境不一样,sass是服务器端处理的,可以用Ruby、node-sass来编译;less需要引入less.js来处理输出,也可以使用工具在服务器端处理成css,也有在线编译的。
  • 变量定义符不一样,less用的是@,而sass用$。
  • sass支持分支语句,less不支持

44、浏览器的同源策略机制 ?

同源策略,又称SOP,全称Same Origin Policy,是浏览器最基本的安全功能。站在浏览器的较短看网页,如果网络上的接口可以不受限制、无需授权随意被人调用,那将是一个非常严重的混乱场景。浏览器为了安全有序,内部实现了同源策略。同源策略,指的是浏览器限制当前网页只能访问同源的接口资源。所谓同源,指当前页面和请求的接口,两方必须是同协议、且同域名、且同端口。只要有一个不相同,则会受到浏览器额约束,不允许请求。但当一个项目变的很大的时候,将所有内容放在一个网站或一个服务器中会让网站变的臃肿且性能低下,所以,在一些场景中,我们需要跨过同源策略,请求到不同源的接口资源,这种场景叫跨域。跨域大致有3种方案:
  • jsonp

    这种方式是利用浏览器不限制某些标签发送跨域请求,例如link、img、iframe、script。通常请求请求回来的资源要在js中进行处理,所以jsonp跨域是利用script标签进行发送,且这种请求方式只能是get请求。

  • cors

    这种方式是让接口资源方面进行授权,授权允许访问。在接口资源处添加响应头即可通过浏览器的同源策略,响应头具体的键值对如下:

    {Access-Control-Allow-Origin: '*'}
    
  • proxy

    这种方式属于找外援的一种方式,浏览器只能限制当前正在打开的web页面发送请求,但无法限制服务器端请求接口资源。所以我们可以将请求发送到自己服务器,然后自己服务器去请求目标接口资源,最后自己服务器将接口资源返回给当前页面,类似于找外援代替自己请求目标接口资源。

    这种方式通常要对服务器进行代理配置,需要对apache服务器、nginx服务器、nodejs服务器进行配置。

45、 浏览器的缓存有哪些 ? 什么时候使用强制缓存 ? 什么时候使用协商缓存 ?

当我们访问同一个页面时,请求资源、数据都是需要一定的耗时,如果可以将一些资源缓存下来,那么从第二次访问开始,就可以减少加载时间,提高用户体验,也能减轻服务器的压力。浏览器缓存分为强缓存和协商缓存,当存在缓存时,客户端第一次向服务器请求数据时,客户端会缓存到内存或者硬盘当中,当第二次获取相同的资源,强缓存和协商缓存的应对方式有所不同。强缓存:当客户端第二次向服务器请求相同的资源时,不会向服务器发送请求,而是直接从内存/硬盘中间读取。缓存由服务器的响应头里 cache-control 和 expires 两个字段决定协商缓存:当客户端第二次向服务器请求相同的资源时,先向服务器发送请求"询问"该请求的文件缓存在ben'd与服务器相比是否更改,如果更改,则更新文件,如果没有就从内存/硬盘中读取。协商缓存由 last-modified 和 etag两个字段决定

46、 数组方法 forEach 和 map 的区别 ?

forEach和map都是循环遍历数组中的每一项。forEach() 和 map() 里面每一次执行匿名函数都支持3个参数:数组中的当前项item,当前项的索引index,原始数组input。匿名函数中的this都是指Window。只能遍历数组。他们的区别是:forEach没有返回值,但map中要有返回值,返回处理后的所有新元素组成的数组。

47、 什么是函数作用域 ? 什么是作用域链 ?

作用域就是在代码执行过程中,形成一个独立的空间,让空间内的变量不会邪泄露在空间外,也让独立空间内的变量函数在独立空间内运行,而不会影响到外部的环境。作用域分为全局作用域和局部作用域,也就是本来有一个巨大的空间,空间内定义的函数内部,就形成了一个独立的小空间,全局作用域是最大的作用域。但是当独立空间内的数据不能满足需求时,是可以从外部获取数据的,也就是说这样的独立空间之间是可以有层级关系的,外部的空间不可以从内部的空间获取数据,但内部的空间可以。当子级空间在父级空间中获取数据的时,父级空间没有的话,父级空间也会到他的父级空间中查找数据,这样形成的链式结构叫作用域链。当将一个变量当做值使用时,会先在当前作用域中查找这个变量的定义和数据,如果没有定义的话,就会去父级作用域中查找,如果父级作用域中有的话就使用这个值,如果父级作用域中也没有的话,就通过父级作用域查找他的父级作用域,直到找到最大的作用域-全局,如果全局也没有就报错。当将一个变量当做数据容器存储,也就是给变量赋值的时候,也要先在自己作用域中查找变量的定义,如果没有就在上一级作用域中查找,直到全局,如果全局作用域中也没有这个变量的定义,就在全局定义这个变量并赋值。

48、 ES6 中 Set 和 Map 的原理 ?

Set 是无重复值的有序列表。根据 `Object.is()`方法来判断其中的值不相等,以保证无重复。 Set 会自动移除重复的值,因此你可以使用它来过滤数组中的重复值并返回结果。 Set并不是数组的子类型,所以你无法随机访问其中的值。但你可以使用`has()` 方法来判断某个值是否存在于 Set 中,或通过 `size` 属性来查看其中有多少个值。 Set 类型还拥有`forEach()`方法,用于处理每个值Map 是有序的键值对,其中的键允许是任何类型。与 Set 相似,通过调用 `Object.is()`方法来判断重复的键,这意味着能将数值 5 与字符串 "5" 作为两个相对独立的键。使用`set()` 方法能将任何类型的值关联到某个键上,并且该值此后能用 `get()` 方法提取出来。Map 也拥有一个 `size` 属性与一个 `forEach()` 方法,让项目访问更容易。

49、 0.1 + 0.2 为什么不等于 0.3, 在项目中遇到要怎么处理 ?

计算机内部存储数据使用2进制存储,两个数字进行的数学运算,首先是将这两个数字以2进制形式,存储在计算机内部,然后在计算机内部使用两个2进制数字进行计算,最后将计算结果的2进制数字转为10进制展示出来。由于10进制的小数在转2进制的时候,规则是小数部分乘以2,判断是否得到一个整数,如果得到整数,转换完成;如果没有得到整数,则继续乘以2判断。所以,0.1和0.2在转换2进制的时候,其实是一个无限死循环,也就是一直乘以2没有得到整数的时候,但计算机内部对于无线死循环的数据,会根据一个标准保留52位。也就是说,计算机内部在存储0.1和0.2的时候,本来就不精准,两个不精准的小数在计算后,距离精准的结果是有一定误差的。项目中碰到这种情况,有3种处理方法:
  • 将小数乘以10的倍数,转为整数,然后计算,计算完成后,再缩小10的倍数,例如:
      var result = ((0.1 * 10) + (0.2 * 10)) / 10// result === 0.3

  • 使用数字的toFixed方法,强制保留小数点后多少位,例:
      var result = (0.1 + 0.2).toFixed(2)// result === 0.30

  • 自定义数字运算方法,当需要进行数学运算的时候,不直接进行,调用自定义的方法进行,例:(加法封装)
  function add(...args){var num = args.find(item => {if(item != 0 && !item){throw new Error("数学运算要使用数字")}})var arr = args.map(item => {var index = (item+'').indexOf('.')if(index >= 0){return (item+'').split('.')[1].length}})arr = arr.filter(item => item)if(arr.length){var max = Math.max(...arr)var data = args.map(item => item * Math.pow(10, max))var data.reduce((a, b) => a + b) / Math.pow(10, max)}else{var data = argsreturn data.reduce((a, b) => a + b)}}// 调用使用:var num1 = add(0.1, 0.2)console.log(num1); // 0.3var num2 = add(1, 2)console.log(num2); // 3var num3 = add(1, 2.1)console.log(num3); // 3.1

50、 什么是模块化思想 ?

就是JS中将不同功能的代码封装在不同的文件中, 再互相引用时不会发生命名冲突的一种思想, 大多数情况下, 一个文件就是一个模块模块化的实现,有多种方案:
  • CommonJS:

    CommonJSnodejs中使用的模块化规范
    nodejs 应用中每个文件就是一个模块,拥有自己的作用域,文件中的变量、函数都是私有的,与其他文件相隔离。模块导出:module.exports=数据,模块导入:require('模块文件路径')

  • ES6的模块化:

    模块功能主要由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

    一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。下面是一个 JS 文件,里面使用export命令输出变量。

  • AMD (Asynchronous Module Definition):

    特点: 提倡依赖前置,在定义模块的时候就要声明其依赖的模块:导入模块require([module],callback);定义模块:define('模块名称', 函数)

  • CMD (Common Module Definition):

    CMD规范是国内SeaJS的推广过程中产生的。提倡就近依赖(按需加载),在用到某个模块的时候再去require。定义模块:define(function (require, exports, module) {}),使用模块:seajs.use()

51、 说说怎么用js 写无缝轮播图

将所有需要轮播的内容动态复制一份,放在原本的容器中,加定时器让整个容器中的内容滚动轮播,当内容轮播到left值为-原本的内容宽度时,快速将内容切换到left值为0的状态。

52、 JS 如何实现多线程 ?

我们都知道JS是一种单线程语言,即使是一些异步的事件也是在JS的主线程上运行的(具体是怎么运行的,可以看我另一篇博客JS代码运行机制)。像setTimeout、ajax的异步请求,或者是dom元素的一些事件,都是在JS主线程执行的,这些操作并没有在浏览器中开辟新的线程去执行,而是当这些异步操作被操作时或者是被触发时才进入事件队列,然后在JS主线程中开始运行。首先说一下浏览器的线程,浏览器中主要的线程包括,UI渲染线程,JS主线程,GUI事件触发线程,http请求线程。JS作为脚本语言,它的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。(这里这些问题我们不做研究)但是单线程的语言,有一个很致命的确定。如果说一个脚本语言在执行时,其中某一块的功能在执行时耗费了大量的时间,那么就会造成阻塞。这样的项目,用户体验是非常差的,所以这种现象在项目的开发过程中是不允许存在的。其实JS为我们提供了一个Worker的类,它的作用就是为了解决这种阻塞的现象。当我们使用这个类的时候,它就会向浏览器申请一个新的线程。这个线程就用来单独执行一个js文件。
    var worker = new Worker(js文件路径);
那么这个语句就会申请一个线程用来执行这个js文件。这样也就实现了js的多线程。

53、 闭包的使用场景 ?

一个函数被当作值返回时,也就相当于返回了一个通道,这个通道可以访问这个函数词法作用域中的变量,即函数所需要的数据结构保存了下来,数据结构中的值在外层函数执行时创建,外层函数执行完毕时理因销毁,但由于内部函数作为值返回出去,这些值得以保存下来。而且无法直接访问,必须通过返回的函数。这也就是私有性。本来执行过程和词法作用域是封闭的,这种返回的函数就好比是一个虫洞,开了挂。闭包的形成很简单,在执行过程完毕后,返回函数,或者将函数得以保留下来,即形成闭包。
  • 防抖:
    function debounce(fn, interval) {let timer = null; // 定时器return function() {// 清除上一次的定时器clearTimeout(timer);// 拿到当前的函数作用域let _this = this;// 拿到当前函数的参数数组let args = Array.prototype.slice.call(arguments, 0);// 开启倒计时定时器timer = setTimeout(function() {// 通过apply传递当前函数this,以及参数fn.apply(_this, args);// 默认300ms执行}, interval || 300)}}
  • 节流:
    function throttle(fn, interval) {let timer = null; // 定时器let firstTime = true; // 判断是否是第一次执行// 利用闭包return function() {// 拿到函数的参数数组let args = Array.prototype.slice.call(arguments, 0);// 拿到当前的函数作用域let _this = this;// 如果是第一次执行的话,需要立即执行该函数if(firstTime) {// 通过apply,绑定当前函数的作用域以及传递参数fn.apply(_this, args);// 修改标识为null,释放内存firstTime = null;}// 如果当前有正在等待执行的函数则直接返回if(timer) return;// 开启一个倒计时定时器timer = setTimeout(function() {// 通过apply,绑定当前函数的作用域以及传递参数fn.apply(_this, args);// 清除之前的定时器timer = null;// 默认300ms执行一次}, interval || 300)}}
  • 迭代器:
    var arr =['aa','bb','cc'];function incre(arr){var i=0;return function(){//这个函数每次被执行都返回数组arr中 i下标对应的元素return arr[i++] || '数组值已经遍历完';}}var next = incre(arr);console.log(next());//aaconsole.log(next());//bbconsole.log(next());//ccconsole.log(next());//数组值已经遍历完
  • 缓存:
    var fn=(function(){var cache={};//缓存对象var calc=function(arr){//计算函数var sum=0;//求和for(var i=0;i<arr.length;i++){sum+=arr[i];}return sum;}return function(){var args = Array.prototype.slice.call(arguments,0);//arguments转换成数组var key=args.join(",");//将args用逗号连接成字符串var result , tSum = cache[key];if(tSum){//如果缓存有   console.log('从缓存中取:',cache)//打印方便查看result = tSum;}else{//重新计算,并存入缓存同时赋值给resultresult = cache[key]=calc(args);console.log('存入缓存:',cache)//打印方便查看}return result;}})();fn(1,2,3,4,5);fn(1,2,3,4,5);fn(1,2,3,4,5,6);fn(1,2,3,4,5,8);fn(1,2,3,4,5,6);
  • getter和setter:
    function fn(){var name='hello'setName=function(n){name = n;}getName=function(){return name;}//将setName,getName作为对象的属性返回return {setName:setName,getName:getName}}var fn1 = fn();//返回对象,属性setName和getName是两个函数console.log(fn1.getName());//getterfn1.setName('world');//setter修改闭包里面的nameconsole.log(fn1.getName());//getter
  • 柯里化:
    function curryingCheck(reg) {return function(txt) {return reg.test(txt)}}var hasNumber = curryingCheck(/\d+/g)var hasLetter = curryingCheck(/[a-z]+/g)hasNumber('test1')      // truehasNumber('testtest')   // falsehasLetter('21212')      // false
  • 循环中绑定事件或执行异步代码:
    var p1 = "ss";var p2 = "jj";function testSetTime(para1,para2){return (function(){console.log(para1 + "-" + para2);})}var test = testSetTime(p1, p2);setTimeout(test, 1000);setTimeout(function(){console.log(p1 + "-" + p2)},1000)
  • 单例模式:
    var Singleton = (function () {var instance;function createInstance() {return new Object("I am the instance");}return {getInstance: function () {if (!instance) {instance = createInstance();}return instance;}};})();

54、 常见的兼容问题有哪些 ?

  • 获取标签节点:

    document.getElementsByClassName(‘类名’)在低版本ie中不兼容。解决方法是使用其他方式获取:

      document.getElementById('id名')document.getElementsByTagName('标签名')document.getElementsByName('name属性值')document.querySelector('css选择器')document.querySelectorAll('css选择器')

* 获取卷去的高度
      // 当有文档声明的时候document.documentElement.scrollTopdocument.documentElement.srollLeft// 没有文档声明的时候document.body.scrollTopdocument.body.scrollLeft
* 解决办法使用兼容写法:
      // 获取var t = document.documentElement.scrollTop || document.body.scrollTopvar l = document.documentElement.srollLeft || document.body.scrollLeft// 设置document.documentElement.scrollTop = document.body.scrollTop = 数值document.documentElement.srollLeft = document.body.scrollLeft = 数值

  • 获取样式
      // W3C标准浏览器window.getComputedStyle(元素)// 低版本IE中元素.currentStyle
  • 使用函数封装的方式兼容:
      function getStyle(ele,attr){if(window.getComputedStyle){return getComputedStyle(ele)[attr]}else{return ele.currentStyle[attr]}}

  • 事件侦听器
      // W3C浏览器ele.addEventListener(事件类型,函数)// 低版本Ieele.attachEvent('on事件类型',函数)
  • 使用函数封装的方式解决:
      function bindEvent(ele,type,handler){if(ele.addEventListener){ele.addEventListener(type,handler)}else if(ele.attachEvent){ele.attachEvent('on'+type,handler)}else{ele['on'+type] = handler}}	

  • 事件解绑
      // W3C浏览器ele.removeEventListener(事件类型,函数)// 低版本Ieele.detachEvent('on事件类型',函数)
  • 使用函数封装的方式解决:
      function unBind(ele,type,handler){if(ele.removeEventListener){ele.removeEventListener(type,handler)}else if(ele.detachEvent){ele.detachEvent('on'+type,handler)}else{ele['on'+type] = null}}

  • 事件对象的获取
      // W3C浏览器元素.on事件类型 = function(e){}元素.addEventListener(事件类型,fn)function fn(e){}// 在低版本IE中元素.on事件类型 = function(){ window.event }元素.addEventListener(事件类型,fn)function fn(){window.event}
  • 使用短路运算符解决:
      元素.on事件类型 = function(e){var e = e || window.event}元素.addEventListener(事件类型,fn)function fn(e){var e = e || window.event}

  • 阻止默认行为
      // W3C浏览器元素.on事件类型 = function(e){e.preventDefault()}// 在低版本IE中元素.on事件类型 = function(){ window.event.returnValue = false }
  • 通过封装函数解决;
      元素.on事件类型 = function(e){var e = e || window.evente.preventDefault?e.preventDefault():e.returnValue=false}

  • 阻止事件冒泡
      // W3C浏览器元素.on事件类型 = function(e){e.stopPropagation()}// 在低版本IE中元素.on事件类型 = function(){ window.event.cancelBubble = true }
  • 通过函数封装解决:
      元素.on事件类型 = function(e){var e = e || window.evente.stopPropagation?e.stopPropagation():e.cancelBubble=true}

  • 获取精准的目标元素
      // W3C浏览器元素.on事件类型 = function(e){e.target}// 在低版本IE中元素.on事件类型 = function(){ window.event.srcElement }
  • 通过短路运算符解决:
      元素.on事件类型 = function(e){var e = e || window.eventvar target = e.target || e.srcElement;}

  • 获取键盘码
      // W3C浏览器元素.on事件类型 = function(e){e.keyCode}// 在低版本火狐中元素.on事件类型 = function(e){e.which}
  • 通过短路运算符解决:
      元素.on事件类型 = function(e){var e = e || window.eventvar keycode = e.keyCode || e.which;}	

55、 在 JS 中如何阻止事件冒泡 ?

使用事件对象阻止事件冒泡,以前的w3c浏览器中,使用事件对象的方法阻止:

    事件对象.stopPropagation()

在ie低版本浏览器中,使用事件对象的属性阻止:

    事件对象.cancelBubble = true

现在的w3c浏览器也支持ie低版本浏览器中的写法,所以以前在阻止事件冒泡的时候,需要考虑兼容写法,现在就不需要了,直接用ie低版本浏览器中的写法即可。

56、两个数组 var A = [1, 5, 6]; var B = [2, 6, 7],实现一个方法,找出仅存在于A 或者 仅 存在于B中的所有数字。

    function getDiff(arr, brr){// 仅存在于arr中的内容var onlyArr = arr.filter(item => !brr.some(v => item === v))// 仅存在于brr中的内容var onlyBrr = brr.filter(v => !arr.some(item => v === item))// 需要哪个就返回哪个,或者一起返回return {"仅存在于arr中的内容": onlyArr,"仅存在于brr中的内容": onlyBrr}}

57、 你了解构造函数吗 ? class 是什么 ? 两者有什么区别 ?

在es5中构造函数其实就是在定义一个类,可以实例化对象,es6中class其实是构造函数的语法糖。但还是有点区别的:
  • 在class内部和class的方法内部,默认使用严格模式
  • class类不存在预解析,也就是不能先调用class生成实例,再定义class类,但是构造函数可以。
  • class中定义的方法默认不能被枚举,也就是不能被遍历。
  • class必须使用new执行,但是构造函数没有new也可以执行。
  • class中的所有方法都没有原型,也就不能被new
  • class中继承可以继承静态方法,但是构造函数的继承不能。

58、是否存在a的值(a0 && a1)为true 的情况 ?

    var value = -1Object.defineProperty(window,'a',{get(){return value+=1;}})if(a===0&&a===1){ // trueconsole.log('success')}

59、for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 1000); } 要求:输出0,1,2,3,4

首先这个面试题考察的是对于js中异步代码以及作用域的理解:js中常见的异步代码包括定时器和ajax。js执行代码的流程是碰到同步代码就执行,碰到异步就交给浏览器的webAPI处理,当webAPI中的异步该执行时,webAPI会将需要执行的回调函数放在任务队列中,等候执行,所以,js中所有的异步代码总会在所有同步代码执行结束后,再执行任务队列中的代码。在这个问题中,循环是同步代码,定时器是异步代码,所以整个循环都执行结束以后才会执行定时器代码。for循环中使用var定义的变量是全局变量,定时器回调函数中输出变量的时候,根据作用域规则,先在当前作用域中变量i的定义表达式,如果没有找到,就去上一级作用域中找,此时,在局部作用域中没有找到,去上级作用域中,也就是全局找到了,全局中的i,因为循环已经执行结束了,所以i的值是5。最终,会输出5个5。其次考察的是对于类似问题的解决方式,间接性判断你是否有过类似情况的开发:这个问题的解决思路就是让回调函数中输出i的时候,不要去全局中找i,因为全局的i在循环执行结束后已经变成5了,根据这个思路,有2种解决办法:
  • 在异步代码外面嵌套一层函数作用域
       for(var i = 0;i < 5; i++){(function(i) {setTimeout(function() {console.log(i)}, 1000)})(i)}

原理是自调用函数会产生作用域,循环5次就会产生5个作用域,每个作用域代码在执行的时候都有形参i传递。所以每个作用域中的i都是不同的,分别是:0 1 2 3 4。当作用域中的异步代码执行的时候,自己作用域中没有i变量的定义,然后上级作用域就是自调用函数的作用域,找到了单独的i。最终可以输出:0 1 2 3 4

    1. 将循环代码中的var换成es6的let
       for(let i = 0;i < 5; i++){setTimeout(function() {console.log(i)}, 1000)}

es6的let自带块级作用域,原理跟第一种解决思路是一样的,转成es5后,代码是一样的。

60、实现一个 add 方法 使计算结果能够满足如下预期: - add(1)(2)(3)() = 6 - add(1,2,3)(4)() = 10

    function add (...args) {if(args.length === 3){return -(args[0] * args[1] * 2 + args[2] * 2)}else{return -args[args.length-1]}}function currying (fn) {let args = []return function _c (...newArgs) {if (newArgs.length) {args = [...args,...newArgs]return _c} else {return fn.apply(this, args)}}}let addCurry = currying(add)var a = addCurry(1)(2)(3)()console.log(-a); // 10var b = addCurry(1,2,3)(4)()console.log(6 - b); // 10

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/13968.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

flask的配置项

flask的配置项 为了使 Flask 应用程序正常运行&#xff0c;有多种配置选项需要考虑。下面是一些基本的 Flask 配置选项&#xff1a; DEBUG: 这个配置项决定 Flask 是否应该在调试模式下运行。如果这个值被设为 True&#xff0c;Flask 将会提供更详细的错误信息&#xff0c;并…

go 查询采购单设备事项V3

一、版本说明 本版本在整合上两次的功能基础上&#xff0c;引进ini配置文件的读取事项&#xff0c;快速读取本地配置文件&#xff0c;完成读取设置 第一版&#xff1a;实现了严格匹配模式的查找 https://blog.csdn.net/wtt234/article/details/131979385 第二版&#xff1a;实…

作为程序员,你很有必要了解一下IVX

一、IVX是什么 iVX 是一个“零代码”的可视化编程平台&#xff0c;拥有方便的在线集成开发环境&#xff0c;不需要下载开发环境&#xff0c;打开浏览器即可随时随地进行项目编辑。iVX 还拥有“一站式”的云资源&#xff0c;通过这一套一站式服务&#xff0c;iVX 可以实现一站式…

详解STM32的GPIO八种输入输出模式,GPIO各种输入输出的区别、初始化的步骤详解,看这文章就行了(超详细)

在STM32微控制器中&#xff0c;常见的输入输出(GPIO)模式有八种&#xff0c;分别是推挽输出、开漏输出、复用推挽输出、复用开漏输出、浮空输入、上拉输入、下拉输入和模拟输入。下面我将为你解释每种模式的特点和区别&#xff0c;并提供相应的示例代码。 文章目录 介绍区别初…

MySql002——关系型数据库基础知识

前言&#xff1a;因为本专栏学习的是MySQL&#xff0c;而MySQL是关系型数据库&#xff0c;所以这篇文章就来介绍一些关系型数据库基础知识&#xff0c;至于其他知识小伙伴们可以自行学习&#xff0c;同时不足之处也欢迎批评指正&#xff0c;谢谢~ 一、MySQL关系型数据库(RDBMS)…

Spring、SpringMVC、SpingBoot的区别

Spring是一个IOC容器&#xff0c;用来管理Bean&#xff0c;使用依赖注入实现控制反转&#xff0c;可以狠方便的整合各种框架&#xff0c;提供AOP(切面)机制弥补OOP(面向对象)的代码重复问题、更方便将不同类不同方法中的共同处理抽取成切面、自动注入给方法使用&#xff0c;比如…

从实践彻底掌握MySQL的主从复制

目录 一、本次所用结构如图---一主多从级联&#xff1a; 二、IP。 三、配置M1&#xff1a; 四、从库M1S1&#xff1a; 五、从库M2配置&#xff1a; 六、 从库M2S1&#xff1a; 一、本次所用结构如图--- 一主多从级联&#xff1a; 二、IP。这里M1S1和M1S2一样的&#xff0…

图技术在 LLM 下的应用:知识图谱驱动的大语言模型 Llama Index

LLM 如火如荼地发展了大半年&#xff0c;各类大模型和相关框架也逐步成型&#xff0c;可被大家应用到业务实际中。在这个过程中&#xff0c;我们可能会遇到一类问题是&#xff1a;现有的哪些数据&#xff0c;如何更好地与 LLM 对接上。像是大家都在用的知识图谱&#xff0c;现在…

查看maven发布时间的方法

查看maven发布时间的方法如下【 打开maven官网 选中Release Notes 即可查看对应版本的发布时间 】

【计算机网络】第 4 课 - 物理层

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、物理层的基本概念 2、物理层协议的主要任务 3、物理层任务 4、总结 1、物理层的基本概念 在计算机网络中&#xff0c;用来…

vim的高级用法-零宽匹配

零宽匹配 vim中&#xff0c;要进行零宽匹配&#xff0c;可以使用特殊字符和元字符来表示零宽度断言&#xff08;lookaround assertions&#xff09;。 如下是常用的零宽度断言 正前瞻断言&#xff08;Positive Lookahead Assertion&#xff09; 匹配某个位置后面跟特定模式…

力扣75——链表

总结leetcode75中链表的算法题解题思路。 上一篇&#xff1a;力扣75——队列 以下代码大部分为本人所写&#xff0c;少部分为官方示例代码。 力扣75——链表 1 删除链表的中间节点2 奇偶链表3 反转链表4 链表最大孪生和1-4 解题总结 1 删除链表的中间节点 题目&#xff1a; 给…

基于多场景的考虑虑热网网损的太阳能消纳能力评估研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

windows切换php版本以及composer

前提 安装php8.2 安装Php7.4 下载 nts是非线程安全的&#xff0c;这里选择线程安全的&#xff0c;选择64位 解压缩 修改系统环境变量 修改为php-7的 cmd中输入php -v查看 找到composer存放路径C:\ProgramData\ComposerSetup\bin 将三个文件复制到php目录下 重启电脑…

2023年深圳杯数学建模赛题浅析

由于今明两日由于一些不可避免的事情&#xff0c;这里仅仅先给大家简单写一个赛题浅析&#xff0c;详细过程步骤思路以及讲解视频预计后天发布 A题 影响城市居民身体健康的因素分析 A题以慢性病为命题背景&#xff0c;给出数据以及题目初步来看来看为一个数据处理数据分析的综…

oracle12c静默安装

目录 前言 安装配置步骤 关闭防火墙,禁止防火墙开机自启 关闭selinux yum安装必要安装包 内网环境下载依赖包的方式 创建用户和组 创建oinstall和dba组 创建oracle用户 设置oracle密码 查看创建结果 修改内核参数 使配置生效 修改用户及文件限制 改文件限制 修改用…

2023-07-29力扣每日一题

链接&#xff1a; 141. 环形链表 题意&#xff1a; 求链表是否有环 解&#xff1a; 刚好昨天做完的初级算法链表题&#xff0c;翻转和暴力 实际代码&#xff1a; #include<iostream> using namespace std; struct ListNode {int val;ListNode *next;ListNode() : …

docker 部署一个单节点的rocketmq

拉取镜像 sudo docker pull rocketmqinc/rocketmq创建数据挂载目录 mkdir -p /docker/rocketmq/data/namesrv/logs mkdir -p /docker/rocketmq/data/namesrv/store mkdir -p /docker/rocketmq/data/broker/logs mkdir -p /docker/rocketmq/data/broker/store /docker/…

PHP使用Redis实战实录1:宝塔环境搭建、6379端口配置、Redis服务启动失败解决方案

宝塔环境搭建、6379端口配置、Redis服务启动失败解决方案 前言一、Redis安装部署1.安装Redis2.php安装Redis扩展3.启动Redis 二、避坑指南1.6379端口配置2.Redis服务启动&#xff08;1&#xff09;Redis服务启动失败&#xff08;2&#xff09;Redis启动日志排查&#xff08;3&a…

《向量数据库指南》:向量数据库Pinecone如何集成LangChain (一)

目录 LangChain中的检索增强 建立知识库 欢迎使用Pinecone和LangChain的集成指南。本文档涵盖了将高性能向量数据库Pinecone与基于大型语言模型(LLMs)构建应用程序的框架LangChain集成的步骤。 Pinecone使开发人员能够基于向量相似性搜索构建可扩展的实时推荐和搜索系统…