javaScript
-
理解MVVM等框架,手写伪代码。
-
ES6新特性,说说class
-
从编译角度谈谈变量提升
-
对象的类型
-
继承中的toString
每一个引用类型会重写toString方法。
所以如果要输出对象的类型,需要使用Object.prototype.toString方法。
将他call到需要输出的对象上。
基本类型6个
boolean、string、null、undefined、number、symbol
引用类型1个
object
-
判断类型
1、instanceof
内部使用原型链的方式进行比较。
所以必须是引用类型,不可以是基本类型。
但是!!如果使用new创建的string为对象(引用类型)
2、typeof
只能对基本类型进行判断,对引用类型(Array、RegExp)返回object
3、constructor
str.constructor == String //true
arr.constructor == Array //true
4、toString
Object.prototype.toString.call(arr) //"[object Array]"
Object.prototype.toString.call(str) //"[object String]"
5、原型链比较proto
str.__proto__ == String.prototype //true
arr.__proto__ == Array.prototype //true
-
继承
不要修改对象的__proto__
属性,修改这个属性会同时修改构造函数的prototype
的对应的参数,然后会修改所有派生的其他对象。
在javaScript编译过程中,分为两个阶段,预编译阶段和执行赋值阶段,在预编译阶段,js编译器会查找代码中的var关键字和函数声明,并将他们在顶部声明并赋值为undfine。执行赋值阶段在进行赋值。
Webpack
-
webpack是什么?运行流程?热更新原理?
webpack是一个打包模块化javaScript的工具,在webpack里一切文件皆为模块,通过loader转换文件,通过plugin注入钩子,最后输出由多个模块组合成的文件,webpack专注构建模块化项目。webpack可以看作是模块打包机:他做的事情是,分析你的项目结构,找到javaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript),并将其打包(通过loader)为合适的格式(css,ES5,javaScript)以供浏览器使用。
image
-
0x02 webpack运行流程
1、初始化参数:从配置文件以及shell命令行获取参数,并整合成输入参数。
2、开始编译:将上一步的参数初始化compiler对象,加载所有配置的插件plugin,执行run方法开始编译。
3、寻找入口文件:通过配置文件的entry来找到所有的入口文件。
4、进行模块的编译:从入口文件出发,将所有依赖的模块通过loader进行编译,之后递归遍历所有引用的模块,编译输出。
5、完成模块编译:在经过第4步以后,得到了每个模块最终的翻译内容以及它们的依赖关系。
6、输出资源:根据入口文件和模块的依赖关系,组装成一个一个包含多个模块的chunk,再把每个chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出文件的最后机会。
7、输出完成,在确定好输出内容以后,根据配置确定输出的路径和文件名,再把文件内容写入到文件系统。
在以上过程中,webpack在特定时间会广播特定事件,plugin可以监听感兴趣的事件,并通过webpackAPI修改运行结果。
-
0x03 loader和plugin的编写思路以及功能特点:
Loader像是一个翻译官,把读到的原文件内容转译成新的文件内容,并且每个loader通过链式操作,将原文件一步一步翻译成想要的样子。
编写loader要遵循单一原则,每个loader只做一种转义功能。每个loader拿到的是原文件内容(source),可以通过返回值的方式将处理后的内容输出,也可以调用this.callback()方法,将内容返回给webpack。还可以通过this.async()生成一个callback函数,再用这个callback将处理后的内容输出出去。此外webpack还为开发者准备了开发loader的工具函数集--------loader-utils。
相对于loader而言,plugin的编写就灵活了许多,webpack运行的生命周期中会广播出很多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果。
-
0x04 webpack的热更新是如何做到的?说明其原理?
webpack的热更新又称热替换(Hot Module Replacement),缩写为HMR。这个机制可以做到不用刷新浏览器而将新更变的模块替换旧的模块。
image
1、第一步,在webpack的watch模式下,文件系统的某一个文件发生变化以后,webpack监听到文件变化,根据配置文件对模块重新打包编译,并将打包后的代码通过简单的javaScript对象保存在内存中。
2、第二步,webpack-dev-server和webpack之间的接口交互,在这一步,主要是dev-server的中间件webpack-dev-middleware和webpack之间的交互,webpack-dev-middleware调用webpack暴露的API对代码进行监控,并且告诉webpack,将代码打包到内存中。
3、第三步,webpack-dev-server对文件变化的监控,当我们的配置文件中devServer.watchContentBase为true的时候,server会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器对应用进行live reload。是刷新浏览器,和HMR是两个概念。
4、第四步,webpack-dev-server通过sockjs(sebpack-dev-server的依赖)在浏览器和服务器之间建立一个websocket长连接,将webpack编译打包的各个阶段的状态信息告知浏览器端,同时也包括第三步中server监听静态文件变化的信息。浏览器根据这些信息进行不同的操作。当然,服务端传递的最主要的信息还是新模块的hash值,后面的步骤根据这个hash值来进行模块的热替换。
5、webpack-dev-server/client端并不能够请求更新的代码,也不会执行热更新模块操作,而是把这些工作交给webpack,webpack/hot/dev-server的工作就是根据webpack-dev-server/client传给他的信息以及dev-server的配置决定是刷新浏览器还是进行热更新,如果仅仅是刷新浏览器,就没有后面的步骤了。
6、HotModuleReplacement.runtime是客户端HMR的中枢,它接受上一步传递给他的新模块的hash值,他通过JsonpTemplate.runtime向server端发送ajax请求,服务端返回一个json,该json包含了所有要更新的模块的hash值,获取到更新列表后,该模块再次通过jsonp请求,获取到最新的模块代码,这就是上图的7、8、9步骤。
7、而第十步是决定HMR成功与否的关键步骤,在该步骤中,HotMoudulePlugin将会对新旧模块进行对比,决定是否更新模块,再决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。
8、最后一步,当HMR失败后,回退到live reload操作,也就是刷新浏览器获取最新打包代码。
0x05 手写promise
function MyPromise(executor) {
let that = this;
this.state = 'pending'
this.value = undefined
this.reason =undefined
this.onFulfilledFunc = [];
this.onRejectedFunc = [];
executor(resolve, reject)
function resolve(value){
if(that.state == 'pending'){
that.value = value;
that.onFulfilledFunc.forEach(fn => fn(value))
that.state = 'resolved'
}
}
function reject(reason){
if(that.state == 'pending'){
that.reason = reason;
that.onRejectedFunc.forEach(fn => fn(reason))
that.state = 'rejected'
}
}
}
MyPromise.prototype.then = function(onFulfilled, onRejected) {
if(this.state == 'resolved'){
if(typeof onFulfilled === 'function'){
onFulfilled(this.value)
}
}
if(this.state == 'rejected'){
if(typeof onRejected === 'function'){
onRejected(this.reason)
}
}
if(this.state === 'pending'){
if(typeof onFulfilled === 'function'){
this.onFulfilledFunc.push(onFulfilled)
}
if(typeof onRejected === 'function'){
this.onRejectedFunc.push(onRejected)
}
}
}
let a = new MyPromise((resolve, reject) => {
setTimeout(()=>{
resolve('assssd')//
}, 1)
})
a.then(res => {
console.log(res)
})
0x06 手写jsonp
(function(window, document){
"use strict"
var jsonp = function(url, data, callback){
//data 为json对象
//将data转化为dataStr
let dataStr = '?'
for (const key in data) {
dataStr += key + '=' + data[ley] + '&'
}
//定义jsonp的callback名字,需要包含随机数字
let cbFuncName = 'my_json_cb_' + Math.random().toString().replace('.', '');
dataStr += 'callBack='+cbFuncName;
//创建jsonp script节点
let script = document.createElement('script');
//修改jsonp script节点的url
script.src = url + dataStr
//全局jsonp函数
window[cbFuncName] = function(data) {
//执行callback
callback(data)
//执行完毕删除script节点
document.body.removeChild(script)
}
//加入script节点进行请求
document.body.appendChild(script)
//将jsonp挂载到window上面
window.$jsonp = jsonp
}
})(window, document)
0x07 手写new
//new
function _new(){
let newObj = {};
let Constructor = Array.prototype.shift.call(arguments);
newObj.__proto__ = Constructor.prototype;
Constructor.apply(newObj, arguments);
return newObj
}
0x08 手写bind/call/apply
//call
/**
* @param this
* @param ...arg
*/
Function.prototype.myCall = function(obj){
let object = obj || window;
let args = [...arguments].slice(1);
object.func = this;//绑定函数
let result = object.func(...args);
delete object.func;
return result
}
//bind
Function.prototype.myBind = function(obj){
let that = this;
const object = obj;
let args = [...arguments].slice(1);
return function(){
let newArgs = [...arguments];
return that.apply(object, args.concat(newArgs))
}
}
0x09 手写双向绑定
<body>
<div></div>
<p></p>
<div style="border-top: 10px solid rgba(0, 0, 0, 0);border-right: 10px solid rgba(0, 0, 0, 0.5);border-left: 10px solid rgba(0, 0, 0, 0);border-bottom: 10px solid rgba(0, 0, 0, 1);background: rgba(0, 0, 0, 0);width: 10px;height: 10px;"></div>
<input type="text" id="txt">
<script>
console.log(Math.round(4, 2))
var obj = {}
Object.defineProperty(obj, 'txt', {
get: function() {
return obj
},
set: function(newValue){
document.body.getElementsByTagName('div')[0].innerHTML = newValue
}
})
document.addEventListener('keyup', (e) => {
obj.txt = e.target.value
})
</script>
</body>
算法
- 二叉树
javaScript前序遍历
// 先序遍历:中,左,右
const TreeNode = {
val: 1,
left: {
val: 2,
left: {
val: 4,
},
right: {
val: 5
}
},
right: {
val: 3,
left: {
val: 6,
},
right: {
val: 7
}
}
};
// 递归方式的先序遍历方法
var preOrderRecur = function(root){
var list = [];
var preOrder = function(root){
if(root === undefined){
return root;
}
list.push(root.val)
preOrder(root.left);
preOrder(root.right);
}
preOrder(root);
return list;
};
// 非递归方式的先序遍历方法
var preOrder = function(TreeNode){
var list = [];
let stack = [TreeNode];
while(stack.length !== 0){
const cur = stack.pop();
const right = cur.right;
const left = cur.left;
list.push(cur.val);
if(right){
stack.push(right);
}
if(left){
stack.push(left);
}
}
return list;
}
var list = preOrderRecur(TreeNode);
console.log('递归前序遍历', list);
var listUnRecur = preOrder(TreeNode);
console.log('非递归前序遍历', listUnRecur);
// [1, 2, 4, 5, 3, 6, 7]
- 链表环
- 冒泡算法都有哪些?优势与区别?
- 时间复杂度是什么?如何计算时间复杂度?
node
- 异步
- 调试
html css
- ie模式和标准模式的盒模型
vue和react的区别
react setState如何优化
shouldComponentUpdate(nextProps, nextState) {
return nextState.someData !== this.state.someData
}
http2头部压缩
http2头部压缩采用HPACK头部压缩,使用霍夫曼编码。
宏任务、微任务、event-loop
宏任务、微任务、event-loop
宏任务:setInterval、setTimeout、setImmediate、IO、requestAnimationFrame
微任务:promise、nextTick、async/await(本质上是promise)、mutationObserver
event-loop:实在宏任务完成后进行微任务检查。
- 注意:宏任务中requestAnimationFarme要先于setTimeout执行。
- setTimeout和setImmediate同级。
- setTimeout为何不准确?如何让setTimeout更准确?
setTimeout不准确是因为代码运行需要时间。
在setTimeout内部计算代码运行的时间,然后减去。
http1.0 http1.1 http2.0 http和https区别
https://www.cnblogs.com/heluan/p/8620312.html
HTTP1.0和HTTP1.1的一些区别
-
缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
-
带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
-
错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
-
Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
-
长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。
HTTP2.0的一些优势
-
新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
-
多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
-
header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
-
服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能。
HTTP和HTTPS的一些区别
-
HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费。
-
HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。
-
HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
-
HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题。
async await 实现原理 generator + yield + promise
https://www.jianshu.com/p/862ab6d1a2f6
主要是通过generator+yield+promise实现
原型链图
image.pnglet和var
var
和let
均有变量遮蔽!!。
var
没有块级作用域,但是let
有块级作用域!!。
map和parseInt问题
[1,2,2,3].map(parseInt)
//(4) [1, NaN, NaN, NaN]
因为parseInt
第二个参数为将要转换的数字进制类型。
2进制没有3这个数字,
3进制没有4这个数字,
以此类推,所有显示NaN
reduce实现map
Array.prototype._map = function(fn) {
return this.reduce((result, item) => [...result, fn(item)], [])
}
Array.prototype._filter = function(fn) {
return this.reduce((result, item) => fn(item) ? [...result, item] : result, [])
}
稀疏数组
1 in [0,,2,3,4]
//false
1 in [0,2,2,3,4]
//true
BFC
多个标签页间进行通讯
- worker
通过postMessage和事件监听
//worker
// sharedWorker所要用到的js文件,不必打包到项目中,直接放到服务器即可
let data = ''
onconnect = function (e) {
let port = e.ports[0]
port.onmessage = function (e) {
if (e.data === 'get') {
port.postMessage(data)
}
else { data = e.data }
}
}
// 这段代码是必须的,打开页面后注册SharedWorker,显示指定worker.port.start()方法建立与worker间的连接
if (typeof Worker === "undefined") {
alert('当前浏览器不支持webworker')
} else {
let worker = new SharedWorker('worker.js')
worker.port.addEventListener('message', (e) => {
console.log('来自worker的数据:', e.data)
}, false)
worker.port.start()
window.worker = worker
}
// 获取和发送消息都是调用postMessage方法,我这里约定的是传递'get'表示获取数据。
window.worker.port.postMessage('get')
window.worker.port.postMessage('发送信息给worker')
//页面A发送数据给worker,然后打开页面B,调用window.worker.port.postMessage('get'),即可收到页面A发送给worker的数据。
- websocket
- 轮询
- 同域cookie
- localStorage
最大数字
javaScript最大数字
判断整数
xxx % 1 === 0
1+-'1'+1 = 1
七层协议
- 物理层
第一层,物理电路连接,网络通信数据传输介质,由电缆和设备构成。 - 数据链路层
第二层,传输以‘帧’为单位的数据包,采用差错控制与流量控制,使得有差错的物理线路变成无差错的数据链路。基于MAC地址实现,SWITCH交换机。 - 网络层
第三层,为数据节点之间传输创建逻辑链路,通过路由选择算法为子网选择合适的路径,实现网络拥塞控制,网络互连。基于IP,实现router路由器。 - 传输层
第四层,提供可靠的端对端服务,处理数据包错误,数据次序,以及一些其他的关键问题,向高层屏蔽下层细节,是非常关键的一层。
TCP/IP,UDP等协议在此层。 - 会话层
第五层,负责维护两个节点的传输连接,确保点到点不中断,管理数据交换。 - 表示层
第六层,处理两个通信系统中交换信息的表示方式,主要包括数据格式的变换,数据的加密解密,数据的压缩与恢复。 - 应用层
最高层,为软件提供很多服务,比如文件服务器,数据库服务,电子邮件与其他网络软件服务。(HTTP、POP、STMP、FTP) - Promise catch
内部采用try...catch实现的,只能catch同步操作
curry
function curry(fn){
//第一个参数是基础执行方法,slice切除
var args=Array.prototype.slice.call(arguments,1);
//直接返回匿名函数
return function(){
//slice新参数以便能调用concat
var innerArgs=Array.prototype.slice.call(arguments);
//将配置的参数和新传入的参数合并
var finalArgs=args.concat(innerArgs);
return fn.apply(null,finalArgs);
};
}
//ES6
var curry = fn => (...left) => (...right) => fn(...left, ...right)
reduce
Array.prototype.customReduce = function(fn , prev) {
for(let i = 0; i<this.length; i++) {
if (typeof prev === 'undefined') {
// prev不存在
prev = fn(this[i], this[i+1], i+1, this);
i++;
} else {
prev = fn(prev, this[i], i, this);
}
}
return prev;
}
- 使用reduce实现map
Array.prototype._map = function(fn, callbackThis) {
// 最终返回的新数组
let res = [];
// 定义回调函数的执行环境
// call第一个参数传入null,则 this指向全局对象,同 map的规则
let CBThis = callbackThis || null;
this.reduce((brfore, after, idx, arr) => {
// 传入map回调函数拥有的参数
// 把每一项的执行结果push进res中
res.push(fn.call(CBThis, after, idx, arr));
}, null);
return res;
};
首页白屏?
当前很多无线页面都使用前端模板进行数据渲染,那么在糟糕的网速情况下,一进去页面,看到的不是白屏就是 loading,这成为白屏问题。
此问题发生的原因基本可以归结为网速跟静态资源
1、css文件加载需要一些时间,在加载的过程中页面是空白的。 解决:可以考虑将css代码前置和内联。
2、首屏无实际的数据内容,等待异步加载数据再渲染页面导致白屏。 解决:在首屏直接同步渲染html,后续的滚屏等再采用异步请求数据和渲染html。
3、首屏内联js的执行会阻塞页面的渲染。 解决:尽量不在首屏html代码中放置内联脚本。(来自翔歌)
解决方案
根本原因是客户端渲染的无力,因此最简单的方法是在服务器端,使用模板引擎渲染所有页面。同时
1减少文件加载体积,如html压缩,js压缩
2加快js执行速度 比如常见的无限滚动的页面,可以使用js先渲染一个屏幕范围内的东西
3提供一些友好的交互,比如提供一些假的滚动条
4使用本地存储处理静态文件。
json不能传输的格式
DOM节点、文件表单等不能转化为文本的信息。
前端性能指标
白屏时间
首屏时间
用户可操作时间
页面总下载时间
inline-block
- 设置letter-spacing: -3px;
- 设置font-size: 0px;
- 设置font-size:0px;会有什么问题?
如果inline-block里面没有内容的话,垂直方向会有间隙,需要设置:
{
font-size: 0px;
vertical-align: bottom;
}
伪类和伪元素
- 伪类是选择器,辅助CSS选择器选不到的元素
比如
:first-child
:hover
:focus - 伪元素是添加的额外的css容器
如
::before
::after
xss和csrf
参考文章XSS
参考文章CSRF
XSS:通过任何手段,在目标网站上留下一段脚本,当受害者执行时记录cookie等信息。
CSRF:通过目标网站植入表单等脚本,诱导用户执行。借用cookie,但是不记录。
display属性
image.pngalign属性
image.pngtext-align属性
image.pngvertical-align属性
image.png数字超过最大安全数字
安全数字bigInt
最大数字1.111112^1023 Number.MAX_VALUE
最大安全数字1.111112^52 Number.MAX_SAFE_INTEGER
使用bigInt可以解决,数字+n
typeof 12n//bigInt
网友评论