前端面试题(2022)

html

1.1 html标签的类型(head, body,!Doctype) 他们的作用是什么

参考答案:

!DOCTYPE 标签:

  • 它是指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令.

head:

  • 是所有头部元素的容器, 绝大多数头部标签的内容不会显示给读者
  • 该标签下所包含的部分可加入的标签有,,,,和

body :

  • 用于定义文档的主体, 包含了文档的所有内容
  • 该标签支持 html 的全局属性和事件属性.

1.2 h5新特性

参考答案:

  • 新增选择器 document.querySelector、document.querySelectorAll
  • 拖拽释放(Drag and drop) API
  • 媒体播放的 video 和 audio
  • 本地存储 localStorage 和 sessionStorage
  • 离线应用 manifest
  • 桌面通知 Notifications
  • 语意化标签 article、footer、header、nav、section
  • 增强表单控件 calendar、date、time、email、url、search
  • 地理位置 Geolocation
  • 多任务 webworker
  • 全双工通信协议 websocket
  • 历史管理 history
  • 跨域资源共享(CORS) Access-Control-Allow-Origin
  • 页面可见性改变事件 visibilitychange
  • 跨窗口通信 PostMessage
  • Form Data 对象
  • 绘画 canvas

H5移除的元素:

  • 纯表现的元素:basefont、big、center、font、s、strike、tt、u
  • 对可用性产生负面影响的元素:frame、frameset、noframes

1.3 伪类和伪元素

参考答案:

伪类:用于已有元素处于某种状态时为其添加对应的样式,这个状态是根据用户行为而动态变化的。

例如:当用户悬停在指定元素时,可以通过:hover来描述这个元素的状态,虽然它和一般css相似,可以为 已有元素添加样式,但是它只有处于DOM树无法描述的状态下才能为元素添加样式,所以称为伪类。

伪元素:用于创建一些不在DOM树中的元素,并为其添加样式。

例如,我们可以通过:before来在一个元素之前添加一些文本,并为这些文本添加样式,虽然用户可以看见 这些文本,但是它实际上并不在DOM文档中。

1.4 html5语义化

参考答案:

在HTML5出来之前,我们习惯于用div来表示页面的章节或者不同模块,但是div本身是没有语义的。但是现在,HTML5中加入了一些语义化标签,来更清晰的表达文档结构。

语义化优点:

  • 易于用户阅读,样式丢失的时候能让页面呈现清晰的结构。
  • 有利于SEO,搜索引擎根据标签来确定上下文和各个关键字的权重。
  • 方便屏幕阅读器解析,如盲人阅读器根据语义渲染网页
  • 有利于开发和维护,语义化更具可读性,代码更好维护,与CSS3关系更和谐。

1.5 audio 标签的api

参考答案:

audio常用属性
src :播放的音乐的url地址(火狐只支持ogg的音乐,而IE9只支持MP3格式的音乐。chrome貌似全支持)
preload:预加载(在页面被加载时进行加载或者说缓冲音频),如果使用了autoplay的话那么该属性失效。
loop:循环播放
controls: 是否显示默认控制条(控制按钮)
autoplay:自动播放

audio属性
duration: 获取媒体文件的总时长,以s为单位,如果无法获取,返回NaN
paused :如果媒体文件被暂停,那么paused属性返回true,反之则返回false
ended: 如果媒体文件播放完毕返回true
muted:用来获取或设置静音状态。值为boolean
volume:控制音量的属性值为0-1;0为音量最小,1为音量最大
startTime:返回起始播放时间
error:返回错误代码,为uull的时候为正常。否则可以通过Music.error.code来获取具体的错误代码: 1.用户终止 2.网络错误 3.解码错误 4.URL无效
currentTime:用来获取或控制当前播放的时间,单位为s。
currentSrc:以字符串形式返回正在播放或已加载的文件

常用的控制用的函数:
load() :加载音频、视频软件
play():加载并播放音频、视频文件或重新播放暂停的的音频、视频
pause():暂停出于播放状态的音频、视频文件
canPlayType(obj) :测试是否支持给定的Mini类型的文件

