# 前端面试真题

在面了腾讯、平安等大厂后发现,面试官很喜欢先提出一个小问题,然后根据这个问题发散性的进一步深入提问,直到你回答不出来。。。
故而我在整理面试题的时候,特意在每个题目里,对涉及到的其他问题添加了关联问题跳转功能(也可能是外部链接,用以对该知识点的详解),并尽可能附上问题的详细解答,这样可以方便构建属于自己的思维和记忆导图。
每一次面试,都是对自己能力的校验,也是职业发展的转折点,强大自己才能有更多的机会去做自己想做的事,真心希望每一个求职者都能找到满意的工作。
问的问题,没听明白,大胆礼貌的pardon,或者按自己的理解问:“这个问题问的是。。。。这个么”?
如果上一步给出的答案是否,那就大胆的说不知道,千万别瞎扯,会给人一种鸡同鸭讲的感觉,非常不爽,沟通能力极差。在技术面中,不懂的就是不懂的,只有ture 或 false

准备面试的时候,只知道面试题的答案是远远不够的,一定要自己理解后,用自己的语言组织出来,这样,才是你自己的知识。就像问vue原理一样,如果真的不知道真正的原理,只去背答案,很难记住的,就算记住了,面试一紧张也铁定忘光光。

# HTML相关

# 简述web语义化及其好处

web 语义化是指通过 HTML 标记表示页面包含的信息,包含了 HTML 标签的语义化和 css 命名的语义化。
HTML 标签的语义化是指:通过使用包含语义的标签 (opens new window)(如 h1-h6)恰当地表示文档结构
css 命名的语义化是指:为 html 标签添加有意义的 class,id 补充未表达的语义

好处:

  • 去掉样式后页面呈现清晰的结构
  • 搜索引擎可以更好地理解页面,有利于SEO
  • 便于团队项目的可持续运作及维护
  • 对盲人读屏器友好

扩展资料:

# data-属性的作用是什么?

为元素添加自定义属性。

# 请谈一下你对网页标准和标准制定机构(W3C)重要性的理解?

网页标准和标准制定机构都是为了能让web发展的更“健康”,开发者遵循统一的标准,可以降低开发难度、开发成本,SEO也会更好做,更不会因为滥用代码导致各种BUG、安全问题,最终提高网站易用性。

# HTML5新增了哪些API和内容?

API:

内容:

  • <nav>,<header>,<section>,<article>,<main>,<footer>,<aside>
  • <audio>,<video>
  • <mark>,<time>,<data>,<output>,<figure>,<figcaption>,<progress>,<meter>

# input和textarea的区别

input:

  • 可以指定type属性的值:text、url、file、mail、submit
  • 可以用value属性指定初始值
  • 宽高只能通过CSS改变

textarea:

  • 没有type属性,可以输入多行文本
  • 初始值需包裹在标签内:<textarea>我是初始值</textarea>
  • 宽高可以通过row和col属性指定,或通过CSS修改

# <img>titlealt有什么区别

  1. title全局属性 (opens new window)之一,用于为元素提供附加的提示信息。通常当鼠标滑动到元素上的时候显示。
  2. alt<img>的特有属性,是图片内容的等价描述,用于图片无法加载时显示及读屏器阅读图片。可高提图片可访问性,除了纯装饰图片外都必须设置有意义的值,搜索引擎会重点分析。

# doctype 是什么,举例常见 doctype 及特点

  1. <!doctype>声明是一个用于告诉浏览器当前 HTML 文档版本的指令,不是一个 HTML 标签
  2. <!doctype>声明必须处于 HTML 文档的头部,在<html>标签之前,HTML5 中不区分大小写
  3. 现代浏览器的 html 布局引擎通过检查有无 doctype 声明决定使用兼容模式还是标准模式对文档进行渲染,一些浏览器有一个准标准模式。

常见 doctype:

  1. HTML 5: <!doctype html>

# HTML 全局属性(global attribute)有哪些

  • title: 元素相关的建议信息
  • class:为元素设置类标识,多个类名用空格分开,CSS 和 javascript 可通过 class 属性获取元素
  • id: 元素 id,文档内唯一
  • style: 行内 css 样式
  • data-*: 为元素增加自定义属性
  • hidden: 表示一个元素是否隐藏
  • lang: 元素内容的的语言
  • accesskey:设置快捷键,提供快速访问元素如aaa在 windows 下的 firefox 中按alt + shift + a可激活元素
  • contenteditable: 指定元素内容是否可编辑
  • contextmenu: 自定义鼠标右键弹出菜单内容
  • dir: 设置元素文本方向
  • draggable: 设置元素是否可拖拽
  • dropzone: 设置元素拖放类型: copy, move, link
  • spellcheck: 是否启动拼写和语法检查
  • tabindex: 设置元素可以获得焦点,通过 tab 可以导航
  • translate: 元素和子孙节点内容是否需要本地化

参考资料:

# 讲一下<meta>标签

<meta>标签用来给页面提供一些元数据信息, 比如针对搜索引擎和更新频度的描述和关键词。
主要有以下几个属性:

  • name:定义文档级元数据的名称。常用的有author、description、keywords、generator、revised、others。
  • http-equiv:没有name时,会采用这个属性的值。常用的有content-type、expires、refresh、set-cookie。
  • content: 此属性包含http-equiv 或name 属性的值,具体取决于所使用的值。

参考资料:
关于 HTML 中 meta 标签的理解和总结 (opens new window)

# CSS相关

# CSS 选择器有哪些

  1. * 通用选择器:选择所有元素,不参与计算优先级,兼容性 IE6+
  2. #X id 选择器:选择 id 值为 X 的元素,兼容性:IE6+
  3. .X 类选择器: 选择 class 包含 X 的元素,兼容性:IE6+
  4. X Y 后代选择器: 选择满足 X 选择器的后代节点中满足 Y 选择器的元素,兼容性:IE6+
  5. X 元素选择器: 选择标所有标签为 X 的元素,兼容性:IE6+
  6. :link:visited:focus:hover:active 链接状态: 选择特定状态的链接元素,顺序 LoVe HAte,兼容性: IE4+
  7. X + Y 直接兄弟选择器:在X 之后第一个兄弟节点中选择满足 Y 选择器的元素,兼容性: IE7+
  8. X > Y 子选择器: 选择 X 的子元素中满足 Y 选择器的元素,兼容性: IE7+
  9. X ~ Y 兄弟: 选择X 之后所有兄弟节点中满足 Y 选择器的元素,兼容性: IE7+
  10. [attr]:选择所有设置了 attr 属性的元素,兼容性 IE7+
  11. [attr=value]:选择属性值刚好为 value 的元素
  12. [attr~=value]:选择属性值为空白符分隔,其中一个的值刚好是 value 的元素
  13. [attr|=value]:选择属性值刚好为 value 或者 value-开头的元素
  14. [attr^=value]:选择属性值以 value 开头的元素
  15. [attr$=value]:选择属性值以 value 结尾的元素
  16. [attr*=value]:选择属性值中包含 value 的元素
  17. [:checked]:选择单选框,复选框,下拉框中选中状态下的元素,兼容性:IE9+
  18. X:after, X::after:after 伪元素,选择元素虚拟子元素(元素的最后一个子元素),CSS3 中::表示伪元素。兼容性:after 为 IE8+,::after 为 IE9+
  19. :hover:鼠标移入状态的元素,兼容性 a 标签 IE4+, 所有元素 IE7+
  20. :not(selector):选择不符合 selector 的元素。不参与计算优先级,兼容性:IE9+
  21. ::first-letter:伪元素,选择块元素第一行的第一个字母,兼容性 IE5.5+
  22. ::first-line:伪元素,选择块元素的第一行,兼容性 IE5.5+
  23. :nth-child(an + b):伪类,选择前面有 an + b - 1 个兄弟节点的元素,其中 n >= 0, 兼容性 IE9+
  24. :nth-last-child(an + b):伪类,选择后面有 an + b - 1 个兄弟节点的元素 其中 n >= 0,兼容性 IE9+
  25. X:nth-of-type(an+b):伪类,X 为选择器,解析得到元素标签,选择前面有 an + b - 1 个相同标签兄弟节点的元素。兼容性 IE9+
  26. X:nth-last-of-type(an+b):伪类,X 为选择器,解析得到元素标签,选择后面有 an+b-1 个相同标签兄弟节点的元素。兼容性 IE9+
  27. X:first-child:伪类,选择满足 X 选择器的元素,且这个元素是其父节点的第一个子元素。兼容性 IE7+
  28. X:last-child:伪类,选择满足 X 选择器的元素,且这个元素是其父节点的最后一个子元素。兼容性 IE9+
  29. X:only-child:伪类,选择满足 X 选择器的元素,且这个元素是其父元素的唯一子元素。兼容性 IE9+
  30. X:only-of-type:伪类,选择 X 选择的元素,解析得到元素标签,如果该元素没有相同类型的兄弟节点时选中它。兼容性 IE9+
  31. X:first-of-type:伪类,选择 X 选择的元素,解析得到元素标签,如果该元素 是此此类型元素的第一个兄弟。选中它。兼容性 IE9+

# display: none;visibility: hidden;的区别

联系:它们都能让元素不可见

区别:

  1. display:none;会让元素完全从渲染树中消失,渲染的时候不占据任何空间;
    visibility: hidden;不会让元素从渲染树消失,渲染时元素继续占据空间,只是内容不可见。
  2. display: none;是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示;
    visibility: hidden;是继承属性,子孙节点由于继承了 hidden 而消失,通过设置 visibility: visible,可以让子孙节点显示。
  3. 修改常规流中元素的 display 通常会造成文档重排。
    修改 visibility 属性只会造成本元素的重绘。
  4. 读屏器不会读取 display: none;元素内容;会读取 visibility: hidden;元素内容。

