HTTP1.0:浏览器与web服务器的链接过程是短暂的,每次链接只处理一个请求和响应,客户端与web服务器创建链接后,只能得到一个web资源。 css
HTTP1.1:在一个tcp链接上能够传送多个http请求和响应,多个请求和响应过程能够重叠进行,增长了更多的请求头和响应头;容许客户端与web服务器创建链接后,在一个链接上获取多个web资源。HTTP/1.1 中增长了持久链接的方法,它的特色是在一个 TCP 链接上能够传输多个 HTTP 请求,只要浏览器或者服务器没有明确断开链接,那么该 TCP 链接会一直保持。 html
关于HTTP2,由于浏览器会有并发请求限制,在 HTTP / 1.1 时代,每一个请求都须要创建和断开, 消耗了好几个 RTT 时间,而且因为 TCP 慢启动的缘由,加载体积大的文件会须要更多 的时间。 在 HTTP / 2.0 中引入了多路复用,可以让多个请求使用同一个 TCP 连接,极大 的加快了网页的加载速度。而且还支持 Header 压缩,进一步的减小了请求的数据大小。前端
Expires、Cache-Control、Last-Modified、Etag这是前端缓存经常使用4种手段。 Expires首部主要是针对HTTP vue
1.0版本,是响应头里的一个头部,是以日期时间的形式返回给客户端,指示能够缓存这个资源(响应实体)直到指定的日期时间。 node
Cache-Control首部是在HTTP 1.1版本之后加入的,提供了细粒度的缓存策略。 react
Last-Modified 在HTTP1.0推出的,指服务器文件的最后修改时间,浏览器会带上If-Modified-Since向服务器发送请求,与服务器文件修改时间Last-Modified作对比,若是时间不一样,则获取数据返回200,不然返回304后调用浏览器本地硬盘的缓存。 jquery
ETag 相似于文件指纹,在HTTP1.1推出,该版本号是由服务端随机生成的,浏览器会带上If-None-Match向服务器发送请求,与服务器文件修改版本ETag作对比,若是版本号不一样,则获取数据返回200,不然返回304后调用浏览器本地硬盘的缓存,这种方式比Last-Modified靠谱。git
instanceof 能够正确的判断对象的类型,由于内部机制是经过判断对象的原型链 中是否是能找到类型的 prototype。此外还有typeof。 typeof null 输出object是由于在JS 的最第一版本中,使用的是32位系统。为了性能考虑使用低位存储了变量的类型信息,000开头表明是对象,然而null 表示为全零,因此将它错误的判断为object。 若是咱们想得到一个变量的正确类型,能够经过0bject. prototype. toString.call(xx)。这样咱们就能够得到相似[object Type]的字符串。程序员
将编译产物缓存并提供随机访问。首先,把安全相关的js文件从静态服务中剥离出来,由一个后端的webserver输出js内容。该server上维护着一个长度必定的数组,构建工具编译好一个js文件后,将该文件的内容发送给web server,web server将接收到的内容顺序填充到数组中;当有用户页面时,浏览器向web server请求该js内容,web server从数组中随机挑选一个,返回给浏览器。除了能够保证安全js的随机性,还能将signature的生成放到web server中完成。构建工具在编译js时将编译的元信息发送给web server,此时并不生成出signature。用户须要请求该js时再根据元信息实时生成一个signature,填充到js文件内容中。这样生成的signature每次都是独立的,经过检测signature的使用次数,能够很容易标识并拦截重放的请求。es6
代码安全,通常是指前端JavaScript代码的安全。一般,若是一段JavaScript代码只能在正常的浏览器中运行,没法或还没有在非正常浏览器的运行环境执行获得结果、没法被等价翻译成其余编程语言的代码,则认为这段代码是安全的。前端代码保护主要一个是以语法树变换为基础的混淆保护,另外一个是以构建私有执行环境为思路的虚拟机保护,谷歌则属于后者。代码混淆的效果因混淆器的负责程度而不一样,基础级别的混淆器混淆出来的代码也很容易被逆向,而虚拟机保护的抗逆向效果好,其原理是在JS的执行环境之上再设计构建一个虚拟机,全部原有业务逻辑的JS代码均转换为该虚拟机可识别的字节码,这样复杂度较高效果好。
前端页面性能一直是前端同窗绕不开的话题,关于页面性能,一种通用而有效的性能优化方式是合理地为页面中的资源文件设置缓存。一般对于一个模块化良好且使用成熟打包工具打包的项目,入口html的缓存策略会被配置为Cache-Control: no-cache,而js/css/image等资源文件会设置一个比较长的缓存时间。但负责数据保护的js文件若是含有动态生成的逻辑,该js文件将不能再使用缓存,不然一旦缓存时间控制不当,将会引起各种数据解密失败的问题。在考虑前端安全的前提下,将数据保护相关的逻辑从整个工程的JavaScript代码中剥离出来,直接inline编译到html页面中,或者编译到一个独立的js文件中,为该js文件单独设置Cache-Control:no-cache的response头部。该js与其余js之间能够使用全局变量、postMessage等方式通讯。
function foo(x) { x = String.fromCharCode.apply(null, x.split('').map(i => i.charCodeAt(0) + 23); return btoa(x) }function bar(y) { y = String.fromCharCode.apply(null, y.split('').reverse().map(i => i.charCodeAt(0) + 13); return btoa(y); } 复制代码
对于同源页面,常见的方式包括: 广播模式:Broadcast Channe / Service Worker / LocalStorage + StorageEvent共享存储模式:Shared Worker / IndexedDB / cookie口口相传模式:window.open + window.opener基于服务端:Websocket / Comet / SSE 等。
而对于非同源页面,则能够经过嵌入同源 iframe 做为“桥”,将非同源页面通讯转换为同源页面
虽然如今的谷歌浏览器是多进程浏览器架构,可是Chrome的默认策略是,每一个标签对应一个渲染进程。可是若是从一个页面打开了新页面,而新页面和当前页面属于同一站点时,那么新页面会复用父页面的渲染进程。官方把这个默认策略叫process-per-site-instance。简单来讲若是几个页面符合同一站点,那么他们将被分配到一个渲染进程里面去,这种状况下,一个页面崩溃了,会致使同一站点的页面同时崩溃,由于他们使用了同一个渲染进程。
XSS解释略。1.利用模板引擎 2.避免内联事件 3.避免拼接 HTML 4.经过 CSP、输入长度配置、接口安全措施等方法,增长攻击的难度,下降攻击的后果 5.可以使用 XSS 攻击字符串和自动扫描工具寻找潜在的 XSS 漏洞
针对CSRF,须要至少从3个方面去考虑。CSRF自动防护策略:同源检测(Origin 和 Referer 验证); CSRF主动防护措施:Token验证 或者 双重Cookie验证 以及配合Samesite Cookie; 保证页面的幂等性,后端接口不要在GET页面中作用户操做。 针对转义库,至少须要考虑HTML 属性、HTML 文字内容、HTML 注释、跳转连接、内联 JavaScript 字符串、内联 CSS 样式表等等
微任务和宏任务是绑定的,每一个宏任务在执行时,会建立本身的微任务队列。 微任务的执行时长会影响到当前宏任务的时长。好比一个宏任务在执行过程当中,产生了 100 个微任务,执行每一个微任务的时间是 10 毫秒,那么执行这 100 个微任务的时间就是 1000 毫秒,也能够说这 100 个微任务让宏任务的执行时间延长了 1000 毫秒。因此你在写代码的时候必定要注意控制微任务的执行时长。 在一个宏任务中,分别建立一个用于回调的宏任务和微任务,不管什么状况下,微任务都早于宏任务执行。
微任务包括 process.nextTick ,promise ,Object.observe ,MutationObserver。宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering。 1. 执行同步代码,这属于宏任务 2. 执行栈为空,查询是否有微任务须要执行 3. 执行全部微任务 4. 必要的话渲染 UI 5. 而后开始下一轮 Event loop,执行宏任务中的异步代码
V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制。新生代中的对象通常存活时间较短,使用 Scavenge GC 算法。 在新生代空间中,内存空间分为两部分,分别为 From 空间和 To 空间。在这两 个空间中,一定有一个空间是使用的,另外一个空间是空闲的。新分配的对象会被放入 From 空间中,当 From 空间被占满时,新生代 GC 就会启动了。算法会检查 From 空 间中存活的对象并复制到 To 空间中,若是有失活的对象就会销毁。当复制完成后将 From 空间和 To 空间互换,这样 GC 就结束了。
某一个空间没有分块的时候、空间中被对象超过必定限制、 空间不能保证新生代中的对象移动到老生代中。而且新生区和老生区标记过程是同一个过程,以后新生代把存活的数据移动到空闲区,老生代把死去的对象加到空闲列表中。
解析器是parser,而解释器是interpreter。之因此存在编译器和解释器,是由于机器不能直接理解咱们所写的代码,因此在执行程序以前,须要将咱们所写的代码“翻译”成机器能读懂的机器语言。按语言的执行流程,能够把语言划分为编译型语言和解释型语言。 JavaScriptCore从SquirrelFish版开始是“基于寄存器”的,V8则不适合用“基于栈”或者“基于寄存器”的说法来描述。不少资料会说,Python、Ruby、JavaScript都是“解释型语言”,是经过解释器来实现的。这么说其实很容易引发误解:语言通常只会定义其抽象语义,而不会强制性要求采用某种实现方式。 例如说C通常被认为是“编译型语言”,但C的解释器也是存在的,例如Ch。一样,C++也有解释器版本的实现,例如Cint。 通常被称为“解释型语言”的是主流实现为解释器的语言,但并非说它就没法编译。例如说常常被认为是“解释型语言”的Scheme就有好几种编译器实现,其中率先支持R6RS规范的大部份内容的是Ikarus,支持在x86上编译Scheme;它最终不是生成某种虚拟机的字节码,而是直接生成x86机器码。实际上不少解释器内部是以“编译器+虚拟机”的方式来实现的,先经过编译器将源码转换为AST或者字节码,而后由虚拟机去完成实际的执行。所谓“解释型语言”并非不用编译,而只是不须要用户显式去使用编译器获得可执行代码而已。
V8是能够直接编译JavaScript生成机器码,而不经过中间的字节码的中间表示的JavaScript引擎,它内部有虚拟寄存器的概念,但那只是普通native编译器的正常组成部分。我以为也不该该用“基于栈”或“基于寄存器”去描述它。 V8在内部也用了“求值栈”(在V8里具体叫“表达式栈”)的概念来简化生成代码的过程,在编译过程当中进行“抽象解释”,使用所谓“虚拟栈帧”来记录局部变量与求值栈的状态;但在真正生成代码的时候会作窥孔优化,消除冗余的push/pop,将许多对求值栈的操做转变为对寄存器的操做,以此提升代码质量。因而最终生成出来的代码看起来就不像是基于栈的代码了。
至于面试官所说的基于栈与基于寄存器的架构,谁更快?看看如今的实际处理器,大多都是基于寄存器的架构,从侧面反映出它比基于栈的架构更优秀。
而对于VM来讲,源架构的求值栈或者寄存器均可能是用实际机器的内存来模拟的,因此性能特性与实际硬件又有点不一样。通常认为基于寄存器的架构对VM来讲也是更快的,缘由是:虽然零地址指令更紧凑,但完成操做须要更多的load/store指令,也意味着更多的指令分派(instruction dispatch)次数与内存访问次数;访问内存是执行速度的一个重要瓶颈,二地址或三地址指令虽然每条指令占的空间较多,但整体来讲能够用更少的指令完成操做,指令分派与内存访问次数都较少。
1. 通常是感官上的长时间运行页面卡顿,猜可能会有内存泄漏。经过DynaTrace(IE)profiles等工具一段时间收集数据,观察对象的使用状况。而后判断是否存在内存泄漏。 2. 工做中避免内存泄漏方法:肯定不使用的临时变量置为null,当前es6普及场景下少使用闭包也是一种方法。
整个JS代码是执行在一条线程里的,它并不像咱们使用的OC、Java等语言,在本身的执行环境里就能申请多条线程去处理一些耗时任务来防止阻塞主线程。JS代码自己并不存在多线程处理任务的能力。可是为何JS也存在多线程异步呢?强大的事件驱动机制,是让JS也能够进行多线程处理的关键。
将渲染进程和操做系统隔离的这道墙就是咱们要聊的安全沙箱。安全沙箱是不能防止 XSS 或者 CSRF 一类的攻击, 安全沙箱的目的是隔离渲染进程和操做系统,让渲染进行没有访问操做系统的权利 XSS 或者 CSRF 主要是利用网络资源获取用户的信息,这和操做系统没有关系的。
instanceof 能够正确的判断对象的类型,由于内部机制是经过判断对象的原型链 中是否是能找到类型的 prototype。
function instanceof(left, right) { // 得到类型的原型 let prototype = right.prototype // 得到对象的原型 left = left.__proto__ // 判断对象的类型是否等于类型的原型 while (true) { if (left === null) return false if (prototype === left) return true left = left.__proto__ } } 复制代码
简单来讲数据劫持就是利用Object.defineProperty()来劫持对象属性的setter和getter操做。vue2利用Object.defineProperty来劫持data数据的getter和setter操做。这使得data在被访问或赋值时,动态更新绑定的template模块。Vue采用了Proxy,Proxy不须要各类hack技术就能够无压力监听数组变化;甚至有比hack更强大的功能——自动检测length。
let onWatch = (obj, setBind, getLogger) => { let handler = { get(target, property, receiver) { getLogger(target, property) return Reflect.get(target, property, receiver); }, set(target, property, value, receiver) { setBind(value); return Reflect.set(target, property, value); } }; return new Proxy(obj, handler); }; let obj = { a: 1 } let value let p = onWatch(obj, (v) => { value = v }, (target, property) => { console.log(`Get '${property}' = ${target[property]}`); }) 复制代码
Service workers 本质上充当 Web 应用程序与浏览器之间的代理服务器,也能够在 网络可用时做为浏览器和网络间的代理。它们旨在(除其余以外)使得可以建立有效的 离线体验,拦截网络请求并基于网络是否可用以及更新的资源是否驻留在服务器上来采 取适当的动做。他们还容许访问推送通知和后台同步 API。 Service workers能够用来作缓存文件,提升首屏速度
Redux核心理念很清晰明了,单一数据源,不可变数据源(单一)state以及纯函数修改state。它的主要流程就是:旧Store -> 用户触发action(from view) -> reducer -> 新State -> view。Redux有一个全局仓库store来保存整个应用程序的state,而且修改store的惟一方式就是经过用户触发action 而后dispatch出去(action), dispatch 函数内部会调用 reducer 而且返回建立一个全新的state(而且销毁旧的state)来更新咱们的store。当store发生更新,view就会触发render函数进行更新。
不过Redux自己只能处理同步事件,Redux 做者(@dan_abramov)将异步流的处理经过提供中间件的方式让开发者自行选择,经常使用的异步流中间件有redux-thunk,还有redux-saga。
中间件 redux-thunk 中间件,它的机制主要经过判断 action 是否为一个函数(内部返回一个promise)。若是是则会当即调用,action 在函数内部能够进行异步流处理(本质仍是同步),而后继续经过dispatch(action)进行同步数据的处理;若是不是函数,则经过next(action)调用下一个中间件或者是进入reducer。redux-thunk的核心思想是扩展action,使得action从一个对象变成一个函数。redux-thunk处理异步便捷,配合asyvc/await更能够使得action同步化。不过redux-thunk中action会所以变的复杂,后期可维护性降低;同时若多人协做,当本身的action调用了别人的action,别人action发生改动,则须要本身主动修改。
// 简化后的核心部分 function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk; 复制代码
Redux-saga是一个用于管理redux应用异步操做的中间件之一,redux-saga经过建立sagas将全部异步操做逻辑收集在一个地方集中处理,能够用来代替redux-thunk中间件。Redux-saga相对于其余异步流中间件,将异步处理单独放在一块儿,不须要修改action,action仍是同步。同时redux-saga的异步控制流程也很强大,好比对于竞态的处理就经过takeLatest()来处理。 redux-saga 是一个用于管理应用程序 Side Effect(反作用,例如异步获取数据,访问浏览器缓存等)的 library,它的目标是让反作用管理更容易,执行更高效,测试更简单,在处理故障时更容易。能够想像为,一个 saga 就像是应用程序中一个单独的线程,它独自负责处理反作用。 redux-saga 是一个 redux 中间件,意味着这个线程能够经过正常的 redux action 从主应用程序启动,暂停和取消,它能访问完整的 redux state,也能够 dispatch redux action。
redux不能够像jquery同样直接更改组件的数据,若是咱们要更新某个节点上的值,首先要产生一个彻底全新的对象,好比图中的节点,在路径上全部的对象都处理更新过的状态,其余节点没有更新变化,由上至下,逐层比较。react节点的数据是不能直接修改的,若想修改就必须先进行复制,不管是浅复制仍是深复制,而后去包含你要修改的部分。
为了性能优化。由于当一个store发生变化,咱们须要通知全部的组件须要更新了。全部的变化都是由action触发,触发在原来旧的state上,造成一个新的state,新旧state是两个彻底不一样的对象,因此当新旧state不是同一个对象的时候,咱们就知道store发生了变化,咱们不须要比较它其中的值是否发生变化,咱们只须要比较两个引用的状态是否是同样便可,这样就能够达到一个性能优化的目的。
同时也代表了redux中的store都是不可变数据,每一个节点都是不可变数据。这样当一个组件绑定在一个节点上,这样我只要判断一个组件先后的引用状态是否相等,就能够知道当前的store有没有变,从而决定是否要更新你的组件,省去了深层次的遍历每一个值是否相等,只比较引用便可。同时便于调试和跟踪
①.处理 HTML 并构建 DOM 树;②.处理 CSS 构建 CSSOM 树;③.将 DOM 与 CSSOM 合并成一个渲染树;④.根据渲染树来布局,计算每一个节点的位置;⑤.调用 GPU 绘制,合成图层,再通过布局与具体WebKit Ports的渲染接口,把渲染树渲染输出到屏幕上,成为了最终呈如今用户面前的Web页面。
防抖:把触发很是频繁的事件(好比按键)合并成一次执行;节流:保证每 X 毫秒恒定的执行次数,间断执行。函数防抖,在一段连续操做结束后,处理回调,利用 clearTimeout 和 setTimeout 实现;函数节流,在一段连续操做中,每一段时间只执行一次,频率较高的事件中使用来提升性能。函数防抖关注必定时间连续触发,只在最后执行一次,而函数节流侧重于一段时间内只执行一次。
_.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; var later = function() { // 据上一次触发时间间隔 var last = _.now() - timestamp; // 上次被包装函数被调用时间间隔last小于设定时间间隔wait if (last < wait && last > 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; // 若是设定为immediate===true,由于开始边界已经调用过了此处无需调用 if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = _.now(); var callNow = immediate && !timeout; // 若是延时不存在,从新设定延时 if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; }; }; 复制代码
柯里化(Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,而且返回接受余下的参数并且返回结果的新函数的技术。
function add(){ var args = [].slice.call(arguments); var fn = function(){ var newArgs = args.concat([].slice.call(arguments)); return add.apply(null,newArgs); } fn.toString = function(){ return args.reduce(function(a, b) { return a + b; }) } return fn ; } // 能够接受任意个数的参数 add(1)(2,3) //6 add(1)(2)(3)(4)(5) //15 复制代码
前者支持动态导入,也就是 require(${path}/xx.js),后者目前不支持,可是 已有提案;
前者是同步导入,由于用于服务端,文件都在本地,同步导入即便卡住主线程 影响也不大。然后者是异步导入,由于用于浏览器,须要下载文件,若是也采 用导入会对渲染有很大影响;
前者在导出时都是值拷贝,就算导出的值变了,导入的值也不会改变,因此如 果想更新值,必须从新导入一次。可是后者采用实时绑定的方式,导入导出的 值都指向同一个内存地址,因此导入值会跟随导出值变化;
后者会编译成 require/exports 来执行的;
MVVM 由如下三个内容组成
View:界面;
Model:数据模型;
ViewModel:做为桥梁负责沟通 View 和 Model;
在 JQuery 时期,若是须要刷新 UI 时,须要先取到对应的 DOM 再更新 UI,这样数据和业务的逻辑就和页面有强耦合。在 MVVM 中,UI 是经过数据驱动的,数据一旦改变就会相应的刷新对应的 UI,UI若是改变,也会改变对应的数据。这种方式就能够在业务处理中只关心数据的流转,而无需直接和页面打交道。ViewModel 只关心数据和业务的处理,不关心 View 如何处理数据,在这种状况下,View 和 Model 均可以独立出来,任何一方改变了也不必定须要改变另外一方,而且能够将一些可复用的逻辑放在一个 ViewModel 中,让多个 View 复用这个 ViewModel。在 MVVM 中,最核心的也就是数据双向绑定,例如 Angluar 的脏数据检测,Vue中的数据劫持。
在开发H5营销页的时候,能够使用引擎Egret,也能够使用canavs,或者帧动画、瓦片地图等等。在一些复杂or附带角色场景的页面,这些场景确定有进入场景、退出场景、再次进入场景、退出场景、清理场景、更新场景的基本功能,大多游戏基本都是这样,场景有了,这个时候就须要手动建立一个管理器用于保障场景的复用性。
比较保守的作法就是,场景归场景管、UI归UI管,场景切换有管理类、UI应该也须要作一个LRU策略,在这里,UI须要根据使用频率来进行清理,好比15分钟一次都没用到,咱们就能够认为该UI资源使用不频繁,这样咱们就能够干掉图集和内存,这里只须要设置一个计时器,每次打开的时候记录上时间,而且我只针对关闭的面板遍历,由于已经打开的面板根本不须要去清理,亦不须要去检测,UI资源中最占内存的仍是图集,若是能够清理掉低频使用的图集就能够剩下不少内存,减小游戏总体内存压力,并且大型游戏UI资源占游戏体积是最大的。
咱们日常使用的H5游戏引擎,底层默认的操做是只要关闭UI就自动清理,这样会形成CPU发热上升,虽然这样积极地清理内存,可是用户体验就降低了。因此,咱们在这里建立的UI管理器,不须要管理UI的释放,只须要给出一个UI面板名,负责调用管理器的开、关就好。
至于UI的释放,能够设计一个机制,除了当前场景,已启用的UI资源15分钟内没有再次启用就自动释放(有点模仿GC的感受)。这里的15分钟计数器检测的是关闭面板的使用次数,面板打开一次就记一次使用次数,这里关闭的面板值得是曾开启的面板。在这里,咱们不须要记录每一个面板以前的开启次数、如今的开启次数,只须要在打开的时候递增次数,给出每一个面板的总次数便可。15分钟检测的时候若是在你设定的频率,咱们能够认为它使用的频繁,若是低于你设置的频率,就能够认为它是低频率使用的面板UI,15分钟后能够清理了。在以上的UI管理器设计思路中,最麻烦的是有些UI资源,它横跨几个面板,属于交叉复用资源,sceneMenu使用了sceneBoot里的资源sound,可是又来该面板的UI被清理掉了,玩家切换到sceneBoot的时候发现没有UI又要从新加载。
这里的MVP,不是LOL里的全场最佳那个意思。View:对应于Activity,负责View的绘制以及与用户交互Model:依然是业务逻辑和实体模型Presenter:负责完成View与Model间的交互View不直接与Model交互,而是经过与Presenter交互来与Model间接交互。 Presenter与View的交互是经过接口来进行的。 而MVVM 模式是将 Presenter 更名为 ViewModel,基本上与 MVP 模式彻底一致。惟一的区别是,它采用双向绑定(data-binding):View的变更,自动反映在 ViewModel,反之亦然。这样开发者就不用处理接收事件和View更新的工做,框架已经帮你作好了。
// 转化HTML至AST对象 function parse(template){ var currentParent; //当前父节点 var root; //最终生成的AST对象 var stack = []; //插入栈 var startStack = []; //开始标签栈 var endStack = []; //结束标签栈 //console.log(template); parseHTML(template,{ start:function start(targetName,attrs,unary,start,end,type,text){//标签名 ,attrs,是否结束标签,文本开始位置,文本结束位置,type,文本, var element = { //咱们想要的对象 tag:targetName, attrsList:attrs, parent:currentParent, //须要记录父对象吧 type:type, children:[] } if(!root){ //根节点哈 root = element; } if(currentParent && !unary){ //有父节点而且不是结束标签? currentParent.children.push(element); //插入到父节点去 element.parent = currentParent; //记录父节点 } if (!unary) { //不是结束标签? if(type == 1){ currentParent = element;//不是结束标签,当前父节点就要切换到如今匹配到的这个开始标签哈,后面再匹配到 startStack.push(element); //推入开始标签栈 } stack.push(element); //推入总栈 }else{ endStack.push(element); //推入结束标签栈 currentParent = startStack[endStack.length-1].parent; //结束啦吧当前父节点切到上一个开始标签,这能理解吧,当前这个已经结束啦 } //console.log(stack,"currentstack") }, end:function end(){ }, chars:function chars(){ } }); console.log(root,"root"); return root; }; // Regular Expressions for parsing tags and attributes var singleAttrIdentifier = /([^\s"'<>/=]+)/; var singleAttrAssign = /(?:=)/; var singleAttrValues = [ // attr value double quotes /"([^"]*)"+/.source, // attr value, single quotes /'([^']*)'+/.source, // attr value, no quotes /([^\s"'=<>`]+)/.source ]; var attribute = new RegExp( '^\\s*' + singleAttrIdentifier.source + '(?:\\s*(' + singleAttrAssign.source + ')' + '\\s*(?:' + singleAttrValues.join('|') + '))?' ); // could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName // but for Vue templates we can enforce a simple charset var ncname = '[a-zA-Z_][\\w\\-\\.]*'; var qnameCapture = '((?:' + ncname + '\\:)?' + ncname + ')'; var startTagOpen = new RegExp('^<' + qnameCapture); var startTagClose = /^\s*(\/?)>/; var endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>'); var doctype = /^<!DOCTYPE [^>]+>/i; var comment = /^<!--/; var conditionalComment = /^<!\[/; //偷懒哈 上面的正则是我在vue上拿下来的,这个后期能够研究,下面的话简单的写两个用用,和vue原版的是有一些差异的 var varText = new RegExp('{{' + ncname + '}}'); //空格与换行符 var space = /^\s/; var checline = /^[\r\n]/; /** type 1普通标签 type 2代码 type 3普通文本 */ function parseHTML(html,options){ var stack = []; //内部也要有一个栈 var index = 0; //记录的是html当前找到那个索引啦 var last; //用来比对,当这些条件都走完后,若是last==html 说明匹配不到啦,结束while循环 var isUnaryTag = false; while(html){ last = html; var textEnd = html.indexOf('<'); if(textEnd === 0){ //这一步若是第一个字符是<那么就只有两种状况,1开始标签 2结束标签 //结束标签 var endTagMatch = html.match(endTag); //匹配 if(endTagMatch){ console.log(endTagMatch,"endTagMatch"); isUnaryTag = true; var start = index; advance(endTagMatch[0].length); //匹配完要删除匹配到的,而且更新index,给下一次匹配作工做 options.start(null,null,isUnaryTag,start,index,1); continue; } //初始标签 var startMatch = parseStartTag(); if(startMatch){ parseStartHandler(startMatch);//封装处理下 console.log(stack,"startMatch"); continue; } } if(html === last){ console.log(html,"html"); break; } } function advance (n) { index += n; html = html.substring(n); } //处理起始标签 主要的做用是生成一个match 包含初始的attr标签 function parseStartTag(){ var start = html.match(startTagOpen); if(start){ var match = { tagName: start[1], // 标签名(div) attrs: [], // 属性 start: index // 游标索引(初始为0) }; advance(start[0].length); var end, attr; while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {//在endClose以前寻找attribute advance(attr[0].length); match.attrs.push(attr); } if (end) { advance(end[0].length); // 标记结束位置 match.end = index; //这里的index 是在 parseHTML就定义 在advance里面相加 return match // 返回匹配对象 起始位置 结束位置 tagName attrs } } } //对match进行二次处理,生成对象推入栈 function parseStartHandler(match){ var _attrs = new Array(match.attrs.length); for(var i=0,len=_attrs.length;i<len;i++){ //这儿就是找attrs的代码哈 var args = match.attrs[i]; var value = args[3] || args[4] || args[5] || ''; _attrs[i] = { name:args[1], value:value } } stack.push({tag: match.tagName,type:1, lowerCasedTag: match.tagName.toLowerCase(), attrs: _attrs}); //推栈 options.start(match.tagName, _attrs,false, match.start, match.end,1); //匹配开始标签结束啦。 } } 复制代码
虚拟 DOM 是一样操做 DOM,不过是把 DOM 树抽象成数据对象,Virtual Dom 它能够使咱们操做这块的数据对象,用数据对象来呈现 DOM 树,在每次视图渲染的时候 patch 取得最优,这种作法使咱们最小量地去修改 DOM,此外 Virtual DOM 还有一个很大的做用是简化 DOM 操做,让数据与 DOM 之间的关系更直观更简单。
虚拟DOM不会进行形成排版与重绘操做;
虚拟DOM进行频繁修改,而后一次性比较并修改真实DOM中须要改的部分(DIFF算法),最后并在真实DOM中进行排版与重绘,减小过多DOM节点排版与重绘损耗;
真实DOM频繁排版与重绘的效率是至关低的;
虚拟DOM有效下降大面积(真实DOM节点)的重绘与排版,由于最终与真实DOM比较差别,能够只渲染局部使用虚拟DOM的损耗计算。像JQuery这种是属于以选择器为导向的框架,Vue和React属于有明确分层架构的MV*框架,而虚拟DOM属于框架中的节点模块,也是核心模块,重中之重。
①.两个相同组件产生相似的 DOM 结构,不一样的组件产生不一样的 DOM 结构;
②.对于同一层次的一组子节点,它们能够经过惟一的 id 进行区分。算法上的优化是 React 整个界面 Render 的基础,事实也证实这两个假设是合理而精确的,保证了总体界面构建的性能。
1.减小回流,避免频繁改动;2.避免逐条改变样式,使用类名去合并样式;3.经常使用优化小操做以下:
使用 translate 替代 top
使用 visibility 替换 display: none ,由于前者只会引发重绘,后者会引起回流(改变了布局)
把 DOM 离线后修改,好比:先把 DOM 给display:none (有一次 Reflow),而后你修改100次,而后再把它显示出来
不要把 DOM 结点的属性值放在一个循环里当成循环里的变量
不要使用 table 布局,可能很小的一个小改动会形成整个 table 的从新布局
动画实现的速度的选择,动画速度越快,回流次数越多,也能够选择使用requestAnimationFrame
CSS 选择符从右往左匹配查找,避免 DOM 深度过深
将频繁运行的动画变为图层,图层可以阻止该节点回流影响别的元素。好比对于 video 标签,浏览器会自动将该节点变为图层。
避免频繁读取会引起回流/重绘的属性,若是确实须要屡次使用,就用一个变量缓存起来。
对具备复杂动画的元素使用绝对定位,使它脱离文档流,不然会引发父元素及后续元素频繁回流。
首先,jQ是以选择器为导向的。
class jQuery { constructor() { const result = document.querySelectorAll(selector) const length = result.length for (let i = 0; i < length; i++) { this[i] = result[i] } this.length = length this.selector = selector } get(index) { return this[index] } each(fn) { for (let i = 0; i < this.length; i++) { const elem = this[i] fn(elem) } } on(type, fn) { return this.each(elem => { elem.addEventListener(type, fn, false) }) } style(data) {} } const $p = new jQuery('p') $p.get(1) $p.each((elem) => console.log(elem.nodeName)) 复制代码
弃用的缘由,就是中间哪些不须要这些数据的组件,也会由于接收到这些数据而去二次渲染
①WebView初始化慢,能够在初始化同时先请求数据,让后端和网络不要闲着。
②后端处理慢,可让服务器分trunk输出,在后端计算的同时前端也加载网络静态资源。③脚本执行慢,就让脚本在最后运行,不阻塞页面解析。
④同时,合理的预加载、预缓存可让加载速度的瓶颈更小。
⑤WebView初始化慢,就随时初始化好一一个WebView待用。
⑥DNS和连接慢,想办法复用客户端使用的域名和连接。
⑦脚本执行慢,能够把框架代码拆分出来,在请求页面以前就执行好。
React的核心组成之一就是可以维持内部状态的自治组件,不过当咱们引入原生的HTMI.表单元素时
( input, select, textarea等),咱们是否应该将全部的数据托管到React组件中仍是将其仍然保留在DOM 元素中呢?这个问题的答案就是受控组件与非受控组件的定义分割。
受控组件(Controlled Component)代指那些交由React 控制而且全部的表单数据统一存放的组件。譬以下面这段代码中username变量值并无存放到DOM元素中,而是存放在组件状态数据中。任什么时候候咱们须要改变username变量值时,咱们应当调用setState函数进行修改。
非受控组件(Uncontrol led Couponent )则是由DOM存放表单数据,并不是存放在React 组件中。咱们能够使用refs来操控DOM元素:
不过实际开发中咱们并不提倡使用非受控组件,由于实际状况下咱们须要更多的考虑表单验证、选择性的开启或者关闭按钮点击、强制输入格式等功能支持,而此时咱们将数据托管到React中有助于咱们更好地以声明式的方式完成这些功能。引入React 或者其余MWW框架最初的缘由就是为了将咱们从繁重的直接操做DOM中解放出来。
Keys 是React用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
在开发过程当中,咱们须要保证某个元素的key 在其同级元素中具备惟-一性。 在React Diff算法中React 会借助元素的Key值来判断该元素是新近建立的仍是被移动而来的元素,从而减小没必要要的元素重渲染。此外,React还须要借助Key值来判断元素与本地状态的关联关系,所以咱们毫不可忽视转换函数中Key的重要性。
以网络请求为例能够在Dart中定义一个MethodChannel对象,而后在Java端实现相同名称的MethodChannel,在Flutter页面中注册后,调用post方法就能够调用对应的Java实现。
使用CSP拦截页面中的非白名单资源、使用HTTPS、让App将其转换为一个Socket请求,并代理WebView的访问、在内嵌的WebView中应该限制容许打开的WebView的域名,并设置运行访问的白名单。或者当用户打开外部连接前给用户强烈而明显的提示。
不会
回答用覆盖率,具体实现方法不会。
瓦片地图
我选flutter,理由在下面。
不必定,这是一个性能 vs. 可维护性的取舍。框架的意义在于为你掩盖底层的 DOM 操做,让你用更声明式的方式来描述你的目的,从而让你的代码更容易维护。没有任何框架能够比纯手动的优化 DOM 操做更快,由于框架的 DOM 操做层须要应对任何上层 API 可能产生的操做,它的实现必须是普适的。框架给你的保证是,你在不须要手动优化的状况下,我依然能够给你提供过得去的性能。
SameSite属性是cookie除了经常使用的path, domain, expire, HttpOnly, Secure的一个专门用于防止csrf漏洞的属性。在cookie上引入SameSite属性提供了三种不一样的方法来控制CSRF漏洞。咱们能够选择不指定属性,也能够使用Strict或Lax将cookie限制为同站点请求。1.设置SameSite = Strict,则表示您的cookie仅在第一方网站中发送使用;2.设置SameSite = Lax,当用户发送GET方法的同步请求时,将会发送cookie;3.当SameSite = None时,这意味着你能够在第三方环境中发送cookie。
基于web容器即基于浏览器的跨平台也作得愈来愈好,天然管线也愈来愈短,与native的一些技术手段来实现性能上的相互补充。好比Egret、Cocos、Laya这些游戏引擎,它们在跨平台方面的作法多以Typescript编写,在iOS和安卓平台的各类浏览器中轻松的运行HTML5游戏,并在不一样平台浏览器里提供近乎一致的用户体验,好比Egret还会提供高效的 JS-C Binding 编译机制,以知足游戏编译为原生格式的需求,不过大多数HTML游戏引擎(好比egret和laya)也属于web容器这个范畴内。web容器框架也有一个明显的致命(在对体验&性能有较高要求的状况下)的缺点,那就是WebView的渲染效率和JavaScript执行性能太差。再加上Android各个系统版本和设备厂商的定制,很难保证所在全部设备上都能提供一致的体验。 泛 Web 容器框架好比ReactNative和Weex,即上层经过面向前端友好的UI,下层经过native的渲染形式,虽然一样使用类HTML+JS的UI构建逻辑,可是最终会生成对应的自定义原生控件,以充分利用原生控件相对于WebView的较高的绘制效率,同时H5与native相互补充来达到更好的用户体验,这也是一种很好的解决方案。缺陷也很明显,随着系统版本变化和API的变化,开发者可能也须要处理不一样平台的差别,甚至有些特性只能在部分平台上实现,这样框架的跨平台特性就会大打折扣。
自绘引擎框架这里专指Flutter框架,从底层就承担跨端的任务和渲染方式,就使用方面来看,就是写样式有点费劲,嵌套警告(根本缘由是我菜)。
小弟以为,当下互联网就业环境并非寒冬,只不过是更加理智,投资人不再是拿张PPT随便说说就能搞到钱了,面试者不再是背背面试题就能找到工做的,就业环境的严峻,提醒咱们更加要注重自身能力的培养,而不是糊弄。更加注重本身的理论知识怎样可以为公司、产品带来更大的价值,曾有面试官这样问我,"你以为公司招你来是作什么的,招你来是让你解决问题的",这同时也要求不只仅要有足够的硬实力,从软实力方面来讲,不只要把事情作好,更要作好向上管理和向下管理。
大多数状况下,在面试过程当中,面试官或者公司是主导方,节奏是跟着面试官走,可是我以为面试其实就是一个相互探讨的过程,不只是公司在选择你,你也在选择公司、之后工做的同事和leader。虽然我在面试蚂蚁金服的过程当中失败了,可是我我的的面试风格就是,我会就是会说一堆,不会我就会说不会,我历来不会尝试搪塞、企图蒙混过关(在有些人看来可能有点傻),可是我就是我,我会就是会,我所能作的,顶多就是向面试官请教和探讨,尽量尝试向面试官表达个人所知所学。因此我此次面试中,更多的是和面试官探讨,也会反问面试官一些问题,不只仅是最后面试官说能够问他几个问题,在面试过程当中就会询问一些,优秀的面试官会跟你探讨,而且讨论出一个合理的方案或者正确的答案,整个过程很是愉悦,好比,阿里的面试官们,面试官打开个人github针对里面的项目一个个询问,并鼓励我继续完善下去。
此次寒冬面试,学历、实力均受限,直接致使没有入职BAT TMD,不过最大的收获就是跟10+多位面试官的交流,再次更加让我认清我本身,认清本身的长处、短板和之后的规划,和对作人作事的见解,在这里真诚的感谢10多位面试官。
最后,我到底去了大公司、小公司仍是外包,不便透露,我只是一个平凡的新手前端。对于一些同窗对于求职去大公司仍是小公司的咨询,个人态度是,在实力容许的状况下,建议本着"工做是为了更好的生活"这个宗旨去综合考虑,由于人生本就是一场不可重启的赌博。