前端经典面试题(60道前端面试题包含JS、CSS、React、网络、浏览器、程序题等)

Navigator对象

  • navigator.userAgent – 返回用户代理头的字符串表示(就是包括浏览器版本信息等的字符串)

  • navigator.cookieEnabled – 返回浏览器是否支持(启用)cookie

6、Cookie、sessionStorage、localStorage的区别

参考答案

共同点:都是保存在浏览器端,并且是同源的

  • Cookie:cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下,存储的大小很小只有4K左右。(key:可以在浏览器和服务器端来回传递,存储容量小,只有大约4K左右)

  • sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持,localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。(key:本身就是一个回话过程,关闭浏览器后消失,session为一个回话,当页面不同即使是同一页面打开两次,也被视为同一次回话)

  • localStorage:localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。(key:同源窗口都会共享,并且不会失效,不管窗口或者浏览器关闭与否都会始终生效)

补充说明一下cookie的作用:

  • 保存用户登录状态。例如将用户id存储于一个cookie内,这样当用户下次访问该页面时就不需要重新登录了,现在很多论坛和社区都提供这样的功能。cookie还可以设置过期时间,当超过时间期限后,cookie就会自动消失。因此,系统往往可以提示用户保持登录状态的时间:常见选项有一个月、三个 月、一年等。

  • 跟踪用户行为。例如一个天气预报网站,能够根据用户选择的地区显示当地的天气情况。如果每次都需要选择所在地是烦琐的,当利用了 cookie后就会显得很人性化了,系统能够记住上一次访问的地区,当下次再打开该页面时,它就会自动显示上次用户所在地区的天气情况。因为一切都是在后 台完成,所以这样的页面就像为某个用户所定制的一样,使用起来非常方便

  • 定制页面。如果网站提供了换肤或更换布局的功能,那么可以使用cookie来记录用户的选项,例如:背景色、分辨率等。当用户下次访问时,仍然可以保存上一次访问的界面风格。

7、Cookie如何防范XSS攻击

参考答案

XSS(跨站脚本攻击)是指攻击者在返回的HTML中嵌入javascript脚本,为了减轻这些攻击,需要在HTTP头部配上,set-cookie:

  • httponly-这个属性可以防止XSS,它会禁止javascript脚本来访问cookie。

  • secure - 这个属性告诉浏览器仅在请求为https的时候发送cookie。

结果应该是这样的:Set-Cookie=…

8、浏览器和 Node 事件循环的区别?

参考答案

其中一个主要的区别在于浏览器的event loop 和nodejs的event loop 在处理异步事件的顺序是不同的,nodejs中有micro event;其中Promise属于micro event 该异步事件的处理顺序就和浏览器不同.nodejs V11.0以上 这两者之间的顺序就相同了.

function test () {

console.log(‘start’)

setTimeout(() => {

console.log(‘children2’)

Promise.resolve().then(() => {console.log(‘children2-1’)})

}, 0)

setTimeout(() => {

console.log(‘children3’)

Promise.resolve().then(() => {console.log(‘children3-1’)})

}, 0)

Promise.resolve().then(() => {console.log(‘children1’)})

console.log(‘end’)

}

test()

// 以上代码在node11以下版本的执行结果(先执行所有的宏任务,再执行微任务)

// start

// end

// children1

// children2

// children3

// children2-1

// children3-1

// 以上代码在node11及浏览器的执行结果(顺序执行宏任务和微任务)

// start

// end

// children1

// children2

// children2-1

// children3

// children3-1

9、简述HTTPS中间人攻击

参考答案

https协议由 http + ssl 协议构成,具体的链接过程可参考SSL或TLS握手的概述

中间人攻击过程如下:

  1. 服务器向客户端发送公钥。

  2. 攻击者截获公钥,保留在自己手上。

  3. 然后攻击者自己生成一个【伪造的】公钥,发给客户端。

  4. 客户端收到伪造的公钥后,生成加密hash值发给服务器。

  5. 攻击者获得加密hash值,用自己的私钥解密获得真秘钥。

  6. 同时生成假的加密hash值,发给服务器。

  7. 服务器用私钥解密获得假秘钥。

  8. 服务器用加秘钥加密传输信息

防范方法:

  1. 服务端在发送浏览器的公钥中加入CA证书,浏览器可以验证CA证书的有效性

10、说几条web前端优化策略

参考答案