# specified value,computed value,used value 计算方法

  • specified value: 计算方法如下:

    1. 如果样式表设置了一个值,使用这个值
    2. 如果没有设值,且这个属性是继承属性,从父元素继承
    3. 如果没有设值,并且不是继承属性,则使用 css 规范指定的初始值
  • computed value: 以 specified value 根据规范定义的行为进行计算,通常将相对值计算为绝对值,例如 em 根据 font-size 进行计算。一些使用百分数并且需要布局来决定最终值的属性,如 width,margin。百分数就直接作为 computed value。line-height 的无单位值也直接作为 computed value。这些值将在计算 used value 时得到绝对值。computed value 的主要作用是用于继承

  • used value:属性计算后的最终值,对于大多数属性可以通过 window.getComputedStyle 获得,尺寸值单位为像素。以下属性依赖于布局,

    • background-position
    • bottom, left, right, top
    • height, width
    • margin-bottom, margin-left, margin-right, margin-top
    • min-height, min-width
    • padding-bottom, padding-left, padding-right, padding-top
    • text-indent

# link@import的区别

  1. link是 HTML 方式, @import是 CSS 方式
  2. link最大限度支持并行下载,@import过多嵌套导致串行下载,出现FOUC (opens new window)
  3. link可以通过rel="alternate stylesheet"指定候选样式
  4. 浏览器对link支持早于@import,可以使用@import对老浏览器隐藏样式
  5. @import必须在样式规则之前,可以在 css 文件中引用其他文件
  6. 总体来说:link 优于@import (opens new window)

# display: block;display: inline;的区别

block元素特点 inline元素特点
处于常规流中时布局时在前后元素位置之间(独占一个水平空间) 水平方向上根据direction依次布局,不会在元素前后进行换行
处于常规流中时,如果width没有设置,会自动填充满父容器 width/height属性对非替换行内元素无效,宽度由元素内容决定
可以应用margin/padding margin/padding在竖直方向上无效,水平方向上有效
在没有设置高度的情况下会扩展高度以包含常规流中的子元素 非替换行内元素的行框高由line-height确定,替换行内元素的行框高由height,margin,padding,border决定
忽略vertical-align vertical-align属性生效
- white-space控制,浮动或绝对定位时会转换为block

延展资料:
可替换元素和非可替换元素 (opens new window)

# PNG,GIF,JPG 的区别及如何选

GIF:

  1. 8 位像素,256 色
  2. 无损压缩
  3. 支持简单动画
  4. 支持 boolean 透明
  5. 适合简单动画

JPEG

  1. 颜色限于 256
  2. 有损压缩
  3. 可控制压缩质量
  4. 不支持透明
  5. 适合照片

PNG

  1. 有 PNG8 和 truecolor PNG
  2. PNG8 类似 GIF 颜色上限为 256,文件小,支持 alpha 透明度,无动画
  3. 适合图标、背景、按钮

# CSS 有哪些继承属性

# 有哪些伪类和伪元素

伪类
伪元素

这里要区分一下伪类和伪元素的概念。根本区别在于有没有创建一个文档树之外的元素。

  • 伪类:伪类用于当已有元素处于的某个状态时,为其添加对应的样式,这个状态是根据用户行为而动态变化的。
  • 伪元素:伪元素用于创建一些不在文档树中的元素,并为其添加样式。

参考资料:
总结伪类和伪元素 (opens new window)

# css3有哪些新属性

  1. 边框:
  • border-radius:圆角边框,border-radius:25px;
  • box-shadow:边框阴影,box-shadow: 10px 10px 5px #888888;
  • border-image:边框图片,border-image:url(border.png) 30 30 round;
  1. 背景:
  • background-size:规定背景图片的尺寸,background-size:63px 100px;
  • background-origin:规定背景图片的定位区域,背景图片可以放置于 content-box、padding-box 或 border-box 区域。background-origin:content-box;
  • CSS3 允许您为元素使用多个背景图像。background-image:url(bg_flower.gif),url(bg_flower_2.gif);
  1. 文本效果:
  • text-shadow:向文本应用阴影,可以规定水平阴影、垂直阴影、模糊距离,以及阴影的颜色。text-shadow: 5px 5px 5px #FF0000;
  • word-wrap:允许文本进行换行。word-wrap:break-word;
  1. 字体:CSS3 @font-face 规则可以自定义字体。
  2. 2D 转换(transform)
  • translate():元素从其当前位置移动,根据给定的 left(x 坐标) 和 top(y 坐标) 位置参数。 transform: translate(50px,100px);
  • rotate():元素顺时针旋转给定的角度。允许负值,元素将逆时针旋转。transform: rotate(30deg);
  • scale():元素的尺寸会增加或减少,根据给定的宽度(X 轴)和高度(Y 轴)参数。transform: scale(2,4);
  • skew():元素翻转给定的角度,根据给定的水平线(X 轴)和垂直线(Y 轴)参数。transform: skew(30deg,20deg);
  • matrix(): 把所有 2D 转换方法组合在一起,需要六个参数,包含数学函数,允许您:旋转、缩放、移动以及倾斜元素。transform:matrix(0.866,0.5,-0.5,0.866,0,0);
  1. 3D 转换
  2. transition:过渡效果,使页面变化更平滑
  3. animation:动画

# 容器包含若干浮动元素时如何清理(包含)浮动

  1. 给需要清除浮动的元素添加clear样式,clear: left/right(视浮动元素而定),或直接clear: both
  2. 容器元素闭合标签前添加额外的无内容块级元素并设置clear: both
  3. 设置容器元素添加伪元素进行清理:
    .container::after {
      content: '.';
      display: block;
      height: 0;
      clear: both;
    }
    
    1
    2
    3
    4
    5
    6
  4. 容器元素触发块级格式化上下文(BFC),例如:在容器元素上设置overflow: auto(overflow的值只要不是默认的visible都可以)

参考资料:
清除浮动的四种方式及其原理理解 (opens new window)

# 什么是 FOUC?如何避免

Flash Of Unstyled Content:用户定义样式表加载之前浏览器使用默认样式显示文档,用户样式加载渲染之后再从新显示文档,造成页面闪烁。
解决方法:把样式表放到文档的head

# 块级格式化上下文的创建和用处

创建规则:

  1. 根元素
  2. 浮动元素(float不是none
  3. 绝对定位元素(position取值为absolutefixed
  4. display取值为inline-block,table-cell, table-caption,flex, inline-flex之一的元素
  5. overflow不是visible的元素

作用:

  1. 可以包含浮动元素
  2. 不被浮动元素覆盖
  3. 阻止父子元素的 margin 折叠

参考资料:
关于CSS-BFC深入理解 (opens new window)

# display,float,position 的关系

  1. 如果display为 none,那么 position 和 float 都不起作用,这种情况下元素不产生框
  2. 如果display不为 none
    • 如果 position 值为 absolute 或者 fixed,框就是绝对定位的,float 的计算值为 none,display 根据下面的表格进行调整。
    • 如果 position 值不为 absolute 或者 fixed,并且 float 不是 none,框是浮动的,display 根据下表进行调整
    • 如果 position 值不为 absolute 或者 fixed,并且 框不是浮动的,如果元素是根元素,display 根据下表进行调整
    • 其他情况下 display 的值为指定值

总结起来:绝对定位、浮动、根元素都需要调整display display转换规则

# 外边距折叠(collapsing margins)

毗邻的两个或多个margin会合并成一个 margin,叫做外边距折叠。规则如下:

  1. 两个或多个毗邻的普通流中的块元素垂直方向上的 margin 会折叠
  2. 浮动元素/inline-block 元素/绝对定位元素的 margin 不会和垂直方向上的其他元素的 margin 折叠
  3. 创建了块级格式化上下文的元素,不会和它的子元素发生 margin 折叠
  4. 元素自身的 margin-bottom 和 margin-top 相邻时也会折叠

# 如何确定一个元素的包含块(containing block)

  1. 根元素的包含块叫做初始包含块,在连续媒体中他的尺寸与 viewport 相同并且 anchored at the canvas origin;对于 paged media,它的尺寸等于 page area。初始包含块的 direction 属性与根元素相同。
  2. positionrelative或者static的元素,它的包含块由最近的块级(displayblock,list-item, table)祖先元素的内容框组成
  3. 如果元素positionfixed。对于连续媒体,它的包含块为 viewport;对于 paged media,包含块为 page area
  4. 如果元素positionabsolute,它的包含块由祖先元素中最近一个position不为static的元素产生

# stacking context,布局规则

z 轴上的默认层叠顺序如下(从下到上):

  1. 根元素的边界和背景
  2. 常规流中的元素按照 html 中顺序
  3. 浮动块
  4. positioned 元素按照 html 中出现顺序

如何创建 stacking context:

  1. 根元素
  2. z-index 不为 auto 的定位元素
  3. a flex item with a z-index value other than 'auto'
  4. opacity 小于 1 的元素
  5. 在移动端 webkit 和 chrome22+,z-index 为 auto,position: fixed 也将创建新的 stacking context

# 如何水平居中一个元素

  1. 如果需要居中的元素为常规流中 inline 元素,为父元素设置text-align: center;即可实现
  2. 如果需要居中的元素为常规流中 block 元素
    1)为元素设置宽度
    2)设置左右 margin 为 0 auto
<body>
    <div class="content">
      嗨!我是honkerzhou
    </div>
</body>

<style>
    body {
        background: #DDD;
        text-align: center; /* 3 */
    }
    .content {
        width: 500px;      /* 1 */
        text-align: left;  /* 3 */
        margin: 0 auto;    /* 2 */

        background: purple;
    }
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

