BFC:
页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。
具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器所没有的一些特性。
触发 BFC
只要元素满足下面任一条件即可触发 BFC 特性:
- body 根元素
- 浮动元素:float 除 none 以外的值
- 绝对定位元素:position (absolute、fixed)
- display 为 inline-block、table-cells、flex
- overflow 除了 visible 以外的值 (hidden、auto、scroll)
同一个 BFC 下外边距会发生折叠, 如果想要避免外边距的重叠,可以将其放在不同的 BFC 容器中。
HTTP1.1和HTTP2.0的区别,HTTP2.0核心优势有哪些 ?
HTTP,人称超文本传输协议,它是在应用层上的协议,与它对接的传输层的协议是TCP。为什么不用UDP呢,因为UDP是不可靠的,而TCP是可以保证请求返回的顺序的。
HTTP 2.0相比于HTTP 1.x,大幅度的提升了web性能,同时向下兼容HTTP1.X协议版本。
Http2.0的核心优势:
1、采用二进制格式传输数据,而非http1.1文本格式,二进制格式在协议的解析和优化扩展上带来了跟多的优势和可能
2、对消息头采用Hpack进行压缩传输,能够节省消息头占用的网络流量,http1.1每次请求,都会携带大量冗余的头信息,浪费了很多宽带资源。
3、异步连接多路复用
4、Server Push,服务器端能够更快的把资源推送到客户端。
5、保持与HTTP 1.1语义的向后兼容性也是该版本的一个关键。
HTTPS:
HTTPS可以说是安全版的HTTP,HTTPS基于安全SSL/TLS(安全套接层Secure Sockets Layer/安全传输层Transport Layer Security)层,即在传统的HTTP和TCP之间加了一层用于加密解密的SSL/TLS层。HTTP默认使用80端口,HTTPS默认使用443端口。
不使用SSL/TLS的HTTP通信,所有信息明文传播,会带来三大风险:
- 窃听风险:第三方可以获取通信内容;
- 篡改风险:第三方可以修改通信内容;
- 冒充风险:第三方可以冒充他人进行通信。
SSL/TLS协议是为了解决这三大风险而设计的,以期达到:
SSL/TLS的基本思路是公钥加密法:客户端先向服务器索要并验证公钥,然后用公钥加密传输来协商生成“对话秘钥”(非对称加密),双方采用“对话秘钥”进行加密通信(对称加密)。
- 信息加密传输:第三方无法窃听;
- 校验机制:一旦被篡改,通信双方会立刻发现;
- 身份证书:防止身份被冒充。
实现 promise:
class Promise {
constructor(executor) {
// 设置属性 status value resolveCbs rejectCbs
}
then(onResolved, onRejected) {}
catch (cb) {
return this.then(null, cb)
}
}
Reflow (重排),Repaint(重绘)
一个页面可以简单看成由两个部分组成:
- DOM节点:描述页面的结构
- DOM节点的属性:描述DOM节点如何呈现
Reflow:当涉及到DOM节点的布局属性发生变化时,就会重新计算该属性,浏览器会重新描绘相应的元素,此过程叫 回流(Reflow)。
Repaint:当影响DOM元素可见性的属性发生变化 (如 color) 时, 浏览器会重新描绘相应的元素, 此过程称为 重绘(Repaint)。因此重排必然会引起重绘。
浏览器在处理重排时,会递归处理DOM节点,所以导致重排的成本高于重绘。
会引起浏览器的重排:
- 调整窗口大小
- 字体大小
- 样式表变动
- 元素内容变化,尤其是输入控件
- CSS伪类激活,在用户交互过程中发生
- DOM操作,DOM元素增删、修改
- width, clientWidth, scrollTop等布局宽高的计算
改进的方法:
- 避免逐条更改样式。建议集中修改样式,例如操作className。
- 避免频繁操作DOM。创建一个documentFragment或div,在它上面应用所有DOM操作,最后添加到文档里。设置display:none的元素上操作,最后显示出来。
- 避免频繁读取元素几何属性(例如scrollTop)。
- 绝对定位具有复杂动画的元素。绝对定位使它脱离文档流,避免引起父元素及后续元素大量的回流
重绘重排的概念,以及最佳实践。一直都知道应该用 transform 代替 margin,但是一被问原理,就不太清楚,查了资料是对 translate3d 的元素进行 GPU 加速。从而让浏览器在渲染动画时从CPU转向GPU。
懒加载:
懒加载就是延迟加载,当访问一个页面的时候,先把img元素或其他元素的背景图片的路径替换为一张大小为1*1px的图片的路径(这样就只请求一次,称占位符),只有当浏览器出现再可视区域的时候,才设置图片的真正路径,再让图片加载出来,这就是图片懒加载;当一个页面内容很多,图片数据巨大的时候,就需要懒加载的方式进行操作了。
原理: 页面中的img元素,如果没有src是不会发送请求去下载图片的。先再页面中把所有的图片统一用一张占位图进行占位。把真正的路径存放再元素的属性里面,要用的时候取出来,在设置。
预加载: 提前加载图片,当用户需要的时候可以直接从本地缓存中渲染出来。实现预加载的方法:1. 用css和js实现。 2. 仅仅用js实现。 3. 使用ajax实现。
懒加载和预加载的对比: 区别: 两者行为相反,一个是提前加载,一个是延迟加载甚至不加载。懒加载对服务器有一定的缓解压力的作用,预加载会增加对服务器的压力。也可以说是牺牲服务器的前端性能,换取更好的用户体验。
js中的隐式转换:
1. 对象和布尔值: 对象先转换为字符串,再转换为数字。布尔值直接转化为数字。[] -> '' -> 0
2. 对象和字符串:对象转为字符串进行比较。
3. 对象和数字: 对象转化为字符串再转化为数字进行比较。
4. 字符串和数字: 字符串转换为数字。
5. 字符串和布尔值: 字符串转为数字。布尔值转换为数字再进行比较。
6. 布尔值和数字: 布尔值直接转换为数字进行比较。
==, ===, Object.js:
简单的说,两个等号再判断时会自动进行类型转换,三个等号不会,类型不同,直接返回false,Object.js()则是三等号的基础上又特别处理了NaN,-0,+0,保证了-0和+0不再相同。但是Object.is(NaN,NaN) -> true;
VUE中如何使用插件:1. 采用es6的import .. from .. 语法或者CommonJS的require()引入。2. 使用全局方法Vue.use(plugin);可以传入一个对象Vue.use(plugin,{option: true})。active-class属于router-lin组件的属性。
vue-router导航钩子函数:
1. 全局导航钩子主要有两种钩子:前置守卫、后置钩子:router.beforeEach,router.afterEach
2. 路由独享的钩子: beforeEnter
3. 组建内的导航钩子:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
完整的路由导航解析流程(不包括其他生命周期):
- 触发进入其他路由。
- 调用要离开路由的组件守卫
beforeRouteLeave
- 调用局前置守卫:
beforeEach
- 在重用的组件里调用
beforeRouteUpdate
- 调用路由独享守卫
beforeEnter
。 - 解析异步路由组件。
- 在将要进入的路由组件中调用
beforeRouteEnter
- 调用全局解析守卫
beforeResolve
- 导航被确认。
- 调用全局后置钩子的
afterEach
钩子。 - 触发DOM更新(
mounted
)。 - 执行
beforeRouteEnter
守卫中传给 next 的回调函数
vue实现数据双向绑定原理:
采用数据劫持结合发布者-订阅者模式,通过Object.defineProperty()来劫持各个属性的setter,getter,再数据变化时可以及时发消息给订阅者,触发相应的监听,组件创建后,会遍历它的data属性,用Object.defindProperty()将其转化为getter/setter,并再内部追踪相关依赖,再属性被访问或修改时可以及时通过变化。ue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}
),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于设置的时间,防抖的情况下只会调用一次,而节流的情况会每隔一定时间调用一次函数。
防抖(debounce): n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。
节流(throttle): 高频事件在规定时间内只会执行一次,执行一次后,只有大于设定的执行周期后才会执行第二次。
JavaScript 事件冒泡、事件捕获和事件委托
new生成一个对象的过程
以 var p=new ClassA( ) 为列
1. 创建空对象;
var p= {};
2. 设置新对象的__proto__属性指向构造函数的prototype对象;
p.__proto__ = ClassA.prototype;
3. 使用新对象调用内部函数call,函数中的this被指向新实例对象:
ClassA.call(p);
4. 将初始化完毕的新对象地址,保存到等号左边的变量中。
事件捕获:是从document到触发事件的那个节点,即自上而下的去触发事件
事件冒泡:IE提出的事件流,和事件捕获相反,即使件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点,是由下而上的去触发事件
事件委托:事件委托是利用事件冒泡,只指定一个事件处理程序来管理某一类型的所有事件。
为什么要用事件委托?
1.在JavaScript中添加到页面上的事件处理程序的个数直接关系到页面的整体运行性能。为什么呢?因为,每个事件处理函数都是对象,对象会占用内存,内存中的对象越多,性能就越差。此外,必须事先指定所有的事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。
2.对有很多个数据的表格以及很长的列表逐个添加事件,简直就是噩梦。所以事件委托,能极大地提高页面的运行性能,减少开发人员的工作量。
flex
是 flex-grow
、flex-shrink
、flex-basis
的缩写,当 flex
取值为 none
,则计算值为 0 0 auto。
WeakSet与Set类似,也是不重复的集合。区别在于:WeakSet的成员只能是对象;WeakSet中的对象是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用。当其他变量不再引用某对象,其将被垃圾回收机制回收,哪怕这个对象被WeakSet引用。这意味着,WeakSet的成员是无法被引用的,无法遍历WeakSet。WeakSet只有add、delete和has三个方法。注意:WeakSet没有size属性,也没有forEach方法。
对于WeakSet不能被遍历,可以这样解释。因为其成员都是弱引用,随时可能消失,遍历机制无法保证其成员一直存在。
WeakSet用来做什么,答,储存DOM节点,这样移除DOM时就可以不用担心内存泄漏了。
foreach,map,filter循环中途是无法停止的,总是会将所有成员遍历完。map(环和filter()循环都会跳过空位,map方法不会跳过undefined和null,但是会跳过空位。forEach方法也会跳过数组的空位。
vue组件中的data必须是函数:
类比引用数据类型
Object是引用数据类型,如果不用function 返回,每个组件的data 都是内存的同一个地址,一个数据改变了其他也改变了;javascipt只有函数构成作用域(注意理解作用域,只有函数的{}
构成作用域,对象的{}
以及 if(){}
都不构成作用域),data是一个函数时,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响。
为什么要写renturn返回:
因为不使用return包裹的数据会在项目的全局可见,会造成变量污染
使用return包裹后数据中变量只在当前组件中生效,不会影响其他组件。
数组的去重:
数组对象去重:
arr.reduce(callback,[initialValue])
callback (执行数组中每个值的函数,包含四个参数)
-
- previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
- currentValue (数组中当前被处理的元素)
- index (当前元素在数组中的索引)
- array (调用 reduce 的数组)
- initialValue (作为第一次调用 callback 的第一个参数。)
1. (a==1&&a==2&&a==3) 有可能为true?
var aᅠ = 1;
var a = 2;
var ᅠa = 3;
if(aᅠ==1 && a== 2 &&ᅠa==3) {
console.log("Why hello there!")
}
主要在于if里面的空格,它是一个Unicode空格字符,不被ECMA脚本解释为空格字符(这意味着它是标识符的有效字符)
通过valueOf toString 方法可以实现,具体代码如下:
const a = {
i: 1,
toString: function () {
return a.i++;
}
}
if(a == 1 && a == 2 && a == 3) {
console.log('Hello World!');
}
数组 toString
会调用本身的 join
方法,这里把自己的join
方法该写为shift
,每次返回第一个元素,而且原数组删除第一个值,正好可以使判断成立。
var a = [1,2,3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3);