(1). 减少HTTP请求数

这条策略基本上所有前端人都知道,而且也是最重要最有效的。都说要减少HTTP请求,那请求多了到底会怎么样呢?首先,每个请求都是有成本的,既包 含时间成本也包含资源成本。一个完整的请求都需要经过DNS寻址、与服务器建立连接、发送数据、等待服务器响应、接收数据这样一个“漫长”而复杂的过程。时间成本就是用户需要看到或者“感受”到这个资源是必须要等待这个过程结束的,资源上由于每个请求都需要携带数据,因此每个请求都需要占用带宽。

另外,由于浏览器进行并发请求的请求数是有上限的,因此请求数多了以后,浏览器需要分批进行请求,因此会增加用户的等待时间,会给 用户造成站点速度慢这样一个印象,即使可能用户能看到的第一屏的资源都已经请求完了,但是浏览器的进度条会一直存在。减少HTTP请求数的主要途径包括:

(2). 从设计实现层面简化页面

如果你的页面像百度首页一样简单,那么接下来的规则基本上都用不着了。保持页面简洁、减少资源的使用时最直接的。如果不是这样,你的页面需要华丽的皮肤,则继续阅读下面的内容。

(3). 合理设置HTTP缓存

缓存的力量是强大的,恰当的缓存设置可以大大的减少HTTP请求。以有啊首页为例,当浏览器没有缓存的时候访问一共会发出78个请求,共600多K 数据(如图1.1),而当第二次访问即浏览器已缓存之后访问则仅有10个请求,共20多K数据(如图1.2)。(这里需要说明的是,如果直接F5刷新页面 的话效果是不一样的,这种情况下请求数还是一样,不过被缓存资源的请求服务器是304响应,只有Header没有Body,可以节省带宽)

怎样才算合理设置?原则很简单,能缓存越多越好,能缓存越久越好。例如,很少变化的图片资源可以直接通过HTTP Header中的Expires设置一个很长的过期头;变化不频繁而又可能会变的资源可以使用Last-Modifed来做请求验证。尽可能的让资源能够 在缓存中待得更久。

(4). 资源合并与压缩

如果可以的话,尽可能的将外部的脚本、样式进行合并,多个合为一个。另外,CSS、Javascript、Image都可以用相应的工具进行压缩,压缩后往往能省下不少空间。

(5). CSS Sprites

合并CSS图片,减少请求数的又一个好办法。

(6). Inline Images

使用data: URL scheme的方式将图片嵌入到页面或CSS中,如果不考虑资源管理上的问题的话,不失为一个好办法。如果是嵌入页面的话换来的是增大了页面的体积,而且无法利用浏览器缓存。使用在CSS中的图片则更为理想一些。

(7). Lazy Load Images

这条策略实际上并不一定能减少HTTP请求数,但是却能在某些条件下或者页面刚加载时减少HTTP请求数。对于图片而言,在页面刚加载的时候可以只 加载第一屏,当用户继续往后滚屏的时候才加载后续的图片。这样一来,假如用户只对第一屏的内容感兴趣时,那剩余的图片请求就都节省了。有啊首页曾经的做法 是在加载的时候把第一屏之后的图片地址缓存在Textarea标签中,待用户往下滚屏的时候才“惰性”加载。

11、你了解的浏览器的重绘和回流导致的性能问题

参考答案

重绘(Repaint)和回流(Reflow)

重绘和回流是渲染步骤中的一小节,但是这两个步骤对于性能影响很大。

  • 重绘是当节点需要更改外观而不会影响布局的,比如改变 color就叫称为重绘

  • 回流是布局或者几何属性需要改变就称为回流。

回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变深层次的节点很可能导致父节点的一系列回流。

所以以下几个动作可能会导致性能问题:

  • 改变 window 大小

  • 改变字体

  • 添加或删除样式

  • 文字改变

  • 定位或者浮动

  • 盒模型

很多人不知道的是,重绘和回流其实和 Event loop 有关。

  1. 当 Event loop 执行完 Microtasks 后,会判断 document 是否需要更新。因为浏览器是 60Hz 的刷新率,每 16ms 才会更新一次。

  2. 然后判断是否有 resize或者 scroll,有的话会去触发事件,所以 resize和 scroll事件也是至少 16ms 才会触发一次,并且自带节流功能。

  3. 判断是否触发了 media query

  4. 更新动画并且发送事件

  5. 判断是否有全屏操作事件

  6. 执行 requestAnimationFrame回调

  7. 执行 InterpObserver回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好

  8. 更新界面

  9. 以上就是一帧中可能会做的事情。如果在一帧中有空闲时间,就会去执行 requestIdleCallback回调。

