【只要看到新的搞不懂的面试/笔试题就会来更新,写的比较简略,答案仅供参考,主要是整理下问题】
-
重排和重绘:
重排一定重绘,重绘不一定重排,重绘就是重新画一遍,重排是重新计算布局,如果只是改变了颜色则只重绘,影响到大小或位置则会重排,此外,添加/删除/获得大小,位置等相关的样式也会触发重排,因为重排不是立即生效的,会在某个时间节点生效,为了得到正确的样式,则会触发重排。频繁重排影响性能,所以需要频繁进行dom操作的节点可以脱离文档流(就不重排了),此外,不要生成一个节点添加一个,那样会触发n次重排,最好全部生成完后一起添加。重排(回流,重写)是重新计算布局,在GUI里边,GUI完事了才能给GPU,然而GUI会被js卡住,所以浏览器的更新是会受js线程影响的。而且GUI老是算,肯定影响浏览器性能,所以少触发重排,这里涉及到渲染优化。
-
es6的class和构造函数:
class这个关键字声明的东西还是个函数,并且指向的是这个类内的构造函数,也就是其实还是构造函数,只不过比通过原型来模拟继承要好理解一些。 -
变量和函数提升:
函数声明先于变量声明,赋值不提升只提升声明,let和const除外。
function foo(){};
var foo=function(){};
var i=2;
实际如下:
function foo(){};
var foo;
var i;
foo=function(){};
i=2;
-
原型链:
就是每个对象都有个原型对象,原型对象又有原型对象,一个方法如果对象内没有直接声明,那就顺着原型链摸,还有就是,如果希望原型不是默认的,那在写构造函数的时候就自己声明一下prototype。 -
prototype ,proto,constructor:
prototype和_proto_是对象,constructor是(构造)函数,prototype是构造函数内的,意思是通过这个构造函数创建的对象的原型都是prototype,对象内的是_proto_,指向的是构造这个对象的构造函数的prototype,constructor也是对象的,指向的是创建这个对象的构造函数。 -
call和apply:
修改上下文,说白了就是改变this,改变环境,这俩的第一个传入参数都是目标this对象,不同是call从第二个传入参数开始一个一个传入所需参数,apply是把所有参数合成一个数组,作为一个参数传入。用法是:对象.方法.call(目标对象,参数1,参数2)和对象.方法.apply(目标对象,[参数1,参数2]) -
bind:
bind也是改变上下文,区别在于,call和apply是改完上下文之后直接运行该函数,而bind是返回一个改变了上下文的新函数,需要再加个括号才能调用,类似于,方法.bind(目标对象)(),它的第一个传入参数是目标对象,第二个开始的传参视为原函数的传参的第一个,举例:var m = a.bind(obj,x); m(y); 这个的意思是,调用m函数,m函数是bind了obj的新函数,并且已经传入了一个参数x,y是第二个函数。也就是说,其实等价于a(x,y),a的this指向obj。 -
Object.assign:
-
arguments:
调用函数的时候会自动创建arguments对象,也就是每个函数内都有这么个对象,它里面存的是传入的参数,参数也可以是函数,要注意的是,传入的参数构成的arguments对象,中的所有属性(包括函数)的this都是arguments对象,因此,如果传入参数是一个函数,这个函数如果没有声明this,则原本指向的是当前所在的环境(上下文,在最外层的话就是window),然而,这个函数一旦作为参数传入了一个函数,则这个函数的this就是arguments了。 -
模拟重载:
arguments可以模拟重载,重载就是一个函数名,传入不同的参数有不同的处理。 -
curry化:
目的:一步步处理参数。比如需要curry化的函数是a,期待5个传入参数,curry化后返回一个新函数b,给b传入1个参数的话,如b(x),它会把x存起来,然后返回b,再运行b(y),又会把y存起来,再返回b,再运行b(1,2,3),这时候一共传入的足够5个参数了,则返回a(x,y,1,2,3)。咋实现都行,主要要考虑的是最后运行a的时候考虑下是否修改上下文,可能要用到bind,apply,call三兄弟。 -
ajax:
一种不会导致页面刷新(性能更高),且可以获取复杂结构数据的,通过js与服务器进行通信的方式。返回来的一般是json格式的数据,再解析成对象就行了。ajax有一些已经实现的接口,jQuery里边有,还有个XMLHttpRequest,一般ajax的接口,传参除了必备的url之类的还要有个success函数,就是成功接收到返回值(状态码200)后的处理。ajax是一种不会使整个页面都刷新的服务器间通信的方式,并且是异步的,并且传输的数据是格式化,便于传输与解析的。
-
同步异步:
执行遇到同步函数/操作,必须得执行完再往下走,但是执行遇到异步函数/操作,就直接跳过去了,js里的异步操作有ajax(毕竟不可能等信息传回来再往下走),事件函数,文件操作,定时器,回调函数(不会等着回调完再继续的)。此外还有异步改同步的写作手法,比如在一个函数内再写另一个函数的调用,promise之类的。
-
promise:
-
回调地狱:
-
匿名函数,箭头函数等:
-
闭包:
就是一个函数,这个函数能够访问一个封闭作用域,它的一个用途是避免使用全局变量时,变量被污染的情况。优点在于可以隐藏变量,缺点在于这部分不会被自动释放的内存可能会导致内存消耗。 -
宏任务和微任务:
其实具体来讲事件队列又分为宏任务和微任务队列,微任务是js引擎发起的,以前js线程没有这个能力,例如,事件触发线程监听到事件触发后,会推入事件队列回调函数,这个就不是js线程推的,所以叫宏任务,同理,http请求线程推进的也是宏任务,还有定时器也是(毕竟都不是js线程弄得),es5规范后,js有了promise和async/await,它俩的then和await都会使得一个函数变成异步任务,进而主动推到微任务队列。此外,nodejs里也有两个类似的函数,一个是setImmediate也是宏任务,不过是放到事件队列的开头,然后nextTick是放到事件队列的结尾,前者是宏,后者是微。
此外,一个js标签内的也是一个宏任务,它通常是第一个被执行栈从事件队列里取走执行的。 -
事件循环:
事件触发线程会监听事件触发,然后把对应的回调函数推入事件队列,定时器线程是计时结束推进事件队列,http线程监听到回复后推进事件队列,也就是兄弟线程们都是为了等着把事件推进队列的,然后js主执行线程结束后,此时不会立即执行事件队列里的任务,而是阻塞结束,GUI线程继续解析,直到又解析到js。等到GUI完事儿以后,事件触发线程会开启事件循环,不停的访问事件队列的头部,推入到执行栈,然后立即执行。这一个任务执行完以后,js又空了,GUI又可以干活(如果有重写工作,且宿主是浏览器,如果不是浏览器就开启下一轮tick),这就是事件循环。【注意宏任务是一个一个完成,不是一口气完成,一个宏任务完成以后,有微任务就把微任务全部执行完毕,没有就GUI渲染(如果需要的话),然后再去拿下一个宏任务。这样一轮叫一次循环,也叫一个tick,可见微任务是跟一个宏任务绑在一块的,不是下一轮tick的(也就是不会在下一轮取宏任务里执行)】事件队列里的是从队列头开始执行的!!!进入事件队列的都是已经满足执行条件,且正在等待中的…
-
把任务变成异步的方法:
有四个把任务变成异步的方法,俩定时器(执行一次和执行多次),还有nodejs里的process.nextTick和setImmediate,定时器是按顺序把任务放到队列里,nextTick是在当前执行栈的尾部,也就是下一轮事件循环的开始(任务队列的开头)添加一个事件,setImmediate是在当前任务队列的尾部添加事件,这个尾部不是说添加进去以后,新的事件来了又排在它后边,而是setImmediate注册的永恒在最后,也就是下下一轮事件循环的开头。要注意nextTick在执行栈尾部添加事件,执行这个事件的时候,还是在当前执行栈!因此,如果这个事件内又有nextTick,则还是在当前执行栈的尾部添加,如果无限嵌套nextTick,那就老也到不了队列(而且会出现,nextTick A内嵌一个nextTick B,A的下面有个定时器C,但是执行上来看,是A,B,C)。
-
setImmediate和setTimeout执行顺序:
当前执行栈里的setImmediate和setTimeout都是注册到同一轮事件循环里,那它俩的执行顺序?只要不是在主线程(当前执行栈)里执行着,那任务队列里的setImmediate和setTimeout(0ms)的执行先后是不确定的!也就是由于理论上它们的执行时间是一样的,因此【理论上】拿到主线程的前后顺序也不好说,但是nodejs文档说setImmediate早于定时器执行,我是这么理解的,setTimeout是有一个默认最小间隔的,而也许setImmediate没有吧(存疑,强烈存疑)。 -
定时器最小间隔:
requestAnimationFrame是按照屏幕刷新频率来调用函数,不刷新就不调用,setImmediate的间隔是写死的,但是可能存在刷新的一个周期内调用两次(浏览器绘制两次)的情况,屏幕不刷新,重绘多少次也看不见,那样太占用cpu,性能又低,现在一般都用requestAnimationFrame。 -
为什么js是单线程:
因为多线程dom操作会导致矛盾,比如如果同时要对dom进行添加和删除,以哪个为准?但是!这个问题在哪个语言的多线程里都有啊!!所以我觉得吧,一开始js没有多线程,可能是因为js一开始的目的仅仅是简单交互,简单的逻辑操作,没有被给予编撰一个大型程序的厚望…现在js也可以多线程编程了。 -
进程和线程:
一个程序最少有一个进程,一个进程可以有一堆线程,所以可以给一个任务写一个进程+一堆线程,也可以分成一堆进程,多进程的健壮性更好,因为进程间不共享资源,且一个进程挂了不影响其他进程,但是由于线程间共享资源,一个线程挂了,它所在的整个进程就挂了。此外,多线程使得任务间有了切换能力,就不会被一个任务卡死,提高了cpu的利用率(比如io操作时,cpu不会闲着没事干)。还有,多线程需要考虑资源锁的问题,多进程就不需要考虑这个,但是开辟一个进程很消耗性能(时间慢点儿),如果执行一个任务开辟一个进程,可能很慢(在win下尤其如此),所以怎样编程还是看情况。 -
Web Worker:
-
js里的多线程:
-
并发和并行:
并行是多个cpu同时执行多个进程,并发是一个cpu来回在多个进程间快速切换,它们都可以看起来像好几个程序在同时执行。 -
堆和栈:
确定大小的都在栈里,额外申请的不确定大小空间(malloc和所有的对象)都在堆里,栈访问速度更快,且由线程独占,堆是线程共享的,一个局部空间运行完了,栈里的立马就清空了,但是堆里的要等系统销毁,所以堆内存会存在泄露。 -
浏览器:
浏览器是多进程的,包括主进程(一个),GPU(绘制在屏幕上)进程(一个),渲染进程(多个,一般一个tag一个,空tag共用一个)也叫浏览器内核,音频进程等,内核进程里边有且只有一个js线程,有且只有一个事件线程,有且只有一个gui线程(解析html,dom,css,计算布局等等),可以有多个下载线程(http通信,一个通信开一个,下载着这一个就阻塞了,等到接到回信,就把回调扔进事件队列里),可以有多个定时器线程(一个线程专注于计算一个的时间,这样比较精确,时间到了就放进事件队列,但不一定是立马执行),webworker可以开一个真·可以执行的线程,但是不能有dom操作。可见 事件触发线程里的都是马上就能执行的,当js线程空闲,则会去找第一个要执行的函数并立即执行。
-
WebWorker与SharedWorker:
-
DOMContentLoaded 和 load:
-
浏览器渲染流程:
拿到url后,解析域名,拿到ip,tcp三次握手建立套接口,浏览器主进程开辟一个http通信线程,发送https请求,拿到二进制数据流扔给渲染进程,渲染进程的GUI线程开始解析html里的各种token,然后组建dom树和cssom树,其中,由于css会阻塞js(js如果要操作css,则必须等到cssom树建立好才能往下走,所以css会卡住js),且js会阻塞dom,所以css放在最前边head里就赶紧解析,而dom解析到js标签以后又会被js阻塞,如果js里面有操纵没有被解析进树里的dom,那js根本就找不到它(因为不在树里),所以js标签最好写在底下,或者加个异步关键字,或者它不操作dom也行。渲染树必须等到cssom和dom树全部完备后才能合成,所以如果这俩树卡了,浏览器就一直白着在那等待,渲染树ok以后,开始布局计算和像素计算,然后把这一大套发送给GPU进程,再往浏览器上画,画完以后,window.load事件触发(domload是dom树ok以后触发,dom树好了就能操作dom了嘛…)。
-
硬件加速,复合图层:
-
响应信息:跟http请求差不多,包括响应行,响应头,空行,正文。响应的响应行包括http版本,状态码(就是那个200啥的),状态的文本描述,状态码有3位数,第一位数有5种,代表5大类别,2开头代表请求接收成功。
-
http请求:
也是个数据包啊,端口是浏览器端口吧,协议是http,一个请求包括请求行,请求头,空行,正文,请求行要包含请求方式,get和post,url资源定位符,还有http版本,这些叫请求状态行,还有请求报头,附加元数据,例如cookie,浏览器语言,客户端接受的内容类型,请求的内容长度等等一大堆,其次是请求正文,只有post有正文(提交的表单数据封在正文里),而get方式的表单会被绑在url上发过去。 -
HTTP协议的无状态性:
有的通信协议,每个请求都有状态,比如目前是hello状态,结束状态,之类的,但是http的每一个请求都是独立的,请求头内包含着所有处理这个请求需要的信息,优点是不会轻易导致回话中断,缺点是需要较复杂的元数据(cookies之类存的),这堆反复重复传输的数据会降低一点协议的传输效率。还有,http在协议的设计上是无状态的,但通过携带元数据,可以在服务层面上有状态。 -
网络七层协议:
网络七层协议是一种感性的分层方式,还有四层五层等,主要是方便理解网络由底至顶的结构逻辑,物理层就是物理设备,数据链路层将规定电信号的打包,接收,发送等,局域网内传输信息都是广播,还是在数据链路层的,用的是MAC地址,但是局域网间通信就需要网络层了,数据给网关,网关再处理,发送,这时候要加一个IP地址,IP地址那个局域网接到包以后会再广播找到ip的mac,然后传到目标端。此外应用层负责出数据,底下还有一层具有对数据进行加密的功能,传输层负责加tcp头等等… -
Cookies:
请求头 -
2个=号和3个=号:
-
短路或||:
-
arr.sort:
-
concat:
-
三目运算符相对其余运算符的优先级:
-
js中强制类型转换:
-
按位运算:
-
隐式转换(包括[]):
-
数组的reduce函数:
-
NAT:
-
ARP:
地址解析协议,也是tcpip家族里的,就是在局域网里广播一下找到目标ip的mac地址,每个目标都看看是不是自己的ip,是的话就给请求方回过去自己的mac。 -
http发送过程:
传输层把应用层的数据分成报文段,打上tcp源+目的端口号,网络层打上源目的ip地址,数据链路层打上源目的mac地址,mac是指导往下发的东西,每转发一次就变了,一开始是默认网关,物理层把封好的帧转成二进制。默认网关那个路由器接收到比特流之后,转成帧到链路层,然后发现该路由器的接受数据的接口的mac刚好就是帧的目标mac,于是拆成包到网络层,发现包的目的ip跟该路由器不一样,所以不是给这个路由器的,于是查询路由表下一跳的接口,网络层把包拆开,生命周期-1之后再封上,链路层改mac,源是自己,目的是出口的mac,最后目的一层层拆开发现mac,ip都一样,然后拆到传输层,传输层把分段再组装回去,最后通过端口给应用。反向只要把源和目的
互换就行了。 -
应用编程接口:
-
Session:
-
http和https:
https就是加上安全套接层ssl,就是更安全的套接口 -
async :
加上这个关键字,js 标签不会阻塞dom了就。 -
渐进式渲染:
-
css阻塞:css无论是style标签还是link的外部css文件都不阻塞dom,但是link的css文件阻塞浏览器渲染,而style标签不阻塞浏览器渲染,
-
js阻塞:
js标签会阻塞dom解析,所以最好把js标签放在body底下,不要阻塞dom解析,还有就是添加async(异步的单词)关键字到脚本标签里,这样这个脚本标签就会在dom解析完以后再执行。还有就是,无论是外部js还是js标签,都会阻塞dom构建的。还有!js是操作dom树里的节点,没有就操作不了,所以它的位置很重要,js标签阻塞dom构建,dom不构建,js又读取不到后边的dom,问题就很大,还有js操纵的也是css树,所以css树不构建好,又会阻塞js,而如果css标签在js底下,就会变成,dom被js阻塞,js被css阻塞,css加载在js底下==被js阻塞…
-
dom解析到html标签还是body标签:
到html,但是js写在body底下的话,实际上有用的标签都解析完了,问题就不大。所以这就是为啥js写它要处理的标签上边,js里面的代码取不到标签的原因。 -
dom标签没有解析完的话,js能取到这个标签吗:
不能。 -
浏览器渲染阻塞:
渲染树是dom树和cssom树的合体,也就是必须解析完dom和css树才能组成渲染树,其中,css树的完备包括内联,内部,外部,必须全部解析完,才能合成渲染树,然后进入下一次渲染。上面写的内部css标签不阻塞浏览器渲染,不是很确切,其实也是“阻塞”的,毕竟要全部解析完才能渲染,但是指定是不如文件下载阻的厉害。
这也是为什么css和各种link都放在head中,早解析早下载早轻松。
-
但是没有css的页面也能显示啊?:
-
层叠样式:
层叠太多影响性能,如果有一个css是给div下的p的a应用,则会寻找所有的a,看看它的爹是不是p,是的话,再上一层是不是div,然后再考虑渲染方式,用id和class的话,定位的快一点,不用这样一层层判断。 -
沙箱:
-
渲染优化:
为了减少重写和重绘,能一起更改的属性最好一起更改,比如el.style.cssText += ‘border-left: 1px; border-right: 2px; padding: 5px;’,虽然高版本浏览器有渲染优化,但是代码层面注意一点也不是坏事。还有,由于获取属性的操作会清空重写的任务队列,如果队列里有任务,那就触发重写,因此,最好把属性都改完了,再依次该获取啥属性获取啥,要不然也会触发好多轮重写。
以及,DOM操作也会触发回流,但如果让它脱离文档流,由于它也不影响别人了,也就不回流了,因此,可以这样子,在js里,先让它脱离文档流,然后进行多次修改,最后再加入文档流,这样可以降低回流次数。不过,脱离文档流和回归文档流还是会触发回流的。
脱离文档流(脱离渲染树)的方法,包括隐藏节点,还有可以暂时把节点挪到某个已经脱离文档流的节点(环境里)。
例如,给某个节点内添加多个子节点,可以先把这个父节点隐藏,然后给它添加多个子节点以后,再可见。还有比较重要的一点,像节点.属性这样可能很好写,但是如果一个循环内写了这么一句话,就会导致每一次都强制清空队列,强制重写,所以最好只获取一次属性,然后用变量储存之后再使用。
-
会导致回流的操作:
页面首次渲染,改变浏览器窗口大小,元素尺寸大小位置变化,添加删除dom元素,激活css伪类,查询位置,大小等属性,js解析到类似添加删除dom元素时,在有优化的情况下,不会立刻触发GUI重写,会隔一段时间再重写,除了获取属性,如果要获取属性,为了得到正确的值,会立刻重写(如果没有重写任务的队列,那就不需要重写了)。 -
proxy:
-
async/await:
-
es6:
-
h5:
h5定义了新的语义化标签,比如头,脚注,文章,导航,以及拥有新的表单输入,颜色,日期,email等等,canvas也是h5。 -
css3:
-
tick:
事件循环中每一次循环称为tick。 -
宿主:
js线程运行的环境,包括“前端”和“后端”,其实就是浏览器和node。 -
内核常驻线程:JS,GUI,事件触发线程,其他的诸如http和定时器都是需要再启动,用完就关了。
-
nodeJS与其他后台语言:
-
DOM:
DOM的出现就是为了让js操纵的,它是由节点组成的一棵树,从html开始。摘抄一句:DOM 为文档提供了结构化表示,并定义了如何通过脚本来访问文档结构。目的其实就是为了能让js操作html元素而制定的一个规范。 -
进程与线程的调度问题:
一个程序启动,一个进程就被创建了,操作系统就分了一块内存给它,这个进程也许需要一些线程来辅助完成工作,也可以申请开辟一个新的进程。多进程是分配几个不同的独立的内存,而多线程是指在一块内存下划分不同的执行流,允许并行的完成一些不同的任务,而不会让一个塞住另一个。不过每个线程内的任务会互相塞住的,比如一个ajax请求会启动一个http线程,然后就塞住了一直等回复,但也只关那个线程的事,不影响别的线程。
-
触发回流,重绘,还触发了事件:
dian -
触发事件的三个阶段:
-
GPU进程的作用:
-
栅格化:
-
bootstrap:
-
chrome工具:
-
cssText:
-
document fragment DOM子树:
-
硬件加速:
-
合成层:
总的来说就是,标签合成DOM树,css合成cssom树,然后按照不同的index(存疑)合成渲染层,每个渲染层都是DOM子树,然后渲染层又按照正确的顺序合成图形层,最终GUI输出位图给GPU展示在屏幕上。但是,某种方式可以使得渲染层生成一个新的独立图形层,这样的渲染层叫合成层,也就是跟基本的图形层不一样的,一个必须交由GPU处理的层,必须交由GPU处理是因为它需要大量图像方面的计算,这样可以通过硬件加速的方式加快渲染,比如3D效果和硬件加速的2D,或者视频标签,而这阴差阳错的使得在该层上的内容的变化不会触发基础图形层的回流和重绘,因此,也可以使用该方式避免回流和重绘,而让某个标签进入合成层的方式就是添加一些需要GPU处理的属性,比如css3的translate之类的。 -
满足以下情况就可以拥有自己的图形层:
给节点增加3D或透视变换的CSS属性,webgl所在的标签或硬件加速的canvas标签,flash,使用动画变换的元素,我的理解是,这些都是放到GPU里计算的情况,而不是常规下CPU计算,所以才在不同的图形层(不同的上下文环境)。 -
布局:
dom树和css树只是决定页面有啥标签,以及有啥样式,但此时还没有计算各个标签具体的位置和大小,所以下来是布局计算,也就是所谓的回流,重写,重排,为了弄清每个对象在网页上的确切大小和位置,浏览器需要从渲染树的根节点开始进行遍历。
布局操作的输出是一个盒子模型,它精确的描述了每个标签的大小,位置,边框,内容的边距之类的。在这之后,也就是GUI的最后一个阶段,绘制(栅格化)。 -
display none与可见性为hidden的标签:
hidden还在dom树里,只不过渲染成空的框,none是都不在树里了。
此外,只有可见节点才会与css匹配,不可见和none就不匹配了,none都不在树里也没啥好配的。 -
三次握手四次挥手:
握手:客户端ack,服务器ack,客户端ack。四次挥手:客户端fin,服务器端ack,客户端await,服务器端fin,客户端ack。 -
css表达式:
-
table 布局:
-
will-change:
-
层爆炸产生原因:
跟合成层重叠的话,元素也会被提升为独立的合成层,虽然大部分浏览器有层压缩机制,但还是要避免这个问题。 -
层爆炸解决:
让合成层元素跟其他元素的z-index不一样,这样就在另一个渲染层里了,就不在一块了,但如果必须覆盖,那就要避免无法压缩的情况。 -
合成层的注意事项:
因为不当操作而误提升了太多合成层导致GPU资源过度消耗,页面滑动就会很卡。
后代有合成层,自己在某些情况下也会提到合成层。 -
合成层的处理:
合成层的位图会交由GPU处理,位图就是栅格图,其余的解析等还是GUI处理,但是它的重写重绘不会影响其它层。也可以把固定不变的区域提升合成层,来减少绘制区域,也就是说,可以把频繁绘制的拿到合成层,也可以反过来把不绘制的拿到合成层。 -
无法压缩合成层的情况:
如下所示,而这些情况也是我们应该尽量避免的:
无法进行会打破渲染顺序的压缩
video 元素的渲染层无法被压缩同时也无法将别的渲染层压缩到 video 所在的合成层上
iframe、plugin 的渲染层无法被压缩同时也无法将别的渲染层压缩到其所在的合成层上
无法压缩有 reflection 属性的渲染层(squashingReflectionDisallowed)
无法压缩有 blend mode 属性的渲染层(squashingBlendingDisallowed)
当渲染层同合成层有不同的裁剪容器(clipping container)时,该渲染层无法压缩(squashingClippingContainerMismatch)
相对于合成层滚动的渲染层无法被压缩(scrollsWithRespectToSquashingLayer)
当渲染层同合成层有不同的具有 opacity 的祖先层(一个设置了 opacity 且小于 1,一个没有设置 opacity,也算是不同)时,该渲染层无法压缩(squashingOpacityAncestorMismatch,同 squashingClippingContainerMismatch)
当渲染层同合成层有不同的具有 transform 的祖先层时,该渲染层无法压缩(squashingTransformAncestorMismatch,同上)
当渲染层同合成层有不同的具有 filter 的祖先层时,该渲染层无法压缩(squashingFilterAncestorMismatch,同上)
当覆盖的合成层正在运行动画时,该渲染层无法压缩(squashingLayerIsAnimating),当动画未开始或者运行完毕以后,该渲染层才可以被压缩 -
cpu和GPU:
这俩可以并行操作,并且GPU对像素的绘图和合成比CPU效率高很多,但是也要考虑通信带宽,以及GPU性能上限的问题。 -
GUI:
解析成DOM树后,树上的每个节点都对应一个渲染对象,同层渲染对象对应一个渲染层,渲染层的理念,最初是用来实现层叠上下文的,满足层叠条件就会出现新的渲染层,常见的触发新渲染层的原因有以下三类:NormalRenderLayer,OverflowClipRenderLayer,NoRenderLayer。然后渲染层们按照一定的顺序合成复合图层,最后GUI给每个复合图层输出位图,位图存在共享内存中(也许这是GUI和GPU进程都能取到它的原因吧),输出给GPU以后再画到屏幕上。 -
理论上来说,复合图层会把所有的渲染层按照一定的顺序合在一起,但是,有些情况会使得渲染层升级新的图形层,这种渲染层叫合成层,
-
CPU与GPU的数据通路:
-
怎么理解GPU是一台单独的计算机:
-
js内存释放:
当一个程序执行完毕时,该程序作用域内变量开辟的内存就会释放。然而闭包导致这部分内存不会被释放(就是闭包的父函数的内存不会被释放)(但是闭包回收后,父函数中被闭包使用的变量也就回收了) -
词法作用域和动态作用域:
词法作用域就是代码里定义的,由各种标识符指示的静态作用域空间,动态作用域只关心this,也就是动态作用域是在调用时确定的。 -
js数据类型:
7种,es6里的是symbol -
var:
js是弱语言类型,var是它的保留字。 -
数组也是一种对象,只有对象这类型,没有数组类型。数组只能下标访问,对象可以键值对。
-
算法:
先梳理问题目标考虑用什么类型的方法(排序,查找),再考虑数据结构,最后考虑问题的特殊情况,确定代码内容无误后,再考虑优化。 -
判断类型的三种方法:
-
强转的方法:
-
<<移位运算符:
-
npm
-
es6的位运算和正则
-
TypeError: 对变量做了错误操作
-
referentError: 没有找到这个变量:
-
http重定向
-
虚拟dom
-
css权重
-
css选择器
-
跨域
-
实现原生ajax
-
typescript,JavaScript
-
less,sass
-
深拷贝,浅拷贝
-
浏览器缓存
-
seo
-
缓存和跨域