问题集合2

问题1
requestAnimationFramesetTimeout对比优势

  • 当页面隐藏或者最小化时,setTimeout仍然在后台执行动画,此时页面不可见或者是不可用状态,动画刷新没有意义,而言浪费CPU。
  • rAF不一样,当页面处理未激活的状态时,该页面的屏幕绘制任务也会被系统暂停,因此跟着系统步伐走的rAF也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了 CPU 开销。
规范中似乎是这么去定义的:
在重新渲染前调用。
很可能在宏任务之后不去调用
与节流对比
优点
  1. 动画保持 60fps(每一帧 16 ms),浏览器内部决定渲染的最佳时机
  2. 简洁标准的 API,后期维护成本低
缺点
  1. 动画的开始/取消需要开发者自己控制,不像 ‘.debounce’ 或 ‘.throttle’由函数内部处理。
  2. 浏览器标签未激活时,一切都不会执行。
  3. 尽管所有的现代浏览器都支持 rAF ,IE9,Opera Mini 和 老的 Android 还是需要打补丁。
  4. Node.js 不支持,无法在服务器端用于文件系统事件。
问题2 DNS解析
  1. 首先查看是否有对应的域名缓存,有的话直接用缓存的ip访问
  2. 如果缓存中没有,则去查找hosts文件, 一般在 c:\windows\system32\drivers\etc\hosts
  3. 如果hosts文件里没找到想解析的域名,则将域名发往自己配置的dns服务器,也叫本地dns服务器ipconfig/all
  4. 如果本地dns服务器有相应域名的记录,则返回记录。
  5. 如果电脑自己的服务器没有记录,会去找根服务器。根服务器全球只要13组,会去找其中之一,找了根服务器后,根服务器会根据请求的域名,返回对应的“顶级域名服务器”
  6. 顶级域服务器收到请求,会返回二级域服务器的地址
  7. 以此类推,最终会发到负责锁查询域名的,最精确的那台dns,可以得到查询结果
  8. 本地dns服务器,把最终的解析结果,返回给客户端。
问题3 XSS CSRF XSS是指黑客往 HTML 文件中或者 DOM 中注入恶意脚本,从而在用户浏览页面时利用注入的恶意脚本对用户实施攻击的一种手段。
注入恶意脚本可以完成这些事情:
  1. 窃取Cookie
  2. 监听用户行为,比如输入账号密码后之间发给黑客服务器
  3. 在网页中生成浮窗广告
  4. 修改DOM伪造登入表单
一般的情况下,XSS攻击有三种实现方式
  • 存储型 XSS 攻击
  • 反射型 XSS 攻击
  • 基于 DOM 的 XSS 攻击
阻止 XSS 攻击的策略
对输入脚本进行过滤或转码
利用 CSP,该安全策略的实现基于一个称作 Content-Security-Policy的 HTTP 首部。
  • 限制加载其他域下的资源文件,这样即使黑客插入了一个 JavaScript 文件,这个 JavaScript 文件也是无法被加载的;
  • 禁止向第三方域提交数据,这样用户数据也不会外泄;
  • 提供上报机制,能帮助我们及时发现 XSS 攻击。禁止执行内联脚本和未授权的脚本;
利用 HttpOnly。服务器可以将某些 Cookie 设置为 HttpOnly 标志,HttpOnly 是服务器通过 HTTP 响应头来设置
CSRF 英文全称是 Cross-site request forgery,所以又称为“跨站请求伪造”,是指黑客引诱用户打开黑客的网站,在黑客的网站中,利用用户的登录状态发起的跨站请求。简单来讲,CSRF 攻击就是黑客利用了用户的登录状态,并通过第三方的站点来做一些坏事。
防护策略
  1. 验证码机制,验证来源站点。主要通过HTTP请求头中的两个Header,Origin只包含域名信息,而Referer包含了具体的 URL 路径
    Origin Header
    Referer Header
  2. 利用Cookie的SameSite属性
    SameSite可以设置为三个值,Strict、Lax和None。
    在Strict模式下,浏览器完全禁止第三方请求携带Cookie。
    在Lax模式只能在 get 方法提交表单况或者a 标签发送 get 请求的情况下可以携带 Cookie
    在None模式下,Cookie将在所有上下文中发送,即允许跨域发送
  3. CSRF Token