减少重绘和回流

  • 使用 translate 替代 top
  • 使用 visibility替换 display: none,因为前者只会引起重绘,后者会引发回流(改变了布局)

把 DOM 离线后修改,比如:先把 DOM 给 display:none(有一次 Reflow),然后你修改100次,然后再把它显示出来

不要把 DOM 结点的属性值放在一个循环里当成循环里的变量

for(let i = 0; i < 1000; i++) {

// 获取 offsetTop 会导致回流,因为需要去获取正确的值

console.log(document.querySelector(‘.test’).style.offsetTop)

}

  • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局

  • 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame

  • CSS 选择符从右往左匹配查找,避免 DOM 深度过深

  • 将频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于 video标签,浏览器会自动将该节点变为图层。

react、Vue


1、写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?

参考答案

vue和react都是采用diff算法来对比新旧虚拟节点,从而更新节点。在vue的diff函数中(建议先了解一下diff算法过程)。在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点(这里对应的是一个key => index 的map映射)。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射,另一种是遍历查找。相比而言。map映射的速度更快。vue部分源码如下:

// vue项目  src/core/vdom/patch.js  -488行

// 以下是为了阅读性进行格式化后的代码

// oldCh 是一个旧虚拟节点数组

if (isUndef(oldKeyToIdx)) {

oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)

}

if(isDef(newStartVnode.key)) {

// map 方式获取

idxInOld = oldKeyToIdx[newStartVnode.key]

} else {

// 遍历方式获取

idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)

}

创建map函数

function createKeyToOldIdx (children, beginIdx, endIdx) {

let i, key

const map = {}

for (i = beginIdx; i <= endIdx; ++i) {

key = children[i].key

if (isDef(key)) map[key] = i

}

return map

}

遍历寻找

// sameVnode 是对比新旧节点是否相同的函数

function findIdxInOld (node, oldCh, start, end) {

for (let i = start; i < end; i++) {

const c = oldCh[i]

if (isDef© && sameVnode(node, c)) return i

}

}

2、React 中 setState 什么时候是同步的,什么时候是异步的?

参考答案

在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用。

**原因:**在React的setState函数实现中,会根据一个变量isBatchingUpdates判断是直接更新this.state还是放到队列中回头再说,而isBatchingUpdates默认是false,也就表示setState会同步更新this.state,但是,有一个函数batchedUpdates,这个函数会把isBatchingUpdates修改为true,而当React在调用事件处理函数之前就会调用这个batchedUpdates,造成的后果,就是由React控制的事件处理过程setState不会同步更新this.state

3、下面输出什么

class Example extends React.Component {

constructor() {

super();

this.state = {

val: 0

};

}

componentDidMount() {

this.setState({val: this.state.val + 1});

console.log(this.state.val);    // 第 1 次 log

this.setState({val: this.state.val + 1});

console.log(this.state.val);    // 第 2 次 log

setTimeout(() => {

this.setState({val: this.state.val + 1});

console.log(this.state.val);  // 第 3 次 log

this.setState({val: this.state.val + 1});

console.log(this.state.val);  // 第 4 次 log

}, 0);

}

render() {

return null;

}

};

1、第一次和第二次都是在 react 自身生命周期内,触发时 isBatchingUpdates 为 true,所以并不会直接执行更新 state,而是加入了 dirtyComponents,所以打印时获取的都是更新前的状态 0。

2、两次 setState 时,获取到 this.state.val 都是 0,所以执行时都是将 0 设置成 1,在 react 内部会被合并掉,只执行一次。设置完成后 state.val 值为 1。

3、setTimeout 中的代码,触发时 isBatchingUpdates 为 false,所以能够直接进行更新,所以连着输出 2,3。

输出: 0 0 2 3

4、为什么虚拟dom会提高性能?

参考答案

虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。

具体实现步骤如下:

用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中

当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异

把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。

css


1、分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景

参考答案

结构:display:none: 会让元素完全从渲染树中消失,渲染的时候不占据任何空间, 不能点击, visibility: hidden:不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,不能点击 opacity: 0: 不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,可以点击