常用audio的事件:
loadstart:客户端开始请求数据
progress:客户端正在请求数据(或者说正在缓冲)
play:play()和autoplay播放时
pausepause():方法促发时
ended:当前播放结束
timeupdate:当前播放时间发生改变的时候。播放中常用的时间处理哦
canplaythrough:歌曲已经载入完全完成
canplay:缓冲至目前可播放状态。

js

2. js基础

2.1 let const var 相关

参考答案:

var ——ES5 变量声明方式

  1. 在变量未赋值时,变量undefined(为使用声明变量时也为undefined)
  2. 作用域——var的作用域为方法作用域;只要在方法内定义了,整个方法内的定义变量后的代码都可以使用

let——ES6变量声明方式

  1. 在变量为声明前直接使用会报错
  2. 作用域——let为块作用域——通常let比var 范围要小
  3. let禁止重复声明变量,否则会报错;var可以重复声明

const——ES6变量声明方式

1. const为常量声明方式;声明变量时必须初始化,在后面出现的代码中不能再修改该常量的值

2. const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动

2.2 js数据类型,区别

参考答案:

基本数据类型:

Number,String,Boolean,null,undefined,symbol,bigint(后两个为ES6新增)

引用数据类型:

object,function(proto Function.prototype)

object:普通对象,数组对象,正则对象,日期对象,Math数学函数对象。

两种数据存储方式:

基本数据类型是直接存储在栈中的简单数据段,占据空间小、大小固定,属于被频繁使用的数据。栈是存储基 本类型值和执行代码的空间。

引用数据类型是存储在堆内存中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆 中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。

两种数据类型的区别:

  1. 堆比栈空间大,栈比堆运行速度快。

  2. 堆内存是无序存储,可以根据引用直接获取。

  3. 基础数据类型比较稳定,而且相对来说占用的内存小。

  4. 引用数据类型大小是动态的,而且是无限的。

2.3 Object.assign的理解

参考答案:

作用:Object.assign可以实现对象的合并。

语法:Object.assign(target, ...sources)

解析

  1. Object.assign会将source里面的可枚举属性复制到target,如果和target的已有属性重名,则会覆盖。
  2. 后续的source会覆盖前面的source的同名属性。
  3. Object.assign复制的是属性值,如果属性值是一个引用类型,那么复制的其实是引用地址,就会存在引用共享的问题。

2.4 constructor的理解

参考答案:

创建的每个函数都有一个prototype(原型)对象,这个属性是一个指针,指向一个对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(继承自构造函数的prototype),指向构造函数的原型对象。注意当将构造函数的prototype设置为等于一个以对象字面量形式创建的新对象时,constructor属性不再指向该构造函数。

2.5 map 和 forEach 的区别

参考答案:

相同点:

  1. 都是循环遍历数组中的每一项

  2. 每次执行匿名函数都支持三个参数,参数分别为item(当前每一项),index(索引值),arr(原数组)

  3. 匿名函数中的this都是指向window

  4. 只能遍历数组

不同点:

  1. map()会分配内存空间存储新数组并返回,forEach()不会返回数据。

  2. forEach()允许callback更改原始数组的元素。map()返回新的数组。

2.6 for of 可以遍历哪些对象

参考答案:

for..of..: 它是es6新增的一个遍历方法,但只限于迭代器(iterator), 所以普通的对象用for..of遍历
是会报错的。

可迭代的对象:包括Array, Map, Set, String, TypedArray, arguments对象等等

2.7 js静态类型检查

参考答案:

js是动态类型语言

静态类型语言 & 动态类型语言

静态类型语言:类型检查发生在编译阶段,因此除非修复错误,否则会一直编译失败

动态类型语言:只有在程序运行了一次的时候错误才会被发现,也就是在运行时,因此即使代码中包含了会 在运行时阻止脚本正常运行的错误类型,这段代码也可以通过编译

