编写一个方法,该方法接收两个参数,分别为 k 和 一个无序的纯数字数组。该方法在执行后,会返回数组中第 k 大的数字。特别注意,如果数组中,有两位数值一样的数字,同数值数字排名并列。如 [3,1,3,2,5,4,5] 中,第 1 大的数字为 5,第2大的数字为 4,第5大的数字为 1?

  • 思路就是先去重,再排序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function fn(k,arr){
    //先去重
    let newArr = [...new Set(arr)]
    //参数验证
    if(k<1||k>newArr.length) return false
    //排序
    newArr.sort((a,b)=>b-a)
    //返回结果
    return newArr[k-1]
    }
    console.log(fn(1,[3,1,3,2,5,4,5]))

proto 和 prototype 之前有什么关系?

首先,所有对象都有__proto__属性,函数这个特殊对象除了有__proto__属性,还有特有的原型属性prototype。
然后,prototype对象默认有两个属性,constructor属性和__proto__属性,prototype属性可以给函数和对象添加可共享(继承)的方法、属性;而__proto__是查找某函数或对象的原型链方式。
constructor这个属性包含了一个指针指回原构造函数。

call(), apply(),bind() 的区别和作用?bind 方法如何来实现?

  • call,apply,bind都是函数原型上的方法
  • call是执行函数,并且函数第一个参数为函数的this指向
  • apply是执行函数,函数第一个参数为函数的this指向,第二个参数是剩余形参的一个数组
  • bind第一个参数为函数的this指向,并且返回一个函数

bind手写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Function.prototype.bind = Function.prototype.bind || function (context) {
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var me = this;
var args = Array.prototype.slice.call(arguments, 1);
var F = function () {};
F.prototype = this.prototype;
var bound = function () {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return me.apply(this instanceof F ? this : context || this, finalArgs);
}
bound.prototype = new F();
return bound;
}

js中基础数据类型有哪几种?了解包装对象么?

  • 5种基本类型,Number,String,Boolean,undefined,null
  • 对象包装:new Number(),new String(),new Boolean(),返回的都是object类型
    Number(),String(),Boolean(),返回的是对应的基础类型

如何判断this?箭头函数的this是什么?

  1. 在函数体中,简单调用该函数时(非显式/隐式绑定下),严格模式下 this 绑定到 undefined,否则绑定到全局对象 window/global;
  2. 一般构造函数 new 调用,绑定到新创建的对象上;
  3. 一般由 call/apply/bind 方法显式调用,绑定到指定参数的对象上;
  4. 一般由上下文对象调用,绑定在该对象上;
  5. 箭头函数中,根据外层上下文绑定的 this 决定 this 指向。
  6. 如果构造函数中显式返回一个值,且返回的是一个对象,那么 this 就指向这个返回的对象;如果返回的不是一个对象,那么 this 仍然指向实例。

什么是同步?什么是异步?

同步和异步是一种消息通知机制

  • 同步阻塞: A调用B,B处理获得结果,才返回给A。A在这个过程中,一直等待B的处理结果,没有拿到结果之前,需要A(调用者)一直等待和确认调用结果是否返回,拿到结果,然后继续往下执行。   做一件事,没有拿到结果之前,就一直在这等着,一直等到有结果了,再去做下边的事
  • 异步非阻塞: A调用B,无需等待B的结果,B通过状态,通知等来通知A或回调函数来处理。

什么是宏任务?什么是微任务?

  • 微任务:一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前。  
  • 宏任务:宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合。  
  • 常见微任务:1. Promise.thenMutaionObserverObject.observe(已废弃;Proxy 对象替代)process.nextTick(Node.js)
  • 常见宏任务:1. script (可以理解为外层同步代码)  2. setTimeout/setInterval  3. UI rendering/UI事件  4.postMessage,MessageChannel  5. setImmediate,I/O(Node.js)

什么是回调?回调使用中存在什么问题?