继承:display: none:是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示。visibility: hidden:是继承属性,子孙节点消失由于继承了hidden,通过设置visibility: visible;可以让子孙节点显式。

性能:displaynone : 修改元素会造成文档回流,读屏器不会读取display: none元素内容,性能消耗较大 visibility:hidden: 修改元素只会造成本元素的重绘,性能消耗较少读屏器读取visibility: hidden元素内容 opacity: 0 :修改元素会造成重绘,性能消耗较少

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

2、清除浮动的方式有哪些?比较好的是哪一种?

参考答案

常用的一般为三种.clearfixclear:both,overflow:hidden;

比较好是 .clearfix,伪元素万金油版本,后两者有局限性.

.clearfix:after {

visibility: hidden;

display: block;

font-size: 0;

content: " ";

clear: both;

height: 0;

}

.clearfix:before, .clearfix:after {

content:“”;

display:table;

}

.clearfix:after{

clear:both;

overflow:hidden;

}

.clearfix{

zoom:1;

}

clear:both:若是用在同一个容器内相邻元素上,那是贼好的,有时候在容器外就有些问题了, 比如相邻容器的包裹层元素塌陷

overflow:hidden:这种若是用在同个容器内,可以形成 BFC避免浮动造成的元素塌陷

4、css sprite 是什么,有什么优缺点

参考答案

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

优点:

  1. 减少 HTTP 请求数,极大地提高页面加载速度

  2. 增加图片信息重复度,提高压缩比,减少图片大小

  3. 更换风格方便,只需在一张或几张图片上修改颜色或样式即可实现

缺点:

  1. 图片合并麻烦

  2. 维护麻烦,修改一个图片可能需要重新布局整个图片,样式

5、link@import的区别

参考答案

  1. link是 HTML 方式, @import是 CSS 方式

  2. link最大限度支持并行下载,@import过多嵌套导致串行下载,出现FOUC

  3. link可以通过rel="alternate stylesheet"指定候选样式

  4. 浏览器对link支持早于@import,可以使用@import对老浏览器隐藏样式

  5. @import必须在样式规则之前,可以在 css 文件中引用其他文件

  6. 总体来说:link 优于@import

6、display: block;display: inline;的区别

参考答案

block元素特点:

1.处于常规流中时,如果width没有设置,会自动填充满父容器 2.可以应用margin/padding 3.在没有设置高度的情况下会扩展高度以包含常规流中的子元素 4.处于常规流中时布局时在前后元素位置之间(独占一个水平空间) 5.忽略vertical-align

inline元素特点

1.水平方向上根据direction依次布局

2.不会在元素前后进行换行

3.受white-space控制

4.margin/padding在竖直方向上无效,水平方向上有效

5.width/height属性对非替换行内元素无效,宽度由元素内容决定

6.非替换行内元素的行框高由line-height确定,替换行内元素的行框高由height,margin,padding,border决定 7.浮动或绝对定位时会转换为block8.vertical-align属性生效

7、容器包含若干浮动元素时如何清理浮动

参考答案

  1. 容器元素闭合标签前添加额外元素并设置clear: both

  2. 父元素触发块级格式化上下文(见块级可视化上下文部分)

  3. 设置容器元素伪元素进行清理推荐的清理浮动方法

/**

* 在标准浏览器下使用

* 1 content内容为空格用于修复opera下文档中出现

*   contenteditable属性时在清理浮动元素上下的空白

  • 2 使用display使用table而不是block:可以防止容器和

*   子元素top-margin折叠,这样能使清理效果与BFC,IE6/7

*   zoom: 1;一致

**/

.clearfix:before,

.clearfix:after {

content: " "; /* 1 */

display: table; /* 2 */

}

.clearfix:after {

clear: both;

}

/**

* IE 6/7下使用

* 通过触发hasLayout实现包含浮动

**/

.clearfix {

*zoom: 1;

}

8、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. 适合图标、背景、按钮

9、display,float,position 的关系

参考答案

  1. 如果display为 none,那么 position 和 float 都不起作用,这种情况下元素不产生框

  2. 否则,如果 position 值为 absolute 或者 fixed,框就是绝对定位的,float 的计算值为 none,display 根据下面的表格进行调整。

  3. 否则,如果 float 不是 none,框是浮动的,display 根据下表进行调整

  4. 否则,如果元素是根元素,display 根据下表进行调整

  5. 其他情况下 display 的值为指定值 总结起来:绝对定位、浮动、根元素都需要调整display