js静态类型检查的方法

Flow是Facebook开发和发布的一个开源的静态类型检查库,它允许你逐渐地向JavaScript代码中添加类型。

TypeScript是一个会编译为JavaScript的超集(尽管它看起来几乎像一种新的静态类型语言)

使用静态类型的优势

  • 可以尽早发现bug和错误
  • 减少了复杂的错误处理
  • 将数据和行为分离
  • 减少单元测试的数量
  • 提供了领域建模(domain modeling)工具
  • 帮助我们消除了一整类bug
  • 重构时更有信心

使用静态类型的劣势

  • 代码冗长
  • 需要花时间去掌握类型

2.8 indexof

参考答案:

语法:str.indexOf(searchValue [, fromIndex])

参数:searchValue:要被查找的字符串值。

如果没有提供确切地提供字符串,[searchValue 会被强制设置为"undefined"], 然后在当前字符串中查 找这个值。

举个例子:'undefined'.indexOf()将会返回0,因为undefined在位置0处被找到,但是'undefine'.indexOf()将会返回 -1 ,因为字符串'undefined'未被找到

fromIndex:可选

数字表示开始查找的位置。可以是任意整数,默认值为0。

如果fromIndex的值小于0,或者大于str.length,那么查找分别从0和str.length开始。(译者 注:fromIndex的值小于0,等同于为空情况;fromIndex的值大于或等于str.length,那么结果 会直接返回-1。)

举个例子,'hello world'.indexOf('o', -5)返回4,因为它是从位置0处开始查找,然后o在位置4处被找到。另一方面,'hello world'.indexOf('o', 11)(或fromIndex填入任何大于11的值) 将会返回-1,因为开始查找的位置11处,已经是这个字符串的结尾了。

返回值:

查找的字符串searchValue的第一次出现的索引,如果没有找到,则返回-1。

若被查找的字符串searchValue是一个空字符串,则返回fromIndex。如果fromIndex值为空,或者fromIndex值小于被查找的字符串的长度,返回值和以下的fromIndex值一样。

如果fromIndex值大于等于字符串的长度,将会直接返回字符串的长度(str.length)

特点:

1. 严格区分大小写

2. 在使用indexOf检索数组时,用‘===’去匹配,意味着会检查数据类型

2.9 iframe有什么优点、缺点

参考答案:

优点:

  1. iframe能够原封不动的把嵌入的网页展现出来。
  2. 如果有多个网页引用iframe,那么你只需要修改iframe的内容,就可以实现调用的每一个页面内容的更改,方便快捷。
  3. 网页如果为了统一风格,头部和版本都是一样的,就可以写成一个页面,用iframe来嵌套,可以增加代码的可重用。
  4. 如果遇到加载缓慢的第三方内容如图标和广告,这些问题可以由iframe来解决。

缺点:

  1. iframe会阻塞主页面的onload事件;
  2. iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。会产生很多页面,不容易管理。
  3. iframe框架结构有时会让人感到迷惑,如果框架个数多的话,可能会出现上下、左右滚动条,会分散访问者的注意力,用户体验度差。
  4. 代码复杂,无法被一些搜索引擎索引到,这一点很关键,现在的搜索引擎爬虫还不能很好的处理iframe中的内容,所以使用iframe会不利于搜索引擎优化(SEO)。
  5. 很多的移动设备无法完全显示框架,设备兼容性差。
  6. iframe框架页面会增加服务器的http请求,对于大型网站是不可取的。

2.10 webComponents

参考答案:

Web Components 总的来说是提供一整套完善的封装机制来把 Web 组件化这个东西标准化,每个框架实现 的组件都统一标准地进行输入输出,这样可以更好推动组件的复用

包含四个部分

1. Custom Elements

2. HTML Imports

3. HTML Templates

4. Shadow DOM

Custom Elements

提供一种方式让开发者可以自定义 HTML 元素,包括特定的组成,样式和行为。支持 Web Components 标准的浏览器会提供一系列 API 给开发者用于创建自定义的元素,或者扩展现有元素。