回调即是函数指针的调用,即是一个通过函数指针调用的函数。使用回调函数有一个很大缺点 就是造成回调地狱,回到地狱是 为了实现某些逻辑出现函数的层层嵌套。回调地狱会造成可读性及可维护性变差。同样每个嵌套函数耦合性强,一层变动会引起其他的结果变动。同样回调地狱如果出现错误不好处理错误。解决回调地狱问题可以通过观察者模式、promise、async /await来处理。

Promise.allSettled 了解吗?动手实现一下 Promise.allSettled?

Promise.allSettled是ES2020新特性,可以执行多个promise对象,获取多个promise对象状态,无论成功或者失败的状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 function MyallSettled(list){
        let resArr = new Array(list.length);
let num = 0
return new Promise(resolve=>{
   list.forEach((item,key)=>{
let obj = {};
obj['status'] = "fulfilled";
obj.value = res;
resArr[key] = obj;
num++
if(num===list.length){
resolve(resArr); 
}
 },err=>{
obj['status'] = "rejected";
obj.reson = err;
resArr[key] = obj;
num++
if(num===list.length){
resolve(resArr); 
}
})
});
}

vue 中组件间有哪些通信方式?

  1. props / $emit适用于父子组件通信这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。
  2. ref 与 $parent / $children适用于父子组件通信ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例$parent / $children:访问父 / 子实例
  3. EventBus ($emit / $on)适用于父子、隔代、兄弟组件通信这种方法通过一个空的 Vue 实例/作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。
  4. $attrs/$listeners适用于隔代组件通信$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind=“$attrs” 传入内部组件。通常配合 inheritAttrs 选项一起使用。$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=“$listeners” 传入内部组件
  5. provide / inject适用于隔代组件通信祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系
  6. Vuex适用于父子、隔代、兄弟组件通信Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

vue 中 v-show 和 v-if 的区别是什么?

  • v-show 只是在 display: none 和 display: block 之间切换。无论初始条件是什么都会被渲染出来,后面只需要切换 CSS,DOM 还是一直保留着的。
  • v-if 的话就得说到 Vue 底层的编译了。当属性初始为 false 时,组件就不会被渲染,直到条件为 true,并且切换条件时会触发销毁/挂载组件,并且基于 v-if 的这种惰性渲染机制,可以在必要的时候才去渲染组件,减少整个页面的初始渲染开销。

keep-alive 组件有什么作用?

  •  如果你需要在组件切换的时候,保存一些组件的状态防止多次渲染,就可以使用 keep-alive 组件包裹需要保存的组件。  
  • 对于 keep-alive 组件来说,它拥有两个独有的生命周期钩子函数,分别为 activated 和 deactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 actived 钩子函数。

说下vue生命周期钩子函数?

  • beforeCreate :这个时期,this变量还不能使用,在data下的数据,和methods下的方法,watcher中的事件都不能获得到。
  • created这个时候可以操作vue实例中的数据和各种方法,但是还不能对”dom”节点进行操作。
  • beforeMounte:在挂载开始之前被调用:相关的 render 函数首次被调用
  • mounted:挂载完毕,这时dom节点被渲染到文档内,一些需要dom的操作在此时才能正常进行
  • beforeUpdate:data中数据已经更新完毕,页面视图还未响应更改
  • updated:数据和视图都更新完毕
  • beforeDestroy:销毁之前,实例上事件、指令等都可以使用,这里组件没有真正的销毁。
  • destroyed:数据、指令、等完全销毁

Vue中computed和watch区别?

  • computed 是计算属性,依赖其他属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容。
  • watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作。

React 的组件间通信都有哪些形式?

  1. 父传子:在 React 中,父组件调用子组件时可以将要传递给子组件的数据添加在子组件的属性中,在子组件中通过 props 属性进行接收。这个就是父组件向子组件通信。
  2. 子传父:React 是单向数据流,数据永远只能自上向下进行传递。当子组件中有些数据需要向父级进行通信时,需要在父级中定义好回调,将回调传递给子组件,子组件调用父级传递过来的回调方法进行通信。
  3. 跨组件通信 - context。使用 context API,可以在组件中向其子孙级组件进行信息传递。

