一、 JS 中 "&&" "||" "!" "&" "|"有什么区别?
在 JS 中"&&" "||" 是逻辑运算符;"&" "|"是位运算符
- JS 中的位运算符:& 规则:两个数值的个位分别相与,同时为 1 才得 1,只要一个为 0 就为 0
31&2 结果为2
原因:31的二进制为11111,2的二进制为10 11111 & 10的结果为:00010,得2
- JS 中的位运算符:| 规则:两个位只要有一个为 1,那么结果都是为 1.否则就为 0
31|2 结果为31
原因:31的二进制为11111,2的二进制为10 11111 | 10的结果为:11111,得31
- JS 中的逻辑运算符:&& 规则:1)、只要&&前面是 false,无论后面是 true 还是 false,结果都将返回&&前面的值;2)、只要&&前面是 true,无论&&后面是 true 还是 false,结果都将返回&&后面的值;
31&&2 得 2
31||0 得 0
- JS 中的逻辑运算符:|| 规则:1)、只要||前面为 false,不管||后面是 true 还是 false,都返回||后面的值;2)、只要||前面为 true,不管||后面是 true 还是 false,都返回||前面的值;
扩展: 0、""、null、false、undefined、NaN 都会判断为 false,其余的都是 true
function(obj) {
var a = obj || {}
}
==== 等价于 ====
function(obj) {
var a;
if(obj===0||obj===""||obj===false||obj===null||obj===undefined) {
a = {}
} else {
a = obj
}
}
function(obj) {
var a = obj ?? {}
}
==== 等价于 ====
function(obj) {
var a;
if(obj===null||obj===undefined){
a = {}
}else {
a = obj
}
}
二、前端模块化
https://segmentfault.com/a/1190000017466120
ES6 Module
和Commonjs
区别?
ES6 Module
静态引入,编译时引入CommonJs
动态引入,执行(执行)时引入- 只有
ES6 Module
才能静态分析,实现Tree-Shaking
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
优势: CommonJS 加载的是一个对象(即 module.exports 属性),该对象只有在脚本运行完才会生成。而 ES6 Modules 不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成
三、为何 Proxy 不能被 Polyfill
- 如
Class
可以用function
模拟 - 如
Promise
可以用callback
来模拟 - 但
Proxy
的功能用Object.defineProperty
无法模拟
四、解决跨域的方式有哪些
同源策略: 域名,协议,端口三个都相同
同源策略的作用:
- 无法用 js 读取非同源的 Cookie、LocalStorage 和 IndexDB
- 无法用 js 获取非同源的 DOM
- 无法用 js 发送非同源的 AJAX 请求
实现跨域的方式?
- jsonp: 利用了 img、script 和 link 标签自身的跨域能力
扩展:
- script 的 src 和 img 的 src 跨域的区别?
原理上都是利用标签的 src 可绕过同源限制,跨域请求的特点;区别在于:img 只能单向发送 get 请求,不可访问响应内容(只是展现),而 script 可对其进行解析
- 如果黑客植入 script 脚本通过 jsonp 的方式对服务器进行攻击,怎么办?
可以通过页面设置的内容安全协议 csp 进行防范
缺点:
1. 只支持 get 请求 <br /> 2. 需要后台配合,将返回结果包装成 callback(res)的形式
- cors
浏览器将 CORS 请求分成两类:简单请求和非简单请求;当发出简单请求,只需要在头信息之中增加一个 Origin 字段。当发出 CORS 非简单请求,会在正式通信之前,增加一次 OPTIONS 查询请求,称为"预检"请求(preflight)
简单请求同时满足的三个条件?
- 请求方式只能是:GET、POST、HEAD
- HTTP 请求头限制这几种字段:Accept、Accept-Language、Content-Language、Content-Type、Last-Event-ID
- Content-type 只能取:application/x-www-form-urlencoded(是 Jquery 的 Ajax 请求默认方式)、multipart/form-data、text/plain
**扩展:**content-type 的类型还有:application/json
预检请求:浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的 XMLHttpRequest 请求,否则就报错。
服务端如何避免每次都发出预检请求?(缓存)
- Access-Control-Max-Age 该字段可选,用来指定本次预检请求的有效期,单位为秒。在有效期间,不用发出另一条预检请求(全局和局部方式)常用
- @CrossOrigin 注解,可细粒度精确到单个请求级别
nginx
vue -> proxy(服务于服务之间是不存在跨域的)
vue-cli2 使用 proxyTable
vue-cli3+ 使用 proxy
proxy: {
'/api': {
target: '这里填的是后端ip 和端口号如:http://27.154.59.202:8000',
changeOrigin: true,
// ws: true, // 如果要代理 webSockets,配置这个参数
// secure: true, //如果是https接口,需要配置这个参数
pathRewrite: {
'^/api': ''
}
}
}
五、JavaScript 是如何运行的?
JS 代码 -> 解析成 AST (期间伴随词法分析、语法分析) -> 生成字节码(V8)-> 生成机器码(编译器)
六、简单描述一下 Babel 的编译过程?
Babel 的作用是 从一种源码到另一种源码,充当转换编译器的作用,可以简述为 解析(解析 JS 代码)->转换(解析和修改 AST)->重建(将修改后的 AST 转换成另一种 JS 代码)
七、浏览器和 Node.js 中的事件循环机制有什么区别?
浏览器中的事件循环:
- macroTasks(宏任务)
script(整体代码)、setTimeout、setInterval、setImmediate、I/O、UI Rendering、event listener
- microTasks(微任务)
process.nextTick、Promise、Object.observe、MutationObserve
主要:一次 EventLoop 循环会处理一个宏任务和所有这次循环中产生的微任务。
NodeJs 中的事件循环
- timersj 阶段:这个阶段执行 timer(setTimeout、setInterval)的回调
- I/O callbacks:执行一些系统调用错误,比如网络通信的错误回调
- idle,prepare:仅 node 内部使用
- poll:获取新的 I/O 事件, 适当的条件下 node 将阻塞在这里
- check:执行 setImmediate() 的回调
- close callbacks:执行 socket 的 close 事件回调
区别
浏览器环境下,microTask 的任务队列是每个 macroTask 执行完之后执行。而在 Node.js 中,microTask 会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会去执行 microTask 队列的任务
八、编译器一般由哪几个阶段组成?
编译器一般由 4 个阶段工作完成:
Parse 阶段:V8 引擎负责将 JS 代码转换成 AST(抽象语法树);
Ignition 阶段:解释器将 AST 转换为字节码,解析执行字节码也会为下一个阶段优化编译提供需要的信息;
TurboFan 阶段:编译器利用上个阶段收集的信息,将字节码优化为可以执行的机器码;
Orinoco 阶段:垃圾回收阶段,将程序中不再使用的内存空间进行回收。
注意:数据类型检查
一般在 Parse 阶段之前 就进行了,因为在生成 AST 之前 就要进行语法分析,提取出句子的结构
九、发布 / 订阅模式和观察者模式的区别是什么?
在观察者模式中,被观察者通常会维护一个观察者列表。当被观察者的状态发生改变时,就会通知观察者
在发布订阅模式中,具体发布者会动态维护一个订阅者的列表:可在运行时根据程序需要开始或停止发布给对应订阅者的事件通知
区别在于发布者本身并不维护订阅列表(它不会像观察者一样主动维护一个列表),它会将工作委派给具体发布者(相当于秘书,任何人想知道我的事情,直接问我的秘书就可以了);订阅者在接收到发布者的消息后,会委派具体的订阅者来进行相关的处理