HTML Imports

一种在 HTMLs 中引用以及复用其他的 HTML 文档的方式。这个 Import 很漂亮,可以简单理解为我们常见 的模板中的include之类的作用

HTML Templates

模板

Shadow DOM

提供一种更好地组织页面元素的方式,来为日趋复杂的页面应用提供强大支持,避免代码间的相互影响

2.11 dva的数据流流向是怎么样的

参考答案:

数据的改变发生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的,当此类行为会改变数据 的时候可以通过dispatch发起一个 action,如果是同步行为会直接通过Reducers改变State,如果是 异步行为(副作用)会先触发Effects然后流向Reducers最终改变State,所以在 dva 中,数据流向非 常清晰简明,并且思路基本跟开源社区保持一致。

19832495-cbfb205b14b77bdb.png

2.12 变量提升

参考答案:

JavaScript是单线程语言,所以执行肯定是按顺序执行。但是并不是逐行的分析和执行,而是一段一段地分析执行,会先进行编译阶段然后才是执行阶段。在编译阶段阶段,代码真正执行前的几毫秒,会检测到所有的变量和函数声明,所有这些函数和变量声明都被添加到名为Lexical Environment的JavaScript数据结构内的内存中。所以这些变量和函数能在它们真正被声明之前使用。

2.13 作用域

参考答案:

概念:作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。

ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。ES6 的到来,为我们提供了‘块级作用域’,可通过新增命令 let 和 const 来体现。

扩展:

var ——ES5 变量声明方式

  1. 在变量未赋值时,变量undefined(为使用声明变量时也为undefined)
  2. 作用域——var的作用域为方法作用域;只要在方法内定义了,整个方法内的定义变量后的代码都可以使用

let——ES6变量声明方式

  1. 在变量为声明前直接使用会报错
  2. 作用域——let为块作用域——通常let比var 范围要小
  3. let禁止重复声明变量,否则会报错;var可以重复声明

const——ES6变量声明方式

const为常量声明方式;声明变量时必须初始化,在后面出现的代码中不能再修改该常量的值

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动

2.14 javascript中arguments相关的问题

参考答案:

arguments

在js中,我们在调用有参数的函数时,当往这个调用的有参函数传参时,js会把所传的参数全部存到一个叫arguments的对象里面。它是一个类数组数据

由来

Javascrip中每个函数都会有一个Arguments对象实例arguments,引用着函数的实参。它是寄生在js函数当中的,不能显式创建,arguments对象只有函数开始时才可用

作用

有了arguments这个对象之后,我们可以不用给函数预先设定形参了,可以动态地通过arguments为函数加入参数

2.15 编码和字符集的区别

参考答案:

字符集是书写系统字母与符号的集合,而字符编码则是将字符映射为一特定的字节或字节序列,是一种规则。通常特定的字符集采用特定的编码方式(即一种字符集对应一种字符编码(例如:ASCII、IOS-8859-1、GB2312、GBK,都是即表示了字符集又表示了对应的字符编码,但Unicode不是,它采用现代的模型))

扩展:

字符:在计算机和电信技术中,一个字符是一个单位的字形、类字形单位或符号的基本信息。即一个字符可以是一个中文汉字、一个英文字母、一个阿拉伯数字、一个标点符号等。

字符集:多个字符的集合。例如GB2312是中国国家标准的简体中文字符集,GB2312收录简化汉字(6763个)及一般符号、序号、数字、拉丁字母、日文假名、希腊字母、俄文字母、汉语拼音符号、汉语注音字母,共 7445 个图形字符。

字符编码:把字符集中的字符编码为(映射)指定集合中的某一对象(例如:比特模式、自然数序列、电脉冲),以便文本在计算机中存储和通过通信网络的传递。

2.16 null 和 undefined 的区别,如何让一个属性变为null

参考答案:

