P6 的标准。这里所有的题都应该十分熟悉。
推荐:★★★☆
Part 2 2018版
JavaScript
酷家乐面试题
1 | // 引用类型(引用值传递和堆栈模型,这是最主要的) |
var 变量声明及原型
1 | function Foo() { |
类型判断
- typeof
- instanceof: 测试构造函数的 prototype 是否出现在对象的原型链之中。由于 prototype 是可更改的,所以同样的表达式不一定会返回一样的结果。
object instanceof constructor
1 | typeof [] === typeof {} // 'object' |
函数防抖和函数节流
- 函数防抖:频繁调用的事件,在事件触发超出时间间隔时才执行,当一次事件执行时,后一次要等时间间隔过去才能再次执行
- 应用场景:输入框校验,输入完成后进行。
函数节流:指定时间间隔内只触发一次,在间隔内触发多次,则只有一次生效
- 应用场景:
- scroll 到底部的判断,debounce 的话只有停止滚动才判断。所以是 throttle.
- 拖拽,缩放事件等。
resize
,scroll
,mousemove
事件等。 - 输入框搜索联想
keyup
时间等。
- 应用场景:
1 | /** |
函数柯里化
- 蚂蚁的题。
1 | function sum(a, b, c) { |
ES6
Promise
1 | const wait = ms => new Promise((resolve) => setTimeout(resolve, ms)) |
Promise 串行的实现
reduce 方法的思路
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21let a = (v = 0) => {
return Promise.resolve(v + 1)
}
let b = (v = 0) => {
return Promise.resolve(v + 3)
}
let c = (v = 0) => {
return Promise.resolve(v + 5)
}
let arr = [a, b, c]
function handlePromiseList(arr) {
return arr.reduce((promise, fn, index) => {
console.info(`当前是 ${index}`)
return promise.then((res) => {
return fn(res)
})
// 需要传入初始值供链式调用
}, Promise.resolve())
}
handlePromiseList(arr)TODO: 循环的思路
for in
和 for of
区别
- 两者在循环数组时,前者循环出的是 key, 后者循环出的是 value
- 后者是用来迭代可迭代对象(
Array
,Map
,Set
,String
,TypedArray
,arguments
),但不包括对象Object
for in
可以迭代出原型上的属性
for in
和 Object.keys
的区别
Object.keys
方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用for in
循环遍历该对象时返回的顺序一致。- 它们的区别在于
Object.keys
只循环对象的可枚举属性。 for in
则循环对象的可枚举属性和对象的原型链上的可枚举属性。
浅比较和浅拷贝的实现
1 | // 这里的 shallowEqual 就是浅比较,这个方法经过小部分的扩展,也可以实现 Object.assign 方法,后续补充上 |
TODO 深拷贝的实现
实现 Array.prototype.map
方法
- 链接中的方法更完善,此为简易版。
1 | Array.prototype.map = function (callback) { |
1 | const a = [1, 2, 3, 4, 5] |
jsonp实现原理及具体实现
- 蚂蚁面试时被问到了 jsonp 的实现原理,虽然答出了原理,但具体的实现逻辑有所遗忘,这里做一个简易版但是是完整的实现。
- 利用 script 标签可以跨域请求资源的原理。
1 | /* |
import 和 require 的区别
- 规范:
- import: ES6 的语法规范——模块化方案。仅在现代浏览器上支持,在其他浏览器上需转成 ES5 运行,也就是说其实仍然是转成 require 的方式运行。
- require: nodejs 提供的社区方案,遵循 CommonJS/AMD 规范。
- 调用时间:
本质:
- import: 值引用,所取的值仅仅是可读的。见下文。数据无法重新赋值,基础数据类型无法更改,对象类型无法赋值,但可以定义属性。在React中查看
- require: 值拷贝,浅拷贝。基础类型相当于直接赋值。对象类型,相当于获取了值,并浅拷贝给当前的值。
1
2
3
4
5
6
7
8
9// a.js
module.exports = 0
// b.js
export const b = 0
// main.js
let a = require('a.js')
import b from 'b.js'
++a // 1
++b // 报错,因为这个值是 read-only 的
写法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// import 的方式较多
import React from 'react'
import * as React from 'react'
import { default as React } from 'react'
import React, { Component } from 'react'
import { Component } from 'react'
import { PureComponent as PC } from 'react'
// export
export default fs
export const fs
export * from 'fs'
export { readFile, writeFile }
export function readFile
// require 方式很少
const fs = require('fs')
module.exports = () => { console.info('A') }
exports.fs = {}
window.open 的使用 - by 丁香园
- window.open
- 该方法接受三个参数:
- strUrl: url 地址。设置的值没有 http 时,打开的新窗口是当前 url 的相对地址。
- strWindowName: 窗口名称,不是窗口标题,并不会显示。如果已存在同名的 strWindowName 的窗口,就不再打开新窗口,而是在那个窗口中加载。除非将它设置为 ‘_blank’.
- strWindowFeature: 窗口的属性,字符串形式,以逗号分隔。
window.open('http://www.google.com', 'newWindow', 'resizable,scrollbars')
this 指向 window - by 丁香园
1 | const obj = { |
React
componentWillMount
- TODO: 为什么不推荐该生命周期,以及为什么不推荐在这里使用 ajax.
componentWillMount
内的报错会阻塞后续生命周期的执行,即 DOM 的挂载等等。- 如果异步请求后续有涉及 DOM 的操作,可能会因为 DOM 还未生成而找不到 DOM 而报错。
- 存疑:v16+ 的版本里,在 fiber 的机制下,开启 async rendering, render 之前的生命周期函数可能会执行多次,导致发送多次 ajax。
- 服务端渲染 SSR,
componentWillMount
也会触发?
CSS
BFC
- 块级格式化上下文: 布局过程中生成的块级盒子区域。规定了内部块级元素的布局规则,默认情况下只有 body 一个 BFC 块级上下文.
- 规则:
- 触发条件:
- 根元素或包含根元素的元素。
- 行内块元素:
display
为inline-block
. - 浮动元素:
float
有值(不为 none). overflow
不为visible
的块元素.- 绝对定位元素:
position
为fixed
或absolute
. - 弹性元素:
display
为flex
或inline-flex
元素的直接子元素。 - 网格元素:
display
为grid
或inline-grid
元素的直接子元素。 - 等等。
- 实际应用:很多 CSS 的布局方案都是通过创建 BFC 来解决的。
- 不使用 BFC 实现文字环绕效果。使用 BFC 实现排列效果。在线 Demo
- 例如解决 margin 合并的问题,方案就是创建两个 BFC。使元素处在不同的 BFC 中。在线 Demo
- 例如清除浮动,父元素添加了
overflow: hidden
属性后,生成了一个 BFC,而 BFC 的高度计算是包括浮动元素在内的,因此计算后的父元素高度包括了浮动元素,当前的原本脱离了文档流的浮动元素又被包括在父级 BFC 内。在线 Demodisplay: inline-block;float: left;position: absolute;
等等能创建 BFC 的方式都可以清除浮动,但还是要看具体的应用场景。
border-box
和 content-box
区别
- box-sizing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18.parent {
box-sizing: border-box;
width: 200px;
height: 200px;
margin: 20px;
padding: 10px;
border: 10px solid pink;
background: red;
}
.child {
width: 100%;
height: 100%;
background: yellow;
}
/* 说明各区域颜色及 child 宽高. */
/*
* border-box 是 width 包括 border, padding,但不包括 margin
*/
用 css 实现子元素宽度为父元素宽度一半的正方形 - by 头条
1 | // 父元素大小未知,这里假定 |
行内元素
- 行内元素例如
<span>
是可以设置宽高的,但是设置无效,除非将display
的值修改为inline-block
或block
。 - 在线 demo
浏览器
同源策略
- 浏览器的同源策略规定,协议相同,域名相同,端口相同(该条 IE 除外)。
- 影响:
Cookie
,LocalStorage
,IndexDB
无法读取。- 无法使用 AJAX.
- 无法获取 DOM.
- 但是有几个标签的请求是绕过同源策略的。
<img src='' />
<link href='' />
<script src='' />
跨域方案
- JSONP
- CORS:比较常规的解决方案。
access-control-allow-origin
这个参数添加域名或设置为*
.- 简单请求浏览器直接发送。
- 非简单请求,浏览器自动先发送预检请求
options
, 然后再发送非简单请求。
- window.postMessage:个人理解比较适用于 iframe 间的通信。
window.postMessage(message, targetOrigin, [transfer])
接收三个参数。message
: 消息内容,会被自动序列化,无需手动序列化。targetOrigin
: 要发送的域名。transfer
: TODO- 手动接收事件
window.addEventListener('message', receiveMessage, false)
message
: 包括data
,origin
,source
data
: 传过来的数据对象。origin
: 发送方的源地址。source
: 发送方窗口的引用。
receiveMessage
: 回调函数。
- node 中间件代理
- 思路是 node 服务端获取到客户端请求,再从 node 服务器发出,请求后数据再返回给客户端。
- 这样从服务端发起请求,没有同源策略影响。
- TODO: nginx 反向代理
- 类似 node 中间件代理,搭建一层服务器作为中转。
- ssh 登录上服务器,修改 nginx 的配置文件。然后重启 nginx.
nginx -s reload
- 在
nginx.conf
文件里修改1
2
3
4
5
6
7
8server {
listen 80;
server_name www.domain1.com;
location / {
proxy_pass www.domain2.com:8080; #反向代理
index index.html;
}
}
- TODO: websocket
前端安全
- TODO:https://github.com/riotkkwok/blog/issues/8,https://zhuanlan.zhihu.com/p/31553667
https://tech.meituan.com/fe_security.html - XSS:
- CSRF
其他
在岛上有100只老虎和1只羊,老虎可以吃草,但他们更愿意吃羊。假设: A:每次只有一只老虎可以吃羊,而且一旦他吃了羊,他自己就变成羊。 B:所有的老虎都是聪明而且完全理性的,他们的第一要务是生存。 问最后这只羊会不会被吃?- 头条的骚题。
从一只老虎开始分析,当一只老虎一只羊时,老虎必定吃羊。
当两只老虎时,老虎不敢吃羊,因为一旦吃了羊,变成了羊,就会被剩下的老虎吃了。
三只老虎时,头一只老虎可以吃了羊,然后问题回到两只老虎的状态,都不敢吃羊。
四只老虎时,老虎如果吃羊,就会回到三只老虎的状态,因此任一老虎都不会吃。
综上所述,老虎数量为奇数时,老虎会吃,为偶数时,不会吃。
单双向数据流的理解
TODO
单向数据流: 简单的理解,可认为水(数据)往低处流。高处是用户,低处是视图。用户的交互行为使得动作发生变更,派发后触发回调函数,进而引起视图的监听事件的调用,最终导致视图的更新。换句话说,视图的变更不会引起数据的变化,否则这就是双向数据绑定了。当然,目前谈论到的数据变化,是以 UI 控件为前提。
user interaction => dispatch(action) => callback => store(update) => change events => view(react component)
双向数据流:
评论