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


    进程与线程的概念

    从本质上说,进程和线程都是 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 的集合
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    2)DOM 节点的创建

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

    <html>
      <head>
        <title>DEMOtitle>
      head>
      <body>
        <div id="container"> 
          <h1 id="title">我是标题h1>
        div>   
      body>
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    // 首先获取父节点
    var container = document.getElementById('container')
    // 创建新节点
    var targetSpan = document.createElement('span')
    // 设置 span 节点的内容
    targetSpan.innerHTML = 'hello world'
    // 把新创建的元素塞进父节点里去
    container.appendChild(targetSpan)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    3)DOM 节点的删除

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

    <html>
      <head>
        <title>DEMO</title>
      </head>
      <body>
        <div id="container">       <h1 id="title">我是标题</h1>
        </div>     </body>
    </html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    // 获取目标元素的父元素
    var container = document.getElementById('container')
    // 获取目标元素
    var targetNode = document.getElementById('title')
    // 删除目标元素
    container.removeChild(targetNode)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    // 获取目标元素的父元素var container = document.getElementById('container')// 获取目标元素var targetNode = container.childNodes[1]// 删除目标元素container.removeChild(targetNode)
    
    
    • 1
    • 2
    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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    现在需要调换 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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这就是 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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    所以,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
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    那如果 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);               // number
    console.log(typeof true);            // boolean
    console.log(typeof 'str');           // string
    console.log(typeof []);              // object    
    console.log(typeof function(){});    // function
    console.log(typeof {});              // object
    console.log(typeof undefined);       // undefined
    console.log(typeof null);            // object
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    (2)instanceof

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

    console.log(2 instanceof Number);                    // false
    console.log(true instanceof Boolean);                // false 
    console.log('str' instanceof String);                // false 
    
    console.log([] instanceof Array);                    // true
    console.log(function(){} instanceof Function);       // true
    console.log({} instanceof Object);                   // true
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    (3) constructor

    console.log((2).constructor === Number); // true
    console.log((true).constructor === Boolean); // true
    console.log(('str').constructor === String); // true
    console.log(([]).constructor === Array); // true
    console.log((function() {}).constructor === Function); // true
    console.log(({}).constructor === Object); // true
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    function Fn(){};
    
    Fn.prototype = new Array();
    
    var f = new Fn();
    
    console.log(f.constructor===Fn);    // false
    console.log(f.constructor===Array); // true
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (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));
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    同样是检测对象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);
    
    
    • 1
    • 2
    • 通过 call 调用数组的 splice 方法来实现转换
    Array.prototype.splice.call(arrayLike, 0);
    
    
    • 1
    • 2
    • 通过 apply 调用数组的 concat 方法来实现转换
    Array.prototype.concat.apply([], arrayLike);
    
    
    • 1
    • 2
    • 通过 Array.from 方法来实现转换
    Array.from(arrayLike);
    
    
    • 1
    • 2

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

    三者的区别:

    • px是固定的像素,一旦设置了就无法因为适应页面大小而改变。
    • em和rem相对于px更具有灵活性,他们是相对长度单位,其长度不是固定的,更适用于响应式布局。
    • em是相对于其父元素来设置字体大小,这样就会存在一个问题,进行任何元素设置,都有可能需要知道他父元素的大小。而rem是相对于根元素,这样就意味着,只需要在根元素确定一个参考值。

    使用场景:

    • 对于只需要适配少部分移动设备,且分辨率对页面影响不大的,使用px即可 。
    • 对于需要适配各种移动设备,使用rem,例如需要适配iPhone和iPad等分辨率差别比较挺大的设备。
  • 相关阅读:
    SpringBoot MongoDB操作封装
    DETR纯代码分享(八)position_encoding.py(models)
    二叉树刷题(完结篇)
    CMake语法结构说明
    java计算机毕业设计高校教材征订管理系统MyBatis+系统+LW文档+源码+调试部署
    windows安装cuda 11.8以及tensorflow-gpu 2.6
    Aspose导出word使用记录
    【羚珑AI智绘营】分分钟带你拿捏SD中的色彩控制
    GEE:Landsat-T1级别影像去云和辐射校正
    最新社区团购电商小程序源码 无bug完美运营版+详细搭建部署教程
  • 原文地址:https://blog.csdn.net/loveX001/article/details/127644740