最全前端面试总结面试了 N家公司总结的高频面试题。 经典!必考必会!

目录

1.数组处理reduce()方法

(1)求数组所有值的和

(2)将二维数组转为一维数组

(3)计算数字中每个元素出现次数。

 (4) 数组去重

2.ES6  map()方法

(1)使用map计算任意复杂的函数

(2)使用Map格式化数组对象

 3.Es6语法对数组去重(2种方式)

(1)用ES6进行最简普通数组去重[...new Set(arr)]

(2)用reduce()方法去重。(例子见上)

4.computed计算和watch区别,用在什么场景?

(1)2者区别

(2)使用场景

5. Vue中created和mounted的区别?

6. DOM事件模型

(1)事件冒泡

(2)事件捕获

(3)Dom事件流

(1)html事件处理程序

(2)DOM0级事件处理程序

(3)DOM2 级事件处理程序

7.插槽,具名插槽

8.vue 父子,兄弟,祖孙如何通信(重要)

1 父子组件通信

(1)父组件向子组件传值

(2)子组件修改父组件属性,调用父组件方法

(3)父组件调用子组件方法

2 .兄弟组件通信

3.祖孙组件通信

9.vue中data为什么是函数?

10.如何处理路由跳转到404

11. slice() 和splice()的区别

12.判断是否是数组的方法(4种方法)

13.for in 和for of的区别

14.vue 中$router 和$route区别

15.this.$route.params和this.$route.query的区别

16.map方法和forEach的区别

forEach

17.原型和原型链

原型链

18 @import和link引入样式的区别

1.从属关系区别

2.加载顺序区别

3.兼容性区别


1.数组处理reduce()方法

reduce() 方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。

reducer 逐个遍历数组元素,每一步都将当前元素的值与上一步的计算结果相加(上一步的计算结果是当前元素之前所有元素的总和)——直到没有更多的元素被相加

(1)求数组所有值的和

let sum = [0, 1, 2, 3].reduce(function (a, b) {
  return a + b
}, 0)

或者用箭头函数
let sum=[1,2,3,4].reduce((a,b)=>a+b,0)

设置了初始值,返回结果20

[1, 2, 3, 4].reduce( 
(previousValue, currentValue, currentIndex, array) 
=> previousValue + currentValue,10 )

(2)将二维数组转为一维数组

let arr = [[0, 1], [2, 3], [4, 5]].reduce(
  function(a, b) {
    return a.concat(b)
  },
  []
)

(3)计算数字中每个元素出现次数。

let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice']

let newName = names.reduce(function (a, b) {
  if (b in a) {
    a[b]++
  }
  else {
    a[b] = 1
  }
  return a
}, {})
// newName is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

 (4) 数组去重

let arr = ['a', 'b', 'a', 'b', 'c']
let newArr = arr.reduce(function (a, b) {
  if (a.indexOf(b) === -1) {
    a.push(b)
  }
  return a
}, [])

console.log(newArr)


2.ES6  map()方法

map() 方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成. 

// 箭头函数
map((element) => { /* … */ })
map((element, index) => { /* … */ })
map((element, index, array) => { /* … */ })

// 回调函数
map(callbackFn)
map(callbackFn, thisArg)

// 内联回调函数
map(function(element) { /* … */ })
map(function(element, index) { /* … */ })
map(function(element, index, array){ /* … */ })
map(function(element, index, array) { /* … */ }, thisArg)

会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组

(1)使用map计算任意复杂的函数

const array1 = [1, 4, 9, 16];

// pass a function to map
const map1 = array1.map(x => x * 2);

console.log(map1);
// expected output: Array [2, 8, 18, 32]

(2)使用Map格式化数组对象

var kvArray = [{key: 1, value: 10},
               {key: 2, value: 20},
               {key: 3, value: 30}];

var reformattedArray = kvArray.map(function(obj) {
   console.log(obj);
   var rObj = {};
   rObj[obj.key] = obj.value;
   return rObj;
});
console.log(reformattedArray);


 3.Es6语法对数组去重(2种方式)

(1)用ES6进行最简普通数组去重[...new Set(arr)]

// 原始数组
var arr = [1,2,3,2,2,4,5,6];
// 使用解构方式 去重的核心方法
var newArr = [...new Set(arr)];
// 去重后数据
console.log(newArr);

(2)用reduce()方法去重。(例子见上)

indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1


4.computed计算和watch区别,用在什么场景?