undefined 表示一个变量自然的、最原始的状态值,而 null 则表示一个变量被人为的设置为空对象,而不是原始状态。所以,在实际使用过程中,为了保证变量所代表的语义,不要对一个变量显式的赋值 undefined,当需要释放一个对象时,直接赋值为 null 即可。

解析:

undefined 的字面意思就是:未定义的值 。这个值的语义是,希望表示一个变量最原始的状态,而非人为操作的结果 。 这种原始状态会在以下 4 种场景中出现:

  1. 声明了一个变量,但没有赋值

  2. 访问对象上不存在的属性

  3. 函数定义了形参,但没有传递实参

  4. 使用 void 对表达式求值

因此,undefined 一般都来自于某个表达式最原始的状态值,不是人为操作的结果。当然,你也可以手动给一个变量赋值 undefined,但这样做没有意义,因为一个变量不赋值就是 undefined 。

null 的字面意思是:空值 。这个值的语义是,希望表示 一个对象被人为的重置为空对象,而非一个变量最原始的状态 。 在内存里的表示就是,栈中的变量没有指向堆中的内存对象

image

null 有属于自己的类型 Null,而不属于Object类型,typeof 之所以会判定为 Object 类型,是因为JavaScript 数据类型在底层都是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。

2.17 数组和伪数组的区别

参考答案

  1. 定义
  • 数组是一个特殊对象,与常规对象的区别:
    • 当由新元素添加到列表中时,自动更新length属性
    • 设置length属性,可以截断数组
    • 从Array.protoype中继承了方法
    • 属性为'Array'
  • 类数组是一个拥有length属性,并且他属性为非负整数的普通对象,类数组不能直接调用数组方法。
  1. 区别
    本质:类数组是简单对象,它的原型关系与数组不同。
  2. 类数组转换为数组
  • 转换方法
    • 使用Array.from()
    • 使用Array.prototype.slice.call()
    • 使用Array.prototype.forEach()进行属性遍历并组成新的数组
  • 转换须知
    • 转换后的数组长度由length属性决定。索引不连续时转换结果是连续的,会自动补位。

2.18 手写一个发布订阅

参考答案

interface CacheProps {
  [key: string]: Array<((data?: unknown) => void)>;
}

class Observer {
  private caches: CacheProps = {}; // 事件中心
  on (eventName: string, fn: (data?: unknown) => void){ // eventName事件名-独一无二, fn订阅后执行的自定义行为
    this.caches[eventName] = this.caches[eventName] || [];
    this.caches[eventName].push(fn);
  }

  emit (eventName: string, data?: unknown) { // 发布 => 将订阅的事件进行统一执行
    if (this.caches[eventName]) {
      this.caches[eventName].forEach((fn: (data?: unknown) => void) => fn(data));
    }
  }

  off (eventName: string, fn?: (data?: unknown) => void) { // 取消订阅 => 若fn不传, 直接取消该事件所有订阅信息
    if (this.caches[eventName]) {
      const newCaches = fn ? this.caches[eventName].filter(e => e !== fn) : [];
      this.caches[eventName] = newCaches;
    }
  }

}  

2.19 介绍下 Set、Map、WeakSet 和 WeakMap 的区别?

参考答案

Set

  1. 成员不能重复;
  2. 只有键值,没有键名,有点类似数组;
  3. 可以遍历,方法有add、delete、has

WeakSet

  1. 成员都是对象(引用);
  2. 成员都是弱引用,随时可以消失(不计入垃圾回收机制)。可以用来保存 DOM 节点,不容易造成内存泄露;
  3. 不能遍历,方法有add、delete、has;

Map

  1. 本质上是键值对的集合,类似集合;
  2. 可以遍历,方法很多,可以跟各种数据格式转换;

WeakMap

  1. 只接收对象为键名(null 除外),不接受其他类型的值作为键名;
  2. 键名指向的对象,不计入垃圾回收机制;
  3. 不能遍历,方法同get、set、has、delete;

2.20 简单说说 js 中有哪几种内存泄露的情况