问题4 开发一个loader和Plugin
  1. loader本质上就是一个函数,这个函数会在我们在我们加载一些文件时执行
  2. 开发loader函数,这个函数必须返回一个buffer或者string
  3. 使用这个loader,我们使用resolveLoader配置项,指定loader查找文件路径,使用loader时候可以直接指定loader的名字
resolveLoader: { // loader路径查找顺序从左往右 modules: ['node_modules', './'] }, module: { rules: [ { test: /\.js$/, use: 'myLoader' } ] }

plugin通常是在webpack在打包的某个时间节点做一些操作,我们使用plugin的时候,一般都是new Plugin()这种形式使用,所以,首先应该明确的是,plugin应该是一个类。
plugin类里面需要实现一个apply方法,这个方法接受一个compiler作为参数,这个compiler是webpack实例.
比如
class DemoPlugin { constructor () { console.log('plugin init') } // compiler是webpack实例 apply (compiler) { // 一个新的编译(compilation)创建之后(同步) compiler.hooks.compile.tap('DemoPlugin', compilation => { console.log(compilation) }) // 生成资源到 output 目录之前(异步) compiler.hooks.emit.tapAsync('DemoPlugin', (compilation, fn) => { console.log(compilation) compilation.assets['index.md'] = { // 文件内容 source: function () { return 'this is a demo' }, // 文件尺寸 size: function () { return 10 } } fn() }) } }module.exports = DemoPlugin

问题5 TCP/IP / 如何保证数据包传输的有序可靠
对字节流分段并进行编号然后通过 ACK 回复超时重发这两个机制来保证。
问题6 粘包问题分析与对策
TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
粘包出现原因
简单得说,在流传输中出现,UDP不会出现粘包,因为它有消息边界
粘包情况有两种,一种是粘在一起的包都是完整的数据包,另一种情况是粘在一起的包有不完整的包。
一种比较周全的对策是:接收方创建一预处理线程,对接收到的数据包进行预处理,将粘连的包分开。实验证明这种方法是高效可行的。
问题7 如何避免重绘或者重排?
  1. 集中改变样式,不要一条一条地修改 DOM 的样式。
  2. 不要把 DOM 结点的属性值放在循环里当成循环里的变量。
  3. 为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会 reflow 的。
  4. 不使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。
  5. 尽量只修改position:absolute或fixed元素,对其他元素影响不大
  6. 动画开始GPU加速,translate使用3D变化
  7. 提升为合成层
问题8
  1. 浏览器一帧做了哪些事情
  2. 接受输入事件
  3. 执行事件回调
  4. 开始一帧
  5. 执行 RAF (RequestAnimationFrame)
  6. 页面布局,样式计算
  7. 渲染
  8. 执行 RIC (RequestIdelCallback)
问题9 webpack中hash、fullhash、chunkhash和contenthash的区别
  • hash--编译产生,每次编译的时候都会实例化一个对象compilation, 该对象掌控着从编译开始到编译结束文件,模块的加载,封闭,优化,分块,哈希,重建等等都是由其负责, 此时的hash是由compilation来创建。跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值
  • fullhash--全量的hash,是整个项目级别的。只要项目中有任何一个的文件内容发生变动,打包后所有文件的hash值都会发生改变。
  • chunkhash-- chunk,每个入口文件都是一个chunk,每个chunk是由入口文件与其依赖所构成,异步加载的文件也被视为是一个chunk。hunkhash是由每次编译模块,根据模块及其依赖模块构成chunk生成对应的chunkhash,文件未被改变时,chunkhash没变,浏览器可以利用缓存机制快速加载,但是, 每个chunk都是有css与js组成, 也就是说当其中一个文件发生变化,这个chunk都会重新编译,比如配置成filename: 'bundle.[name].[chunkhash].js',一个js文件中引入了css,修改后js,css文件的hash值都会变。
  • contenthash--针对文件内容生成不同的hash, 只有当文件内容发生变化此hash才会重新生成,利用mini-css-extract-plugin插件取提取出每个chunk的css文件,将css与js隔离开,然后将css更改,改动了哪个文件,哪个文件hash值才会变,比如配置成
new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // both options are optional filename: "css/[name].[contenthash].css", chunkFilename: "css/[name].[contenthash].css" })

问题10 6.toString()转换报错6.的点有歧义,计算机不知道是get符号还是小数点。括号把6括起来就不会报错
{}.toString()转换报错,{}计算机不知道是代码块还是一个对象,括号括起来输出[object Object]
[].toString()数组中每一个元素以字符串形式输出,这里输出空
[]==[] false,比较引用,引用值不一样
[]==![] true []地址不为空,![]为false
1.如果两个值类型相同,则比较他们的值或者引用地址
2.如果两个值类型不同,他们可能相等。根据下面规则进行类型转换再比较:
  • 1.如果一个是null、一个是undefined,那么相等。
  • 2.如果一个是字符串,一个是数值,把字符串转换成数值再进行比较。
  • 3.如果任一值是 true,把它转换成 1 再比较;如果任一值是 false,把它转换成 0再比较。
  • 4.如果一个是对象,另一个是数值或字符串,把对象转换成基础类型的值再比较。对象转换成基础类型,利用它的toString或者valueOf方法。
问题11 SVG Canvas SVG
  • 不依赖分辨率(矢量图)
  • 每一个图形都是一个 DOM元素
  • 支持事件处理器
  • 适合大型渲染区域的应用程序(谷歌地图)
  • 可以通过脚本和CSS进行修改
  • 只能通过脚本修改对象数量较小 (<10k)、图面更大时性能更佳
  • 不适合游戏应用
Canvas
  • 依赖分辨率(位图)
  • 单个HTML元素,相当于
  • 不支持事件处理器
  • 文本渲染能力差
  • 图面较小,对象数量较大(>10k)时性能最佳
  • 适合图像密集型的游戏应用
问题12 '1'.toString()为什么可以调用?
var s = new Object('1'); s.toString(); s = null;

  1. 创建Object类实例。由于Symbol和BigInt的出现,对它们调用new都会报错,目前ES6规范也不建议用new来创建基本类型的包装类。
  2. 调用实例方法。
  3. 执行完方法立即销毁这个实例。
整个过程体现了基本包装类型的性质,而基本包装类型恰恰属于基本数据类型,包括Boolean, Number和String。
问题13 Object.is Object在严格等于的基础上修复了一些特殊情况下的失误,具体来说就是+0和-0,NaN和NaN。
function is(x, y) { if (x === y) { //运行到1/x === 1/y的时候x和y都为0,但是1/+0 = +Infinity, 1/-0 = -Infinity, 是不一样的 return x !== 0 || y !== 0 || 1 / x === 1 / y; } else { //NaN===NaN是false,这是不对的,我们在这里做一个拦截,x !== x,那么一定是 NaN, y 同理 //两个都是NaN的时候返回true return x !== x && y !== y; }

问题14,浏览器不支持Promise.allSettled 当浏览器不支持 Promise.allSettled ,可以如此 polyfill
if (!Promise.allSettled) { const rejectHandler = reason => ({status: "rejected", reason}) const resolveHandler = value => ({status: "fulfilled", value}) Promise.allSettled = promises => Promise.all( promises.map((promise) => Promise.resolve(promise) .then(resolveHandler, rejectHandler) ) // 每个 promise 需要用 Promise.resolve 包裹下 // 以防传递非 promise ); }

问题15 如何让 (a == 1 && a == 2 && a == 3) 的值为true?
部署[Symbol.toPrimitive] 接口
let a = { [Symbol.toPrimitive]: (function (hint) { let i = 1; return function () { return i++; }; })(), };

【问题集合2】或者数据劫持
let i =1 let a = new Proxy({},{ i:1, get:function(){ return ()=>this.i++ } })

    推荐阅读