(1)2者区别

computed 计算属性,通过属性计算得来的

属性的结果会被缓存,除非依赖的响应式属性发生变化才重新计算。当依赖的响应式属性没有变化的时候,调用当前函数的结果会从缓存中读取。必须用return来返回结果。

watch 属性监听 ,监听属性的变化。主要监听数据的变化,然后执行相关业务逻辑。

(2)使用场景

computed   当一个属性受多个属性影响的时候。

                   场景:购物车商品结算

watch          当一个数据影响多条数据的时候

                    场景:搜索数据


5. Vue中created和mounted的区别?

created:模板渲染成html前调用
mounted:模板渲染成html后调用,页面初始化后,对dom节点进行操作

6. DOM事件模型

事件是用户或者浏览器执行的动作,比如点击事件等。DOM支持大量事件,JS和html之间的交互事通过事件实现的。

事件流:描述的事件的触发顺序

(1)事件冒泡

事件冒泡是IE的事件流。最具体的元素接受事件后逐级向上传播,直到传到Document对象上。谷歌浏览器会传到window对象上。

阻止事件冒泡
e.stopPropagation()可以中断冒泡。 捕获无法取消。

(2)事件捕获

从不具体的节点到具体的节点,一般从document对象传播,与事件冒泡相反。很少使用。

(3)Dom事件流

有3个阶段:事件捕获阶段,目标阶段,事件冒泡阶段。

事件处理程序:响应事件的函数。

(1)html事件处理程序

HTML 的on属性。用这个方法指定的监听代码,只会在冒泡阶段触发。

<button onclick="doSomething()">点击</button>

(2)DOM0级事件处理程序

其实就是,元素节点的事件属性。

var btn = document.getElementById('btn');
btn.onclick = function () {
  console.log('被点击了!');
};

获取按钮的引用,并给它指定事件onclick,点击后在控制台输出'被点击了!'。
如果想删除事件处理程序,只需要这样 btn.onclick = null; // 删除事件处理程序

(3)DOM2 级事件处理程序

addEventListener()removeEventListener(),用于添加和删除事件处理程序。

target.addEventListener(eventType, listener[, useCapture]);

eventType —事件名称,大小写敏感
listener — 监听函数
useCapture — 可选参数,默认false,表示监听函数只在冒泡阶段被触发。true按捕获方向执行函数

function print() {
  console.log('Print Hello world');
}

var btn = document.getElementById('btn');
btn.addEventListener('click', print, false);

上面代码的意思就是,节点id为btn的元素,使用addEventListener方法绑定click事件,点击的时候会监听函数print会发生。另外,因为useCapture设置为false,所以该函数只在冒泡阶段触发。
 

7.插槽,具名插槽

插槽Slot  通俗的理解就是“占坑”,在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑(替换组件模板中slot位置)

有时一个组件我们需要多个插槽,这个时候可以用具名插槽。

如对于一个带有如下模板的 <base-layout> 组件:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

一个不带 name 的 <slot> 出口会带有隐含的名字“default”。

在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:

<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

任何一种写法都会渲染出:注意 v-slot 只能添加在 <template> 上)

<div class="container">
  <header>
    <h1>Here might be a page title</h1>
  </header>
  <main>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </main>
  <footer>
    <p>Here's some contact info</p>
  </footer>
</div>

8.vue 父子,兄弟,祖孙如何通信(重要)

1 父子组件通信

(1)父组件向子组件传值

        父组件通过使用子组件标签,在内部配置v:bind指令传属性,也就是:title=“title”         :obj="obj"等等,而子组件通过配置props:{}来接收父组件传递过来的属性

(2)子组件修改父组件属性,调用父组件方法

       子组件配置 $emit传值, 父组件配置 v-on 接收

(3)父组件调用子组件方法

       给子组件绑定ref 通过this.$ref调用子组件的方法

2 .兄弟组件通信

  1. 两个子组件通过父组件通信
  2. vuex
  3. 全局事件总线$eventBus

3.祖孙组件通信

  1. 全局事件总线$eventBus
  2. vuex
  3. 使用attr /attr/listener

9.vue中data为什么是函数?

1. 防止data复用;

2. 保证数据独立性;vue组件中的data数据都应该是相互隔离,互不影响的

3. 作用域;每复用一次组件,返回一份新的data,拥有自己的作用域,让各个组件实例维护各自的数据。

总结来说,如果data是一个函数的话,这样每复用一次组件,就会返回一份新的data。

