var adder = {
base : 1,
add : function(a) {
var f = v => v + this.base;
return f(a);
},
addThruCall: function(a) {
var f = v => v + this.base;
var b = {
base : 2
};
return f.call(b, a);
}
};
console.log(adder.add(1)); // 输出 2
console.log(adder.addThruCall(1)); // 仍然输出 2
Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
position属性用于指定一个元素在文档中的定位方式。top,right,bottom 和 left 属性则决定了该元素的最终位置。简言之,可以改变元素的位移。在浏览器页面渲染的时候,top/left可以控制元素的位置,也就是说,改变top/left,就会改变render tree的结构,必定会引起页面layout回流和repaint重绘。
基础知识集锦
JS基础数据类型、复杂类型分别是什么以及其区别?null是对象吗?
基础数据类型:boolean、string、number、null、undefined、symbol。
复杂数据类型:object
区别:
null不是对象:
虽然typeof null 的结果为object,但是这个只是 JavaScript 语言本身的一个 bug。js对象底层是以二进制表示,若前三位为零的二进制,则判定为对象。而null在底层中的表示为都是零,即其前三位也是零,所以会被判定为对象。
如何让 (a == 1 && a == 2 && a == 3) 的值为true?
利用隐式转换规则。
==
操作符在左右数据类型不一致时,会先进行隐式转换。a == 1 && a == 2 && a == 3
的值意味着其不可能是基本数据类型。因为如果 a 是 null 或者是 undefined bool类型,都不可能返回true。因此可以推测 a 是复杂数据类型,JS 中复杂数据类型只有 `object。Object 转换为原始类型会调用什么方法?
Symbol.toPrimitive
是一个内置的 Symbol 值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数。防抖(debounce)函数的作用是什么?有哪些应用场景,请实现一个防抖函数
防抖的作用是控制函数在一定时间内的执行次数。防抖意味着N秒内函数只会被执行一次,如果N秒内再次被触发,则重新计算延迟时间。
场景:
实现:
实例参考
说一说你对JS执行上下文栈和作用域链的理解?
JS执行上下文
执行上下文就是当前JS代码被解析和执行时所在环境的抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行。
执行上下文创建过程中,需要做以下几件事:
作用域
作用域负责收集和维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
作用域有两种工作模型:词法作用域和动态作用域,JS采用的是词法作用域工作模型,词法作用域意味着作用域是由书写代码时变量和函数声明的位置决定的。
JS执行上下文栈(后面简称执行栈)
执行栈,也叫做调用栈,具有 LIFO (后进先出) 结构,用于存储在代码执行期间创建的所有执行上下文。
代码说明:
伪代码:
作用域链
作用域链就是从当前作用域开始一层一层向上寻找某个变量,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链。
什么是BFC?BFC的布局规则是什么?如何创建BFC?
BFC 是 Block Formatting Context 的缩写,即块格式化上下文。
BFC布局规则
margin
属性决定。属于同一个BFC的两个相邻Box的margin会发生重叠【符合合并原则的margin合并后是使用大的margin】如何创建BFC
BFC的应用
防止 margin 重叠
根据BFC规则,同一个BFC内的两个两个相邻Box的
margin
会发生重叠,因此我们可以在div外面再嵌套一层容器,并且触发该容器生成一个 BFC,即产生两个 BFC,自然也就不会再发生margin
重叠清除内部浮动
自适应多栏布局
根据规则,BFC的区域不会与float box重叠。因此,可以触发生成一个新的BFC。
let、const、var 的区别有哪些?
声明方式
变量提升
暂时性死区
重复声明
块作用域有效
初始值
重新赋值
var
会
不存在
允许
不是
非必须
允许
let
不会
存在
不允许
是
非必须
允许
const
不会
存在
不允许
是
let/const 定义的变量不会出现变量提升,而 var 定义的变量会提升。
相同作用域中,let 和 const 不允许重复声明,var 允许重复声明。
const 声明变量时必须设置初始值.
const 声明一个只读的常量,这个常量不可改变。
let/const 声明的变量仅在块级作用域中有效。而 var 声明的变量在块级作用域外仍能访问到。
顶层作用域中 var 声明的变量挂在window上(浏览器环境)。
let/const有暂时性死区的问题,即let/const 声明的变量,在定义之前都是不可用的。如果使用会抛出错误。
深拷贝和浅拷贝的区别是什么?如何实现一个深拷贝?
深拷贝
浅拷贝
package.json 中的 peerDependencies作用?
peerDependencies
的目的是提示宿主环境去安装满足插件peerDependencies所指定依赖的包,然后在插件import或者require所依赖的包的时候,永远都是引用宿主环境统一安装的npm包,最终解决插件与所依赖包不一致的问题。指定当前组件的依赖以其版本。如果组件使用者在项目中安装了其他版本的同一依赖,会提示报错。
package.json 中的 peerDependencies
React的虚拟DOM优势?
它减少了同一时间内的页面多处内容修改所触发的浏览器reflow和repaint的次数,可能把多个不同的DOM操作集中减少到了几次甚至一次,优化了触发浏览器reflow和repaint的次数
compose实现
webpack中loaders作用?plugins和loaders区别?是否写过webpack插件
箭头函数与普通函数区别?能不能作为构造函数
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的
this
,arguments
,super
或new.target
,没有prototype
属性。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。普通函数:
箭头函数:
不会创建自己的
this,它只会从自己的作用域链的上一层继承this
。通过 call 或 apply 调用时,由于箭头函数没有自己的this指针,通过call()
或apply()
方法调用一个函数时,只能传递参数,他们的第一个参数会被忽略。yield
关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作函数生成器。EventLoop 相关,有哪些宏任务和微任务?特点?对 requestAnimationFrame 的理解
this的原理
例子:
this
指的是函数运行时所在的环境。对于obj.foo()
来说,foo
运行在obj
环境,所以this
指向obj
;对于foo()
来说,foo
运行在全局环境,所以this
指向全局环境。所以,两者的运行结果不一样。变量
obj
是一个地址(reference)。后面如果要读取obj.foo
,引擎先从obj
拿到内存地址,然后再从该地址读出原始的对象,返回它的foo
属性。原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。如下:
注意,
foo
属性的值保存在属性描述对象的value
属性里面。函数时,引擎会将函数单独保存在内存中,然后再将函数的地址赋值给
foo
属性的value
属性。由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。JavaScript 允许在函数体内部,引用当前环境的其他变量。由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,
this
就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。引用
什么是内存泄漏和内存溢出?
垃圾回收机制:引用计数法,即语言引擎有一张”引用表”,保存了内存里面所有的资源(通常是各种值)的引用次数。如果一个值的引用次数是
0
,就表示这个值不再用到了,因此可以将这块内存释放。内存泄漏解决:
手动清除引用;
用新的数据结构:WeakSet (成员只能是对象,而不能是其他类型的值) 和 WeakMap (只接受对象作为键名)。(它们对于值的引用都是不计入垃圾回收机制的)
Reflect
静态方法:
Reflect.apply(target, thisArg, args)
Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.get
方法查找并返回target
对象的name
属性,如果没有该属性,则返回undefined
。如果name
属性部署了读取函数(getter),则读取函数的this
绑定receiver
。Reflect.set(target, name, value, receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
引用
Worker知识
作用就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。
同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
DOM 限制
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用
document
、window
、parent
这些对象。但是,Worker 线程可以navigator
对象和location
对象。通信联系
Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
脚本限制
Worker 线程不能执行
alert()
方法和confirm()
方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(
file://
),它所加载的脚本,必须来自网络。Webpack 工作原理
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
CDN加速
什么是CDN
CDN 又叫内容分发网络,通过把资源部署到世界各地,用户在访问时按照就近原则从离用户最近的服务器获取资源,从而加速资源的获取速度。 CDN 其实是通过优化物理链路层传输过程中的网速有限、丢包等问题来提升网速的。
接入CDN
当把资源都放到同一个 CDN 服务上去时,网页是能正常使用的。 但需要注意的是由于 CDN 服务一般都会给资源开启很长时间的缓存,例如用户从 CDN 上获取到了
index.html
这个文件后, 即使之后的发布操作把index.html
文件给重新覆盖了,但是用户在很长一段时间内还是运行的之前的版本,这会新的导致发布不能立即生效。要避免以上问题,业界比较成熟的做法是这样的:
app_a6976b6d.css
文件。 带上 Hash 值的原因是文件名会随着文件内容而变化,只要文件发生变化其对应的 URL 就会变化,它就会被重新下载,无论缓存时间有多长。除此之外,浏览器有一个规则是同一时刻针对同一个域名的资源并行请求是有限制的话(具体数字大概4个左右,不同浏览器可能不同), 你会发现上面的做法有个很大的问题。由于所有静态资源都放到了同一个 CDN 服务的域名下,也就是上面的
cdn.com
。 如果网页的资源很多,例如有很多图片,就会导致资源的加载被阻塞,因为同时只能加载几个,必须等其它资源加载完才能继续加载。 要解决这个问题,可以把这些静态资源分散到不同的 CDN 服务上去, 例如把 JavaScript 文件放到js.cdn.com
域名下、把 CSS 文件放到css.cdn.com
域名下、图片文件放到img.cdn.com
域名下。Webpack接入CDN
Webpack构建需要实现以下几点:
不同类型的资源放到不同域名的 CDN 服务上去,以防止资源的并行加载被阻塞。
const path = require('path'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const {WebPlugin} = require('web-webpack-plugin');
module.exports = { // 省略 entry 配置... output: { // 给输出的 JavaScript 文件名称加上 Hash 值 filename: '[name]_[chunkhash:8].js', path: path.resolve(_dirname, './dist'), // 指定存放 JavaScript 文件的 CDN 目录 URL publicPath: '//js.cdn.com/id/', }, module: { rules: [ { // 增加对 CSS 文件的支持 test: /.css$/, // 提取出 Chunk 中的 CSS 代码到单独的文件中 use: ExtractTextPlugin.extract({ // 压缩 CSS 代码 use: ['css-loader?minimize'], // 指定存放 CSS 中导入的资源(例如图片)的 CDN 目录 URL publicPath: '//img.cdn.com/id/' }), }, { // 增加对 PNG 文件的支持 test: /.png$/, // 给输出的 PNG 文件名称加上 Hash 值 use: ['file-loader?name=[name][hash:8].[ext]'], }, // 省略其它 Loader 配置... ] }, plugins: [ // 使用 WebPlugin 自动生成 HTML new WebPlugin({ // HTML 模版文件所在的文件路径 template: './template.html', // 输出的 HTML 的文件名称 filename: 'index.html', // 指定存放 CSS 文件的 CDN 目录 URL stylePublicPath: '//css.cdn.com/id/', }), new ExtractTextPlugin({ // 给输出的 CSS 文件名称加上 Hash 值 filename:
[name]_[contenthash:8].css
, }), // 省略代码压缩插件配置... ], };以上代码中最核心的部分是通过
publicPath
参数设置存放静态资源的 CDN 目录 URL, 为了让不同类型的资源输出到不同的 CDN,需要分别在:output.publicPath
中设置 JavaScript 的地址。css-loader.publicPath
中设置被 CSS 导入的资源的的地址。WebPlugin.stylePublicPath
中设置 CSS 文件的地址。引用
递增 (++)
递增运算符为其操作数增加1,返回一个数值。
如果使用前置(prefix),即运算符位于操作数的前面(如 ++x),那么将会在递增后返回数值。
// 后置 var x = 3; y = x++; // y = 3, x = 4
// 前置 var a = 2; b = ++a; // a = 3, b = 3
算法的时间与空间复杂度
时间复杂度的公式是: T(n) = O( f(n) ),其中f(n) 表示每行代码执行次数之和,而 O 表示正比例关系,这个公式的全称是:算法的渐进时间复杂度。
window.requestAnimationFrame
window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。调函数会在浏览器下一次重绘之前执行
当你准备更新动画时你应该调用此方法。这将使浏览器在下一次重绘之前调用你传入给该方法的动画函数(即你的回调函数)。回调函数执行次数通常是每秒60次,但在大多数遵循W3C建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。
语法
参数
callback
下一次重绘之前更新动画帧所调用的函数(即上面所说的回调函数)。该回调函数会被传入
DOMHighResTimeStamp
参数,该参数与performance.now()
的返回值相同,它表示requestAnimationFrame()
开始去执行回调函数的时刻。返回值
一个
long
整数,请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义。你可以传这个值给window.cancelAnimationFrame()
以取消回调函数。HTTP状态码备忘单
什么是 HTTP 状态码
Fernando Doglio在他的书中 - 使用NodeJS的REST API开发将状态代码定义为:
当客户端向服务器发出请求时,服务器提供HTTP(超文本传输协议)响应状态代码,这使我们能够了解网站后端发生的情况,确定需要修复的错误。
HTTP 状态码
响应信息
1xx:临时回应,表示客户端请继续
1xx 的状态会被浏览器 HTTP 库直接处理掉,不会让上层应用知晓
成功
200 - OK:成功传输
请求成功
201 - Created:创建
资源已创建,服务器已确认。它对POST或PUT请求的响应很有用。此外,新资源可以作为响应正文的一部分返回。
204 - No content:没有内容
该操作请求成功,但没有返回任何内容。对于不需要响应主体的操作很有用,例如 DELETE 操作。
205 - Reset Content:重置内容
表示请求成功,但响应报文不含实体的主体部分,但是与 204 响应不同在于要求请求方重置内容。
重定向
3xx: 当服务器通知客户端请求的目标有变化,希望客户端进一步处理,将使用这些。
为什么需要重定向?
301 - moved permanently:永久移动
用于通知浏览器所请求的文件已被移动,并且它应该从服务器提供的位置(响应的Location首部)请求文件,并记住该新位置以供将来参考。这只能用于HTTP GET和HEAD请求。
此资源已移至另一个位置,并返回该位置。当URL随着时间的推移而变化时(尤其是由于版本,迁移或其他一些破坏性更改),此标头特别有用,保留旧标头并将重定向返回到新位置允许旧客户端更新其引用自己的时间。
302 - found(找到):临时重定向
相似301; 但它是临时重定向。它将客户端从旧资源引导到新资源,但它不会告诉搜索引擎更新页面的索引(保存的还是旧地址的索引)。告诉客户端浏览另一个URL。
304- Not Modified:客户端缓存没有更新
产生的前提:客户端本地已经有缓存的版本,并且在 Request 中告诉了服务端,当服务端通过时间或 tag,发现没有更新的时候,就会返回一个不含 body 的 304 状态码
307 - temporary redirect:暂时移动
临时重定向,和302含义类似,但是期望客户端保持请求方法不变向新的地址发出请求
客户端错误
4xx: 定义客户端错误,这是服务器认为Web浏览器出错的地方。
400 - bad request:不良请求
发出的请求有问题(例如,可能缺少一些必需的参数)。对400响应的良好补充可能是开发人员可用于修复请求的错误消息
401 - unauthorized:未经授权
当拥有请求的用户无法访问所请求的资源时,对身份验证特别有用。服务器响应www-authenticate,客户端请求首部Authenticate。
403 - forbidden:禁止 - 资源不可访问,但与401不同,身份验证不会影响响应。
通常在请求的文件有效但文件无法提供时发出,这通常是由于服务器端权限问题导致Web服务器不允许将文件提供给客户端。如IP被列入黑名单;同一IP地址发送请求过多,被服务器屏蔽;DNS解析错误等。
401 与 403 的区别:
404 - Not Found:请求的资源不存在
这可能是最常见且经常出现的错误。当Web浏览器请求服务器上不存在的文件时,会发生此问题。
405 - 方法不允许
不允许在资源上使用HTTP动词(例如POST,GET,PUT等) - 例如,在只读资源上执行PUT。
服务端错误
5xx: 定义服务器端错误。尽管客户端提供了有效请求,但这些都是服务器部分发生的错误。
500 - internal sever error:内部服务器错误
这是一个不幸的模糊通用错误代码。只要认为服务器遇到与任何更具体的错误代码不匹配的错误,就会发出它。
501 - Not Implemented:未实现
服务器要么不识别请求方法,要么不支持请求。
502 - Bad Gateway:错误网关
对用户访问请求的响应超时造成的。或当Web浏览器联系充当另一个服务器的代理的Web服务器并且从另一个服务器获得无效响应时,会发生这种情况。
503 - service unavailable:服务不可用
这通常在服务器暂时性错误(暂时处于超负载或正在停机维护)的情况下遇到,此时服务器无法处理请求,可以一会再试
继承与原型链
每个实例对象( object )都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象( proto ) ,层层向上直到一个对象的原型对象为
null
。根据定义,null
没有原型,并作为这个原型链中的最后一个环节。HTML rel属性的作用
rel是什么
rel属性通常用来描述链接之间的关系,也就是说目的地址(href) 跟源(站点)之间的关系。rel属性通常出现在a,link标签中。rel是relationship的英文缩写。
rel的属性值有哪些
alternate
– 文档的替代版本(比如打印页、翻译或镜像)appendix
– 定义文档的附加信息bookmark
– 书签chapter
– 当前文档的章节contents
–文档的目录copyright
– 当前文档的版权glossary
– 词汇help
– 链接帮助信息\帮助文档index
– 当前文档的索引next
– 记录文档的下一页.(浏览器可以提前加载此页)nofollow
– 不被用于计算PageRankprev
– 记录文档的上一页.(定义浏览器的后退键)section
– 作为文档的一部分start
– 通知搜索引擎,文档的开始stylesheet
– 定义一个外部加载的样式表subsection
– 作为文档的一小部分me
–告诉搜索引擎,这是自己的内容home
–告诉搜索引擎,这是返回首页license
–描述版权friend
–这是朋友的tag
–标签same
–相同的链接什么是nofollow?
前端安全问题
iframe
a、如何让自己的网站不被其他网站的 iframe 引用?
b、如何禁用,被使用的 iframe 对当前网站某些操作?
c、设置
X-Frame-Options
HTTP响应头用来给浏览器 指示允许一个页面 可否在
frame
,iframe
,embed
或者object
中展现的标记。站点可以通过确保网站没有被嵌入到别人的站点里面,从而避免 clickjacking 攻击。deny
表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。
sameorigin
表示该页面可以在相同域名页面的 frame 中展示。
allow-from *uri*
表示该页面可以在指定来源的 frame 中展示。
配置nginx:
XSS
Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。
XSS 的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。
XSS的分类
存储型XSS
存储型 XSS 的攻击步骤:
这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
反射型XSS
反射型 XSS 的攻击步骤:
反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。
反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。
由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。
POST 的内容也可以触发反射型 XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见。
DOM型XSS
DOM 型 XSS 的攻击步骤:
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
XXS攻击的预防
预防存储型和反射型 XSS 攻击
预防DOM型XSS攻击
.innerHTML
、.outerHTML
、document.write()
时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用.textContent
、.setAttribute()
等。v-html
/dangerouslySetInnerHTML
功能,就在前端 render 阶段避免innerHTML
、outerHTML
的 XSS 隐患。location
、onclick
、onerror
、onload
、onmouseover
等,`` 标签的href
属性,JavaScript 的eval()
、setTimeout()
、setInterval()
等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免。输入内容长度控制
CSP:HTTP 响应头
Content-Security-Policy
允许站点管理者控制用户代理能够为指定的页面加载哪些资源。CSP 允许在一个资源中指定多个策略, 包括通过
Content-Security-Policy
头, 以及Content-Security-Policy-Report-Only
头,和meta
组件。示例: 禁用不安全的内联/动态执行, 只允许通过 https加载这些资源 (images, fonts, scripts, etc.)
```nginx + html // header Content-Security-Policy: default-src https:
// meta tag
```
其他
CSRF
CSRF(Cross-site request forgery), 即跨站请求伪造,通过伪装成受信任用户的请求来利用受信任的网站。指的是黑客诱导用户点击链接,打开黑客的网站,然后黑客利用用户目前的登录状态发起跨站请求。
攻击方式:
防范措施
利用cookie的SameSite属性。(问题:SameSite不支持子域名)
SameSite
可以设置为三个值,Strict
、Lax
和None
。a. 在
Strict
模式下,浏览器完全禁止第三方请求携带Cookie。比如请求sanyuan.com
网站只能在sanyuan.com
域名当中请求才能携带 Cookie,在其他网站请求都不能。b. 在
Lax
模式,就宽松一点了,但是只能在get 方法提交表单
况或者a 标签发送 get 请求
的情况下可以携带 Cookie,其他情况均不能。c. 在
None
模式下,也就是默认模式,请求会自动携带上 Cookie。验证来源站点。
用到请求头中的两个字段: Origin和Referer。
其中,Origin只包含域名信息,而Referer包含了
具体
的 URL 路径,包含了当前请求页面的来源页面的地址,即表示当前页面是通过此来源页面里的链接进入的。当然,这两者都是可以伪造的,通过 Ajax 中自定义请求头即可,安全性略差。
验证码、Token验证。
在前后端交互中携带一个有效验证“令牌”来防范CSRF攻击,大概流程:
数字证书认证机构的业务流程
HTTP1.0、HTTP1.1 和 HTTP2.0 的区别
HTTP的基本优化
影响一个 HTTP 网络请求的因素主要有两个:带宽和延迟。
带宽:如果说我们还停留在拨号上网的阶段,带宽可能会成为一个比较严重影响请求的问题,但是现在网络基础建设已经使得带宽得到极大的提升,我们不再会担心由带宽而影响网速,那么就只剩下延迟了。
延迟:
HTTP1.0和HTTP1.1的一些区别
HTTP1.0最早在网页中使用是在1996年,那个时候只是使用一些较为简单的网页上和网络请求上,而HTTP1.1则在1999年才开始广泛应用于现在的各大浏览器网络请求中,同时HTTP1.1也是当前使用最为广泛的HTTP协议。 主要区别主要体现在:
HTTP2.0和HTTP1.X相比的新特性
HTTP2.0的多路复用和HTTP1.X中的长连接复用有什么区别?
HTTP缓存
强缓存与弱缓存
缓存可以简单的划分成两种类型:
强缓存
(200 from cache
)与协商缓存
(304
)区别简述如下:
200 from cache
)时,浏览器如果判断本地缓存未过期,就直接使用,无需发起http请求304
)时,浏览器会向服务端发起http请求,然后服务端告诉浏览器文件未改变,让浏览器使用本地缓存对于协商缓存,使用
Ctrl + F5
强制刷新可以使得缓存无效但是对于强缓存,在未过期时,必须更新资源路径才能发起新的请求(更改了路径相当于是另一个资源了,这也是前端工程化中常用到的技巧)
缓存头部简述
上述提到了强缓存和协商缓存,那它们是怎么区分的呢?
答案是通过不同的http头部控制
先看下这几个头部:
这些就是缓存中常用到的头部,这里不展开。仅列举下大致使用。
属于强缓存控制的:
注意:
Max-Age
不是一个头部,它是Cache-Control
头部的值属于协商缓存控制的:
可以看到,上述有提到
http1.1
和http1.0
,这些不同的头部是属于不同http时期的再提一点,其实HTML页面中也有一个meta标签可以控制缓存方案-
Pragma
不过,这种方案还是比较少用到,因为支持情况不佳,譬如缓存代理服务器肯定不支持,所以不推荐
头部的区别
首先明确,http的发展是从http1.0到http1.1
而在http1.1中,出了一些新内容,弥补了http1.0的不足。
http1.0中的缓存控制:
Pragma
:严格来说,它不属于专门的缓存控制头部,但是它设置no-cache
时可以让本地强缓存失效(属于编译控制,来实现特定的指令,主要是因为兼容http1.0,所以以前又被大量应用)Expires
:服务端配置的,属于强缓存,用来控制在规定的时间之前,浏览器不会发出请求,而是直接使用本地缓存,注意,Expires一般对应服务器端时间,如Expires:Fri, 30 Oct 1998 14:19:41
If-Modified-Since/Last-Modified
:这两个是成对出现的,属于协商缓存的内容,其中浏览器的头部是If-Modified-Since
,而服务端的是Last-Modified
,它的作用是,在发起请求时,如果If-Modified-Since
和Last-Modified
匹配,那么代表服务器资源并未改变,因此服务端不会返回资源实体,而是只返回头部,通知浏览器可以使用本地缓存。Last-Modified
,顾名思义,指的是文件最后的修改时间,而且只能精确到1s
以内http1.1中的缓存控制:
Cache-Control
:缓存控制头部,有no-cache、max-age等多种取值Max-Age
:服务端配置的,用来控制强缓存,在规定的时间之内,浏览器无需发出请求,直接使用本地缓存,注意,Max-Age是Cache-Control头部的值,不是独立的头部,譬如Cache-Control: max-age=3600
,而且它值得是绝对时间,由浏览器自己计算If-None-Match/E-tag
:这两个是成对出现的,属于协商缓存的内容,其中浏览器的头部是If-None-Match
,而服务端的是E-tag
,同样,发出请求后,如果If-None-Match
和E-tag
匹配,则代表内容未变,通知浏览器使用本地缓存,和Last-Modified不同,E-tag更精确,它是类似于指纹一样的东西,基于FileEtag INode Mtime Size
生成,也就是说,只要文件变,指纹就会变,而且没有1s精确度的限制。Max-Age相比Expires?
Expires
使用的是服务器端的时间但是有时候会有这样一种情况-客户端时间和服务端不同步
那这样,可能就会出问题了,造成了浏览器本地的缓存无用或者一直无法过期
所以一般http1.1后不推荐使用
Expires
而
Max-Age
使用的是客户端本地时间的计算,因此不会有这个问题因此推荐使用
Max-Age
。注意,如果同时启用了
Cache-Control
与Expires
,Cache-Control
优先级高。E-tag相比Last-Modified?
Last-Modified
:而
E-tag
:如果同时带有
E-tag
和Last-Modified
,服务端会优先检查E-tag
各大缓存头部的整体关系如下图
如何解决ajax跨域
一般ajax跨域解决就是通过JSONP解决或者CORS解决,如以下:(注意,现在已经几乎不会再使用JSONP了,所以JSONP了解下即可)
JSONP方式解决跨域问题
jsonp解决跨域问题是一个比较古老的方案(实际中不推荐使用),这里做简单介绍(实际项目中如果要使用JSONP,一般会使用JQ等对JSONP进行了封装的类库来进行ajax请求)
实现原理
JSONP之所以能够用来解决跨域方案,主要是因为
script
脚本拥有跨域能力,而JSONP正是利用这一点来实现。具体原理如图实现流程
JSONP的实现步骤大致如下(参考了来源中的文章)
客户端网页网页通过添加一个
script
元素,向服务器请求JSON数据,这种做法不受同源政策限制请求时,接口地址是作为构建出的脚本标签的src的,这样,当脚本标签构建出来时,最终的src是接口返回的内容
服务端对应的接口在返回参数外面添加函数包裹层
由于
script
>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。注意,一般的JSONP接口和普通接口返回数据是有区别的,所以接口如果要做JSONO兼容,需要进行判断是否有对应callback关键字参数,如果有则是JSONP请求,返回JSONP数据,否则返回普通数据
使用注意
基于JSONP的实现原理,所以JSONP只能是“GET”请求,不能进行较为复杂的POST和其它请求,所以遇到那种情况,就得参考下面的CORS解决跨域了(所以如今它也基本被淘汰了)
CORS解决跨域问题
CORS的原理上文中已经介绍了,这里主要介绍的是,实际项目中,后端应该如何配置以解决问题(因为大量项目实践都是由后端进行解决的),这里整理了一些常见的后端解决方案:
Node.js后台配置(express框架)
Node.js的后台也相对来说比较简单就可以进行配置。只需用express如下配置:
JAVA后台配置
JAVA后台配置只需要遵循如下步骤即可:
第一步:获取依赖jar包
下载 cors-filter-1.7.jar, java-property-utils-1.9.jar 这两个库文件放到lib目录下。(放到对应项目的webcontent/WEB-INF/lib/下)
第二步:如果项目用了Maven构建的,请添加如下依赖到pom.xml中:(非maven请忽视)
其中版本应该是最新的稳定版本,CORS过滤器
第三步:添加CORS配置到项目的Web.xml中( App/WEB-INF/web.xml)
请注意,以上配置文件请放到web.xml的前面,作为第一个filter存在(可以有多个filter的)
代理请求方式解决接口跨域问题
注意,由于接口代理是有代价的,所以这个仅是开发过程中进行的。
与前面的方法不同,前面CORS是后端解决,而这个主要是前端对接口进行代理,也就是:
JS 继承
组合方式实现继承
运用
Object.create(proto)
与call
实现:异步加载
异步加载的方式
异步加载的区别
defer
脚本会在DOMContentLoaded
和load
事件之前执行。async
会在load
事件之前执行,但并不能确保与DOMContentLoaded
的执行先后顺序。为什么用transform写动画不用position top left?
主要还是从浏览器渲染的性能方面考虑。
我们知道,浏览器中有JS引擎和渲染引擎,对于HTML页面的渲染就靠渲染引擎来完成。
浏览器渲染主要包括Parse Html、Recalculate Style、Layout、Paint、Image Decode、Image Resize和Composite Layers等。相对应的中文表述就是:html解析、查找并计算样式、排布、绘制、图片解码、图片大小设置、合并图层并输出页面到屏幕。浏览器最终渲染出来的页面,跟Photoshop有点类似,是由多个图层合并而来。
transform是通过创建一个RenderLayers合成层,拥有独立的GraphicsLayers。每一个GraphicsLayers都有一个Graphics Context,其对应的RenderLayers会paint进Graphics Context中。合成器(Compositor)最终会负责将由Graphics Context输出的位图合并成最终屏幕展示的图案。
满足如下条件的RenderLayers,会被认为是一个独立的合成层:
如果RenderLayer是一个合成层,那么它有属于它自己的单独的GraphicsLayer,否则它和它的最近的拥有GraphicsLayer的父layer共用一个GraphicsLayer。
由此可见,transform发生在Composite Layer这一步,它所引起的paint也只是发生在单独的GraphicsLayer中,并不会引起整个页面的回流重绘。
我们经常会听到GPU会加速渲染,那GPU在这里又扮演什么角色呢?
前面说到,合成器会负责将层合成绘制为最终的屏幕画面。在硬件加速体系结构,合成由GPU负责。在chrome浏览器多进程模型中,有一个专门的进程来负责传递Render进程的命令,即GPU进程。Render进程和GPU进程是通过共享内存传递的。
Render进程可以快速的将命令发给命令缓冲区,并且返回到CPU密集的render活动中,留给GPU进程去处理这些命令。我们可以充分利用多内核机器上的GPU进程和CPU进程。这也是为什么GPU会加速渲染,使transform渲染速度更快的又一原因。
position
属性用于指定一个元素在文档中的定位方式。top
,right
,bottom
和left
属性则决定了该元素的最终位置。简言之,可以改变元素的位移。在浏览器页面渲染的时候,top/left
可以控制元素的位置,也就是说,改变top/left
,就会改变render tree的结构,必定会引起页面layout回流和repaint重绘。因此,从浏览器性能考虑,transform会比
top/left
更省时间。transform实际上也是用到了GPU加速,也就是说占用了内存。由此可见创建GraphicsLayer,虽然节省了layout,paint阶段,但Layer创建的越多,占用内存就会越大,而过多的渲染开销会超过性能的改善。
因此,当且仅当需要的时候,才会为元素创建渲染层。