10、如何水平居中一个元素

参考答案

  • 如果需要居中的元素为常规流中 inline 元素,为父元素设置text-align: center;即可实现

  • 如果需要居中的元素为常规流中 block 元素,1)为元素设置宽度,2)设置左右 margin 为 auto。3)IE6 下需在父元素上设置text-align: center;,再给子元素恢复需要的值

aaaaaa aaaaaa a a a a a a a a

  • 如果需要居中的元素为浮动元素,1)为元素设置宽度,2)position: relative;,3)浮动方向偏移量(left 或者 right)设置为 50%,4)浮动方向上的 margin 设置为元素宽度一半乘以-1

aaaaaa aaaaaa a a a a a a a a

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

aaaaaa aaaaaa a a a a a a a a

  • 如果需要居中的元素为绝对定位元素,1)为元素设置宽度,2)设置左右偏移量都为 0,3)设置左右外边距都为 auto

aaaaaa aaaaaa a a a a a a a a

JavaScript


1、JS有几种数据类型,其中基本数据类型有哪些?

参考答案

七种数据类型

  • Boolean

  • Null

  • Undefined

  • Number

  • String

  • Symbol (ECMAScript 6 新定义)

  • Object

(ES6之前)其中5种为基本类型:string,number,boolean,null,undefined,

ES6出来的Symbol也是原始数据类型 ,表示独一无二的值

Object为引用类型(范围挺大),也包括数组、函数,

2、Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?

参考答案

const promise = new Promise((resolve, reject) => {

console.log(1)

resolve()

console.log(2)

})

promise.then(() => {

console.log(3)

})

console.log(4)

输出结果是:

1

2

4

3

promise构造函数是同步执行的,then方法是异步执行的

Promise new的时候会立即执行里面的代码 then是微任务 会在本次任务执行完的时候执行 setTimeout是宏任务 会在下次任务执行的时候执行

3、JS的四种设计模式

参考答案

工厂模式

简单的工厂模式可以理解为解决多个相似的问题;

function CreatePerson(name,age,sex) {

var obj = new Object();

obj.name = name;

obj.age = age;

obj.sex = sex;

obj.sayName = function(){

return this.name;

}

return obj;

}

var p1 = new CreatePerson(“longen”,‘28’,‘男’);

var p2 = new CreatePerson(“tugenhua”,‘27’,‘女’);

console.log(p1.name); // longen

console.log(p1.age);  // 28

console.log(p1.sex);  // 男

console.log(p1.sayName()); // longen

console.log(p2.name);  // tugenhua

console.log(p2.age);   // 27

console.log(p2.sex);   // 女

console.log(p2.sayName()); // tugenhua

单例模式

只能被实例化(构造函数给实例添加属性与方法)一次

// 单体模式

var Singleton = function(name){

this.name = name;

};

Singleton.prototype.getName = function(){

return this.name;

}

// 获取实例对象

var getInstance = (function() {

var instance = null;

return function(name) {

if(!instance) {//相当于一个一次性阀门,只能实例化一次

instance = new Singleton(name);

}

return instance;

}

})();

// 测试单体模式的实例,所以a===b

var a = getInstance(“aa”);

var b = getInstance(“bb”);

沙箱模式

将一些函数放到自执行函数里面,但要用闭包暴露接口,用变量接收暴露的接口,再调用里面的值,否则无法使用里面的值

let sandboxModel=(function(){

function sayName(){};

function sayAge(){};

return{

sayName:sayName,

sayAge:sayAge

}

})()

发布者订阅模式

就例如如我们关注了某一个公众号,然后他对应的有新的消息就会给你推送,

//发布者与订阅模式

var shoeObj = {}; // 定义发布者

shoeObj.list = []; // 缓存列表 存放订阅者回调函数

// 增加订阅者

shoeObj.listen = function(fn) {

shoeObj.list.push(fn); // 订阅消息添加到缓存列表

}

// 发布消息