如果使用对象的话,每个实例(组件)上使用的data数据是相互影响的

10.如何处理路由跳转到404

vue路由未匹配到路由跳转到指定404页面的两种常见方法

(1)通过vue-router重定向实现

当我们输入错误URL地址等各种原因导致我们不能继续访问,那么我们可以使用重定向的方法,只要在路由中添加该代码就可以了。

let router = new VueRouter({
    routes:[
       {path:'/',redirect:{name:"home"}},  // 重定向到主页
       {name:'home',path:'/home',component:Home},
       {path:'/music',component:Music},
       {path:'/movie',component:Movie},
       {name:'img',path:'/picture22',component:Picture},
       {name:'musicDetail',path:'/musicDetail/:id/:name',component:MusicDetail},
       {path:'*',component:NotFound},//全不匹配的情况下,返回404,路由按顺序从上到下,依次匹配。最后一个*能匹配全部,
    ]
});

(2)路由全局守卫

router.beforeEach((to, from, next) => {
  if (to.matched.length ===0) {  //如果未匹配到路由
    from.name ? next({ name:from.name }) : next('/');   //如果上级也未匹配到路由则跳转登录页面,如果上级能匹配到则转上级路由
  } else {
    next();    //如果匹配到正确跳转
  }
});

11. slice() 和splice()的区别

slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变

slice()                    // 不截取
slice(start)           //start为正从start到末尾。 start为负,从倒数第几个开始
slice(start, end)     //包含start ,不包含end。

实例

1.  slice(2).                //返回数组从索引为2到结束

2.  slice(2,4)              //返回从索引为2到索引为3的数组

3.  slice(-2).              //返回倒数后2个

4.  slice(2,-1).          //从索引为2开始 到 倒数第二个值。

const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];

console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]

console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]


console.log(animals.slice(-2));
// expected output: Array ["duck", "elephant"]

console.log(animals.slice(2, -1));
// expected output: Array ["camel", "duck"]

console.log(animals.slice());
// expected output: Array ["ant", "bison", "camel", "duck", "elephant"]

splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

splice(start)                                            //从索引为start开始删除所有元素
splice(start, deleteCount)                 //start从索引开始的位置,deleteCount表示要移除的数组元素的个数
splice(start, deleteCount, item1)         //item1,item2要添加进数组的元素
splice(start, deleteCount, item1, item2, itemN)

实例:

1. splice(2, 0, "drum");           //从索引2的位置删除0个元素,插入drum. 

2.splice(3, 1)                         //从索引 3 的位置开始删除 1 个元素

3.splice(-2, 1)                        //从索引 -2 的位置开始删除 1 个元素

4.splice(2);                            //从索引 2 的位置开始删除所有元素

const months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
// inserts at index 1
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "June"]

months.splice(4, 1, 'May');
// replaces 1 element at index 4
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "May"]

12.判断是否是数组的方法(4种方法)

(1)instanceof 判断。

const a = [];
const b = {};
console.log(a instanceof Array);//true
console.log(a instanceof Object);//true,在数组的原型链上也能找到Object构造函数

就是要判断一个Object是不是数组(在JavaScript当中,数组实际上也是一种对象),

如果这个Object的原型链上能够找到Array构造函数的话,那么这个Object应该及就是一个数组,如果这个Object的原型链上只能找到Object构造函数的话,那么它就不是一个数组

(2)用Array对象的isArray方法判断

const a = [];
const b = {};
Array.isArray(a);//true
Array.isArray(b);//false

(3)用构造函数constructor判断

const a = [];
console.log(a.constructor == Array);//true

constructor属性是可以改写的,constructor属性被修改之后,就无法用这个方法判断数组是数组了

(4)用Object的toString方法判断

Object.prototype.toString.call(['Hello','Howard'])    //"[object Array]"

可以用写一个方法来判断数组是否为数组:

const isArray = (something)=>{
    return Object.prototype.toString.call(something) === '[object Array]';
}

cosnt a = [];
const b = {};
isArray(a);//true
isArray(b);//false

注意⚠️:typeof无法判断是否是数组。typeof会将null,对象和数组判断为对象。

const a = null;
const b = {};
const c= [];
console.log(typeof(a)); //Object
console.log(typeof(b)); //Object
console.log(typeof(c)); //Object

13.for in 和for of的区别

for of 和 for in都是用来遍历的属性

