JavaScript
自检问题
- 什么是值?什么是类型?什么是变量? 它们之间的区别和联系
- 基本类型和引用类型的区别是什么?null 和 undefined 区别是什么?
- “一切皆对象”怎么理解? number 也是对象么?字符串也是对象么?
- 基础类型存放在栈上,引用类型存放在堆上,请问是为什么? 字符串是存放在栈上么?对象中有一个 number 属性,那么 number 属性是存放在堆上还是栈上?
- == 的判断逻辑是什么?
- 作用域的本质是什么?闭包和作用域的关系是什么?
- let,const,var 三者的本质不同是什么?为什么不推荐使用 var
- 数组的本质是什么,运用了什么样的设计模式?数组和对象的关系是什么?
- 原型链能够实现所谓的继承的本质原因是什么?
- 箭头函数是用来解决什么问题的?
- 什么是高阶函数?用处和用法?
- 什么是异步编程,为什么说它对 Web 开发很重要?
参考回答
1.什么是值?什么是类型?什么是变量? 它们之间的区别和联系?
值是数据;类型是数据的类型,包括基本类型和引用类型;变量是一个用于保存任意值的命名占位符;
2.基本类型和引用类型的区别是什么?null 和 undefined 区别是什么?
根据值是否具有不可变性(有固定的长度),将其类型划分为基本类型和引用类型。内存空间分为栈内存和堆内存:
栈内存:
- 存储的值大小固定
- 空间较小
- 可以直接操作其保存的变量,运行效率高
- 由系统自动分配存储空间
基本类型的值存储在栈内存当中,由于存储的值大小固定,因此具有不可变性。let num = 3; num = 5
这并不代表值3变成了值5,而是在num = 5
时,栈又开辟了新的空间存储值5,变量num指向了这片空间,这也叫按值传递(每次赋值给变量时,都会创建该值的副本)。
堆内存:
- 存储的值大小不定,可动态调整
- 空间较大,运行效率低
- 无法直接操作其内部存储,使用引用地址读取
- 通过代码进行分配空间
引用类型的值存储在堆内存中,他在栈中只存储了一个固定长度的地址,这个地址指向堆内存中的值。可以轻易的这些值。let obj = {a:1}; let obj1 = obj; obj1.a = 3; console.log(obj.a) //3
,其中let obj1 = obj
定义一个变量obj1
,并使用存储在obj
变量中的地址来初始化obj1
(这个地址也在内存中赋值了一份,两个地址共同指向{a:1}),这是一个引用传递。
undefined类型只有一个值undefined,表示已声明但未初始化变量的值。typeof(已声明但未初始化 / 未声明 的变量)
返回的都是undefined(逻辑上表示都无法执行实际操作,虽然这两个变存在根本性差异);null类型只有一个值null,表示一个空对象指针,这也是typeof null
返回object的原因(不同对象在底层都表现为二进制,js中二进制前3位都是0的话会被判断为object类型,null的二进制表示全都是0,,所以会有上面的bug);undefined 值是由 null 值派生而来的,ES3增加这个值的目的是为了明确空对象指针和未初始化变量的区别,console.log(null == undefined); // true
3.“一切皆对象”怎么理解? number 也是对象么?字符串也是对象么?
并非所有数据类型都是对象,基本类型都不是对象。之所有一些基本类型的值可以访问属性和方法,是因为引擎自动把字面量转化成js中的内置对象(如:String、Number、Boolean、Object、Function、Array、Date、Error、RegExp)
4.基础类型存放在栈上,引用类型存放在堆上,请问是为什么? 字符串是存放在栈上么?对象中有一个 number 属性,那么 number 属性是存放在堆上还是栈上?
因为基础类型在创建的时候已确定大小,而引用类型创建的时候无法确定大小?或由栈和堆的特点决定的,栈存值大小固定且效率高,而对空间大而运行效率低;
字符串: 存在堆里,栈中为引用地址,如果存在相同字符串,则引用地址相同。
数字:小整数存在栈中,其他类型存在堆中。
其他类型:引擎初始化时分配唯一地址,栈中的变量存的是唯一的引用。
5.== 的判断逻辑是什么?
先将两个值进行强制类型转换,再确定操作符是否相等。
转换操作数的类型时,相等和不相等操作符遵循如下规则:
如果一个操作数是布尔值,则将布尔值转换为数值再比较
如果一个操作数是字符串,另一个是数值,则尝试将字符串转换为数值再比较
- 注:布尔值和字符串都会使用
Number()
进行转换,Number()
比parseInt
严格很多如果一个操作数是对象,另一个不是对象,则调用对象的
valueOf()
方法取其原始值再比较
toString()
返回对象的字符串表示。valueOf()
返回对象对应的字符串、数值或布尔值表示。通常与toString()
的返回值相同。
| 对象 | 返回值 | | --- | --- | | Array | 数组的元素被转换为字符串,这些字符串由逗号分隔,连接在一起。其操作与 Array.toString 和 Array.join方法相同。 | | Boolean | Boolean 值 | | Date | 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC | | Function | 函数本身 | | Number | 数字值 | | Obejct | 对象本身,这是默认情况 | | String | 字符串值 |
进行比较时,相等和不相等操作符遵循如下规则
- null和undefined相等
- null和undefined不能转换为其他类型的值再进行比较
- 如果一个操作数是NaN,则相等操作符返回false,即使另一个操作数是NaN
- 如果两个操作数都是对象,则比较它们是不是同一个对象(指向同一个对象)。
全等相比于相等,不会进行类型转换,推荐使用,有助于在代码中保持数据类型的完整性。
6. 作用域的本质是什么?闭包和作用域的关系是什么?
作用域是一套规则,规定了在运行时代码的某些特定部分中变量、函数和对象的可访问性。作用域链是作用域这套规则的实现。
JavaScript 属于解释型语言,JavaScript 的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:
解释阶段:
- 词法分析
- 语法分析
- 作用域规则确定
执行阶段:
- 创建执行上下文
- 执行函数代码
- 垃圾回收
JavaScript 解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。执行上下文最明显的就是 this 的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。
函数在调用激活时,会开始创建对应的执行上下文,在执行上下文生成的过程中,变量对象、作用域链、this的值会被分别确定。
在 fn 函数中,取自由变量 x 的值时,要到哪个作用域中取?——要到创建 fn 函数的那个作用域中取,无论 fn 函数将在哪里调用。
“无论函数是在哪里调用,也无论函数是如何调用的,其确定的词法作用域永远都是在函数被声明的时候确定下来的”
闭包使得作用域链上的变量对象不被垃圾回收机制回收,可以记住并访问所在作用域,因为嵌套函数(闭包)的引用被保留了
7. let,const,var 三者的本质不同是什么?为什么不推荐使用 var
作用域规则:var声明的变量在函数作用域下有效,let和const声明的变量在块作用域下有效
变量提升:var声明的变量存在变量提升,let和const不存在
重复声明/赋值:var声明的变量可以重复声明和赋值,let和const不可以
let和const的区别:const声明变量时必须同时初始化,且变量的值不能改变
为什么不推荐使用var?var创建的变量都是挂载在顶级对象上面,重复定义会改变之前定义的值,会造成全局变量不可控,变量污染;重复声明不报错,代码不易维护。使用let和const,变量有了明确的作用域、声明位置和不变的值。
推荐使用const?可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作。优先使用 const 来声明变量,只在提前知道未来会有修改时,再使用 let。这样可以让开发者更有信心地推断某些变量的值永远不会变,同时也能迅速发现因意外赋值导致的非预期行为。
8. 数组的本质是什么,运用了什么样的设计模式?数组和对象的关系是什么?
数组是特殊的对象,typeof arr // "object"
数组是特殊的对象,继承了对象原型的所有方法,但是多了作为数组原型的方法,例如push、pop等
9. 原型链能够实现所谓的继承的本质原因是什么?
依靠_proto_
。构造函数的实例有一个_proto_
属性指向构造函数的原型,当访问实例的属性时,如果在当前对象上找不到,就访问对象的_proto_
,以此访问到了构造函数的属性,实现继承机制。
10. 箭头函数是用来解决什么问题的?
主要设计目的是以特定的方式改变this的行为特性,解决this相关编码的一个特殊而又常见的痛点。在箭头函数内部,this绑定不是动态的,而是词法的。箭头函数的this指向其所在的作用域的变量对象。
11. 什么是高阶函数?用处和用法?
高阶函数是一个将函数作为参数或者返回值的函数。例如map、filter。
12. 什么是异步编程,为什么说它对 Web 开发很重要?
js的需要长时间运行的代码,交给浏览器另一个线程去运行,运行完毕之后再添加到js的单线程中去运行,保证了js程序不堵塞。
浏览器
自检问题
- 事件模型
- 一文看懂浏览器事件循环
- 浏览器安全策略
- 事件循环
- BOM API
- Chrome 浏览器中的进程和线程
参考回答
1. 浏览器事件模型 参考
浏览器是事件驱动的,什么都抽象为事件。当一个事件发生的时候,浏览器会初始化一个事件对象,然后这个事件对象会按照事件传播机制进行传播。事件传播机制分为三个阶段,开发者可以选择监听不同的阶段,以达到目的。
事件代理是利用事件传播机制,将本来是子元素的事件处理函数,放到父元素上去。这样当子元素发生事件时,通过事件传播依然可以执行这个事件处理函数,即父元素代理了这个事件。这样做的好处是减少事件注册,节省内存占用;减少与DOM交互次数,提高性能。
事件循环就像是浏览器的调度器,决定v8什么时候执行什么代码。
v8:v8引擎的内存空间分为栈(执行栈)和堆
浏览器:浏览器提供了web API(包括DOM),和事件队列(任务队列)
宏任务:
- script(整体代码)
- setTimeout()
- setInterval()
- postMessage
- I/O
- UI交互事件
微任务:
- new Promise().then(回调)
- MutationObserver(html5 新特性)
注:全局代码属于宏任务
在v8引擎编译并执行js代码时,会将同步代码压入执行栈,执行代码时如果碰到异步事件,会将异步事件挂起(此时浏览器另一个线程会来处理这个异步事件),异步事件有了结果后将其放入事件队列,等待主线程中同步代码全部执行完,再把事件队列中的任务一个个放入执行栈执行。
只不过异步事件又分为宏任务和微任务,相应地事件队列分为宏任务队列和微任务队列,在将事件队列中的任务加入执行栈的时候,先把微任务执行了,再执行宏任务。
注:v8执行代码时,碰到函数,会有一个创建阶段,一个执行阶段。在创建阶段,v8会将当前函数的执行上下文压入执行上下文栈(此栈随着执行栈的销毁而销毁),执行上下文包括变量对象、作用域链、this的值。v8执行代码时,查询变量就到当前函数的执行上下文去找,找不到就沿着作用域链去找。而这个作用域链取决于静态作用域(在函数声明时就已经决定了),不管v8何时调用函数,要查找的变量只要还在执行上下文栈中就会被查找到。闭包的存在就使得它上一层的执行上下文保留下来了。
2. 浏览器安全策略
- BOM API
- Chrome 浏览器中的进程和线程
网络
自检问题
- WebSocket 工作在七层(或者四层)中的哪一层?和 HTTP 是什么关系? 是基于 UDP 的还是 TCP 的?
- TCP 为什么是三次握手?为什么是四次挥手?
- CDN 的工作原理是什么?
- 运营商劫持是什么?如何防范?
- HTTPS 一定是安全的么?如果不是,那么在什么情况下是不安全的?
- 如何劫持 HTTPS 请求。 比如你需要抓 HTTPS 的包,怎么做?
- 支付宝和微信的离线支付是怎么做的?
- Token 和 Cookie 有什么区别和联系呢?其分别是为了解决什么样的事情?
- WebSocket 需要 cookie 么?为什么?
- WebSocket 是怎么实现点对点通信和广播通信的?
- 如果访问你的 APP 很慢,你自己无法重现。 初步定位到网络问题, 那么你怎么能具体定位到问题呢?
- traceroute, Ping 的原理是什么?
- 192.168.0.1 和 192.168.1.1 如何通信?
- DNS 是如何泄漏个人隐私的?怎么防范?
- 从网络协议模型(七层 or 四层)的角度分析一下,浏览器访问 192.168.3.4:8088 的具体过程。
参考回答
1. WebSocket 工作在七层(或者四层)中的哪一层?和 HTTP 是什么关系? 是基于 UDP 的还是 TCP 的?
理清 WebSocket 和 HTTP 的关系、WebSocket 教程
工作再应用层;WebSocket和HTTP都是基于TCP协议的两个不同的协议,WebSocket依赖于HTTP连接;基于TCP
2. TCP 为什么是三次握手?为什么是四次挥手?
TCP三次握手才能保证双方都有发送和接受信息的能力,若是两次握手,客户端或许因为网络堵塞等原因放弃这个连接了,此时服务器还以为他自己发出确认请求后连接就建立了,为其分配内存之类的,白白浪费很多资源。
TCP关闭连接时,服务器要响应客户端的关闭请求,但此时服务器端的数据可能还没有传完,只好先发送一个确认ask包,等自己数据传输完了再发送一个fin包,这样就有了两次挥手,而不是三次握手一样,将一次握手和确认合并在一起l
3. CDN 的工作原理是什么?
CDN的工作原理是将源站的资源缓存在CDN各个节点上,当请求命中了某个节点的资源缓存时,立即返回客户端,避免每个请求的资源都通过源站获取,避免网络拥塞、缓解源站压力,保证用户访问资源的速度和体验。
4. 运营商劫持是什么?如何防范?
运营商是指提供宽带服务的ISP,包括中国电信、移动、联通,为了经济利益,会劫持用户的访问,称为运营商劫持。运营商劫持分为DNS劫持、http劫持、https劫持。
DNS劫持已被严厉接管,是违法行为;对于http劫持,可以加入防运营商劫持代码,判断ip是否来自黑名单,是则丢弃返回包,也可以拆包发送,让其检测不到是http请求。
5. HTTPS 一定是安全的么?如果不是,那么在什么情况下是不安全的?
https通信如果检测出不合法的证书时,会进行风险提示,用户仍然可以授权信任证书继续操作,此时如果出现中间人攻击,https就不是安全的了。
6. 如何劫持 HTTPS 请求。 比如你需要抓 HTTPS 的包,怎么做?
可以自己签发证书,在客户端安装自己的根证书,这个证书就被浏览器信任,然后作为中间人劫持https请求。
7. 支付宝和微信的离线支付是怎么做的?
8. Token 和 Cookie 有什么区别和联系呢?其分别是为了解决什么样的事情?
傻傻分不清之 Cookie、Session、Token、JWT
Cookie是弥补http无状态的问题的,使用cookie可以使服务器识别前后两个请求是否来自同一浏览器,从而进行会话跟踪;Token是访问资源接口时所需要的资源凭证,是类似cookie的一种验证信息。
Token和cookie的认证流程很相似,都是服务端发给客服端,客户端在下次请求时带上token或cookie来进行验证;只不过token做了加密,而且是开发者自己决定要不要储存、发送给服务端(cookie默认储存与发送),不存在同源限制,比cookie更灵活。
9. WebSocket 需要 cookie 么?为什么?
WebSocket不需要cookie,因为使用WebSocket,客户端与服务端就一直保持连接的状态,双方可以互发数据,服务器也知道客户端的身份,不需要cookie来做验证。
10. WebSocket 是怎么实现点对点通信和广播通信的?
11. 如果访问你的 APP 很慢,你自己无法重现。 初步定位到网络问题, 那么你怎么能具体定位到问题呢?
12. traceroute, Ping 的原理是什么?
13. 192.168.0.1 和 192.168.1.1 如何通信?
14. DNS 是如何泄漏个人隐私的?怎么防范?
15. 从网络协议模型(七层 or 四层)的角度分析一下,浏览器访问 192.168.3.4:8088 的具体过程。
浏览器根据ip地址与目标服务器建立TCP连接,浏览器发出http请求报文,由套接字传到运输层,将请求报文封装成TCP报文段,在网络层封装成ip数据报,查找路由表,确定通过哪个路径到达目的主机,ICMP提供网络传输中的差错检测;在数据链路层通过ARP将目的主机的ip地址映射成MAC地址,将ip数据报封装成帧;物理层将帧中的一个个比特从一个节点移动到下一个节点;服务器监听8088端口的客户端请求,再把响应信息返回给客户端