面试题汇总©


# 1.从输入一个URL地址到浏览器完成渲染的整个过程

  1. 浏览器地址栏输入URL并回车
  2. 浏览器查找当前URL是否存在缓存,并比较缓存是否过期
  3. DNS解析URL对应的IP
  4. 根据IP建立TCP连接(三次握手)
  5. 发送http请求
  6. 服务器处理请求,浏览器接受HTTP响应
  7. 浏览器解析并渲染页面
  8. 关闭TCP连接(四次握手)

详解:从输入一个URL地址到浏览器完成渲染的整个过程 (opens new window)

# 2.css优先级怎么计算的

  • 第一优先级:!important会覆盖页面内任何位置的元素样式
  • 1.内联样式,如style="color:green",权值为1000
  • 2.ID选择器,如#app,权值为0100
  • 3.类,伪类,属性选择器,如.foo,:first-child,div[class="foo"],权值为0010
  • 4.标签,伪元素选择器,如div::frist-line,权值为0001
  • 5.通配符,子类选择器,兄弟选择器,如*,>,+,权值为0000
  • 6.继承的样式没有权值

# 3.HTTP状态码

  • 200:请求被正常处理
  • 204:请求被受理但没有资源可以返回
  • 206:客户端只是请求资源中的一部分,服务器只对请求的部分资源执行GET方法,相应报文中通过Content-Range指定范围的资源。
  • 301:永久性重定向
  • 302:临时重定向
  • 303:与302状态码有相似功能,只是它希望客户端在请求一个URI的时候,能通过GET方法重定向到另一个URI上
  • 304:(未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
  • 307:临时重定向,与302类似,只是强制要求使用POST方法
  • 400:(错误请求) 服务器不理解请求的语法(一般为参数错误)。
  • 401:(未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
  • 403:(禁止) 服务器拒绝请求。(一般为客户端的用户权限不够)
  • 404:(未找到) 服务器找不到请求的网页。
  • 500:服务器内部错误
  • 503:服务器正忙

# 4.HTTP2.0 做了哪些改进,3.0呢?

HTTP2.0 特性如下

  • 二进制分帧传输
  • 多路复用
  • 头部压缩
  • 服务器推送
    Http3.0 相对于 Http2.0 是一种脱胎换骨的改变!
    http 协议是应用层协议,都是建立在传输层之上的。我们也都知道传输层上面不只有 TCP 协议,
    还有另外一个强大的协议 UDP 协议,2.0 和 1.0 都是基于 TCP 的,因此都会有 TCP 带来的硬伤以及局限性。
    而 Http3.0 则是建立在 UDP 的基础上。所以其与 Http2.0 之间有质的不同。

http3.0 特性如下

  • 连接迁移
  • 无队头阻塞
  • 自定义的拥塞控制
  • 前向安全和前向纠错

# 5.position有哪些值,作用分别是什么

  • static

static(没有定位)是position的默认值,元素处于正常的文档流中,会忽略left,top,right,bottom 和 z-index 属性。

  • relative

relative(相对定位)是指给元素设置相对于原本位置的定位,元素并不脱离文档流,因此元素原本的位置会被保留,其他的元素位置不会受到影响

一般用在子元素相对于父元素进行定位

  • absolute(绝对定位)是指给元素设置绝对的定位,相对定位的对象可以分为两种情况:
  1. 设置了absolute的元素如果存在有祖先元素设置了position为relative或者absolute,则这时元素的定位对象为此已设置position的祖先元素
  2. 如果并没有设置了position属性的祖先元素,则此时相对于body进行定位。

使用场景:跟随图标 图标使用不依赖定位父级的 absolute 和 margin 属性进行定位,这样,当文本的字符个数改变时,图标的位置可以自适应

  • fixed 可以简单的说说特殊版的 absolute,fixed元素总是相对于body定位。

使用场景:侧边栏或者广告图

  • inherit 继承父元素的position属性,但需要注意的是 IE8 以及以往的版本都不支持 inherit 属性。

  • 设置了 sticky 的元素,在屏幕范围(viewport)时该元素的位置并不受到定位影响(设置是 top、left 等属性无效),当该元素的位置将要移出偏移范围时,定位又会变成 fixed,根据设置的 left、top 等属性成固定位置的效果。

当元素在容器中被滚动超过指定的偏移值时,元素在容器内固定在指定位置。亦即如果你设置了 top: 50px,那么在 sticky 元素到达距离相对定位的元素顶部 50px 的位置时固定,不再向上移动(相当于此时 fixed 定位)。

# 6.new一个对象的过程中经历了什么「红宝书」

  1. 在内存中创建一个新对象。
  2. 这个新对象内部的[[Prototype]]特性被赋值为构造函数的prototype属性。
  3. 构造函数内部的this被赋值为这个新对象(即this指向新对象)。
  4. 执行构造函数内部的代码(给新对象添加属性)。
  5. 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
ar a = new Foo("zhang","jake");

new Foo{
    // 在内存中创造一个新对象
    var obj = {};
    // 给这个新对象内部的Prototype赋值为构造函数的prototype
    obj.__proto__ = Foo.prototype;
    // 构造函数内部的代码这样执行
    var result = Foo.call(obj,"zhang","jake");
    // 如果构造函数返回非空对象,则返回该对象;否则,返回刚刚创建的对象。
    return typeof result === 'obj'? result : obj;
}

谁被new了,this就指向谁。

# 7.输出了啥

function test(person) {
  person.age = 26
  person = {
    name: 'yyy',
    age: 30
  }
  return person
}
const p1 = {
  name: 'yck',
  age: 25
}
const p2 = test(p1)
console.log(p1) // -> {name:'yck',age:26};默认的,被person.age更改了
console.log(p2) // -> {name:'yyy',age:30};挂载到原型上了

# 8.typeof 是否能正确判断类型?instanceof能正确判断对象的原理是什么?

typeof对于原始类型来说,除了null都可以显示正确的类型 typeof对于对象来说,除了函数都会显示object,所以说typeof并不能准确判断变量到底是什么类型

instanceof的内部机制是通过原型链来实现的

  • 可以用来判断对象的正确类型
  • 对于原始类型来说,你想直接通过 instanceof 来判断类型是不行的,当然我们还是有办法让 instanceof 判断原始类型的
class PrimitiveString {
    static [Symbol.hasInstance](x){
        return typeof x === 'String'
    }
}
console.log('hello world' instanceof PrimitiveString) // true
  • 其实自己写一个typeOf做更加准确的判断也未尝不可
function typeOf(obj){
  return Object.prototype.toString.call(obj).slice(8,-1);
}

# 9.类型转化

在JS中类型转换只有三种情况:

  • 转换为布尔值
  • 转换为数字
  • 转换为字符串
  1. 转Boolean

在条件判断的时候,除了undefinednullfalseNaN、''、0、-0,其他所有值都转为true,包括所有对象。

  1. 对象转原始类型

对象在转换类型的时候,会调用内置的 [[ToPrimitive]] 函数,对于该函数来说,算法逻辑一般来说如下:

  • 如果已经是原始类型了,那就不需要转换了
  • 如果需要转字符串类型就调用x.toString(),转换为基础类型的话就返回转换的值。不是字符串类型的话就先调用valueOf,结果不是基础类型的话再调用toString.

valueOf解释

valueOf方法由Object后面的每个对象继承。 每个内置的核心对象都会覆盖此方法以返回适当的值。如果对象没有原始值,则valueOf将返回对象本身。

  • 调用x.valueOf(),如果转换为基础类型,就返回转换的值。
  • 如果都没有返回原始类型,就会报错。

当然你也可以重写Symbol.toPrimitive,该方法在转原始类型时调用优先级最高。

let a = {
    valueOf(){
        return 0
    },
    toString(){
        return '1'
    },
    [Symbol.toPrimitive](){
        return 2
    }
}
1 + a // => 3

四则运算符

加法运算符不同于其他几个运算符,它有以下几个特点:

  • 运算中其中一方为字符串,那么就会把另一方也转化为字符串
  • 如果一方不是字符串或者数字,那么会将它转化为数字或者字符串
1 + '1' // '2'
true + true // 2
4 + [1,2,3] // "41,2,3"

另外对于加法还要注意这个表达式 'a' + + 'b'

'a' + + 'b' // -> "aNaN"

那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转化为数字

4 * '3' // 12
4 * [] // 0
4 * [1,2] // NaN

比较运算符

  1. 如果是对象,就通过toPrimitive转换对象
  2. 如果是字符串,就通unicode字符串索引来比较
let a = {
    valueOf() {
        return 0
    },
    toString() {
        return '1'
    }
}
a > -1 // true
// a是对象,会通过valueOf转化为原始类型再比较值

# 10.keep-alive组件有什么用

如果你需要在组件切换的时候,保存一些组件的状态防止多次渲染,则可以使用keep-alive组件包裹需要保存的组件。

对于keep-alive组件来说,它拥有两个独有的生命周期钩子函数,分别为activateddeactivated。用keep-alive包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行deactivated勾子函数,命中缓存渲染后会执行actived勾子函数。

# 11.v-show 与 v-if 区别

v-show只是在display: nonedisplay: block之间切换。无论初始条件是什么都会被渲染出来,后面只需要切换 CSS,DOM 还是一直保留着的。所以总的来说v-show在初始渲染时有更高的开销,但是切换开销很小,更适合于频繁切换的场景。

v-if的话就得说到 Vue 底层的编译了。当属性初始为false时,组件就不会被渲染,直到条件为 true,并且切换条件时会触发销毁/挂载组件,所以总的来说在切换时开销更高,更适合不经常切换的场景。

# 12.组件中 data 什么时候可以使用对象

组件复用时所有组件实例都会共享data,如果data是对象的话,就会造成一个组件修改data以后会影响到其他所有组件,所以需要将data写成函数,每次用到就调用一次函数获得新的数据。

当我们使用new Vue()的方式的时候,无论我们将data设置为对象还是函数都是可以的,因为new Vue()的方式是生成一个根组件,该组件不会复用,也就不存在共享data的情况了。

# 13.闭包,这个函数有哪几种操作方法。

定义:函数A里面有函数B,函数B可以访问到函数A里面的变量,那么函数B就是闭包。

for (var i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}
// 输出5个6

way1:划分作用域「let」

for(let i = 1;i <= 5; i++){
    setTimeout(function timer() {
        console.log(i)
    },i * 1000)
}

way2:使用闭包方式

for(var i=1;i<=5;i++){
  (function (j) {
    setTimeout(function timer() {
      console.log(j)
    }, j*1000)
  })(i)
}

way3:使用setTimeout的第三个参数

for(var i=1;i<=5;i++){
  setTimeout(function timer(j) {
    console.log(j)
  }, i*1000,i)
}

# 14.['1','2','3'].map(parseInt)

  • 能够得到什么?

Ans :[1,NaN,NaN]

  • map函数的callback:
var new_array = arr.map(function callback(currentValue[,index[,array]])){
  // Return element for new_array
}[,thisArg]

这个callback一共可以接收三个参数,其中第一个参数代表当前被处理的元素,而第二个参数代表该元素的索引。

  • 而parseInt则是用来解析字符串的,使字符串成为指定基数的整数。parseInt(string, radix)接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。也就是说:“我们接受的处理的值是填入的字符串的,然后radix是索引”

  • 模拟

  1. parseInt('1',0)//radix为0时,且string参数不以0x0开头时,按照10为基数处理。这个时候返回1.
  2. parseInt('2',1)//基数为1(1进制)表示的数中,最大值小于2,所以无法解析,返回NaN
  3. parseInt('3',2)//基数为2(2进制)表示的数中,最大值小于3,所以无法解析,返回NaN