区别

  1. for in  用于遍历数组或 对象的属性
  2. for of 只能用于数组,不能用于对象,
  3. for in 得到对象的key或数组的索引
  4. for offorEach一样, 是直接得到值
   const arr = ['a', 'b', 'c']
   // for in 循环
   for (let i in arr) {
       console.log(i)         //输出  0  1  2
   }
   
   // for of
   for (let i of arr) {
       console.log(i)         //输出  a   b   c
   }

   const obj = {
        a: 1,
        b: 2,
        c: 3
    }
    for (let i in obj) {
        console.log(i)    //输出 : a   b  c
    }
    for (let i of obj) {
        console.log(i)    //输出: Uncaught TypeError: obj is not iterable 报错了
    }

 

forEach专门用来循环数组,可以直接取到元素,同时也可以取到index值


let arr = ['a', 'b', 'c', 'd']
    arr.forEach( (val, index)=> {
    console.log('index:'+index+','+'val:'+val) // val是当前元素,index当前元素索引,arr数组
})

14.vue 中$router 和$route区别

(1)通过 this.$router 访问路由器,相当于获取了整个路由文件。全局的 router 实例,里面包含很多方法和实例。this.$router.push,和router-link跳转一样

(2)通过 this.$route 访问的是当前路由,获取和当前路由有关的信息

15.this.$route.params和this.$route.query的区别

1.相同点 都可以用来传参数,获取参数

eg:传参
    this.$router.push({
        path: '/monitor',
        query: {
            id: id,
         }
    }
    this.$router.push({
        path: '/monitor',
        params: {
            id: id,
         }
    }

取参
    this.$route.query.id
    this.$route.params.id

2.不同点

使用query传参url中显示参数刷新路由跳转页面参数不消失
使用params传参url中不显示参数,刷新路由跳转页面参数消失

eg:
  query:  http://172.19.186.224:8080/#/monitor?id=1
  params: http://172.19.186.224:8080/#/monitor

16.map方法和forEach的区别

相同点:map & forEach 都是用来更方便地遍历数组的。

区别map不会改变原始数组 会返回一个新数组。

forEach 会改变原始数组,没有返回值,即它返回的是 undefined。

forEach

// 箭头函数
forEach((element) => { /* … */ })
forEach((element, index) => { /* … */ })
forEach((element, index, array) => { /* … */ })

// 回调函数
forEach(callbackFn)
forEach(callbackFn, thisArg)



callbackFn.     // 为数组中每个元素执行的函数。

函数调用时带有以下参数:

element.        // 数组中正在处理的当前元素。

index.          //数组中正在处理的当前元素的索引。

array.          //forEach() 方法正在操作的数组。

thisArg 可选.    //可选参数。当执行回调函数 callbackFn 时,用作 this 的值。

forEach方法用于调用数组的每个元素,并将元素传递给回调函数。

const items = ['item1', 'item2', 'item3'];
const copyItems = [];

items.forEach((item) => {
  copyItems.push(item);
});

17.原型和原型链

5条原型规则是学习原型链的基础   

(1)  所有的引用类型(数组,对象,函数)都具有对象特性,即可以自由扩展属性(除了null以外)

(2)  所有的引用类型(数组,对象,函数)都有一个_proto_属性(隐式原型),属性值是一个普通的对象。

(3)  所有的函数,都有一个prototype属性(显式原型),属性值也是一个普通对象。

(4)  所有的引用类型(数组,对象,函数)_proto_属性(隐式原型)值指向它的构造函数的“prototype" 属性值(显式原型)。

         var obj={};    obj._proto_ === Object.prototype

  (5)    当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的_proto_(即它的构造函数的prototype)中寻找。

原型链

JS为了避免死循环将Object.prototype的__proto__设为null,所以到这里还没有找到的话就返回null。

18 @import和link引入样式的区别

1.从属关系区别

@import是 CSS 提供的语法规则,只有导入样式表的作用;

link是HTML提供的标签,不仅可以加载 CSS 文件,还可以定义 RSS、rel 连接属性等。

2.加载顺序区别

加载页面时,link标签引入的 CSS 被同时加载;

@import引入的 CSS 将在页面加载完毕后被加载。

3.兼容性区别

@import是 CSS2.1 才有的语法,故只可在 IE5+ 才能识别;

link标签作为 HTML 元素,不存在兼容性问题。

猜你喜欢

转载自blog.csdn.net/weixin_39089928/article/details/124967170