JS有哪几种数据类型?
- 字符串、数字、布尔、null、undefined、symbol
变量声明提升?let、var、const的区别?
- 声明的变量会被提升到函数的最顶部
- let 块级作用域
var 全局变量
const 常量,下面不能改变。
ES6 语法你平常能用到哪些?
- `=> promise let const 模板字符串 import export`
undefined和null有什么区别?
- undefined:未定义,值缺失不存在;声明未赋值。
- null:表示“没有对象”,即该处不应该有值;原型链的终点。
Promise、Promise.all、Promise.race 分别怎么用?
- `function fn(){return new Promise((resolve, reject)=>{成功时调用 resolve(数据);失败时调用 reject(错误)})}
fn().then(success1, fail1)` - `Promise.all([promise1, promise2]).then(success1, fail1)`
promise1和promise2都成功才会调用success1 - `Promise.race([promise1, promise2]).then(success1, fail1)`
promise1和promise2只要有一个成功就会调用success1
this 指向?
- 全局环境下,this 始终指向全局对象(window)
箭头函数和普通函数有什么区别?如果把箭头函数转换为不用箭头函数的形式,如何转换
- 箭头函数是匿名函数,不能作为构造函数,不能使用new
- 箭头函数不绑定arguments,取而代之用rest参数“…”解决
- this的作用域不同,箭头函数不绑定this,会捕获函数定义的上下文中的this值,作为自己的this值,且一直不变
- 箭头函数没有原型对象
es6实现数组深拷贝
- `let arr1 = [1,2,3]` //原数组 let [ ...arr2 ] = arr1 //新数组
- 浅拷贝对指针的拷贝,拷贝后两个指针指向同一个内存空间,改变一个数组会同时改变另一个数组。
- 深拷贝对指针和指针指向的内容都进行拷贝,深拷贝后的两个数组完全独立,存储在不同的地址。
闭包是什么?
- 「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。
-
`function foo(){
var local = 1
function bar(){
local++
return local
}
return bar
}
var func = foo()
func()`
local 变量和 bar 函数就组成了一个闭包(Closure)。 - 闭包就是能够读取其他函数内部变量的函数。
什么是跨域?有哪些方法?
- 跨域:违背了同源策略就会跨域。
- 同源策略:是浏览器的安全策略,就是指协议、域名、端口必须完全一致。
-
跨域解决:
1、jsonp跨域:
JSONP 只支持get请求、不支持post请求。(类似往页面添加一个script标签,通过src属性去触发对指定地址的请求,故只能是Get请求)
2、nginx反向代理。
3、后端修改header。
图片懒加载的原理动画有几种实现方式,性能对比
- 视口位置计算:
1、基于 offsetTop(偏移量顶部) < clientHeight(内部高度) + scrollTop(滚动顶部) 实现懒加载
2、基于 getBoundingClientRect 实现懒加载。// 获取元素的大小及位置 - 交集观察法:
基于 Intersection Observer API 实现懒加载
Intersection Observer API 提供了一种异步观察目标元素与祖先元素或顶级文档 viewport 的交集中的变化的方法。 - HTML 原生支持:
基于 img 标签的 loading 属性实现懒加载 -
基于offsetTop + clientHeight + scrollTop 实现懒加载
offsetTop:只读属性。代表着一个元素相对于其offsetParent的元素顶部内边距的距离。
scrollTop:可以获取一个元素内容垂直滚动的像素数。简单来说,一个元素的scrollTop代表着这元素的内容顶部到视口顶部的距离,即滚动距离。
clientHeight:元素可视区域范围高度。
在每次触发懒加载计算的时候,程序会对所有的图片都进行判断,如果符合公式 img.offsetTop < `document.documentElement.scrollTop + document.documentElement.clientHeight`, 即元素偏移距离小于文档滚动高度和视图高度的和,说明元素出现在当前视图底部的上方,那我们就执行替换真正图片的程序,否则跳过不作处理。 -
实例化一个 IntersectionObserver 对象 observer,注册回调函数用于处理结果集。
for 循环,观察所有的 img元素。
回调函数中,每次触发回调传入结果集,遍历判断 isIntersecting 属性,为 true 则将 src 赋值给 src 进行更新,并且在更新之后,通过 observer.unobserve 注销对该元素的观察。 - img 标签上原生支持的属性 loading。依靠原生支持以及浏览器的优化,性能上自然不必多说。原生支持的 lazy-loading, 依然需要配合 javaScript 做一个可用性判断,以决定当前程序要采用何种懒加载方式。
聊一聊DOM事件流、冒泡、捕获
- DOM事件流:就是事件的流向,先捕获,再到事件源,最后再冒泡。一共分为三个阶段:捕获阶段(从window对象传导到目标节点(上层传到底层)称为“捕获阶段”(capture phase),捕获阶段不会响应任何事件;) - 事件源 目标阶段(在目标节点上触发,称为“目标阶段”) - 冒泡阶段(从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段)。
- 冒泡:是从子级到父级的方向的传播。
- 捕获:是从父级到子级的方向的传播。
-
阻止事件冒泡的方式:
`event.stopPropagation()`
阻止默认事件 `event.prentDefault()`
事件委托是什么
- 事件委托也称之为事件代理,把原本绑定在子元素上的事件委托给父元素。让父元素担当事件监听职务。事件委托的原理是事件冒泡。
- 事件代理即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层。
-
优点:
1、可以大量节省内存占用,减少事件注册,比如在ul上代理所有li的click事件就非常棒。
2、可以实现当新增子对象时无需再次对其绑定(动态绑定事件)
EventLoop是什么
- Event Loop即事件循环,是指浏览器或Node的一种解决javascript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。
- 事件循环的进程模型
1、选择当前要执行的任务队列,选择任务队列中最先进入的任务,如果任务队列为空null,则执行跳转微任务的执行步骤。
2、将事件循环中的任务设置为已选择任务。
3、执行任务。
4、将事件循环中当前运行任务设置为null。
5、将已经运行完成的任务从任务队列中删除。
6、microtasks步骤:进入microtask检查点。
7、更新界面渲染。
8、返回第一步。
-
bilibili
事件循环的整体流程
- 先清空调用栈call stack中的同步代码
- 执行微任务队列中的微任务
- 尝试渲染DOM
- 触发Event loop反复询问回调队列callbackqueue中是否有执行的语句,有则放在callback继续执行。
事件循环的执行过程
-
- 同步代码,调用栈执行后直接出栈
- 异步代码,放到Web API中,等待时机,等合适的时候放入回调队列(callbackQueue),等到调用栈空时eventLoop开始工作,轮询
- 微任务执行时机比宏任务要早
宏任务、微任务是什么
- 宏任务是由浏览器规定的;微任务是由ES6语法规定的。
- 宏任务:setTimeout、setInterval、DOM事件、AJAX请求。
- 微任务:Promise、async/await
- DOM渲染
- 结论:微任务执行时机比宏认为早。
- 微任务 > DOM渲染 > 宏任务
-
// Event loopconsole.log('hello wen')setTimeout(function callback(){console.log('回调函数执行!')},3000)console.log('hi aRui')
// 微任务比宏任务执行早console.log(1)setTimeout(()=>{console.log(2)},0)Promise.resolve().then(()=>{console.log(3)})console.log(4)// 1 4 3 2