写点什么

一年前端面试打怪升级之路

作者:loveX001
  • 2022-11-02
    浙江
  • 本文字数:10447 字

    阅读完需:约 34 分钟

进程与线程的概念

从本质上说,进程和线程都是 CPU 工作时间片的一个描述:


  • 进程描述了 CPU 在运行指令及加载和保存上下文所需的时间,放在应用上来说就代表了一个程序。

  • 线程是进程中的更小单位,描述了执行一段指令所需的时间。


进程是资源分配的最小单位,线程是 CPU 调度的最小单位。


一个进程就是一个程序的运行实例。详细解释就是,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程进程是运行在虚拟内存上的,虚拟内存是用来解决用户对硬件资源的无限需求和有限的硬件资源之间的矛盾的。从操作系统角度来看,虚拟内存即交换文件;从处理器角度看,虚拟内存即虚拟地址空间。


如果程序很多时,内存可能会不够,操作系统为每个进程提供一套独立的虚拟地址空间,从而使得同一块物理内存在不同的进程中可以对应到不同或相同的虚拟地址,变相的增加了程序可以使用的内存。


进程和线程之间的关系有以下四个特点:


(1)进程中的任意一线程执行出错,都会导致整个进程的崩溃。


(2)线程之间共享进程中的数据。


(3)当一个进程关闭之后,操作系统会回收进程所占用的内存, 当一个进程退出时,操作系统会回收该进程所申请的所有资源;即使其中任意线程因为操作不当导致内存泄漏,当进程退出时,这些内存也会被正确回收。


(4)进程之间的内容相互隔离。 进程隔离就是为了使操作系统中的进程互不干扰,每一个进程只能访问自己占有的数据,也就避免出现进程 A 写入数据到进程 B 的情况。正是因为进程之间的数据是严格隔离的,所以一个进程如果崩溃了,或者挂起了,是不会影响到其他进程的。如果进程之间需要进行数据的通信,这时候,就需要使用用于进程间通信的机制了。


Chrome 浏览器的架构图: 从图中可以看出,最新的 Chrome 浏览器包括:


  • 1 个浏览器主进程

  • 1 个 GPU 进程

  • 1 个网络进程

  • 多个渲染进程

  • 多个插件进程


这些进程的功能:


  • 浏览器进程:主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。

  • 渲染进程:核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。

  • GPU 进程:其实, GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。

  • 网络进程:主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。

  • 插件进程:主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。


所以,打开一个网页,最少需要四个进程:1 个网络进程、1 个浏览器进程、1 个 GPU 进程以及 1 个渲染进程。如果打开的页面有运行插件的话,还需要再加上 1 个插件进程。


虽然多进程模型提升了浏览器的稳定性、流畅性和安全性,但同样不可避免地带来了一些问题:


  • 更高的资源占用:因为每个进程都会包含公共基础结构的副本(如 JavaScript 运行环境),这就意味着浏览器会消耗更多的内存资源。

  • 更复杂的体系架构:浏览器各模块之间耦合性高、扩展性差等问题,会导致现在的架构已经很难适应新的需求了。

z-index 属性在什么情况下会失效

通常 z-index 的使用是在有两个重叠的标签,在一定的情况下控制其中一个在另一个的上方或者下方出现。z-index 值越大就越是在上层。z-index 元素的 position 属性需要是 relative,absolute 或是 fixed。


z-index 属性在下列情况下会失效:


  • 父元素 position 为 relative 时,子元素的 z-index 失效。解决:父元素 position 改为 absolute 或 static;

  • 元素没有设置 position 属性为非 static 属性。解决:设置该元素的 position 属性为 relative,absolute 或是 fixed 中的一种;

  • 元素在设置 z-index 的同时还设置了 float 浮动。解决:float 去除,改为 display:inline-block;

GET 和 POST 的请求的区别