shoeObj.trigger = function() {

for (var i = 0, fn; fn = this.list[i++]😉 {

fn.apply(this, arguments);//第一个参数只是改变fn的this,

}

}

// 小红订阅如下消息

shoeObj.listen(function(color, size) {

console.log(“颜色是:” + color);

console.log(“尺码是:” + size);

});

// 小花订阅如下消息

shoeObj.listen(function(color, size) {

console.log(“再次打印颜色是:” + color);

console.log(“再次打印尺码是:” + size);

});

shoeObj.trigger(“红色”, 40);

shoeObj.trigger(“黑色”, 42);

代码实现逻辑是用数组存贮订阅者, 发布者回调函数里面通知的方式是遍历订阅者数组,并将发布者内容传入订阅者数组

4、列举出集中创建实例的方法

参考答案

1.字面量

let obj={‘name’:‘张三’}

2.Object构造函数创建

let Obj=new Object()

Obj.name=‘张三’

3.使用工厂模式创建对象

function createPerson(name){

var o = new Object();

o.name = name;

};

return o;

}

var person1 = createPerson(‘张三’);

4.使用构造函数创建对象

function Person(name){

this.name = name;

}

var person1 = new Person(‘张三’);

5、简述一下前端事件流

参考答案

HTML中与javascript交互是通过事件驱动来实现的,例如鼠标点击事件onclick、页面的滚动事件onscroll等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。

什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM2级事件流包括下面几个阶段。

  • 事件捕获阶段

  • 处于目标阶段

  • 事件冒泡阶段

addEventListeneraddEventListener是DOM2 级事件新增的指定事件处理程序的操作,这个方法接收3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。

IE只支持事件冒泡

6、Function._proto_(getPrototypeOf)是什么?

参考答案

获取一个对象的原型,在chrome中可以通过__proto__的形式,或者在ES6中可以通过Object.getPrototypeOf的形式。

那么Function.proto是什么么?也就是说Function由什么对象继承而来,我们来做如下判别。

Function.proto==Object.prototype //false

Function.proto==Function.prototype//true

我们发现Function的原型也是Function。

我们用图可以来明确这个关系:

7、简述一下原型 / 构造函数 / 实例

参考答案

  • 原型(prototype): 一个简单的对象,用于实现对象的 属性继承。可以简单的理解成对象的爹。在 Firefox 和 Chrome 中,每个JavaScript对象中都包含一个__proto__(非标准)的属性指向它爹(该对象的原型),可obj.__proto__进行访问。

  • 构造函数: 可以通过new来 新建一个对象的函数。

  • 实例: 通过构造函数和new创建出来的对象,便是实例。实例通过__proto__指向原型,通过constructor指向构造函数

这里来举个栗子,以Object为例,我们常用的Object便是一个构造函数,因此我们可以通过它构建实例。

// 实例

const instance = new Object()

则此时, 实例为instance构造函数为Object,我们知道,构造函数拥有一个prototype的属性指向原型,因此原型为:

// 原型

const prototype = Object.prototype

这里我们可以来看出三者的关系:

实例.proto === 原型

原型.constructor === 构造函数

构造函数.prototype === 原型

// 这条线其实是是基于原型进行获取的,可以理解成一条基于原型的映射线

// 例如:

// const o = new Object()

// o.constructor === Object   --> true

// o.proto = null;

// o.constructor === Object   --> false

实例.constructor === 构造函数

8、简述一下JS继承,并举例

参考答案

在 JS 中,继承通常指的便是 原型链继承,也就是通过指定原型,并可以通过原型链继承原型上的属性或者方法。

  • 最优化: 圣杯模式

var inherit = (function(c,p){

var F = function(){};

return function(c,p){

F.prototype = p.prototype;

c.prototype = new F();

c.uber = p.prototype;

c.prototype.constructor = c;

}

})();

  • 使用 ES6 的语法糖 class / extends

9、函数柯里化

参考答案

在函数式编程中,函数是一等公民。那么函数柯里化是怎样的呢?

函数柯里化指的是将能够接收多个参数的函数转化为接收单一参数的函数,并且返回接收余下参数且返回结果的新函数的技术。

函数柯里化的主要作用和特点就是参数复用、提前返回和延迟执行。

在一个函数中,首先填充几个参数,然后再返回一个新的函数的技术,称为函数的柯里化。通常可用于在不侵入函数的前提下,为函数 预置通用参数,供多次重复调用。

const add = function add(x) {

return function (y) {

return x + y

}

}

const add1 = add(1)

add1(2) === 3

add1(20) === 21

10、说说bind、call、apply 区别?

参考答案

call 和 apply 都是为了解决改变 this 的指向。作用都是相同的,只是传参的方式不同。