代码效果见:嗨!我是honkerzhou (opens new window)

  1. 如果需要居中的元素为浮动元素
    1)设置position: relative;
    2)浮动方向偏移量(left 或者 right)设置为 50%
    3)为元素设置宽度
    4)浮动方向上的 margin 设置为元素宽度一半乘以-1
<body>
    <div class="content">
      嗨!我是honkerzhou
    </div>
</body>

<style>
    body {
        background: #DDD;
    }
    .content {
        width: 500px;         /* 1 */
        float: left;

        position: relative;   /* 2 */
        left: 50%;            /* 3 */
        margin-left: -250px;  /* 4 */

        background-color: purple;
    }
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

代码效果见:嗨!我是honkerzhou (opens new window)

  1. 如果需要居中的元素为绝对定位元素
    方法一:
    1)偏移量设置为 50%
    2)为元素设置宽度
    3)偏移方向外边距设置为元素宽度一半乘以-1

    <body>
        <div class="content">
          嗨!我是honkerzhou
        </div>
    </body>
    
    <style>
        body {
            background: #DDD;
        }
        .content {
            position: absolute;
            left: 50%;
            width: 800px;
            margin-left: -400px;
    
            background-color: purple;
        }
    </style>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

代码效果见:嗨!我是honkerzhou (opens new window)

方法二:
1)设置左右偏移量都为 0
2)为元素设置宽度
3)设置左右外边距都为 auto

<body>
    <div class="content">
      嗨!我是honkerzhou
    </div>
</body>

<style>
    body {
      background: #ddd;
    }
    .content {
        position: absolute;
        left: 0; 
        right: 0;
        width: 800px;
        margin: 0 auto;

        background-color: purple;
    }
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

原理讲解:
先理解以下知识点:

  • positon: absolute;: 不为元素预留空间,通过指定元素相对于最近的非 static 定位祖先元素的偏移,来确定元素位置。绝对定位的元素可以设置外边距(margins),且不会与其他边距合并。
  • 当position设置为absolute或fixed时,left属性指定了定位元素左外边距边界与其包含块左边界之间的偏移。right、top、bottom属性同理。