React中如何实现路由懒加载?

在 React 16 中,新增了 lazy 方法,通过 lazy 方法可以轻松实现组件懒加载,当然要实现路由懒加载的话,其实也只需要把路由组件结合 lazy 使用即可。  参考代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import {Route} from "react-router-dom";
        import React,{Suspense} from 'react';
        const HomeView = React.lazy(()=>import("./home"));
        function App(){
           return <div>
              <h1>路由懒加载</h1>
              <Route path="/" exact render={()=>{
                    return <Suspense fallback={<div>组件Loading进来之前的占位内容</div>}>                    
<HomeView />              
</Suspense>              
}} />           
</div>        
}        
export default App;

在上述代码中,我们使用 lazy 引入了一个动态组件,然后将该组件放入了根路由中。这样的话只有用户访问网站首页时,才会动态加载这个组件。  这里要注意,在 React 规范中,lazy 和 Suspense 必须配合使用,lazy 引入的动态组件必须要放入 Suspense 中,Suspense 的 fallback 属性是 lazy 的组件没有加载进来之前的占位内容。

React 的生命周期函数都有哪些,分别有什么作用?

  • 挂载阶段:
  1. constructor: 初始化组件,初始化组件的 state 等。
  2. static getDerivedStateFromProps():该函数用于将 props 中的信息映射到 state 中。
  3. render: 生成虚拟DOM
  4. componentDidMount:组件挂载完成,通过在该函数中去处理副作用
  • 更新阶段:
  1. static getDerivedStateFromProps()
  2. shouldComponentUpdate():该生命周期函数用于判断是否要进行组件更新。
  3. render():生成虚拟DOM
  4. getSnapshotBeforeUpdate():组件已经完成 diff,即将更新真实 DOM,用户获取上一次的DOM快照。该函数必须搭配componentDidUpdate 一块使用,返回值会变成 componentDidUpdate 第三个参数。
  5. componentDidUpdate(): 组件更新完成,通常在该函数中进行副作用处理。 即将卸载: componentWillUnmount:组件即将卸载,用于删除组件添加到全局的数据或事件。

请描述一下 cookies sessionStorage和localstorage区别?

  1. cookie在浏览器与服务器之间来回传递;sessionStorage和localStorage不会把数据发给服务器,仅在本地保存
  2. 数据有效期不同,cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭;sessionStorage:仅在当前浏览器窗口关闭前有效;localStorage 始终有效,长期保存
  3. 作用域不用,sessionStorage不在不同的浏览器窗口中共享;sessionStorage不在不同的浏览器窗口中共享;cookie也是在所有同源窗口中都是共享的
  4. 存储大小不同,cookie数据不能超过4k,sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大

浏览器为什么要阻止跨域请求? 如何解决跨域?每次跨域请求都需要 到达服务端吗?(快手)

因为会有XSS攻击,所以浏览器要阻止跨域。JSONP,CORS资源共享,nginx代理,nodejs中间件代理,WebSocket协议跨域。跨域并非浏览器限制了发起跨站请求,而是跨站请求可以正常发起,但是返回结果被浏览器拦截了。
每次需求都会发出,服务器端也会做出响应,只是浏览器端在接受响应的时候会基于同源策略进行拦截。有些浏览器不允许从 HTTPS 的跨域访问 HTTP,比如 Chrome 和 Firefox,这些浏览器在请求还未发出的时候就会拦截请求,这是一个特例。

一般放在localStorage中,虽然容易容易受到XSS攻击。放在cookie中则容易受到CSRF攻击,且不符合Restful 最佳实践。

WebSocket 是怎么实现点对点通信和广播通信的?

广播式即服务端有消息时,会将所有消息发送给连接了当前endpoint的浏览器。