除了第一个参数外,call 可以接收一个参数列表,apply 只接受一个参数数组。

let a = {

value: 1

}

function getValue(name, age) {

console.log(name)

console.log(age)

console.log(this.value)

}

getValue.call(a, ‘yck’, ‘24’)

getValue.apply(a, [‘yck’, ‘24’])

bind和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过 bind实现柯里化。

(下面是对这三个方法的扩展介绍)

如何实现一个 bind 函数

对于实现以下几个函数,可以从几个方面思考

  • 不传入第一个参数,那么默认为 window

  • 改变了 this 指向,让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除?

Function.prototype.myBind = function (context) {

if (typeof this !== ‘function’) {

throw new TypeError(‘Error’)

}

var _this = this

var args = […arguments].slice(1)

// 返回一个函数

return function F() {

// 因为返回了一个函数,我们可以 new F(),所以需要判断

if (this instanceof F) {

return new _this(…args, …arguments)

}

return _this.apply(context, args.concat(…arguments))

}

}

如何实现一个call函数

Function.prototype.myCall = function (context) {

var context = context || window

// 给 context 添加一个属性

// getValue.call(a, ‘yck’, ‘24’) => a.fn = getValue

context.fn = this

// 将 context 后面的参数取出来

var args = […arguments].slice(1)

// getValue.call(a, ‘yck’, ‘24’) => a.fn(‘yck’, ‘24’)

var result = context.fn(…args)

// 删除 fn

delete context.fn

return result

}

如何实现一个apply函数

Function.prototype.myApply = function (context) {

var context = context || window

context.fn = this

var result

// 需要判断是否存储第二个参数

// 如果存在,就将第二个参数展开

if (arguments[1]) {

result = context.fn(…arguments[1])

} else {

result = context.fn()

}

delete context.fn

return result

}

11、箭头函数的特点

参考答案

function a() {

return () => {

return () => {

console.log(this)

}

}

}

console.log(a()()())

箭头函数其实是没有 this的,这个函数中的 this只取决于他外面的第一个不是箭头函数的函数的 this。在这个例子中,因为调用 a符合前面代码中的第一个情况,所以 this是 window。并且 this一旦绑定了上下文,就不会被任何代码改变。

程序阅读题


1、下面程序输出的结果是什么?

function sayHi() {

console.log(name);

console.log(age);

var name = “Lydia”;

let age = 21;

}

sayHi();

  • A: Lydia 和 undefined

  • B: Lydia 和 ReferenceError

  • C: ReferenceError 和 21

  • D: undefined 和 ReferenceError

参考答案

在函数中,我们首先使用var关键字声明了name变量。这意味着变量在创建阶段会被提升(JavaScript会在创建变量创建阶段为其分配内存空间),默认值为undefined,直到我们实际执行到使用该变量的行。我们还没有为name变量赋值,所以它仍然保持undefined的值。

使用let关键字(和const)声明的变量也会存在变量提升,但与var不同,初始化没有被提升。在我们声明(初始化)它们之前,它们是不可访问的。这被称为“暂时死区”。当我们在声明变量之前尝试访问变量时,JavaScript会抛出一个ReferenceError

关于let的是否存在变量提升,我们何以用下面的例子来验证:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
img

下面是我在学习HTML和CSS的时候整理的一些笔记,有兴趣的可以看下:

HTML、CSS部分截图

进阶阶段

进阶阶段,开始攻 JS,对于刚接触 JS 的初学者,确实比学习 HTML 和 CSS 有难度,但是只要肯下功夫,这部分对于你来说,也不是什么大问题。

JS 内容涉及到的知识点较多,看到网上有很多人建议你从头到尾抱着那本《JavaScript高级程序设计》学,我是不建议的,毕竟刚接触 JS 谁能看得下去,当时我也不能,也没那样做。

我这部分的学习技巧是,增加次数,减少单次看的内容。就是说,第一遍学习 JS 走马观花的看,看个大概,去找视频以及网站学习,不建议直接看书。因为看书看不下去的时候很打击你学下去的信心。

然后通过一些网站的小例子,开始动手敲代码,一定要去实践、实践、实践,这一遍是为了更好的去熟悉 JS 的语法。别只顾着来回的看知识点,眼高手低可不是个好习惯,我在这吃过亏,你懂的。

1、JavaScript 和 ES6

