《面试整理1》

250 阅读8分钟

1. 如何获取url里的?后边的部分

涉及到哪些API?这里参考这篇文章的讲解

示例 url = http://www.jianshu.com/search?q=js&page=1&type=note

  1. window.location.href(设置或获取整个 URL 为字符串)

    console.log(window.location.href)

打印结果:http://www.jianshu.com/search?q=123&page=1&type=note

  1. window.location.protocol(设置或获取 URL 的协议部分)

console.log(window.location.protocol)

打印结果:http:

  1. window.location.host(设置或获取 URL 的主机部分)

console.log(window.location.host)

打印结果:www.jianshu.com

  1. window.location.port(设置或获取与 URL 关联的端口号码)

console.log(window.location.port)

打印结果:空字符(如果采用默认的80端口(update:即使添加了:80),那么返回值并不是默认的80而是空字符)

  1. window.location.pathname(设置或获取与 URL 的路径部分(就是文件地址))

console.log(window.location.pathname)

打印结果:/search

  1. window.location.search(设置或获取 href 属性中跟在问号后面的部分)

console.log(window.location.search)

打印结果:?q=123&page=1&type=note

  1. window.location.hash(设置或获取 href 属性中在井号“#”后面的分段)

console.log(window.location.hash)

打印结果:空字符(因为url中没有)

用window.location.search获取到?q=123&page=1&type=note 以后,可以用str.substring(1) 拿到?后边的内容,然后用str.split('&') 拿到['q=123','page=1','type=note'] 要想拿到具体某个查询参数的值,可以遍历这个数组,对每一项用= split。

2. 用setTimeout模拟setInterval,而且能随时暂停

前置知识:setTimeout的返回值是一个整数,它的id,而且如果连续调用,每次得到的返回值都是不同的,连续的整数,我们调用clearTimeout(id)就是根据id,找到对应的计时器,清除。

var timeMap={}
function mySetInterval(fn,delay){
  var key=Symbol()
  function exec(){
    timeMap[key]=setTimeout(()=>{
      fn()
      exec()
    },delay)
  }
  exec()
  return key
}
function myClearInterval(key){
  if(key in timeMap){
    clearTimeout(timeMap[key])
    delete timeMap[key]
  }
}

定义两个函数,用mySetInterval启动定时器,用myClearInterval清除定时器。

首先,用timeout模拟interval,就是要不停的循环调用timeout,就是递归。所以最先想到的是,在mySetInterval里边,定义一个执行函数exec。exec做什么事呢?就是调用setTimeout,固定时间间隔后,执行函数fn,然后递归调用自己exec。这样就能做到最简单的模拟interval。

但是怎么停掉这个定时器呢?可以利用setTimeout的返回值,我们知道,每次setTimeout都会返回一个不同的id,我们要把这个id return 出去,才能让外边清除。但是每次的id都在变化,如果在函数的最后return,得到的肯定不是最新的那个id。

我们可以换个思路,不return id,而是用一个对象的键,保存每次不同的id。这个键key,是一直不变的,变的只是它对应的值,我们把每次setTimeout的返回值也就是id保存在key里,所以id在变,但是key没变。我们可以把这个key return 出去,外边就可以通过它拿到最新的id。

然后要想停掉,就调用myClearInterval,接收mySetInterval的返回值。

let key=mySetInterval(()=>{console.log(1)},1000)
setTimeout(()=>{
	myClearInterval(key)
},5000)

3. setTimeout的第三个参数

var timeoutID = setTimeout(function[, delay, arg1, arg2, ...]);

我们比较常用的就是前两个参数,但是其实也可以传第三个甚至更多参数,它们会被当作参数传给前边的回调函数。

delay也是可选的,如果没传,默认是0

4. MVC与MVVM

1. MVC

三个对象:M:Model 数据层,负责管理所有数据;V:View 视图层,负责渲染UI;C:Controller 控制器,负责其他控制逻辑

C就是M和V的桥梁,实际界面层的各种变化都要经过它控制,像用户从界面上提交数据,也是由Controller负责控制,然后把变化应用到数据层。

2. MVVM

MVVM是作为MVC模式的一种补充出现的。不管是什么模式,最终目的都是将Model里的数据展示在View视图上。MVVM相比于MVC来说,将前端开发者所要控制的逻辑越来越轻量。

MVVM分为Model、View、ViewModel三者。

Model 代表数据模型,数据和业务逻辑都在Model层中定义;

View 代表UI视图,负责数据的展示;

ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作;

Model 和 View 并无直接关联,而是通过 ViewModel 来进行联系的,Model 和 ViewModel 之间有着双向数据绑定的联系。因此当 Model 中的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改变的数据也会在 Model 中同步。

这种模式实现了 Model 和 View 的数据自动同步,因此开发者只需要专注对数据的维护操作即可,而不需要自己操作 dom。

5. Vue.nextTick

首先,它的用法:在下次DOM更新循环结束之后,执行一个延迟回调。在修改数据之后立即使用这个方法,可以获取更新以后的DOM。

Vue在实现数据响应式的时候,并不是我改变数据后,DOM就立即变化。Vue在更新DOM的时候是异步执行的。如果监听到数据变化了,Vue会开启一个任务队列,然后在下一次的事件循环中再执行,也就是再更新DOM。Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

<div id="example">{{message}}</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
  vm.$el.textContent === 'new message' // true
})