参考答案

  1. 意外的全局变量;
  2. 闭包;
  3. 未被清空的定时器;
  4. 未被销毁的事件监听;
  5. DOM 引用;

2.21 异步笔试题

请写出下面代码的运行结果:

// 今日头条面试题
async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}
async function async2() {
  console.log('async2')
}
console.log('script start')
setTimeout(function () {
  console.log('settimeout')
})
async1()
new Promise(function (resolve) {
  console.log('promise1')
  resolve()
}).then(function () {
  console.log('promise2')
})
console.log('script end')

//题目的本质,就是考察setTimeout、promise、async await的实现及执行顺序,以及 JS 的事件循环的相关问题。
//答案
//script start
//async1 start
//async2
//promise1
//script end
//async1 end
//promise2
//settimeout

2.22 json和xml数据的区别

参考答案

  1. 数据体积方面:xml是重量级的,json是轻量级的,传递的速度更快些。
  2. 数据传输方面:xml在传输过程中比较占带宽,json占带宽少,易于压缩。
  3. 数据交互方面:json与javascript的交互更加方便,更容易解析处理,更好的进行数据交互
  4. 数据描述方面:json对数据的描述性比xml较差
  5. xml和json都用在项目交互下,xml多用于做配置文件,json用于数据交互。

2.23 JavaScript有几种方法判断变量的类型?

参考答案

  1. 使用typeof检测当需要判断变量是否是number, string, boolean, function, undefined等类型时,可以使用typeof进行判断。

  2. 使用instanceof检测instanceof运算符与typeof运算符相似,用于识别正在处理的对象的类型。与typeof方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。

  3. 使用constructor检测constructor本来是原型对象上的属性,指向构造函数。但是根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用constructor属性的。

2.24 代码解释题

参考答案

题目:

// 写出执行结果,并解释原因
var min = Math.min();
max = Math.max();
console.log(min < max);
// 写出执行结果,并解释原因

答案
false

解析

  • 按常规的思路,这段代码应该输出 true,毕竟最小值小于最大值。但是却输出 false
  • MDN 相关文档是这样解释的
    • Math.min 的参数是 0 个或者多个,如果多个参数很容易理解,返回参数中最小的。如果没有参数,则返回 Infinity,无穷大。
    • 而 Math.max 没有传递参数时返回的是-Infinity.所以输出 false

2.25 代码解析题

参考答案

题目

var company = {
    address: 'beijing'
}
var yideng = Object.create(company);
delete yideng.address
console.log(yideng.address);
// 写出执行结果,并解释原因

答案
beijing

解析
这里的 yideng 通过 prototype 继承了 company的 address。yideng自己并没有address属性。所以delete操作符的作用是无效的。

扩展
1.delete使用原则:delete 操作符用来删除一个对象的属性。
2.delete在删除一个不可配置的属性时在严格模式和非严格模式下的区别:
(1)在严格模式中,如果属性是一个不可配置(non-configurable)属性,删除时会抛出异常;
(2)非严格模式下返回 false。
3.delete能删除隐式声明的全局变量:这个全局变量其实是global对象(window)的属性
4.delete能删除的:
(1)可配置对象的属性(2)隐式声明的全局变量 (3)用户定义的属性 (4)在ECMAScript 6中,通过 const 或 let 声明指定的 "temporal dead zone" (TDZ) 对 delete 操作符也会起作用
delete不能删除的:
(2)显式声明的全局变量 (2)内置对象的内置属性 (3)一个对象从原型继承而来的属性
5.delete删除数组元素:
(1)当你删除一个数组元素时,数组的 length 属性并不会变小,数组元素变成undefined
(2)当用 delete 操作符删除一个数组元素时,被删除的元素已经完全不属于该数组。
(3)如果你想让一个数组元素的值变为 undefined 而不是删除它,可以使用 undefined 给其赋值而不是使用 delete 操作符。此时数组元素是在数组中的
6.delete 操作符与直接释放内存(只能通过解除引用来间接释放)没有关系。

你可能感兴趣的:(前端面试题(2022))