在这个过程你会发现,有很多 JS 知识点你并不能更好的理解为什么这么设计,以及这样设计的好处是什么,这就逼着让你去学习这单个知识点的来龙去脉,去哪学?第一,书籍,我知道你不喜欢看,我最近通过刷大厂面试题整理了一份前端核心知识笔记,比较书籍更精简,一句废话都没有,这份笔记也让我通过跳槽从8k涨成20k。

JavaScript部分截图

2、前端框架

前端框架太多了,真的学不动了,别慌,其实对于前端的三大马车,Angular、React、Vue 只要把其中一种框架学明白,底层原理实现,其他两个学起来不会很吃力,这也取决于你以后就职的公司要求你会哪一个框架了,当然,会的越多越好,但是往往每个人的时间是有限的,对于自学的学生,或者即将面试找工作的人,当然要选择一门框架深挖原理。

以 Vue 为例,我整理了如下的面试题。

Vue部分截图

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

(name);

console.log(age);

var name = “Lydia”;

let age = 21;

}

sayHi();

  • A: Lydia 和 undefined

  • B: Lydia 和 ReferenceError

  • C: ReferenceError 和 21

  • D: undefined 和 ReferenceError

参考答案

在函数中,我们首先使用var关键字声明了name变量。这意味着变量在创建阶段会被提升(JavaScript会在创建变量创建阶段为其分配内存空间),默认值为undefined,直到我们实际执行到使用该变量的行。我们还没有为name变量赋值,所以它仍然保持undefined的值。

使用let关键字(和const)声明的变量也会存在变量提升,但与var不同,初始化没有被提升。在我们声明(初始化)它们之前,它们是不可访问的。这被称为“暂时死区”。当我们在声明变量之前尝试访问变量时,JavaScript会抛出一个ReferenceError

关于let的是否存在变量提升,我们何以用下面的例子来验证:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-khNw8ned-1712941035914)]
[外链图片转存中…(img-T4HE5UV0-1712941035915)]
[外链图片转存中…(img-z9tLHaZz-1712941035915)]
[外链图片转存中…(img-s22hXZhZ-1712941035916)]
[外链图片转存中…(img-j2I7tEwO-1712941035916)]
[外链图片转存中…(img-80hoUoST-1712941035916)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-xoaog4Mx-1712941035917)]

下面是我在学习HTML和CSS的时候整理的一些笔记,有兴趣的可以看下:

HTML、CSS部分截图

进阶阶段

进阶阶段,开始攻 JS,对于刚接触 JS 的初学者,确实比学习 HTML 和 CSS 有难度,但是只要肯下功夫,这部分对于你来说,也不是什么大问题。

JS 内容涉及到的知识点较多,看到网上有很多人建议你从头到尾抱着那本《JavaScript高级程序设计》学,我是不建议的,毕竟刚接触 JS 谁能看得下去,当时我也不能,也没那样做。

我这部分的学习技巧是,增加次数,减少单次看的内容。就是说,第一遍学习 JS 走马观花的看,看个大概,去找视频以及网站学习,不建议直接看书。因为看书看不下去的时候很打击你学下去的信心。

然后通过一些网站的小例子,开始动手敲代码,一定要去实践、实践、实践,这一遍是为了更好的去熟悉 JS 的语法。别只顾着来回的看知识点,眼高手低可不是个好习惯,我在这吃过亏,你懂的。

1、JavaScript 和 ES6

在这个过程你会发现,有很多 JS 知识点你并不能更好的理解为什么这么设计,以及这样设计的好处是什么,这就逼着让你去学习这单个知识点的来龙去脉,去哪学?第一,书籍,我知道你不喜欢看,我最近通过刷大厂面试题整理了一份前端核心知识笔记,比较书籍更精简,一句废话都没有,这份笔记也让我通过跳槽从8k涨成20k。

JavaScript部分截图

2、前端框架

前端框架太多了,真的学不动了,别慌,其实对于前端的三大马车,Angular、React、Vue 只要把其中一种框架学明白,底层原理实现,其他两个学起来不会很吃力,这也取决于你以后就职的公司要求你会哪一个框架了,当然,会的越多越好,但是往往每个人的时间是有限的,对于自学的学生,或者即将面试找工作的人,当然要选择一门框架深挖原理。

以 Vue 为例,我整理了如下的面试题。

Vue部分截图

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-GtGjQwoz-1712941035917)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值