在修改数据后,马上读取,读到的还是旧值。在Vue.nextTick里读,就能读到新值了。因为它里边的回调会在DOM更新后被执行。

也可以使用this.$nextTick() 用法相同

6. v-ifv-show 的区别

相同点:它们都是控制DOM元素的显示和隐藏

不同点:

  1. 实现方式不同:v-if是通过判断后边数据的真假值,如果为真,就显示,在DOM树上添加一个节点,如果是假,就隐藏,把这个节点从DOM上移除。所以v-if是不断把节点从DOM树上添加/移除来控制显示和隐藏的。在第一次渲染时,如果后边的值是假,就什么都不做。只有在条件第一次变为真的时候,才开始局部编译。 而v-show在第一次渲染,不管后边条件是否为真,都会被编译,存在于DOM树中。他是通过切换CSS,来控制元素的显示和隐藏。这个元素始终存在于DOM树上,占据一个位置。如果后边条件为假,就隐藏,通过修改display为none,来隐藏。所以它只是在修改元素的CSS样式。

  2. 性能消耗:正因为v-if要进行DOM的添加和删除,所以切换消耗更高,不适合需要频繁切换的场景。v-show更适合频繁切换,就是初始渲染消耗高。

7. HTTP请求方法

  1. GET 用于获取资源,请求指定页面的信息,并且会返回响应主体。

  2. HEAD 类似于GET,也是请求资源,但是返回的响应中没有具体内容,用于获取报头,获取服务器的响应头信息

  3. POST 用于提交数据,比如表单提交,要提交的数据放在请求体中。不幂等,因为POST请求可能会创建新的资源或修改已有资源。

  4. DELETE 请求服务器删除指定页面

  5. PUT 向指定资源的位置上上传最新内容。通过该方法客户端可以将指定资源的最新数据传送给服务器取代指定的资源的内容。

  6. OPTIONS 允许客户端查看服务器的性能。通常会请求服务器返回支持的所有HTTP方法。

  7. TRACE 请求服务器回显其收到的请求信息,该方法主要用于HTTP请求的测试或诊断。

幂等:对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。

8. null undefined区别

typeof null === 'object' 可以把null理解成‘没有对象’,一个空对象,什么都没有,这个地方不应该有值。用法:作为函数的参数,表示该函数的参数不是对象;作为对象原型链的终点。Number(null) 转为数值是0

typeof undefined === 'undefined' 表示缺少值,可以理解成,这个地方本来应该有值,但是现在没有值。 用法:比如声明了一个变量,但是没有赋值,就是undefined;调用了一个函数,应该提供的参数没有提供,也是undefined;对象有个属性没赋值,也是undefined;如果一个函数没有返回值,默认返回undefined。Number(undefined) 转为数值是NaN

9. require和import的区别

参考文章

require属于CommonJS模块规范,而import属于ES6模块规范。

  1. CommonJS模块:

    它规定,每个模块内部,module变量表示当前模块,module.exports表示当前模块的对外接口,它是一个对象。在其他地方用require加载某个模块,得到的是module.exports属性。

区别主要有两点:

  1. require加载,只能在运行时加载,运行时在能确定模块的依赖关系,输入输出等。因为它加载的是一个对象,这个对象只有在脚本执行时才能得到。 而import可以在编译时就完成加载,因为它是静态导入,不是对象

  2. require加载得到的值是只是这个值的拷贝。加载完以后,原来模块里这个值的变化,反应不到外边。就算原来模块里的值变了,加载得到的也还是一开始的那个值。 而import导入的值是引用。导入的这个变量,会随着原来模块里的变量的变化一起变化。如果原来模块里的变了,那在外边读到的值也是新的值。