面试锦盒一

177 阅读12分钟

又到了金三银四的时候。整理了近期面试一些高频的面试题,分享大家一起学习。向offer前进

七层模型

应用层

最切近用户的一层,为计算机用户提供应用接口,也为用户提供各种网络服务,我们常用的各种网络服务协议有:HTTPHTTPSFtpPOP3,SWTP

  • 在客户端和服务器中经常会有数据的请求,这个时候就用到了http(hyper text transfer protocol)(超文本传输协议)或者https,在后端设计接口的时候也经常用到该协议。

  • FTP文件传输协议,在一些资源网站,比如百度网盘,迅雷等应该是基于此协议

  • SWTPsimple mail transfer protocol(简单邮件传输协议)在一个项目中在用户邮箱验证登陆码登陆功能的时候,用到了这个协议

表示层

提供各种应用层数据的编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别。如果必要,该层可提供一种标准形式,用户将计算机内部的多种数据格式转换成通信中采用的标准表达形式。在项目的开发过程中,为了方便数据的传输,可以使用base64对数据进行编解码,按照功能划分base64应该在是在表示层。

会话层

会话层就是负责建立,管理,终止表示层实体之间的通信会话,该层的通信有不同设备中的应用程序之间的服务请求和响应组成。

传输层

传输层建立了主机端到端的链接,传输层的作用是为上层协议提供端到端的可靠透明的数据传输服务,包括处理差错控制和流量控制等,该层向高层屏蔽了下层数据通信的细节,使得高层用户看到的只是在两个传输实体间的一条主机到主机,可由用户控制和设定的可靠的数据通路。我们通常说的TCP和DUP就是在这一层。端口号即是这里的端。

网络层

本层通过IP寻址来建立两个节点之间的连接。为源端的传输层送来的分组,选择合适的路由和交换节点,正确无误的按照地址传给目的端的传输层。就是通常说的IP层。这一层就是我们经常说的IP协议层,我们可以这样理解网络层规定了数据包的传输路线,而传输层规定了数据包的传输方式。

数据链路层

将比特组合成子节,再将字节组合成帧,使用链路层地址(以太网使用mac地址)来访问介质,并进行差错检测,网络层和数据链路层的对比,通过上面的描述,我们或许可以认为,网络层是规划了数据包的传输路线,而数据链路层就是传输路线。

物理层

最终信号的传输是通过物理层实现的,通过物理介质传输比特流,规定了电平,速度和电缆帧脚,常用的设备有集线器,中继器,调制解调器,网线,双绞线,同轴电缆,这些都是物理层的传输介质。

javaScript有几种数据类型?对应的存贮位置是什么?

原始数据类型(undefined,null,boolean,string,number),直接存贮在栈中的简单数据段,占据空间小,大小固定,属于被频繁使用的数据。所以存储在栈中。

引用数据类型(object,array,function)存贮在堆中,占据空间大,大小不固定,如果存在栈中将会影响程序的性能,引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器查找引用值时,会首先检索在栈中的位置。取得地址后从堆中获得实体。

什么是堆,什么是栈?他们之间有什么区别和联系?

在数据结构中,栈中数据存储方式是先进后出,而堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定,完全二叉树是堆的一种实现方式。

在操作系统中,内存被分为栈区和堆区。

栈区内存由编译器自动分配释放,存放函数的参数值,局部变量等,

堆区内存一般由程序员自动分配释放,若不释放,程序结束时可能由垃圾回收机制回收。

内部属性[[class]] 是什么?

所有typeof 返回值为"object"的对象都包含一个内部属性[[class]]我们可以把它看作内部的一个分类。而非传统面向对象意义上的类。这个属性无法访问,一般通过 object.prototype.toString(...)来查看。

object.prototype.toString.call([1,2,3])
// "[object Array]"

虽然Null()和Undefined()这样的原生的构造函数并不存在,但是内部的[[class]]属性仍然是"Null"和"Undefined"。

其他基本类型值的情况有所不同。基本类型值被各自的封装对象自动包装,所以他们的内部[[class]] 属性分别为"String","Number","Boolean"。基本类型值没有.length和.toString()这样的属性和方法,需要通过封装对象才能访问,此时js 引擎会自动为基本数据类型包装一个封装对象。

null 和 undefined的区别?

首先undefined和null都是基本数据类型。

这两个基本数据类型都有一个值就是undefined和null。

undefined 代表的含义就是未定义null 代表的含义就是空对象,一般声明了但还没定义的时候会返回undefined,null一般会赋值一些可能会返回对象的变量。作为初始化。

当我们对两种类型使用typeof 进行判断的时候,null类型会返回"object",当我们使用双等号对两种类型的值进行比较的时候会返回true,使用三等号会返回false。

javascript原型,原型链有什么特点?

在js中我们使用构造函数来新建一个对象,每一个构造函数的内部都有一个prototype属性值。这个属性值是一个对象。这个对象中包含了可以由该构造函数的所有的实例共享的属性和方法。当我们使用构造函数新建一个对象后,这个对象的内部将包含一个指针,这个指针指向构造函数的prototype属性对应的值。在es5中这个指针被称为对象的原型。一般来说我们是不能取到这个值的。但是在浏览器中都实现了__proto__ 属性来让我们访问这个属性。但是最好不好使用。es5中新增了一个object.getPrototypeOf() 方法,我们可以通过这个方法来获取对象的原型。

