自我介绍
1. js的protoType是干嘛的?
- 每一个函数都会有一个
protoType属性,指向它的实例原型。 - 每一个JavaScript实例对象(null 除外)在创建的时候都会有一个
__proto__属性指向它的原型。 - 每一个实例原型都有一个
constructor属性指向它的构造函数。
如上图,当读取实例的属性时,如果在实例本身找不到,就会查找在实例关联的实例原型上的属性,如果还查不到,就去找实例原型的原型,直到找到最顶层。
原型也是一个对象,他是通过Object创建的,所以原型的__proto__指向Object的protoType。
而Object.protoType的原型是 null。而通过__proto__串联起来的这条链也就叫原型链。
参考文章:JavaScript深入之从原型到原型链
2. 怎么判断一个属性是这个对象本身的属性,还是它继承来的属性?
使用hasOwnProperty方法,此方法只会检测对象本身是否具有这个属性,而不会检测原型链。这个方法是Object.protoType上的一个方法。
const a = {
b: 1,
}
console.log(a.hasOwnProperty('b'));
3. 如何判断一个对象是什么类型?
- 使用
typeOf, 但typeOf 对于数组和对象返回的都是 object,不能精准检测。 - 使用
instanceof运算符,用于检测构造函数的protoType属性是否出现在某个实例对象的原型链上。所以可以使用 xx instanceof Array 判断是否是数组以及组合来判断是否是对象。 - 使用
constructor,constructor 是实例原型的属性,指向构造函数,可以判断数组和对象,缺点是 null 和 undefined 没有此属性,并且在自定义对象上,可能在重写protoType的时候丢失原有的constructor。 - 使用
toString, 是Object.protoType的一个属性,返回格式[object Xxx],对于非Object对象来说,一般使用Object.prototype.toString.call()判断。
4. 什么是闭包?
闭包是一个引用了自由变量的函数,即使创建它的上下文已经销毁,它仍然存在。
自由变量是指,在函数中使用的,既不是函数参数,也不是函数局部变量的变量。
5. 闭包导致内存泄漏的场景?
内存泄漏一般发生在内存回收的步骤上,如:全局变量,没有及时销毁的计时器。闭包引用的变量无法被垃圾回收。
6. 如何避免内存泄漏?
- 在退出函数前,将不使用的局部变量删除,赋值为null;
- 避免变量的循环赋值和引用;
7. js中的变量提升?
在es6之前,js中只有两种作用域,一种是全局作用域,一种是函数作用域。变量提升指的是,不论我们在哪里声明变量,js解释器都会将此变量提升到作用域顶部。也就是说,变量可以在声明前使用,值是undefined。
在es6之后,新增了块级作用域的概念,新增了let和const命令,使用这两个命令声明变量不受变量提升的约束。且let和const只在所属的块级作用域中有效,在声明变量之前,该变量都是不可用的,这被称为暂时性死区。
引申:函数提升:函数的提升会把函数的声明和定义都提升到作用域顶部。且函数的声明比变量的声明优先级更高。
8. var 和 let 除了变量提升,还有什么区别吗?
- 作用域不同:var 只有函数作用域, let 存在于块级作用域。
- var 的变量可以重复声明,let 只能声明一次。
9. es5 中 this 的指向?
函数的this指向调用它的对象。如果是全局调用,就指向window,因为所有全局对象都是挂载在window下面的。
可以通过apply,call,bind,new来改变this的指向,通过new关键字,改变this的优先级最高。
es6中箭头函数没有this,它的this是指向外层第一个不是箭头函数的函数的this。且一旦绑定了this,就不会被任何代码改变。
10. promise 的工作原理?
promise是为了解决异步调用嵌套代码可读性差的问题。promise有三种状态,pending(初始),fulfilled(成功),rejected(失败)。状态一旦变化,就不会再变。
Promise构造函数接收一个函数,规定这个函数有两个函数入参,一个是resolve,一个是reject,当函数成功时,调用resolve,失败时调用reject,来改变状态。一旦状态改变,就会调用promise.then或promise.catch。
Promise.then 返回一个Promise实例,因此可以对其进行链式调用。
11. promise 链式调用时,其中有一个then抛出异常,此时promise是怎么处理的?
promise的错误具有冒泡的性质,如果其中一个then抛出异常,promise就会跳过后面的then,他的错误会一直向后传递,直到被catch语句捕获。
12. promise.all 和 promise.race 两个函数是做什么的?
primise.all 和 promise.race 的入参都是一个promise数组,promise.all 在所有异步操作执行完后才会执行then回调,而promise.race 只要有一个异步操作执行完成,就会执行then回调。
13. promise.all 中其中有一个失败了会怎么处理?
其中有一个失败了会直接进入catch,不会进入 then回调。
14. 同源策略?
同源是指协议、域名、端口号相同。浏览器限制同源是为了保证用户信息安全,防止恶意网站窃取数据。
15. 如果不同源,出现跨域问题,怎么解决?
- jsonp,利用script标签没有同源限制的漏洞进行通信,缺点是只能发送get请求
- CORS,服务端配置 Access-Control-Allow-Origin 开启,表示哪些域名可以访问资源,如果设置通配符,则表示所有网站都可访问。
- document.domain,只适用于二级域名相同的情况。
- postMessage,通常用于获取嵌入页面的第三方数据。
16. 在跨域的时候什么时候会发送OPTIONS请求?
OPTIONS请求也叫做预检请求,可以检查服务器支持哪些http请求方法。
当一个请求不是简单请求时,浏览器会发送OPTIONS请求获知服务器是否允许该请求。
满足以下条件就是一个简单请求:
- Method: 请求的方法是 GET、 POST 及 HEAD
- Header: 请求头是 Content-Type、 Accept-Language、 Content-Language 等
- Content-Type: 请求类型是 application/x-www-form-urlencoded、 multipart/form-data 或 text/plain
而在项目中常见的 Content-Type:application/json, Authorization: <token> 都是典型的非简单请求,在发送请求前往往会发送OPTIONS请求。
17. html 中为什么css放在上面,js放在下面?
css使用href异步加载,不会造成页面的阻塞,放在html头部,可以和DOM 树一同渲染,可以防止页面出现白屏,闪跳或布局混乱。 js使用src同步加载,会阻塞其他资源的加载和dom的解析,影响到首次渲染,所以为了减少首次渲染的时长,应该尽量把js放到html的底部。
(此处还可以引申到浏览器的渲染原理。)
18. 浏览器的缓存机制?强缓存,协商缓存?
- 浏览器每次发起请求都会在浏览器缓存中查找请求的结果以及缓存标识。
- 浏览器每次拿到返回结果都会将返回结果和缓存标识存入浏览器缓存中。
浏览器的缓存机制分为两种:强缓存和协商缓存。
强缓存主要使用(Expires、Cache-Control),Expires是在http/1.0 时使用的,它规定什么时间以后缓存过期。但是由于可能会存在客户端时间与服务器端时间不一致,导致缓存失效的问题,http/1.1之后可以使用 Cache-Control来规定最大有效时长,且两者同时存在时,Cache-Control 优先级更高。
协商缓存主要使用(Last-Modified/If-Modify-Since、ETag/If-None-Match)。
服务器设置 Last-Modified 最后的修改时间。If-Modify-Since是浏览器下次请求的时候,携带上次服务器返回的Last-Modified,服务器检查请求头时,发现If-Modify-Since字段,对比最后修改时间和服务器存储的资源最后修改时间,判断资源是否有更新,如果没有,则返回304,浏览器使用缓存,如果有,返回200以及新的资源。
ETag是服务器设置的该资源的唯一标识,If-None-Match 是浏览器下次请求时,携带上次服务器返回的ETag,服务器会对比ETag和服务器存储的资源唯一标识是否一致,是,则返回304,表示资源没有更新,使用缓存,不是,则返回200以及新的资源。 两者同时存在时,ETag/If-None-Match 的优先级更高。
强缓存优先于协商缓存,若强缓存生效,则直接使用缓存,若不生效,则进行协商缓存。协商缓存由服务器决定是否使用缓存,若协商缓存失效,则重新请求结果,并存入浏览器缓存中,若缓存生效,则返回304,继续使用浏览器缓存。
参考文章:彻底理解浏览器缓存机制
19. 关于Cookie,二级域名和三级域名两个cookie之间的关系是什么?比如baidu.com 和 abc.baidu.com ,浏览器会怎么管理二者的cookie?
二级域名的Cookie可以共享给三级域名。三级域名的Cookie只能用于自己本身。
20. 主流框架为什么要采用 virtral DOM?有什么好处?
以前的框架,例如 JQuery,都是采用简单粗暴的方式重新构建DOM替换旧DOM的,问题也很明显:1. 性能消耗高。2. 无法保存状态(焦点,滚动等)。
virtral DOM 实际也是操作DOM树进行渲染更新,但是只针对修改部分进行局部渲染,将影响降到最低。
- 用js对象结构描述DOM树结构,然后用它去构建真实的DOM
- 当状态改变后,构建新的js对象结构,与旧的js对象结构做对比,找出差异点
- 针对差异之处进行重新构建,更新视图
就是用js做了一层映射,操作简单且速度远高于直接比较DOM。
参考文章: React diff 算法实现分析
21. DOM 更新的时候,React采用什么样的策略来更新?将O(n^3)的复杂度降低到O(n)的复杂度,讲一下 diff 算法?(一共有3种方法)
React 的 diff 算法 分为3种策略。
- tree diff:web UI 中,DOM节点跨层级的移动操作特别少,可以忽略不计。
- component diff:拥有相同类的两个组件生成相似的树形结构,不同类的两个组件生成不同的树形结构。
- element diff:对于同一层级的一组子节点使用唯一的id进行区分。
tree diff:对树分层比较,只对同一层次的节点比较,如果该节点不存在,则直接删除该节点及其子节点,不再进一步比较。通过这样的方法,只需要遍历一次就可以完成整棵树的比较。
component diff:同一类型的两个组件,使用分层比较的方式。可以通过componentShouldUpdate告知React是否需要判断计算。不同类型的组件,直接替换整个组件的所有节点。
element diff:当处于统一层级时,diff提供三种操作:删除,插入,移动。通过组件的key进行判断。
参考文章: React diff 算法实现分析、 React diff 算法
22. Redux 用来做什么的?
Redux 是一个状态管理工具。主要有三个基本概念,就是 store,action,reducer。
store是存储数据的地方。当view更新的时候,会发出一个action,通知store更新。Reducer是sotre接收action后的一个计算过程。它是一个纯函数,就是当输入一致的时候,也一定会得到相同的输出。
23. 讲一下比较熟悉的 React Hooks方法。
useState:该方法接收一个参数作为初始值,返回一个state,以及一个更新state的方法。 useEffect:该方法接收两个参数,第一个参数是一个函数,第二个参数是useEffect依赖的值数组,当数组的值发生改变时,useEffect才会重新创建订阅。 useContext:解决在组件之间共享状态,逐层传递props的问题,接收一个context对象,返回这个context 当前的值。
24. useEffect 第二个参数传入一个空数组,会怎样?
useEffect仅在组件挂载和卸载的时候执行,意思是告诉React,这个effect只需要运行一次。
25. useCallback 或 useMemo 是否用到过?
useCallback和useMemo可用于缓存函数的引用和值。useCallback 参数是一个回调函数,以及一个依赖数组。返回一个 memoized 版本的回调函数。useMemo参数是一个值。
useCallback(fn, deps) === useMemo(() => fn, deps);。对于组件内部的对象,数组,函数等,如果用在其他hooks 的依赖数组,或是通过 props 传递出去,都需要使用 useCallback / useMemo。
26. 对 webpack 的理解,打包过程? 平时使用过程中用到的 plugin 或 loader ?
webpack是一个模块打包工具,可以递归打包项目中的所有模块(指定一个入口,分析所有依赖,递归查找相关依赖)。最终生成几个打包好的文件。
步骤:
- 从配置文件和shell语句中读取和合并参数,组合成最终的参数。
- 开始编译:用上一步得到的参数初始化 Compiler 对象。加载所有配置的插件,执行对象的run方法开始执行编译。
- 确定入口:根据配置中的 entry 找出所有入口文件。
- 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行编译,再找出该模块以来的模块,递归编译。
- 完成模块编译,得到了每个模块被编译后的最终内容以及依赖关系。
- 根据入口和模块的依赖关系,组装成一个个宝很多个模块的Chunk,再把每一个Chunk转换成一个单独的文件加入输出列表。
- 确定好输出内容后,根据配置确定输出的路径及文件名,把文件名写入文件系统。
loader:是一种打包方案,webpack只识别js文件,我们可以通过配置loader,告诉webpack 如何处理对应的文件。常用的有 css-loader,less-loader,babel-loader等。
plugin:HotModuleReplacementPlugin 热更新,html-webpack-plugin,根据模板自动生成html代码,自动引入css,js文件,等。
27. babel-loader 是做什么的?
babel-loader 是一个npm包,它使得 webpack 可以通过 babel 将 js 代码转移成浏览器能够识别的js代码。
28. 你有什么问题来问我吗?
- 了解一下您的团队。室外3d模型。toB。
- 是否所有人都在上海?
- 请您对我做个评价,给些建议。
浏览器同源策略,options,缓存机制可以讲得更细一些。技术栈要看一下,react diff的三个方案要应该说出来。react hooks 也要熟练。