前端面试系列(二)

前端面试系列(二)

如何理解执行上下文

每一段js代码的执行之前都会创建一个上下文环境

JavaScript执行上下文是指代码运行环境。

执行上下文有三种:

  1. 全局执行上下文
  2. 函数执行上下文
  3. eval执行上下文

如何理解作用域链

JavaScript代码执行前会创建上下文环境(包含变量、作用域链、this)

也就是从当前环境向父级一层层的查找变量的过程,这个链就是作用域链。

实例:

1
2
3
4
5
var name = 'wang'

function hello() {
console.log(name);
}

以上代码:执行 hello() 的时候,会先在hello()的作用域内查找变量 name ,如果没有找到则去hello()的父级作用域查找。

如何理解原型链

每个函数都有一个 prototype 属性,每个函数实例对象都有一个 __proto__ 属性,这个属性指向函数的 prototype

当我们访问实例对象的属性或者方法的时候,会先从自身构造函数中查找,如果找不到就通过 __proto__ 去原型中查找,这个查找过程所访问的链就是原型链。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// parent class
function Human() {
this.age = 30;
this.say = function() {
return 'hello';
}
}

// son class
function Man() {
this.name = 'Adam';
}

// son class inherits parent class via prototype chain
Man.prototype = new Human();

// create a Man() example
var lee = new Man();

// compare,return true
lee.__proto__ === Man.prototype;

// print "age",return 30
console.log(lee.age);

上面会先查找 lee 再查找 Human ,找不到再往上找。

闭包

闭包就是定义在一个函数内部的函数,这样就可以使用内部函数访问到外部函数中的局部变量。

实例:

正常情况下无法访问到函数内部的局部变量:

1
2
3
4
function f1() {
var name = 'mason';
}
console.log(name);

输出:

1
undefined

使用闭包:

1
2
3
4
5
6
7
8
function f1() {
var name = 'mason';
return function f2() {
console.log(name);
}
}
var f = f1();
f();

输出:

1
mason

总结:

由于在 JavaScript 中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,比如 f2 记住了它诞生的环境 f1 ,所以从 f2 可以得到 f1 的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。

继承有哪些方法

  1. 原型继承
  2. 构造继承
  3. 实例继承
  4. call(),apply()
  5. ES6的class extends

深拷贝、浅拷贝

javascript的数据类型总体分为基本类型和引用类型。

基本类型保存值,引用类型保存指针。

浅拷贝:共用一个引用地址,方法有直接对象复制、 Object.assign

深拷贝:创建新的内存地址,方法有:先 JSON.stringify 转化为字符串再 JSON.parse ,还有深度递归遍历。

如何判断一个对象是数组

方法:

1
Object.prototype.toString.call([])

输出:

1
"[object Array]"

数组有哪些方法

  1. push()
  2. pop()
  3. shift()
  4. unshift()
  5. concat()
  6. join()
  7. reverse()
  8. sort()
  9. map()
  10. forEach()
  11. filter()
  12. indexOf()
  13. includes()
  14. slice()
  15. splice()

slice()和splice()的区别:

slice():截取数组,参数有起始点和结束点,不改变原数组,返回新数组。

splice():删除,参数有起始点、长度、待插入的元素。

DOM常用操作

创建节点:

  1. createElement
  2. createTextNode
  3. createDocumentFragment

修改节点:

  1. appendChild
  2. insertBefore
  3. replaceChild

CSS清除浮动的方法有哪些

  • 父元素设置高度,手动撑开
  • 浮动元素结尾添加空标签,设置空标签clear: both
  • 父元素设置overflow: hidden
  • 父元素添加伪类:after和zoom

CSS选择器优先级

!important > 内联样式 > id选择器 > 类选择器/属性选择器/伪类 > 元素选择器

CSS三列布局

  • 浮动布局

左边区域 float: left; ,右边区域 float: right; ,中间区域设置 margin-leftmargin-right

1
2
3
4
5
<div class="layout">
<div class="layout_left"></div>
<div class="layout_center"></div>
<div class="layout_right"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.layout {
width: 1200px;
overflow: hidden;
}

.layout_left {
width: 20%;
float: left;
}

.layout_right {
width: 20%;
float: right;
}

.layout_center {
margin-left: 5%;
margin-right: 5%;
}
  • 定位布局

三列布局的父元素设置 position: relative; ,左边区域 position: absolute; 并同时设置 left ,右边区域 position: absolute; 并同时设置 right

中间部分设置 position: absolute; 并设置 margin-leftmargin-right

  • flex布局
1
2
3
4
5
6
7
8
9
10
11
.layout_left {
flex: 0 0 200px;
}