当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去它的原型对象上去找这个属性,这个原型对象又会有自己的原型,于是就这样一直找,这就是原型链的概念。原型连的尽头一般都是Object.prptotype这就是我们新建的对象为什么会有toString()等方法的原因。

js对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时与之对象的对象也会继承这一改变。

typeOf NaN的结果是什么?

typeof NaN。 结果是 number

NaN是一个特殊的值,他和自身不想等,是唯一一个非自反的值。而 NaN!= NaN 结果为true。

isNaN和Number.isNaN 函数的区别?

函数isNaN接收参数后,会尝试将这个参数转化为数值,任何不能转化为数值的值都会返回true,因此非数值也会返回true,会影响NaN的判断。

函数Number.isNaN 会首先判断传入参数是否为数字,如果是数字在继续判断是否为NaN,这种方法对于NaN的判断准确。

什么是DOM和BOM?

DOM指的是文档对象模型,他指的是把文档当作一个对象来对待,这个对象主要定义了处理网页内容的方法和接口。

BOM指的是浏览器对象模型,它指的是把浏览器当作一个对象来对待。这个对象主要定义了与浏览器进行交互的方法和接口,BOM的核心是window。而window对象具有双重角色。他即通过js访问浏览窗口的一个接口,又是一个global对象。着意味着在网页中定义的任何对象变量和函数,都作为全局对象的一个属性和方法存在,window对象含有location 对象和navigator对象,screen对象,并且DOM的最根本的对象document对象也是DOM的window对象的子对象。

什么是闭包,为什么要用它?

闭包是有权访问另一个函数作用域中变量的函数,创建闭包最常用的方法就是在一个函数内创建另一个函数,创建的函数可以访问到当前函数的局部变量。

闭包有两个用途

1、使我们在函数外可以访问到函数内部的变量,通过使用闭包,我们可以通过在外部调用闭包函数,从而在外部可以访问到函数内部的变量,可以使用这种方式来创建私有变量。

2、使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收。

其实闭包的本质就是作用域链的一个特殊应用,主要了解作用于链的创建过程,就能理解闭包的实现原理。

.call 和 .apply 的区别?

他们的作用是一模一样,区别在于传入的参数形式不同

apply接受两个参数,第一个参数指定了函数体内this对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以是类数组,apply方法把这个集合中的元素作为参数传递给被调用的函数。

call传入的参数数量不固定,跟apply相同的是,第一个参数也是代表函数体内的this指向,从第二个参数往后,每个参数被依次传入函数。

javascript 类数组对象的定义?

一个拥有length 属性和若干索引属性的对象就可以被称为类数组对象,类数组和数组相似但不能调用数组的方法。 常见的类数组对象有arguments 和DOM方法的返回结果,还有一个函数也可以被看作是类数组对象,因为它含有length属性,代表可接受的参数个数。

常见的类数组转换为数组的方法有这样几种

  • 通过call调用数组的slice方式实现转换
Array.prototype.slice.call(arrayLike)
  • 通过call调用数组的splice方法来实现转换
Array.prototype.splice.call(arrayLike,0)
  • 通过apply调用数组的concat方法来实现转换
Array.prototype.concat.apply([],arrayLike)
  • 通过Array.form 方式来转换
Array.form(arrayLike)

js中的作用域与变量提升

变量提升的表现时,无论我们在函数中何处声明的变量,好像都被提升到了函数的首部,我们可以在变量声明前访问到而不会报错。

造成变量声明提升的本质原因是js引擎在代码执行前有一个解析的过程,创建了执行上下文,初始化了一些代码执行时需要用到的对象,当我们访问一个变量的时候,我们回到当前执行上下文的作用域链中去查找,而作用域链的首部指向的是当前执行上下文的变量对象,这个变量对象是当前执行上下文的一个属性,它包含了函数的形参,所有函数和变量声明。这个对象是在代码解析的过程中创建的,这就是会出现变量声明提升的根本原因。

如何编写高性能的js

1、使用位运算符代替一些简单的四则运算

2、避免使用过深的嵌套循环

3、不要使用未定义的变量

4、当需要多次访问数组长度时,可以使用变量保存起来,避免每次都会去进行属性查找。

如何封装一个js 的类型判断函数

const getType=(value)=>{
    if(value===null){
      return null + "";
    }
    if(typeof value==="object"){
      const valueClass = Object.prototype.toString.call(value);
      const type = valueClass.split(" ")[1].split("");
      type.pop();
      return type.join("").toLowerCase();
    }
  }

如何优化SPA 应用的首屏加载速度慢的问题?

  • 将公共的js库通过scrpit 标签外部引入,减少app.bundel的大小,让浏览器并行下载资源,提高下载速度。
  • 在配置路由时,页面和组件使用懒加载的方式引用,进一步缩小app.bundle的体积,在调用某个组件时在加载对应的js文件
  • root中插入loading或者骨架屏 prerender-spa-plugin,提升用户体验
  • 如果在webview 中的页面,可以进行页面预加载。
  • 独立打包异步组件公共的Bundle,以提高复用性&缓存命中率
  • 静态文件本地缓存,有两种方式分别为Http缓存,设置cache-Control,Last-Modified,Etag等响应头和Service Worker离线缓存。
  • 配合PWD
  • SSR
  • root插入loading,或者骨架屏
  • 使用Tree Shaking减少业务代码体积。