Frontend Interview

This article is created for recording problems encountered in my interview.

什么是IIFE(立即调用函数表达式)

它是立即调用函数表达式(Immediately-Invoked Function Expression),简称 IIFE。函数被创建后立即被执行:

1
2
3
4
(function IIFE(){
console.log( "Hello!" );
})();
// "Hello!"

在避免污染全局命名空间时经常使用这种模式,因为 IIFE(与任何其他正常函数一样)内部的所有变量在其作用域之外都是不可见的。

ref: https://www.jianshu.com/p/d4d2eb4be216

闭包是什么?举个例子

闭包是在另一个函数(称为父函数)中定义的函数,并且可以访问在父函数作用域中声明和定义的变量。
闭包可以访问三个作用域中的变量:
1) 在自己作用域中声明的变量
2) 在父函数中声明的变量
3) 在全局作用域中声明的变量
闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。闭包就是就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配。当在一个函数内定义另外一个函数就会产生闭包

为什么要用闭包?

  • 匿名自执行函数:我们知道所有的变量,如果不加上var关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处,比如:别的函数可能误用这些变量;造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的)。除了每次使用变量都是用var关键字外,我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,可以用闭包。
  • 结果缓存:我们开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var globalVar = "abc";
// 自调用函数
(function outerFunction (outerArg) { // outerFunction 作用域开始
// 在 outerFunction 函数作用域中声明的变量
var outerFuncVar = 'x';
// 闭包自调用函数
(function innerFunction (innerArg) { // innerFunction 作用域开始
// 在 innerFunction 函数作用域中声明的变量
var innerFuncVar = "y";
console.log(
"outerArg = " + outerArg + "
" +
"outerFuncVar = " + outerFuncVar + "
" +
"innerArg = " + innerArg + "
" +
"innerFuncVar = " + innerFuncVar + "
" +
"globalVar = " + globalVar);
// innerFunction 作用域结束
})(5); // 将 5 作为参数
// outerFunction 作用域结束
})(7); // 将 7 作为参数

innerFunction 是在 outerFunction 中定义的闭包,可以访问在 outerFunction 作用域内声明和定义的所有变量。除此之外,闭包还可以访问在全局命名空间中声明的变量。
上述代码的输出将是:

1
2
3
4
5
outerArg = 7
outerFuncVar = x
innerArg = 5
innerFuncVar = y
globalVar = abc

ref: https://www.jianshu.com/p/d4d2eb4be216

已知两个funcion A & B,如何实现先调用一次A,往后全部调用B

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
// 用闭包模拟私有方法
// 此方法可避免非核心的方法弄乱代码的公共接口部分
let t = function() {
let time = 0;
const A = () => {
console.log("A", time);
};
const B = () => {
console.log("B", time);
};
return () => {
time++;
if (time === 1) {
A();
} else {
B();
}
};
}(); // this is closure
let test = 4;
while(test > 0) {
t();
test--;
}
// output:
// A 1
// B 2
// B 3
// B 4

ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

mock data

适用于后端没有给出测试阶段的假接口和模拟数据情况
1) 直接在页面写死数据,后期等接口来了,再改成动态的; 哈哈哈这是最简单的,也是最笨的,所以小型的项目,不出5个页面的可以解决,或是每个页面的数据很少的可以解决,但是不推荐,后期太麻烦
2) 在js里直接声明变量,并给变量赋值,在逻辑脚本中使用,并渲染到dom
3) 将模拟数据编辑成json数据或者是零碎的js脚本中,通过请求,取回数据,并进行业务逻辑处理,渲染到dom
4) 最理想的方式:

  • 前后台在需求分解之后,一起定义好接口api,包含:请求url(项目前缀+具体的接口名称)、请求方式、请求参数、数据响应
  • 前端研发人员根据接口约定,模拟请求返回对应的数据,完成对应的交互
  • 后台人员根据接口约定,完成对应的api,并完成对应的自测
  • 待后台人员交付接口api后,前端人员直接修改接口项目前缀,切换到对应的环境,即可进入项目提测

ref: https://zhuanlan.zhihu.com/p/77199413

数组去重

  1. indexOf循环去重
  2. ES6 Set去重;Array.from(new Set(array))
  3. Object 键值对去重;把数组的值存成 Object 的 key 值,比如 Object[value1] = true,在判断另一个值的时候,如果 Object[value2]存在的话,就说明该值是重复的。

JS实现跨域

  1. JSONP:通过动态创建script,再请求一个带参网址实现跨域通信。document.domain + iframe跨域:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
  2. location.hash + iframe跨域:a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。
  3. window.name + iframe跨域:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。
  4. postMessage跨域:可以跨域操作的window属性之一。
  5. CORS:服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求,前后端都需要设置。
  6. 代理跨域:启一个代理服务器,实现数据的转发

ref: https://segmentfault.com/a/1190000011145364

通过nginx反向代理解决前端访问的跨域问题

ref: https://www.ucloud.cn/yun/97273.html

js页面性能优化

  • 减少HTTP请求
  • 使用内容发布网络(CDN)
  • 添加本地缓存
  • 压缩资源文件
  • 将CSS样式表放在顶部,把javascript放在底部(浏览器的运行机制决定)
  • 避免使用CSS表达式
  • 减少DNS查询
  • 使用外部javascript和CSS
  • 避免重定向
  • 图片lazyLoad

防抖节流

CSS flex

http://www.ruanyifeng.com/blog/2015/07/flex-examples.html