Post 和 Get 是 HTTP 请求的两种方法,其区别如下:


  • 应用场景: GET 请求是一个幂等的请求,一般 Get 请求用于对服务器资源不会产生影响的场景,比如说请求一个网页的资源。而 Post 不是一个幂等的请求,一般用于对服务器资源会产生影响的情景,比如注册用户这一类的操作。

  • 是否缓存: 因为两者应用场景不同,浏览器一般会对 Get 请求缓存,但很少对 Post 请求缓存。

  • 发送的报文格式: Get 请求的报文中实体部分为空,Post 请求的报文中实体部分一般为向服务器发送的数据。

  • 安全性: Get 请求可以将请求的参数放入 url 中向服务器发送,这样的做法相对于 Post 请求来说是不太安全的,因为请求的 url 会被保留在历史记录中。

  • 请求长度: 浏览器由于对 url 长度的限制,所以会影响 get 请求发送数据时的长度。这个限制是浏览器规定的,并不是 RFC 规定的。

  • 参数类型: post 的参数传递支持更多的数据类型。

对 Flex 布局的理解及其使用场景

Flex 是 FlexibleBox 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。任何一个容器都可以指定为 Flex 布局。行内元素也可以使用 Flex 布局。注意,设为 Flex 布局以后,子元素的 float、clear 和 vertical-align 属性将失效。采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis),项目默认沿水平主轴排列。


以下 6 个属性设置在容器上


  • flex-direction 属性决定主轴的方向(即项目的排列方向)。

  • flex-wrap 属性定义,如果一条轴线排不下,如何换行。

  • flex-flow 属性是 flex-direction 属性和 flex-wrap 属性的简写形式,默认值为 row nowrap。

  • justify-content 属性定义了项目在主轴上的对齐方式。

  • align-items 属性定义项目在交叉轴上如何对齐。

  • align-content 属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。


以下 6 个属性设置在项目上


  • order 属性定义项目的排列顺序。数值越小,排列越靠前,默认为 0。

  • flex-grow 属性定义项目的放大比例,默认为 0,即如果存在剩余空间,也不放大。

  • flex-shrink 属性定义了项目的缩小比例,默认为 1,即如果空间不足,该项目将缩小。

  • flex-basis 属性定义了在分配多余空间之前,项目占据的主轴空间。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为 auto,即项目的本来大小。

  • flex 属性是 flex-grow,flex-shrink 和 flex-basis 的简写,默认值为 0 1 auto。

  • align-self 属性允许单个项目有与其他项目不一样的对齐方式,可覆盖 align-items 属性。默认值为 auto,表示继承父元素的 align-items 属性,如果没有父元素,则等同于 stretch。


简单来说: flex 布局是 CSS3 新增的一种布局方式,可以通过将一个元素的 display 属性值设置为 flex 从而使它成为一个 flex 容器,它的所有子元素都会成为它的项目。一个容器默认有两条轴:一个是水平的主轴,一个是与主轴垂直的交叉轴。可以使用 flex-direction 来指定主轴的方向。可以使用 justify-content 来指定元素在主轴上的排列方式,使用 align-items 来指定元素在交叉轴上的排列方式。还可以使用 flex-wrap 来规定当一行排列不下时的换行方式。对于容器中的项目,可以使用 order 属性来指定项目的排列顺序,还可以使用 flex-grow 来指定当排列空间有剩余的时候,项目的放大比例,还可以使用 flex-shrink 来指定当排列空间不足时,项目的缩小比例。

常见的 DOM 操作有哪些

1)DOM 节点的获取

DOM 节点的获取的 API 及使用:


getElementById // 按照 id 查询getElementsByTagName // 按照标签名查询getElementsByClassName // 按照类名查询querySelectorAll // 按照 css 选择器查询
// 按照 id 查询var imooc = document.getElementById('imooc') // 查询到 id 为 imooc 的元素// 按照标签名查询var pList = document.getElementsByTagName('p') // 查询到标签为 p 的集合console.log(divList.length)console.log(divList[0])// 按照类名查询var moocList = document.getElementsByClassName('mooc') // 查询到类名为 mooc 的集合// 按照 css 选择器查询var pList = document.querySelectorAll('.mooc') // 查询到类名为 mooc 的集合
复制代码

2)DOM 节点的创建

创建一个新节点,并把它添加到指定节点的后面。 已知的 HTML 结构如下:


<html>  <head>    <title>DEMO</title>  </head>  <body>    <div id="container">       <h1 id="title">我是标题</h1>    </div>     </body></html>
复制代码


要求添加一个有内容的 span 节点到 id 为 title 的节点后面,做法就是:


// 首先获取父节点var container = document.getElementById('container')// 创建新节点var targetSpan = document.createElement('span')// 设置 span 节点的内容targetSpan.innerHTML = 'hello world'// 把新创建的元素塞进父节点里去container.appendChild(targetSpan)
复制代码

3)DOM 节点的删除

删除指定的 DOM 节点, 已知的 HTML 结构如下:


<html>  <head>    <title>DEMO</title>  </head>  <body>    <div id="container">       <h1 id="title">我是标题</h1>    </div>     </body></html>
复制代码


需要删除 id 为 title 的元素,做法是:


// 获取目标元素的父元素var container = document.getElementById('container')// 获取目标元素var targetNode = document.getElementById('title')// 删除目标元素container.removeChild(targetNode)
复制代码


或者通过子节点数组来完成删除:


// 获取目标元素的父元素var container = document.getElementById('container')// 获取目标元素var targetNode = container.childNodes[1]// 删除目标元素container.removeChild(targetNode)
复制代码

4)修改 DOM 元素

修改 DOM 元素这个动作可以分很多维度,比如说移动 DOM 元素的位置,修改 DOM 元素的属性等。


将指定的两个 DOM 元素交换位置, 已知的 HTML 结构如下:


<html>  <head>    <title>DEMO</title>  </head>  <body>    <div id="container">       <h1 id="title">我是标题</h1>      <p id="content">我是内容</p>    </div>     </body></html>
复制代码


现在需要调换 title 和 content 的位置,可以考虑 insertBefore 或者 appendChild:


// 获取父元素var container = document.getElementById('container')   
// 获取两个需要被交换的元素var title = document.getElementById('title')var content = document.getElementById('content')// 交换两个元素,把 content 置于 title 前面container.insertBefore(content, title)
复制代码

transition 和 animation 的区别

  • transition 是过度属性,强调过度,它的实现需要触发一个事件(比如鼠标移动上去,焦点,点击等)才执行动画。它类似于 flash 的补间动画,设置一个开始关键帧,一个结束关键帧。

  • animation 是动画属性,它的实现不需要触发事件,设定好时间之后可以自己执行,且可以循环一个动画。它也类似于 flash 的补间动画,但是它可以设置多个关键帧(用 @keyframe 定义)完成动画。

title 与 h1 的区别、b 与 strong 的区别、i 与 em 的区别?

  • strong 标签有语义,是起到加重语气的效果,而 b 标签是没有的,b 标签只是一个简单加粗标签。b 标签之间的字符都设为粗体,strong 标签加强字符的语气都是通过粗体来实现的,而搜索引擎更侧重 strong 标签。

  • title 属性没有明确意义只表示是个标题,H1 则表示层次明确的标题,对页面信息的抓取有很大的影响

  • i 内容展示为斜体,em 表示强调的文本


参考:前端进阶面试题详细解答

await 到底在等啥?

await 在等待什么呢? 一般来说,都认为 await 是在等待一个 async 函数完成。不过按语法说明,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值(换句话说,就是没有特殊限定)。


因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。注意到 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的。所以下面这个示例完全可以正确运行:


function getSomething() {    return "something";}async function testAsync() {    return Promise.resolve("hello async");}async function test() {    const v1 = await getSomething();    const v2 = await testAsync();    console.log(v1, v2);}test();
复制代码


await 表达式的运算结果取决于它等的是什么。


  • 如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。

  • 如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。


来看一个例子:


function testAsy(x){   return new Promise(resolve=>{setTimeout(() => {       resolve(x);     }, 3000)    }   )}async function testAwt(){      let result =  await testAsy('hello world');  console.log(result);    // 3秒钟之后出现hello world  console.log('cuger')   // 3秒钟之后出现cug}testAwt();console.log('cug')  //立即输出cug
复制代码


这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。await 暂停当前 async 的执行,所以'cug''最先输出,hello world'和‘cuger’是 3 秒钟后同时出现的。

页面有多张图片,HTTP 是怎样的加载表现?

  • HTTP 1下,浏览器对一个域名下最大 TCP 连接数为 6,所以会请求多次。可以用多域名部署解决。这样可以提高同时请求的数目,加快页面图片的获取速度。

  • HTTP 2下,可以一瞬间加载出来很多资源,因为,HTTP2 支持多路复用,可以在一个 TCP 连接中发送多个 HTTP 请求。

强类型语言和弱类型语言的区别

  • 强类型语言:强类型语言也称为强类型定义语言,是一种总是强制类型定义的语言,要求变量的使用要严格符合定义,所有变量都必须先定义后使用。Java 和 C++等语言都是强制类型定义的,也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。例如你有一个整数,如果不显式地进行转换,你不能将其视为一个字符串。

  • 弱类型语言:弱类型语言也称为弱类型定义语言,与强类型定义相反。JavaScript 语言就属于弱类型语言。简单理解就是一种变量类型可以被忽略的语言。比如 JavaScript 是弱类型定义的,在 JavaScript 中就可以将字符串'12'和整数 3 进行连接得到字符串'123',在相加的时候会进行强制类型转换。


两者对比:强类型语言在速度上可能略逊色于弱类型语言,但是强类型语言带来的严谨性可以有效地帮助避免许多错误。

对 async/await 的理解

async/await 其实是Generator 的语法糖,它能实现的效果都能用 then 链来实现,它是为优化 then 链而开发出来的。从字面上来看,async 是“异步”的简写,await 则为等待,所以很好理解 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。当然语法上强制规定 await 只能出现在 asnyc 函数中,先来看看 async 函数返回了什么:


async function testAsy(){   return 'hello world';}let result = testAsy(); console.log(result)
复制代码



所以,async 函数返回的是一个 Promise 对象。async 函数(包含函数语句、函数表达式、Lambda 表达式)会返回一个 Promise 对象,如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。


async 函数返回的是一个 Promise 对象,所以在最外层不能用 await 获取其返回值的情况下,当然应该用原来的方式:then() 链来处理这个 Promise 对象,就像这样:


async function testAsy(){   return 'hello world'}let result = testAsy() console.log(result)result.then(v=>{    console.log(v)   // hello world})
复制代码


那如果 async 函数没有返回值,又该如何?很容易想到,它会返回 Promise.resolve(undefined)


联想一下 Promise 的特点——无等待,所以在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致。


注意:Promise.resolve(x) 可以看作是 new Promise(resolve => resolve(x)) 的简写,可以用于快速封装字面量对象或其他对象,将其封装成 Promise 实例。

CSS 优化和提高性能的方法有哪些?

加载性能:


(1)css 压缩:将写好的 css 进行打包压缩,可以减小文件体积。


(2)css 单一样式:当需要下边距和左边距的时候,很多时候会选择使用 margin:top 0 bottom 0;但 margin-bottom:bottom;margin-left:left;执行效率会更高。


(3)减少使用 @import,建议使用 link,因为后者在页面加载时一起加载,前者是等待页面加载完成之后再进行加载。


选择器性能:


(1)关键选择器(key selector)。选择器的最后面的部分为关键选择器(即用来匹配目标元素的部分)。CSS 选择符是从右到左进行匹配的。当使用后代选择器的时候,浏览器会遍历所有子元素来确定是否是指定的元素等等;


(2)如果规则拥有 ID 选择器作为其关键选择器,则不要为规则增加标签。过滤掉无关的规则(这样样式系统就不会浪费时间去匹配它们了)。


(3)避免使用通配规则,如*{}计算次数惊人,只对需要用到的元素进行选择。


(4)尽量少的去对标签进行选择,而是用 class。


(5)尽量少的去使用后代选择器,降低选择器的权重值。后代选择器的开销是最高的,尽量将选择器的深度降到最低,最高不要超过三层,更多的使用类来关联每一个标签元素。


(6)了解哪些属性是可以通过继承而来的,然后避免对这些属性重复指定规则。


渲染性能:


(1)慎重使用高性能属性:浮动、定位。


(2)尽量减少页面重排、重绘。


(3)去除空规则:{}。空规则的产生原因一般来说是为了预留样式。去除这些空规则无疑能减少 css 文档体积。


(4)属性值为 0 时,不加单位。


(5)属性值为浮动小数 0.**,可以省略小数点之前的 0。


(6)标准化各种浏览器前缀:带浏览器前缀的在前。标准属性在后。


(7)不使用 @import 前缀,它会影响 css 的加载速度。


(8)选择器优化嵌套,尽量避免层级过深。


(9)css 雪碧图,同一页面相近部分的小图标,方便使用,减少页面的请求次数,但是同时图片本身会变大,使用时,优劣考虑清楚,再使用。


(10)正确使用 display 的属性,由于 display 的作用,某些样式组合会无效,徒增样式体积的同时也影响解析性能。


(11)不滥用 web 字体。对于中文网站来说 WebFonts 可能很陌生,国外却很流行。web fonts 通常体积庞大,而且一些浏览器在下载 web fonts 时会阻塞页面渲染损伤性能。


可维护性、健壮性:


(1)将具有相同属性的样式抽离出来,整合并通过 class 在页面中进行使用,提高 css 的可维护性。


(2)样式与内容分离:将 css 代码定义到外部 css 中。

对盒模型的理解

CSS3 中的盒模型有以下两种:标准盒子模型、IE 盒子模型 盒模型都是由四个部分组成的,分别是 margin、border、padding 和 content。


标准盒模型和 IE 盒模型的区别在于设置 width 和 height 时,所对应的范围不同:


  • 标准盒模型的 width 和 height 属性的范围只包含了 content,

  • IE 盒模型的 width 和 height 属性的范围包含了 border、padding 和 content。


可以通过修改元素的 box-sizing 属性来改变元素的盒模型:


  • box-sizeing: content-box表示标准盒模型(默认值)

  • box-sizeing: border-box表示 IE 盒模型(怪异盒模型)

为什么会有 BigInt 的提案?

JavaScript 中 Number.MAX_SAFE_INTEGER 表示最⼤安全数字,计算结果是 9007199254740991,即在这个数范围内不会出现精度丢失(⼩数除外)。但是⼀旦超过这个范围,js 就会出现计算不准确的情况,这在⼤数计算的时候不得不依靠⼀些第三⽅库进⾏解决,因此官⽅提出了 BigInt 来解决此问题。

如何判断元素是否到达可视区域

以图片显示为例:


  • window.innerHeight 是浏览器可视区的高度;

  • document.body.scrollTop || document.documentElement.scrollTop 是浏览器滚动的过的距离;

  • imgs.offsetTop 是元素顶部距离文档顶部的高度(包括滚动条的距离);

  • 内容达到显示区域的:img.offsetTop < window.innerHeight + document.body.scrollTop;

ES6 模块与 CommonJS 模块有什么异同?

ES6 Module 和 CommonJS 模块的区别:


  • CommonJS 是对模块的浅拷⻉,ES6 Module 是对模块的引⽤,即 ES6 Module 只存只读,不能改变其值,也就是指针指向不能变,类似 const;

  • import 的接⼝是 read-only(只读状态),不能修改其变量值。 即不能修改其变量的指针指向,但可以改变变量内部指针指向,可以对 commonJS 对重新赋值(改变指针指向),但是对 ES6 Module 赋值会编译报错。


ES6 Module 和 CommonJS 模块的共同点:


  • CommonJS 和 ES6 Module 都可以对引⼊的对象进⾏赋值,即对对象内部属性的值进⾏改变。

数据类型检测的方式有哪些

(1)typeof


console.log(typeof 2);               // numberconsole.log(typeof true);            // booleanconsole.log(typeof 'str');           // stringconsole.log(typeof []);              // object    console.log(typeof function(){});    // functionconsole.log(typeof {});              // objectconsole.log(typeof undefined);       // undefinedconsole.log(typeof null);            // object
复制代码


其中数组、对象、null 都会被判断为 object,其他判断都正确。


(2)instanceof


instanceof可以正确判断对象的类型,其内部运行机制是判断在其原型链中能否找到该类型的原型


console.log(2 instanceof Number);                    // falseconsole.log(true instanceof Boolean);                // false console.log('str' instanceof String);                // false 
console.log([] instanceof Array); // trueconsole.log(function(){} instanceof Function); // trueconsole.log({} instanceof Object); // true
复制代码


可以看到,instanceof只能正确判断引用数据类型,而不能判断基本数据类型。instanceof 运算符可以用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。


(3) constructor


console.log((2).constructor === Number); // trueconsole.log((true).constructor === Boolean); // trueconsole.log(('str').constructor === String); // trueconsole.log(([]).constructor === Array); // trueconsole.log((function() {}).constructor === Function); // trueconsole.log(({}).constructor === Object); // true
复制代码


constructor有两个作用,一是判断数据的类型,二是对象实例通过 constrcutor 对象访问它的构造函数。需要注意,如果创建一个对象来改变它的原型,constructor就不能用来判断数据类型了:


function Fn(){};
Fn.prototype = new Array();
var f = new Fn();
console.log(f.constructor===Fn); // falseconsole.log(f.constructor===Array); // true
复制代码


(4)Object.prototype.toString.call()


Object.prototype.toString.call() 使用 Object 对象的原型方法 toString 来判断数据类型:


var a = Object.prototype.toString;
console.log(a.call(2));console.log(a.call(true));console.log(a.call('str'));console.log(a.call([]));console.log(a.call(function(){}));console.log(a.call({}));console.log(a.call(undefined));console.log(a.call(null));
复制代码


同样是检测对象 obj 调用 toString 方法,obj.toString()的结果和 Object.prototype.toString.call(obj)的结果不一样,这是为什么?


这是因为 toString 是 Object 的原型方法,而 Array、function 等类型作为 Object 的实例,都重写了 toString 方法。不同的对象类型调用 toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法(function 类型返回内容为函数体的字符串,Array 类型返回元素组成的字符串…),而不会去调用 Object 上原型 toString 方法(返回对象的具体类型),所以采用 obj.toString()不能得到其对象类型,只能将 obj 转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用 Object 原型上的 toString 方法。

与缓存相关的 HTTP 请求头有哪些

强缓存:


  • Expires

  • Cache-Control


协商缓存:


  • Etag、If-None-Match

  • Last-Modified、If-Modified-Since

对类数组对象的理解,如何转化为数组

一个拥有 length 属性和若干索引属性的对象就可以被称为类数组对象,类数组对象和数组类似,但是不能调用数组的方法。常见的类数组对象有 arguments 和 DOM 方法的返回结果,函数参数也可以被看作是类数组对象,因为它含有 length 属性值,代表可接收的参数个数。


常见的类数组转换为数组的方法有这样几种:


  • 通过 call 调用数组的 slice 方法来实现转换


Array.prototype.slice.call(arrayLike);
复制代码


  • 通过 call 调用数组的 splice 方法来实现转换


Array.prototype.splice.call(arrayLike, 0);
复制代码


  • 通过 apply 调用数组的 concat 方法来实现转换


Array.prototype.concat.apply([], arrayLike);
复制代码


  • 通过 Array.from 方法来实现转换


Array.from(arrayLike);
复制代码

px、em、rem 的区别及使用场景

三者的区别:


  • px 是固定的像素,一旦设置了就无法因为适应页面大小而改变。

  • em 和 rem 相对于 px 更具有灵活性,他们是相对长度单位,其长度不是固定的,更适用于响应式布局。

  • em 是相对于其父元素来设置字体大小,这样就会存在一个问题,进行任何元素设置,都有可能需要知道他父元素的大小。而 rem 是相对于根元素,这样就意味着,只需要在根元素确定一个参考值。


使用场景:


  • 对于只需要适配少部分移动设备,且分辨率对页面影响不大的,使用 px 即可 。

  • 对于需要适配各种移动设备,使用 rem,例如需要适配 iPhone 和 iPad 等分辨率差别比较挺大的设备。


用户头像

loveX001

关注

还未添加个人签名 2022-09-01 加入

还未添加个人简介

评论

发布
暂无评论
一年前端面试打怪升级之路_JavaScript_loveX001_InfoQ写作社区