因此,为了满足left: 0;right: 0;,浏览器会自动根据margin: 0 auto;中设置的auto来自动计算代码中绝对定位的元素块的margin-left和margin-right的值,使两者相等(两者的值都为(body块的宽度-content块的宽度)/2

代码效果见:嗨!我是honkerzhou (opens new window)

# 如何水平竖直居中一个元素

# 双飞翼和圣杯布局

# flex和grid布局

# css sprite 是什么,有什么优缺点

概念:将多个小图片拼接到一个图片中。通过 background-position 和元素尺寸调节需要显示的背景图案。

优点:

  1. 减少 HTTP 请求数,极大地提高页面加载速度
  2. 增加图片信息重复度,提高压缩比,减少图片大小
  3. 更换风格方便,只需在一张或几张图片上修改颜色或样式即可实现

缺点:

  1. 图片合并麻烦
  2. 维护麻烦,修改一个图片可能需要重新布局整个图片,样式 css sprite介绍 (opens new window) css sprite优缺点详解 (opens new window)

# CSS Moudle 和 Scoped

# CSS预编译器的作用

  • 模块化
  • 避免全局命名冲突
  • 避免嵌套过深

# JS相关

# element.getAttribute()和 element.propName 有什么联系和区别

首先,需要明确以下几点:

  • element.getAttribute(),返回在HTML代码中该元素对应特性的特性值
  • element.propName, 返回在DOM中该元素对象对应属性的属性值
  • 当浏览器解析网页时,会将 HTML 标准的(即非自定义的)特性映射为 DOM 属性(我称之为DOM的标准属性)

所以,两者的联系
element.getAttribute()获取到的大多数HTML 标准特性的特性值 === element.propName获取到的DOM中对应的标准属性的属性值(有部分除外,详见区别2

两者的区别

  1. element.getAttribute()可以获取自定义特性值,而element.propName不能获取对应的属性(因为浏览器不会将自定义特性映射为对应的属性,如果是data-开头的自定义特性,则会映射到DOM 的 dataset 属性)
  2. element.getAttribute()得到的值永远是字符串或 null,而element.propName得到的值可以是任意合法 js 类型。因为有部分特性,它们虽然有对应的属性名,但属性的值与通过 getAttribute()返回的特性的值并不相同:
style 事件处理程序(eg: onclick="alert('Hello world!')") checked href
element.getAttribute() 返回 style 特性值中包含的 CSS 文本 返回相应代码的字符串(eg: alert('Hello world!')) 返回特性值字符串 返回特性值字符串
element.propName 返回一个CSSStyleDeclaration对象 返回一个 JavaScript 函数,如果未在元素中指定相应特性,则返回 null(eg: f onclick(event) {alert('Hello world!')}) 返回布尔值 返回解析后的完整 uri
  1. element.getAttribute()对于不存在的特性返回 null,而element.propName对于不存在的属性返回undefined
  2. element.getAttribute()对特性名称的大小写不敏感,而element.propName对属性名称大小写敏感

注意:如果对特性和属性看的比较懵逼,请看看拓展资料

拓展资料:
详解 HTML attribute 和 DOM property (opens new window)

# offsetWidth/offsetHeight,clientWidth/clientHeight 与 scrollWidth/scrollHeight 的区别

  • offsetWidth/offsetHeight指的是元素在屏幕上占用的所有视觉空间, 返回值包含content + padding + border + (可见的)垂直滚动条的宽度/水平滚动条的高度,效果与 e.getBoundingClientRect()相同
  • clientWidth/clientHeight 返回值只包含content + padding,如果有滚动条,也不包含滚动条
  • scrollWidth/scrollHeight 返回值包含content + padding + 溢出内容的尺寸

元素尺寸

# focus/blur 与 focusin/focusout 的联系和区别

联系focusfocusin都在元素获得焦点时触发,blurfocusout 都在元素失去焦点时触发
区别focus/blur不冒泡,focusin/focusout冒泡

# mouseover/mouseout 与 mouseenter/mouseleave 的联系和区别

联系:
都是DOM3级事件中定义的鼠标事件

区别:

  1. 定义不同
    • mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发
    • mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发,在光标移动到后代元素上不会触发
    • mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素
    • mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发,在光标移动到后代元素上不会触发
  2. mouseover/mouseout冒泡事件;mouseenter/mouseleave不冒泡

联系

  1. 都会在浏览器端保存,有大小限制,同源限制

区别

  1. cookie 会在请求时发送到服务器,作为会话标识,服务器可修改 cookie;而Web Storage 不会发送到服务器
  2. cookie 有 path 概念,子路径可以访问父路径 cookie,父路径不能访问子路径 cookie;而Web Storage没有
  3. cookie 有 secure 属性可以强制要求 HTTPS 传输;而Web Storage不会强制要求 HTTPS 传输
  4. 有效期不同:cookie 在设置的有效期内有效,默认为浏览器关闭;sessionStorage 在窗口关闭前有效,localStorage 长期有效,直到用户删除
  5. 共享范围不同:sessionStorage 不能多文档共享,localStorage 在同源文档之间共享,cookie 在同源且符合 path 规则的文档之间共享
  6. 每个域名下的cookie数量有限制,每个 cookie 长度不能超过 4k。而每个域名下Web Storage 大小支持能达到 5M

# javascript 跨域通信

同源:两个文档同源需满足

  1. 协议相同
  2. 域名相同
  3. 端口相同

跨域通信:js 进行 DOM 操作、通信时如果目标与当前窗口不满足同源条件,浏览器为了安全会阻止跨域操作。跨域通信通常有以下方法

  • 跨域资源共享(CORS),目标服务器返回 HTTP 头部**Access-Control-Allow-Origin: ***即可像普通 ajax 一样访问跨域资源
  • 如果是 log 之类的简单单项通信,新建<img>(图像Ping),<script>,<link>,<iframe>元素,通过 src,href 属性设置为目标 url。实现跨域请求
  • 如果请求json 数据,使用<script>进行 jsonp 请求
  • 跨文档消息传送(cross-document messaging,简称为 XDM): targetWindow.postMessage(data, origin);其中 data 是需要发送的对象,origin 是表示消息接收方来自哪个域的字符串。window.addEventListener('message', handler, false);handler 的 event.data 是 postMessage 发送来的数据,event.origin 是发送消息的文档所在的域,event.source 是发送消息的文档的 window 对象的代理
  • 内部服务器代理请求跨域 url,然后返回数据
  • Web Sockets(同源策略对 Web Sockets 不适用)

# javascript 有哪几种数据类型

七种基本数据类型(也称为简单数据类型)

一种引用类型(也称为复杂数据类型)

  • Object(JavaScript 中还有9种内置对象(对象的子类型)——String、Number、Boolean、Object、Function、Array、Date、RegExp和Error)

# 深拷贝和浅拷贝

腾讯一面,问到了对象在内存中存储
js 深拷贝 vs 浅拷贝 (opens new window)

基本数据类型:存储在栈内存
引用数据类型:存储在堆内存,并在栈内存存储着指向堆内存地址的指针

深拷贝和浅拷贝是针对引用数据来说的: 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。 浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址

浅拷贝的实现方式:

  1. Object.assign()
  2. Array.prototype.concat()
  3. Array.prototype.slice()

深拷贝的实现方式:

  1. JSON.parse(JSON.stringify()): 注意,会忽略函数及原型成员
  2. 手写递归
    //定义检测数据类型的功能函数
  function checkedType(target) {
    return Object.prototype.toString.call(target).slice(8, -1)
  }
  //实现深度克隆---对象/数组
  function clone(target) {
    //判断拷贝的数据类型
    //初始化变量result 成为最终克隆的数据
    let result, targetType = checkedType(target)
    if (targetType === 'Object') {
      result = {}
    } else if (targetType === 'Array') {
      result = []
    } else {
      return target
    }
    //遍历目标数据
    for (let i in target) {
      //获取遍历数据结构的每一项值。
      let value = target[i]
      //判断目标结构里的每一值是否存在对象/数组
      if (checkedType(value) === 'Object' ||
        checkedType(value) === 'Array') { //对象/数组里嵌套了对象/数组
        //继续遍历获取到value值
        result[i] = clone(value)
      } else { //获取到value值是基本的数据类型或者是函数。
        result[i] = value;
      }
    }
    return result
  }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  1. 函数库lodash

# 什么闭包,闭包有什么用

闭包是指有权访问另外一个函数作用域中的变量的函数。闭包作用域链通常包括三个部分:

  1. 函数本身作用域。
  2. 闭包定义时的作用域。
  3. 全局作用域。

闭包常见用途:

  1. 创建特权方法用于访问控制
  2. 事件处理程序及回调

# 介绍下面向对象编程 和 函数式编程

  1. 面向对象编程: 现实世界中的一切事物皆可抽象为编程中的对象,事物的特性对应对象的属性,事物的行为对应对象的方法。面向对象编程三大特征:封装(有利于应用程序去耦)、继承(有利于代码复用)、多态(有利于编程的灵活性);
  2. 函数式编程: 函数式编程认为世界就是由事物和事物之间的关系构成的;

# javascript 有哪几种方法定义函数

  1. 函数声明表达式 (opens new window)
  2. function 操作符 (opens new window)
  3. Function 构造函数 (opens new window)
  4. ES6:arrow function (opens new window)

# 应用程序存储和离线 web 应用

该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性。改用Service Workers

HTML5 新增应用程序缓存,允许 web 应用将应用程序自身保存到用户浏览器中,用户离线状态也能访问。

  1. 为 html 元素设置 manifest 属性:<html manifest="myapp.appcache">,其中后缀名只是一个约定,真正识别方式是传输manifest 文件时以text/cache-manifest作为 MIME 类型。所以需要配置服务器保证设置正确
  2. manifest 文件首行为CACHE MANIFEST,其余就是要缓存的 URL 列表,每个一行,相对路径都相对于 manifest 文件的 url。注释以#开头
  3. url 分为三种类型:
    • CACHE:为默认类型。
    • NETWORK:表示资源从不缓存。
    • FALLBACK:每行包含两个 url,第二个 URL 是指需要加载和存储在缓存中的资源, 第一个 URL 是一个前缀。任何匹配该前缀的 URL 都不会缓存,如果从网络中载入这样的 URL 失败的话,就会用第二个 URL 指定的缓存资源来替代。以下是一个文件例子:
CACHE MANIFEST

CACHE:
myapp.html
myapp.css
myapp.js

FALLBACK:
videos/ offline_help.html

NETWORK:
cgi/
1
2
3
4
5
6
7
8
9
10
11
12

# javascript 有哪些方法定义对象

  1. 对象字面量: var obj = {};

  2. Object构造函数: var obj = new Object();

    1、2两种方式存在的问题:使用同一个接口创建很多对象,会产生大量的重复代码

  3. 工厂模式:用函数来封装以特定接口创建对象的细节

     function createPerson(name, age, job){
       var o = new Object();
       o.name = name;
       o.age = age;
       o.job = job;
       o.sayName = function(){
         alert(this.name);
       };
       return o;
     } 
     var person1 = createPerson("Nicholas", 29, "Software Engineer");
     var person2 = createPerson("Greg", 27, "Doctor"); 
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    存在的问题:无法知道一个对象的类型

  4. 构造函数模式:

     function Person(name, age, job){
         this.name = name;
         this.age = age;
         this.job = job;
         this.sayName = function(){
           alert(this.name);
         };
     }
     var person1 = new Person("Nicholas", 29, "Software Engineer");
     var person2 = new Person("Greg", 27, "Doctor");
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

存在的问题:每个方法都要在每个实例上重新创建一遍

改进:

 function Person(name, age, job){
   this.name = name;
   this.age = age;
   this.job = job;
   this.sayName = sayName;
 }
 function sayName(){
   alert(this.name);
 }
 var person1 = new Person("Nicholas", 29, "Software Engineer");
 var person2 = new Person("Greg", 27, "Doctor"); 
1
2
3
4
5
6
7
8
9
10
11

存在的问题

  • 在全局作用域中定义的函数实际上只能被某个对象调用
  • 如果对象需要定义很多方法,那么就要定义很多个全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了
  1. 原型模式:

     function Person(){
     }
     Person.prototype.name = "Nicholas";
     Person.prototype.age = 29;
     Person.prototype.job = "Software Engineer";
     Person.prototype.sayName = function(){
       alert(this.name);
     };
     var person1 = new Person();
     person1.sayName(); //"Nicholas"
     var person2 = new Person(); 
     person2.sayName(); //"Nicholas"
     alert(person1.sayName == person2.sayName); //true
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    存在的问题:中每添加一个属性和方法就要敲一遍 Person.prototype

    改进:

     function Person(){
     }
     Person.prototype = {
       name : "Nicholas",
       age : 29,
       job : "Software Engineer",
       sayName : function () {
         alert(this.name);
       }
     }; 
     //重设构造函数
     Object.defineProperty(Person.prototype, "constructor", {
       enumerable: false,
       value: Person
     }); 
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    原型模式存在的问题:使用引用类型值的属性时,操作该属性会影响所有实例

  2. 组合使用构造函数模式和原型模式:构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性

     function Person(name, age, job){
       this.name = name;
       this.age = age;
       this.job = job;
       this.friends = ["Shelby", "Court"];
     }
     Person.prototype = {
       constructor : Person,
       sayName : function(){
         alert(this.name);
       }
     }
     var person1 = new Person("Nicholas", 29, "Software Engineer");
     var person2 = new Person("Greg", 27, "Doctor");
     person1.friends.push("Van");
     alert(person1.friends); //"Shelby,Count,Van"
     alert(person2.friends); //"Shelby,Count"
     alert(person1.friends === person2.friends); //false
     alert(person1.sayName === person2.sayName); //true 
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  3. 动态原型模式:

     function Person(name, age, job){
       //属性
       this.name = name;
       this.age = age;
       this.job = job; 
       //方法
       if (typeof this.sayName != "function"){
         Person.prototype.sayName = function(){
           alert(this.name);
         };
       }
     }
     var friend = new Person("Nicholas", 29, "Software Engineer");
     friend.sayName();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  4. 寄生构造函数模式:

function Person(name, age, job){
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function(){
    alert(this.name);
  };
  return o;
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas" 
1
2
3
4
5
6
7
8
9
10
11
12
  1. 稳妥构造函数模式:
  function Person(name, age, job){

    //创建要返回的对象
    var o = new Object(); 
    //可以在这里定义私有变量和函数
    //添加方法
    o.sayName = function(){
      alert(name);
    };

    //返回对象
    return o;
  } 
1
2
3
4
5
6
7
8
9
10
11
12
13

# ==运算符判断相等的流程是怎样的

  • 如果两个值类型相同,按照===比较方法进行比较
  • 如果类型不同,先转换操作数(通常称为强制转型),然后按照===比较方法进行比较
    • 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false 转换为 0,而true 转换为 1;
    • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
    • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法,用得到的基本类型值按照前面的规则进行比较;

例外:

  1. null 和 undefined 是相等的
  2. 比较相等性之前, null 和 undefined 不会转换成其他任何值
  3. NaN和任何值都不相等(包括NaN)
  4. 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true;否则,返回 false。

拓展资料:

不同类型对象的valueOf()方法的返回值

对象 返回值
Boolean 布尔值。
String 字符串值。
Number 数字值。
Array 返回数组对象本身。
Object 对象本身。这是默认情况。
Date 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。
Function 函数本身。
RegExp 正则对象
Math 和 Error 对象没有 valueOf 方法。

# ===运算符判断相等的流程是怎样的

  • 如果两个值不是相同类型,它们不相等
  • 如果两个值是相同类型,则:
    1. 如果两个值都是 null 或者都是 undefined,它们相等
    2. 如果两个值都是布尔类型 true 或者都是 false,它们相等
    3. 如果都是数值型并且数值相等,他们相等, -0 等于 +0
    4. 如果他们都是字符串并且在相同位置包含相同字符编码值,它们相等;如果在长度或者内容上不等,它们不相等;
    5. 如果他们指向相同对象、数组、函数,它们相等;如果指向不同对象,他们不相等
    6. NaN和任何值都不相等(包括NaN)

补充知识:
ES6中的Object.is()的判断规则除了以下几点,其他都与===运算符一致:

  • +0 不等于 -0
  • -0 不等于 0
  • NaN 等于 NaN

# <,>,<=,>=的比较规则

  • 如果两个操作数都是数值,则执行数值比较。
  • 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值。
  • 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较。
  • 如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较
  • 如果一个操作数是对象,则调用这个对象的 valueOf()方法,用得到的结果按照前面的规则执行比较。如果对象没有 valueOf()方法,则调用 toString()方法,并用得到的结果根据前面的规则执行比较。

拓展资料:

不同类型对象的valueOf()方法和toString()方法的返回值

对象 valueOf()方法返回值 toString()方法返回值
Boolean 布尔值。 "[object Boolean]"
String 字符串值。 "[object String]"
Number 数字值。 "[object Number]"
Array 返回数组对象本身。 "[object Array]"
Object 对象本身。这是默认情况。 "[object Object]"
Date 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。 "[object Date]"
Function 函数本身。 "[object Function]"
RegExp 正则对象 "[object RegExp]"
Math 没有 valueOf 方法 "[object Math]"
Error 没有 valueOf 方法 "[object Error]"

# 对象到字符串的转换步骤

  1. 如果对象有 toString()方法,javascript 调用它。如果返回一个原始值(primitive value 如:string number boolean),将这个值转换为字符串作为结果
  2. 如果对象没有 toString()方法或者返回值不是原始值,javascript 寻找对象的 valueOf()方法,如果存在就调用它,返回结果是原始值则转为字符串作为结果
  3. 否则,javascript 不能从 toString()或者 valueOf()获得一个原始值,此时 throws a TypeError

# 对象到数字的转换步骤

  1. 如果对象有valueOf()方法并且返回原始值,javascript将返回值转换为数字作为结果
  2. 否则,如果对象有toString()并且返回原始值,javascript将返回结果转换为数字作为结果
  3. 否则,throws a TypeError

# +运算符工作流程

  1. 如果两个操作符都是数值,执行常规的加法计算,然后根据下列规则返回结果:
    • 如果有一个操作数是 NaN,则结果是 NaN;
    • 如果是 Infinity 加 Infinity,则结果是 Infinity;
    • 如果是-Infinity 加-Infinity,则结果是-Infinity;
    • 如果是 Infinity 加-Infinity,则结果是 NaN;
    • 如果是+0 加+0,则结果是+0;
    • 如果是-0 加-0,则结果是-0;
    • 如果是+0 加-0,则结果是+0。
  2. 如果有一个操作数是字符串,那么就要应用如下规则:
    • 如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来;
    • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接起来。
    • 如果有一个操作数是对象、数值或布尔值,则调用它们的 toString()方法取得相应的字符串值,然后再应用前面关于字符串的规则。对于 undefined 和 null,则分别调用 String()函数并取得字符串"undefined"和"null"

# 函数内部 arguments 变量有哪些特性,有哪些属性,如何将它转换为数组

  • arguments 所有函数中都包含的一个局部变量,是一个类数组对象,对应函数调用时的实参。如果函数定义同名参数会在调用时覆盖默认对象
  • arguments[index]分别对应函数调用时的实参,并且通过 arguments 修改实参时会同时修改实参
  • arguments.length 为实参的个数(Function.length 表示形参长度)
  • arguments.callee 为当前正在执行的函数本身,使用这个属性进行递归调用时需注意 this 的变化
  • arguments.caller 为调用当前函数的函数(已被遗弃)
  • 转换为数组:var args = Array.prototype.slice.call(arguments, 0);

# DOM 事件模型是如何的

DOM事件流由“DOM2级事件”规定,包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。

# XHR(XMLHttpRequest)的使用

参见:JS高程第21章21.1.1-XHR的用法
其中的readyStateChange事件可用load事件代替,参见:JS高程第21章21.3.1-load事件

# CommonJS、requirejs、ES6的对比

CommonJS、requirejs、ES6的对比 (opens new window)

# svg,canvas,webgl的对比

svg,canvas,webgl的对比 (opens new window)

# document.ready和document.onload两个事件的区别

  • document.ready: 表示文档结构已经加载完成(不包含图片等非文字媒体文件);
  • document.onload: 表示页面包含图片等文件在内的所有元素都加载完成。

# javascript里函数参数arguments是数组么

不是,是类数组对象,除了可以使用方括号语法访问它的每一个元素和具有length 属性外,不具备其他数组特征

# 如何实现bind()函数

function bind(fn, context) {
    return function() {
        return fn.apply(context, arguments)
    }
}
1
2
3
4
5

# 箭头函数与普通函数的区别

  1. 箭头函数的语法更加简洁,作用域和this的绑定也更加直观;
  2. 箭头函数没有自己的this,函数内部的this其实是外部代码块的this注意:对象不构成单独的作用域),指向定义时所在的对象,而不是使用时所在的对象;而普通函数的this指向运行时所在的作用域;
  3. 箭头函数无法通过call()apply()bind()去改变箭头函数内的this指向(因为箭头函数没有自己的this);
  4. 箭头函数中没有argumentssupernew.target变量,这些其实都是指向外层函数对应的变量;
  5. 箭头函数不可以当作构造函数,不可以使用arguments对象,不可以使用yield命令;

# ES6中class是怎么实现的

ES5中组合使用构造函数模式和原型模式的语法糖,保留的constructor()方法等价于构造函数,自定义方法等价于在构造函数的原型对象上添加方法属性

# 网络与安全

# 手写Promise

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class myPromise() {
  constructor(executor) {
    this._status = PENDING
    this._resolveQueue = []
    this._rejectQueue = []
    let _resolve = (val) => {
      if (this._status !== PENDING) return
      this._status = FULFILLED
      while (this._resolveQueue.length) {
        const callback = this._resolveQuere.shift()
        callback(val)
      }
    }
    let _reject = (val) => {
      if (this._status !== REJECTED) return
      this._status = FULFILLED
      while (this._resolveQueue.length) {
        const callback = this._resolveQueue.shift()
        callback(val)
      }
    }
    executor(_resolve, _reject)
  }
  then(resolveFn, rejectFn) {
    return new Promise(resolve, reject) => {
      const fulfilledFn = value => {
        try {
          let x = resolveFn(value)
          x instanceof Promise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }
      this._resolveQueue.push(fulfilledFn)

      const rejectedFn = value => {
        try {
          let x = rejectFn(value)
          x instanceof Promise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }
      this._rejectQueue.push(rejectedFn)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# HTTP method

  1. GET是最常用的方法,通常用于请求服务器发送某个资源
  2. HEAD与 GET 类似,但服务器在响应中值返回首部,不返回实体的主体部分
  3. PUT让服务器用请求的主体部分来创建一个由所请求的 URL 命名的新文档,或者,如果那个 URL 已经存在的话,就用干这个主体替代它
  4. POST起初是用来向服务器输入数据的。实际上,通常会用它来支持 HTML 的表单。表单中填好的数据通常会被送给服务器,然后由服务器将其发送到要去的地方。
  5. TRACE会在目的服务器端发起一个环回诊断,最后一站的服务器会弹回一个 TRACE 响应并在响应主体中携带它收到的原始请求报文。TRACE 方法主要用于诊断,用于验证请求是否如愿穿过了请求/响应链。
  6. OPTIONS方法请求 web 服务器告知其支持的各种功能。可以查询服务器支持哪些方法或者对某些特殊资源支持哪些方法。
  7. DELETE请求服务器删除请求 URL 指定的资源
  8. PATCH用于资源的部分更新,当资源不存在时,PATCH会创建一个新的资源
  9. CONNECT通常用于SSL加密服务器的链接与非加密的HTTP代理服务器的通信。

常见的HTTP Method深度解析 (opens new window)

# http和https的区别?

  • HTTP(Hypertext Transfer Protocol)超文本传输协议是用来在Internet上传送超文本的传送协议,它可以使浏览器更加高效,使网络传输减少。但HTTP协议采用明文传输信息,存在信息窃听、信息篡改和信息劫持的风险。
  • HTTPS(Secure Hypertext Transfer Protocol) 安全超文本传输协议是一个安全的通信通道,它基于HTTP开发,用于在客户计算机和服务器之间交换信息。HTTPS使用安全套接字层(SSL)进行信息交换,简单来说HTTPS是HTTP的安全版,是使用TLS/SSL加密的HTTP协议。

# http缓存机制

# HTTP 状态码及其含义

参考RFC 2616 (opens new window)

  • 1XX:信息状态码
    • 100 Continue:客户端应当继续发送请求。这个临时相应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。服务器必须在请求完成后向客户端发送一个最终响应
    • 101 Switching Protocols:服务器已经理解 le 客户端的请求,并将通过 Upgrade 消息头通知客户端采用不同的协议来完成这个请求。在发送完这个响应最后的空行后,服务器将会切换到 Upgrade 消息头中定义的那些协议。
  • 2XX:成功状态码
    • 200 OK:请求成功,请求所希望的响应头或数据体将随此响应返回
    • 201 Created
    • 202 Accepted
    • 203 Non-Authoritative Information
    • 204 No Content
    • 205 Reset Content
    • 206 Partial Content
  • 3XX:重定向
    • 300 Multiple Choices
    • 301 Moved Permanently
    • 302 Found
    • 303 See Other
    • 304 Not Modified
    • 305 Use Proxy
    • 306 (unused)
    • 307 Temporary Redirect
  • 4XX:客户端错误
    • 400 Bad Request:
    • 401 Unauthorized:
    • 402 Payment Required:
    • 403 Forbidden:
    • 404 Not Found:
    • 405 Method Not Allowed:
    • 406 Not Acceptable:
    • 407 Proxy Authentication Required:
    • 408 Request Timeout:
    • 409 Conflict:
    • 410 Gone:
    • 411 Length Required:
    • 412 Precondition Failed:
    • 413 Request Entity Too Large:
    • 414 Request-URI Too Long:
    • 415 Unsupported Media Type:
    • 416 Requested Range Not Satisfiable:
    • 417 Expectation Failed:
  • 5XX: 服务器错误
    • 500 Internal Server Error:
    • 501 Not Implemented:
    • 502 Bad Gateway:
    • 503 Service Unavailable:
    • 504 Gateway Timeout:
    • 505 HTTP Version Not Supported:

# HTTP request 报文结构是怎样的

rfc2616 (opens new window)中进行了定义:

  1. 首行是Request-Line包括:请求方法请求 URI协议版本CRLF
  2. 首行之后是若干行请求头,包括general-headerrequest-header或者entity-header,每个一行以 CRLF 结束
  3. 请求头和消息实体之间有一个CRLF 分隔
  4. 根据实际请求需要可能包含一个消息实体 一个请求报文例子如下:
GET /Protocols/rfc2616/rfc2616-sec5.html HTTP/1.1
Host: www.w3.org
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36
Referer: https://www.google.com.hk/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: authorstyle=yes
If-None-Match: "2cc8-3e3073913b100"
If-Modified-Since: Wed, 01 Sep 2004 13:24:52 GMT

name=qiu&age=25
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# HTTP response 报文结构是怎样的

rfc2616 (opens new window)中进行了定义:

  1. 首行是状态行包括:HTTP 版本,状态码,状态描述,后面跟一个 CRLF
  2. 首行之后是若干行响应头,包括:通用头部,响应头部,实体头部
  3. 响应头部和响应实体之间用一个 CRLF 空行分隔
  4. 最后是一个可能的消息实体 响应报文例子如下:
HTTP/1.1 200 OK
Date: Tue, 08 Jul 2014 05:28:43 GMT
Server: Apache/2
Last-Modified: Wed, 01 Sep 2004 13:24:52 GMT
ETag: "40d7-3e3073913b100"
Accept-Ranges: bytes
Content-Length: 16599
Cache-Control: max-age=21600
Expires: Tue, 08 Jul 2014 11:28:43 GMT
P3P: policyref="http://www.w3.org/2001/05/P3P/p3p.xml"
Content-Type: text/html; charset=iso-8859-1

{"name": "qiu", "age": 25}
1
2
3
4
5
6
7
8
9
10
11
12
13

# TCP三次握手四次挥手

关于 TCP/IP,必知必会的十个问题 (opens new window)

# XSS和CSRF

# web综合问题

# 高并发

# 前端需要注意哪些 SEO

  1. 合理的 title、description、keywords:搜索对着三项的权重逐个减小,title 值强调重点即可,重要关键词出现不要超过 2 次,而且要靠前,不同页面 title 要有所不同;description 把页面内容高度概括,长度合适,不可过分堆砌关键词,不同页面 description 有所不同;keywords 列举出重要关键词即可
  2. 语义化的 HTML 代码,符合 W3C 规范:语义化代码让搜索引擎容易理解网页
  3. 重要内容 HTML 代码放在最前:搜索引擎抓取 HTML 顺序是从上到下,有的搜索引擎对抓取长度有限制,保证重要内容一定会被抓取
  4. 重要内容不要用 js 输出:爬虫不会执行 js 获取内容
  5. 非装饰性图片必须加 alt
  6. 提高网站速度:网站速度是搜索引擎排序的一个重要指标
  7. 少用 iframe:搜索引擎不会抓取 iframe 中的内容

# 如何进行网站性能优化

雅虎 Best Practices for Speeding Up Your Web Site (opens new window)

  • content 方面

    1. 减少 HTTP 请求:合并文件、[CSS 精灵](###css sprite 是什么,有什么优缺点)、inline Image
    2. 减少 DNS 查询:DNS 查询完成之前浏览器不能从这个主机下载任何任何文件。方法:DNS 缓存、将资源分布到恰当数量的主机名,平衡并行下载和 DNS 查询
    3. 避免重定向:多余的中间访问
    4. 使 Ajax 可缓存
    5. 非必须组件延迟加载
    6. 未来所需组件预加载
    7. 减少 DOM 元素数量
    8. 将资源放到不同的域下:浏览器同时从一个域下载资源的数目有限,增加域可以提高并行下载量
    9. 减少 iframe 数量
    10. 不要 404
  • Server 方面

    1. 使用 CDN
    2. 添加 Expires 或者 Cache-Control 响应头
    3. 对组件使用 Gzip 压缩
    4. 配置 ETag
    5. Flush Buffer Early
    6. Ajax 使用 GET 进行请求
    7. 避免空 src 的 img 标签
  • Cookie 方面

    1. 减小 cookie 大小
    2. 引入资源的域名不要包含 cookie
  • css 方面

    1. 将样式表放到页面顶部
    2. 不使用 CSS 表达式
    3. 使用不使用@import
    4. 不使用 IE 的 Filter
  • Javascript 方面

    1. 将脚本放到页面底部
    2. 将 javascript 和 css 从外部引入
    3. 压缩 javascript 和 css
    4. 删除不需要的脚本
    5. 减少 DOM 访问
    6. 合理设计事件监听器
  • 图片方面

    1. 优化图片:根据实际颜色需要选择色深、压缩
    2. 优化 css 精灵
    3. 不要在 HTML 中拉伸图片
    4. 保证 favicon.ico 小并且可缓存
  • 移动方面

    1. 保证组件小于 25k
    2. Pack Components into a Multipart Document

# 从浏览器地址栏输入 url 到显示页面的步骤(以 HTTP 为例)

  1. 在浏览器地址栏输入 URL
  2. 浏览器查看缓存,如果请求资源在缓存中并且新鲜,跳转到转码步骤
    1. 如果资源未缓存,发起新请求
    2. 如果已缓存,检验是否足够新鲜,足够新鲜直接提供给客户端,否则与服务器进行验证。
    3. 检验新鲜通常有两个 HTTP 头进行控制ExpiresCache-Control
      • HTTP1.0 提供 Expires,值为GMT(格林威治标准时间)表示缓存新鲜日期
      • HTTP1.1 增加了 Cache-Control: max-age=,值为以秒为单位的最大新鲜时间
  3. 浏览器解析 URL获取协议,主机,端口,path
  4. 浏览器组装一个 HTTP(GET)请求报文
  5. 浏览器获取主机 ip 地址,过程如下:
    1. 浏览器缓存
    2. 本机缓存
    3. hosts 文件
    4. 路由器缓存
    5. ISP DNS 缓存
    6. DNS 递归查询(可能存在负载均衡导致每次 IP 不一样)
  6. 打开一个 socket 与目标 IP 地址,端口建立 TCP 链接三次握手如下:
    1. 客户端发送一个 TCP 的SYN=1,Seq=X的包到服务器端口
    2. 服务器发回SYN=1, ACK=X+1, Seq=Y的响应包
    3. 客户端发送ACK=Y+1, Seq=Z
  7. TCP 链接建立后发送 HTTP 请求
  8. 服务器接受请求并解析,将请求转发到服务程序,如虚拟主机使用 HTTP Host 头部判断请求的服务程序
  9. 服务器检查HTTP 请求头是否包含缓存验证信息如果验证缓存新鲜,返回304等对应状态码
  10. 处理程序读取完整请求并准备 HTTP 响应,可能需要查询数据库等操作
  11. 服务器将响应报文通过 TCP 连接发送回浏览器
  12. 浏览器接收 HTTP 响应,然后根据情况选择关闭 TCP 连接或者保留重用,关闭 TCP 连接的四次挥手如下
    1. 主动方发送Fin=1, Ack=Z, Seq= X报文
    2. 被动方发送ACK=X+1, Seq=Z报文
    3. 被动方发送Fin=1, ACK=X, Seq=Y报文
    4. 主动方发送ACK=Y, Seq=X报文
  13. 浏览器检查响应状态码:是否为 [1XX,3XX, 4XX, 5XX,这些情况处理与 2XX 不同](###HTTP 状态码及其含义)
  14. 如果资源可缓存,进行缓存
  15. 对响应进行解码(例如 gzip 压缩)
  16. 根据资源类型决定如何处理(假设资源为 HTML 文档)
  17. 解析 HTML 文档,构件 DOM 树,下载资源,构造 CSSOM 树,执行 js 脚本,这些操作没有严格的先后顺序,以下分别解释
  18. 构建 DOM 树
    1. Tokenizing:根据 HTML 规范将字符流解析为标记
    2. Lexing:词法分析将标记转换为对象并定义属性和规则
    3. DOM construction:根据 HTML 标记关系将对象组成 DOM 树
  19. 解析过程中遇到图片、样式表、js 文件,启动下载
  20. 构建CSSOM 树
    1. Tokenizing:字符流转换为标记流
    2. Node:根据标记创建节点
    3. CSSOM:节点创建 CSSOM 树
  21. 根据 DOM 树和 CSSOM 树构建渲染树 (opens new window):
    1. 从 DOM 树的根节点遍历所有可见节点,不可见节点包括:1)script,meta这样本身不可见的标签。2)被 css 隐藏的节点,如display: none
    2. 对每一个可见节点,找到恰当的 CSSOM 规则并应用
    3. 发布可视节点的内容和计算样式
  22. js 解析如下
    1. 浏览器创建 Document 对象并解析 HTML,将解析到的元素和文本节点添加到文档中,此时document.readystate 为 loading
    2. HTML 解析器遇到没有 async 和 defer 的 script 时,将他们添加到文档中,然后执行行内或外部脚本。这些脚本会同步执行,并且在脚本下载和执行时解析器会暂停。这样就可以用 document.write()把文本插入到输入流中。同步脚本经常简单定义函数和注册事件处理程序,他们可以遍历和操作 script 和他们之前的文档内容
    3. 当解析器遇到设置了async属性的 script 时,开始下载脚本并继续解析文档。脚本会在它下载完成后尽快执行,但是解析器不会停下来等它下载。异步脚本禁止使用 document.write(),它们可以访问自己 script 和之前的文档元素
    4. 当文档完成解析,document.readState 变成 interactive
    5. 所有defer脚本会按照在文档出现的顺序执行,延迟脚本能访问完整文档树,禁止使用 document.write()
    6. 浏览器在 Document 对象上触发 DOMContentLoaded 事件
    7. 此时文档完全解析完成,浏览器可能还在等待如图片等内容加载,等这些内容完成载入并且所有异步脚本完成载入和执行,document.readState 变为 complete,window 触发 load 事件
  23. 显示页面(HTML 解析过程中会逐步显示页面)

HTTP访问过程

# web 开发中会话跟踪的方法有哪些

  1. cookie
  2. session
  3. url 重写
  4. 隐藏 input
  5. ip 地址

# 什么是渐进增强

渐进增强是指在 web 设计时强调可访问性、语义化 HTML 标签、外部样式表和脚本。保证所有人都能访问页面的基本内容和功能同时为高级浏览器和高带宽用户提供更好的用户体验。核心原则如下:

  • 所有浏览器都必须能访问基本内容
  • 所有浏览器都必须能使用基本功能
  • 所有内容都包含在语义化标签中
  • 通过外部 CSS 提供增强的布局
  • 通过非侵入式、外部 javascript 提供增强功能
  • end-user web browser preferences are respected

# Vue相关

目前看这个视频中vue源码部分理解原理: https://www.bilibili.com/video/av24099073?p=62,带着面试题去看,看中匹配答案相关代码 30 道 Vue 面试题,内含详细讲解(涵盖入门到精通,自测 Vue 掌握程度) (opens new window) 深入解析Vue依赖收集原理 (opens new window)

# Vue的双向数据绑定原理

Vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的 settergetter ,在数据变动时发布消息给订阅者,触发相应的监听回调。
具体步骤:

  1. 对需要 Observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 settergetter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化;
  2. Compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图;
  3. Watcher 订阅者是 ObserverCompile 之间通信的桥梁,主要做的事情是:
    • 在自身实例化时往属性订阅器(dep)里面添加自己;
    • 自身必须有一个 update() 方法;
    • 待属性变动,dep.notice() 通知时,能调用自身的 update() 方法,并触发 Compile 中绑定的回调,则功成身退。
  4. MVVM作为数据绑定的入口,整合ObserverCompileWatcher 三者,通过 Observer 来监听自己的 model 数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 ObserverCompile 之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

# 请详细说下你对Vue生命周期的理解?

Vue生命周期总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后:

  1. 创建前/后(beforeCreate/created): 在 beforeCreate 阶段,vue实例的挂载元素el还没有。
  2. 载入前/后(beforeMount/mounted):
    • beforeMount 阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message 还未替换。
    • mounted 阶段,vue实例挂载完成,data.message 成功渲染。
  3. 更新前/后(beforeUpdate/updated):当 data 变化时,会触发 beforeUpdateupdated 方法。
  4. 激活时/停用时(activated/deactivated):keep-alive 组件激活时调用/keep-alive 组件停用时调用,在服务器端渲染期间都不被调用。
  5. 销毁前/后(beforeDestroy/destroyed):在执行 destroyed 方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。
  6. 捕获到错误(errorCaptured):当捕获一个来自子孙组件的错误时被调用。

# Vue组件通信的方式

Vue组件通信全面总结 (opens new window)

  1. props 和 $emit()
  2. EventBus: 兄弟组件引入同一个Vue实例,利用Vue自带的 $on() 和 $emit()
  3. $root: 访问根实例
  4. $parent: 访问父组件实例
  5. $refs: 为子组件提供 ref 属性后,可通过$refs访问到子组件实例
  6. provide 和 inject (依赖注入)
    • provide 实例选项允许我们指定我们想要提供给后代组件的数据/方法
    • inject 实例选项接收指定的我们想要添加在这个实例上的 property
provide: function () {
  return {
    getMap: this.getMap
  }
}
1
2
3
4
5
inject: ['getMap']
1

# 说出至少4种Vue当中的指令和它的用法

官方答案:指令 (opens new window)

# v-model是什么?怎么使用?Vue中的标签怎么绑定事件?

v-model用于在表单控件或者组件上创建双向绑定;
使用方法:

<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
1
2

Vue中的标签使用v-on绑定事件。

# v-show,v-if的区别

  • v-show根据表达式之真假值,切换元素的display CSS 属性。在DOM中真实存在。
  • v-if根据表达式的值的真假条件渲染元素。在切换时元素及它的数据绑定/组件被销毁并重建。

# v-for 和 v-if 为什么不推荐在同一元素上使用

  1. v-for 的优先级高于 v-if,所以不管v-if表达式的值是否为真都会渲染整个列表,增加了不必要开销
  2. 渲染和逻辑处理更加耦合,可维护性 (对逻辑的更改和扩展) 降低

# Vue应用如何seo?

  1. 如果只有个别页面要求 SEO ,则可预渲染(在构建时简单地生成针对特定路由的静态 HTML 文件。优点是设置预渲染更简单,并可以将你的前端作为一个完全静态的站点),使用prerender-spa-plugin;
  2. 愿意折腾:Vue SSR
  • 优点:更好的SEO;首屏加载更快
  • 缺点:有开发条件限制,如只支持beforeCreate和Created钩子,只能运行在Node.js环境;更多的服务器负载
  1. 不愿意折腾:Nuxt.js。

# Vuex是什么,怎么使用,哪种功能场景使用它?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。在main.js引入store,注入。
Vuex开始使用 (opens new window)
使用场景:单页应用中、组件之间的状态、音乐播放、登录状态、加入购物车

# Vue和React的区别

vue和React的区别之我见 (opens new window)

# axios是什么?怎么使用?描述使用它实现登录功能的流程

axios是基于promise用于浏览器和node.js的http客户端。
一个axios的简单教程 (opens new window)

# 笔试题

# css部分

四、 写出以下 %分别有多少 px

<div class="box">
    <div class="box-item"></div>
</div>
<style>
    .box {
        position: relative;
        width: 1000px;
        height: 500px;
    }
    .box-item {
        position: absolute;
        top: 50%;
        bottom : 50%; 
        left: 50%;
        right: 50%;
        width: 50%;
        height: 50%;
        padding-top: 10%;
        padding-bottom: 10%;
        margin-right: 10%;
        margin-top: 10%;
    }
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

说实话,看到这道题,刚开始我是自信满满的,可算到后面自己就越来越不确定了,为什么会出这么简单的题呢?😵由此可见,自己的基本功真不扎实呀!为了展示,我加了两个背景,效果见CodePen (opens new window)

解析

  1. 首先明确box-item块会相对于box块定位,并且box块是box-item块的包含块;
  2. top, bottom, left, right, width, height, padding, margin这些属性的值为%时,计算的规则如下:
    • top, bottom, height: 基于包含元素的高度;
    • left, right, width, padding-left, padding-right, margin-left, margin-right, padding-top, padding-bottom, margin-top, margin-bottom: 基于包含元素的宽度;
  3. 最容易混淆以致出错的就是 padding-top, padding-bottom, margin-top, margin-bottom,也是本题的主要考察点:margin和padding四个方向的值为%时,都是基于包含元素的宽度计算的,一定要记住!

答案

<div class="box">
    <div class="box-item"></div>
</div>
<style>
    .box {
        position: relative;
        width: 1000px;
        height: 500px;
    }
    .box-item {
        position: absolute;
        top: 50%; /* 250px; */
        bottom : 50%; /* 250px; */
        left: 50%; /* 500px; */
        right: 50%; /* 500px; */
        width: 50%; /* 500px; */
        height: 50%; /* 250px; */
        padding-top: 10%; /* 100px; */
        padding-bottom: 10%; /* 100px; */
        margin-right: 10%; /* 100px; */
        margin-top: 10%; /* 100px; */
    }
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# js部分

# 数组去重,用ES5和ES6分别实现

  1. ES5:
function unique(arr) {
    var obj = {};
    var resArr = [];
    for (var i=0; i<arr.length-1; i++) {
        if (!obj[arr[i]]) {
            obj[arr[i]] = true;
            resArr.push(arr[i]);
        }
    }
    return resArr;
}
var arr = [1, 1, 'abc', 'abc','true', true, true, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', {}, {}];
console.log( unique(arr) ); // [ 1, "abc", "true", false, undefined, null, NaN, {} ]
1
2
3
4
5
6
7
8
9
10
11
12
13
function unique(arr) {
	var obj = {};
	return arr.filter(function(item, index, arr) {
		return obj.hasOwnProperty(typeof item + item) ? false :(obj[typeof item + item] = true)
	})
}
var arr = [1, 1, 'abc', 'abc','true', true, true, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', {}, {}];
console.log( unique(arr) ); // [ 1, "abc", "true", true, false, undefined, null, NaN, "NaN", {} ]
1
2
3
4
5
6
7
8
  1. ES6:
const arr = [1, 1, 'abc', 'abc','true', true, true, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', {}, {}];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 'abc', 'true', true, false, undefined, null, NaN, 'NaN', {}, {}],有两个{}是因为两个对象总是不相等
1
2
3

# 请写出以下代码运行结果

function Foo() {
    getName = function () { alert (1); };
    return this;
}
Foo.getName = function () { alert (2); };
Foo.prototype.getName = function () { alert (3); };
var getName = function () { alert (4); }
function getName() { alert (5); }

Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

解析: 别人的分析过程 (opens new window)
javascript的运算符优先级 (opens new window)

答案:

Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1
getName(); // 1
new Foo.getName(); // 等于执行new (Foo.getName)(); 结果为2
new Foo().getName(); // 等于执行(new Foo()).getName();  结果为3
new new Foo().getName(); // 等于执行new ((new Foo()).getName)();  结果为3
1
2
3
4
5
6
7

# 下面的代码会在console中输出什么?为什么?

var myObject = {
    foo: "bar",
    func: function () {
        var self = this;
        console.log("outer func: this.foo = " + this.foo);
        console.log("outer func: self.foo = " + self.foo);
        (function(){
            console.log("inner func: this.foo = " + this.foo);
            console.log("inner func: self.foo = " + self.foo);
        }());
    }
};
myObject.func();
1
2
3
4
5
6
7
8
9
10
11
12
13

答案:

var myObject = {
    foo: "bar",
    func: function () {
        var self = this;
        console.log("outer func: this.foo = " + this.foo); // outer func: this.foo = bar
        console.log("outer func: self.foo = " + self.foo); // outer func: self.foo = bar
        (function(){
            console.log("inner func: this.foo = " + this.foo); // inner func: this.foo = undefined
            console.log("inner func: self.foo = " + self.foo); // inner func: self.foo = bar
        }());
    }
};
myObject.func();
1
2
3
4
5
6
7
8
9
10
11
12
13

# 下面两个函数的返回值是什么?为什么?

function foo1() {
  return {
		bar: "hello"
	};
}

function foo2() {
	return
	{
		bar: "hello"
	};
}
1
2
3
4
5
6
7
8
9
10
11
12

解析:

  1. 自动插入分号(ASI) (opens new window)规则会影响 return 语句,JS引擎在return语句后扫描到“行终止符”(本例中为“换行符”)时,会自动补上一个分号,return语句便结束了。所以函数foo2在JS引擎看来是这样的:
 function foo2() {
     return;
     {
         bar: "hello"
     };
 }
1
2
3
4
5
6
  1. 当在函数体中执行完return语句之后,函数将会停止执行并退出。如果指定一个值,则这个值返回给函数调用者。如果省略该值,则返回undefined;

答案:
函数foo1返回:{bar: "hello"},函数foo2返回:undefined

# 写出如下代码的输出,为什么?

['1', '2', '3'].map(parseInt)
1

参考答案:

[1, NaN, NaN]
1

参考解析: map函数提供的回调函数接受三个参数:数组项的值、数组项的位置和数组本身 Array.prototype.map(function callback(currentValue[, index[, array]]) { // Return element for new_array }[, thisArg])

parseInt(string[, radix]),根据指定的radix,把字符串转化为整数。 接受两个参数:字符串,转换时使用的基数(即多少进制) string: 对于非字符串,会自动使用toString()转化;自动忽略字符串前的空白; radix: 有效取值为[2, 36]之间的整数。未指定时,由函数自行决定基数

在基数为 undefined,或者基数为 0 或者没有指定的情况下,JavaScript 作如下处理:

如果字符串 string 以"0x"或者"0X"开头, 则基数是16 (16进制).
如果字符串 string 以"0"开头, 基数是8(八进制)或者10(十进制),那么具体是哪个基数由实现环境决定。ECMAScript 5 规定使用10,但是并不是所有的浏览器都遵循这个规定。因此,永远都要明确给出radix参数的值。
如果字符串 string 以其它任何值开头,则基数是10 (十进制)。
1
2
3
4
5

返回值: 返回解析后的整数值。 如果被解析参数的第一个字符无法被转化成数值类型,则返回 NaN

所以这题可以看成是这样:

['1', '2', '3'].map(function (item, index) {
  return parseInt(item, index)
})
1
2
3

# 在下面的代码中,数字1-4会以什么顺序输出?为什么会这样输出?

(function() {
	console.log(1);
	setTimeout(function(){console.log(2)}, 1000);
	setTimeout(function(){console.log(3)}, 0);
	console.log(4);
})();
1
2
3
4
5
6

解析:

JavaScript 是一个单线程序的解释器,因此一定时间内只能执行一段代码。为了控制要执行的代码,就有一个 JavaScript 任务队列。这些任务会按照将它们添加到队列的顺序执行。setTimeout()的第二个参数告诉 JavaScript 再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了以后再执行。

答案:
1、4、3、2。

五、 以下哪条语句会产生运行错误:

A. var obj = ();
B. var obj = [];
C. var obj = {};
D. var obj = //;
1
2
3
4

答案: A

# 写出如下代码的打印结果

function changeObjProperty(o) {
  o.siteUrl = "http://www.baidu.com"
  o = new Object()
  o.siteUrl = "http://www.google.com"
} 
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl);
1
2
3
4
5
6
7
8

"http://www.baidu.com"

# 输出以下代码运行结果

// example 1
var a={}, b='123', c=123;  
a[b]='b';
a[c]='c';  
console.log(a[b]);

---------------------
// example 2
var a={}, b=Symbol('123'), c=Symbol('123');  
a[b]='b';
a[c]='c';  
console.log(a[b]);

---------------------
// example 3
var a={}, b={key:'123'}, c={key:'456'};  
a[b]='b';
a[c]='c';  
console.log(a[b]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

c b c

# 请写出如下代码的打印结果

function Foo() {
  Foo.a = function() {
    console.log(1)
  }
  this.a = function() {
    console.log(2)
  }
}
Foo.prototype.a = function() {
  console.log(3)
}
Foo.a = function() {
  console.log(4)
}
Foo.a();
let obj = new Foo();
obj.a();
Foo.a();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

4 2 1

# 算法部分

# 代码实现二叉搜索树以及先序、中序、后序遍历

class Node {
  constructor(key) {
    this.key = key
    this.left = null
    this.right = null
  }
}

class BinarySearchTree {
  constructor() {
    this.root = null
  }
  // 插入一个键
  insert(key) {
    if (this.root === null) {
      this.root = new Node(key)
    } else {
      this.insertNode(this.root, key)
    }
  }
  // 插入一个节点
  insertNode(node, key) {
    if (key < node.key) {
      if (node.left === null) {
        node.left = new Node(key)
      } else {
        this.insertNode(node.left, key)
      }
    } else if (key > node.right) {
      if ( node.right === null) {
        node.right = new Node(key)
      } else {
        this.insertNode(node.right, key)
      }
    }
  }
  // 先序遍历
  preOrderTraverse(cb) {
    this.preOrderTraverseNode(this.root, cb)
  }
  preOrderTraverseNode(node, cb) {
    if (node !== null) {
      cb(node.key)
      this.preOrderTraverseNode(node.left, cb)
      this.preOrderTraverseNode(node.right, cb)
    }
  }
  // 中序遍历
  inOrderTraverse(cb) {
    this.inOrderTraverseNode(this.root, cb)
  }
  inOrderTraverseNode(node, cb) {
    if (node !== null) {
      this.inOrderTraverseNode(node.left, cb)
      cb(node.key)
      this.inOrderTraverseNode(node.right, cb)
    }
  }
  // 后序遍历
  postOrderTraverse(cb) {
    this.postOrderTraverseNode(this.root, cb)
  }
  postOrderTraverseNode(node, cb) {
    if (node !== null) {
      this.postOrderTraverseNode(node.left, cb)
      this.postOrderTraverseNode(node.right, cb)
      cb(node.key)
    }
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

# 冒泡排序

冒泡排序比较所有相邻的两个项,如果第一个比第二个大,则交换它们。元素项向上移动至正确的顺序,就好像气泡升至表面一样,冒泡排序因此得名。

function bubbleSort(arr) {
  const { length } = arr
  for (let i = 0; i < length; i++) {
    for (let j = 0; j < length - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
      }
    }
  }
  return arr
}
1
2
3
4
5
6
7
8
9
10
11

时间复杂度: O(n2)
改进:

function bubbleSort(arr) {
  const { length } = arr
  for (let i = 0; i < length; i++) {
    for (let j = 0; j < length - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
      }
    }
  }
  return arr
}
1
2
3
4
5
6
7
8
9
10
11

时间复杂度: O(n2)

# 选择排序

选择排序大致的思路是找到数据结构中的最小值并将其放置在第一位,接着找到第二小的值并将其放在第二位,以此类推。选择排序算法是一种原址比较排序算法。

function selectionSort(arr) {
  const { length } = arr
  let indexMin
  for (let i = 0; i < length - 1; i++) {
    indexMin = i
    for (let j = i; j < length; j++) {
      if (arr[indexMin] > arr[j]) {
        indexMin = j
      }
    }
    if (i !== indexMin) {
      [arr[i], arr[indexMin]] = [arr[indexMin], arr[i]]
    }
  }
  return arr
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

时间复杂度: O(n2)

# 插入排序

插入排序每次排一个数组项,以此方式构建最后的排序数组。首先假定第一项已经排序了,接着,它和第二项进行比较————第二项是应该待在原位还是插到第一项之前呢?从而,头两项就已正确排序,接着和第三项比较(它是该插入到第一、第二还是第三的位置呢),以此类推。

function insertionSort(arr) {
  let { length } = arr
  let temp
  for (let i = 1; i < length; i ++) {
    let j = i
    temp = arr[i]
    while (j > 0 && arr[j - 1] > temp) {
      arr[j] = arr[j - 1]
      j--
    }
    arr[j] = temp
  }
  return arr
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

排序小型数组时,此算法比选择排序和冒泡排序性能要好。

# 归并排序

归并排序是一种分而治之算法。其思想是将原始数组切分成较小的数组,直到每个小数组只有一个位置,接着将小数组归并成较大的数组,直到最后只有一个排序完毕的大数组。

function mergeSort(arr) {
  const { length } = arr
  if (length < 2) return arr
  const midIndex = Math.floor(length / 2)
  const left = mergeSort(arr.slice(0, midIndex))
  const right = mergeSort(arr.slice(midIndex))
  arr = merge(left, right)
  return arr
}
function merge (left, right) {
  let result = []
  let i = 0
  let j = 0
  while (i < left.length && j < right.length) {
    result.push(left[i] < right[j] ? left[i++] : right[j++])
  }
  result = rusult.concat(i < left.length ? left.slice(i) : right.slice(j))
  return result
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

复杂度为 O(nlog(n))

# 给出一个整数数组,请选择一种排序方法排序

快速排序 (opens new window)

Last Updated: 11/9/2022, 6:38:52 AM