.layout_right {
flex: 0 0 200px;
}

.layout_center {
flex: 1;
}

谈一下flex布局

flex是弹性布局。

包含flex-container和flex-item。

常用属性:flex-direction、flex-wrap、justify-content、align-items。

水平居中 justify-content:center 水平两头居中 justify-content:space-between 垂直居中 align-items:center。

什么是盒模型

内容区、 paddingbordermargin

分为IE盒模型和w3c盒模型

box-sizing: border-box;

box-sizing: content-box

IE盒模型的 width 包含了content、 borderpadding

transition动画和animation的区别

transition用于做简单的过渡效果。

animation可以做复杂的效果。

h5自适应方案

使用 rem ,rem是根据 htmlfont-size 来设置的。

call(), apply(), bind()

作用都是改变函数的作用域。

call() , apple() 可以直接执行, bind() 不会立刻执行。

call() 单个传参, apply() 用数组传参。

观察者和发布订阅的区别

发布订阅有调度中心,

浏览器解析渲染页面的过程

  1. 浏览器解析html文档并生成DOM树。
  2. 浏览器解析CSS生成CSSOM树。
  3. DOM和CSSOM关联,生成渲染树。
  4. 根据渲染树进行布局。
  5. 根据渲染树进行绘制。
  6. 将图像信息发送给GPU,最终显示在显示器上。

谈一谈EventLoop事件循环

JavaScript本身是单线程,也就是同一时刻只能干一件事,JS任务包含了同步任务和异步任务,遇到执行函数会将其放入调用栈(先进后出)中,遇到setTimeout/setInterval等异步任务时,会把它放入到消息队列中,等主线程的任务执行完成以后,再回过头执行消息队列中的异步任务,如果异步任务中仍然有异步任务,会继续放入消息队列,以此类推,便形成了一个事件循环。

GET和POST的区别

  • GET传输大小2K-8K,POST无限制
  • 都不安全,GET通过url明文传输,POST通过body传输
  • 浏览器会记录GET请求,不会记录POST
  • GET对浏览器后退无影响,POST会再次提交
  • 浏览器收藏:GET可以,POST不可以
  • GET可缓存,POST不会
  • GET用的url编码,POST支持多种编码
  • GET产生一个TCP数据包,POST产生2个TCP数据包
  • GET主要是拉取数据,POST主要是提交数据

防抖和节流

两者目的都是希望一段时间内不要重复请求。

应用场景:搜索,滚动。

区别:防抖是规定时间内只触发一次,再次调用时间重新计算。节流是在固定时间内只触发一次。

数组去重

  • ES6 set去重
  • Object key去重
  • 两层循环遍历生成新的不重的数组
  • indexOf()
  • sort()排序后依次对比

数组排序

  • sort()排序

  • 冒泡排序

  • 选择排序

  • 插入排序

常用设计模式

  • 单例模式
  • 工厂模式
  • 观察者模式
  • 适配器模式

for…of

es2015的语法

可以用来遍历数组、类数组、map/set/字符串等等。

  1. 数组遍历
1
for (const number of [1, 2, 3]) {}
  1. 类数组遍历
1
for (const number of arguments) {}
  1. 字符串遍历
1
2
const msg = 'hello';
for (const character of msg) {}
  1. Map遍历
1
2
3
4
5
6
const map = new Map();
map.set("name", '前端未来');
map.set("author", '河畔一角');
for (const item of map) {}
// 或者
for (const [key, val] of map) {}
  1. Set遍历
1
2
const names = new Set(['Tom', 'Jack', 'Lily']);
for (let name of names) {}

前端常见攻击方式

  • XSS
  • CSRF
  • sql注入
  • html脚本注入

常见跨域方案

跨域源于浏览器的同源政策:必须同协议、同域名、同端口。

  • JSONP(js调用)
  • CORS(后台设置)
  • Nginx反向代理(运维)

网站常用优化方案

思路:

  • 减少请求次数

  • 压缩资源大小

  • 提高响应速度和加载速度

  • 优化资源加载时机

  • 优化加载方式

方法:

  • 合并、压缩、混淆前端代码
  • 服务器nginx开启gzip压缩
  • 静态资源使用CDN
  • 图标做base64处理以减小大小
  • css放head,js放body中的最后面
  • 设置缓存
  • css和js的rel属性设置prefetch或preload可预加载资源
  • 按需加载组件库
  • 按需import和require
  • 服务端渲染SSR,加快首屏渲染,利于SEO
  • 页面使用骨架屏提高首页加载速度
  • 尽量少使用jpeg和png,转而使用JPEG 2000, JPEG XR, WebP
  • 图片懒加载

评论