Web前端2019面试总结2

1.js继承:

想要继承,就必须要提供个父类(继承谁,提供继承的属性)

    

组合继承(组合原型链继承和借用构造函数继承)(常用)

    

    重点:结合了两种模式的优点,传参和复用

    特点:1、可以继承父类原型上的属性,可以传参,可复用。

       2、每个新实例引入的构造函数属性是私有的。

    缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。

2.对象赋值(深拷贝):

JSON.parse(JSON.stringify(objectToClone)) #这种方法能解决绝大部分业务

1 var arr=[{a:1,b:2},{c:1,d:2}];
2 var arr2=JSON.parse(JSON.stringify(arr));
3     
4 arr2[1].d=7;//可以修改值
5 console.log(arr,arr2)

3.数组去重:

方法一:

定义一个变量数组 res 保存结果,遍历需要去重的数组,如果该元素已经存在在 res 中了,则说明是重复的元素,如果没有,则放入 res 中

 1 function unique(a) {
 2 var res = [];
 3 for (var i = 0, len = a.length; i < len; i++) {
 4var item = a[i];
 5for (var j = 0, jLen = res.length; j < jLen; j++) {
 6if (res[j] === item)
 7break;
 8 ​ }
 9if (j === jLen)
10 ​ res.push(item);
11 }
12 return res;
13 }
14 var a = [1, 1, '1', '2', 1];
15 var ans = unique(a);
16 console.log(ans); // => [1, "1", "2"]

不考虑兼容性的精简版

1 functi
on
unique(a) { 2 var res = a.filter(function(item, index, array) { 3return array.indexOf(item) === index; 4 }); 5 return res; 6 } 7 var a = [1, 1, '1', '2', 1]; 8 var ans = unique(a); 9 console.log(ans); // => [1, "1", "2"]

方法二:

法一是将原数组中的元素和结果数组中的元素一一比较,我们可以换个思路,将原数组中重复元素的最后一个元素放入结果数组中。

 1 function unique(a) {
 2 var res = [];
 3 for (var i = 0, len = a.length; i < len; i++) {
 4 for (var j = i + 1; j < len; j++) {
 5// 这一步十分巧妙,如果发现相同元素,则 i 自增进入下一个循环比较
 6if (a[i] === a[j])
 7 ​ j = ++i;
 8 ​ }
 9 ​ res.push(a[i]);
10 }
11 return res;
12 }
13 var a = [1, 1, '1', '2', 1];
14 var ans = unique(a);
15 console.log(ans); // => ["1", "2", 1]

方法三:(sort)

将数组用 sort 排序后,理论上相同的元素会被放在相邻的位置,那么比较前后位置的元素就可以了。

1 function unique(a) {
2 return a.concat().sort().filter(function(item, pos, ary) {
3return !pos || item != ary[pos - 1];
4 });
5 }
6 var a = [1, 1, 3, 2, 1, 2, 4];
7 var ans = unique(a);
8 console.log(ans); // => [1, 2, 3, 4]

方法四:(ES6)

ES6 部署了 Set 以及 Array.from 方法,太强大了!如果浏览器支持,完全可以这样:

1 function unique(a) {
2 return Array.from(new Set(a));
3 }
4 
5 var a = [{name: "hanzichi"}, {age: 30}, new String(1), new Number(1)];
6 var ans = unique(a);
7 console.log(ans); // => [Object, Object, String, Number]

4.使用原型对象封装一个方法:

 1 function Person (info){
 2     this._init_(info);
 3 }
 4 
 5 Pserson.prototype = {
 6     constructor : Person,
 7     _init_ : function(info) {
 8         this.name = info.name;
 9         this.age = info.age;
10         this.sex = info.sex;
11     }
12     sayHello:function(){
13         console.log('hello');
14     }
15 }

5.闭包的特点,注意点:

优点:减少全局变量

缺点:父函数每调用一次会产生不同的闭包,内存泄漏

6.预解析:

script:自上而下进行解析,

函数:由里到外进行解析。

但是浏览器在执行JS代码的时候会分成两部分操作:预解析以及逐行执行代码

预解析:浏览器在开始工作的时候会先解读JS代码的关键字:比如:var function 参数等,并把解析到的内容存入一个类似仓库的地方,这个过程一般称为JS预解析。

并且,在这个阶段所有的变量,在正式运行代码之前,都会提前赋值为未定义。

7.js事件循环机制:

执行栈:当javascript代码执行的时候会将不同的变量存于内存中的不同位置:堆(heap)和栈(stack)中来加以区分。其中,堆里存放着一些对象。而栈中则存放着一些基础类型变量以及对象的指针。 但是我们这里说的执行栈和上面这个栈的意义却有些不同。

我们知道,当我们调用一个方法的时候,js会生成一个与这个方法对应的执行环境(context),又叫执行上下文。这个执行环境中存在着这个方法的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。 而当一系列方法被依次调用的时候,因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。

当一个脚本第一次执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入执行栈中,然后从头开始执行。如果当前执行的是一个方法,那么js会向执行栈中添加这个方法的执行环境,然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。。这个过程反复进行,直到执行栈中的代码全部执行完毕。

事件队列:js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码...,如此反复,这样就形成了一个无限的循环。这就是这个过程被称为“事件循环(Event Loop)”的原因。

微任务和宏任务:以上的事件循环过程是一个宏观的表述,实际上因为异步任务之间并不相同,因此他们的执行优先级也有区别。不同的异步任务被分为两类:微任务(micro task)和宏任务(macro task)。

以下事件属于宏任务:

  • setInterval()
  • setTimeout()

以下事件属于微任务

  • new Promise()
  • new MutaionObserver()

前面我们介绍过,在一个事件循环中,异步事件返回结果后会被放到一个任务队列中。然而,根据这个异步事件的类型,这个事件实际上会被对应的宏任务队列或者微任务队列中去。并且在当前执行栈为空的时候,主线程会 查看微任务队列是否有事件存在。如果不存在,那么再去宏任务队列中取出一个事件并把对应的回到加入当前执行栈;如果存在,则会依次执行队列中事件对应的回调,直到微任务队列为空,然后去宏任务队列中取出最前面的一个事件,把对应的回调加入当前执行栈...如此反复,进入循环。

我们只需记住当当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。

8.防抖函数和节流函数:

防抖(debounce)

所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

节流(throttle)

所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。

对于节流,一般有两种方式可以实现,分别是时间戳版和定时器版。

9.浏览器兼容性问题:

  1. 不同浏览器的标签默认的margin和padding不同,解决方案:统一设置为0
  2. 块属性标签float后,又有横行的margin情况下,在IE6显示margin比设置的大,解决方案:在float的标签样式控制中加入 display:inline;将其转化为行内属性
  3. 垂直居中.将 line-height 设置为当前 div 相同的高度, 再通过 vetical-align: middle.( 注意内容不要换行.)

11.H5丶C3丶ES6新特性: 

H5新特性:

  1. HTML5 语义化新标签
  2. HTML5 Canvas

  3. HTML5  Audio(音频)、Video(视频)

  4. HTML5 Input 类型

  5. HTML5 Web 存储

CSS3新特性:

  1. CSS3选择器
  2. CSS3 边框(Borders
  3. CSS3 背景
  4. 2D 3D转换
  5. CSS3 过渡
  6. CSS3 动画
  7. CSS3 盒模型
  8. CSS3伸缩布局盒模型(弹性盒)
  9. CSS3 媒体查询

ES6新特性:

  1. const 与 let 变量
  2. 模板字面量
  3. 结构
  4. 对象字面量简写法
  5. 展开运算符
  6. 剩余参数(可变参数)
  7. ES6箭头函数

12.promise的用法:

Promise是一个构造函数,自己身上有all、reject、resolve这几个眼熟的方法,原型上有then、catch等同样很眼熟的方法。

参考:https://www.cnblogs.com/whybxy/p/7645578.html

13.CSS3中如何监听动画执行完成?
在动画执行完毕之后执行某个操作,则应该监听transitionend事件
 1 mydiv.addEventListener('transitionend', function(){ 2 // do somthing 3 }, false) 
 
14.js面向对象

公共的属性和方法, 添加到原型上

15.rem
rem是CSS3新增的相对长度单位,是指相对于根元素htmlfont-size计算值的大小。简单可理解为屏幕宽度的百分比。
 
16.一组span标签设置 display:inline-block;出现间隔空隙怎么解决?
出现原因是取决于父元素的 font-size ,设置为0就可以了。
 

vue问题:

1.怎样增加vue cli 指令:

//局部指令
var app = new Vue({
    el: '#app',
    data: {    
    },
    // 创建指令(可以多个)
    directives: {
        // 指令名称
        dir1: {
            inserted(el) {
                // 指令中第一个参数是当前使用指令的DOM
                console.log(el);
                console.log(arguments);
                // 对DOM进行操作
                el.style.width = '200px';
                el.style.height = '200px';
                el.style.background = '#000';
            }
        }
    }
})
1 // 全局指令
2 Vue.directive('dir2', {
3     inserted(el) {
4         console.log(el);
5     }
6 })
1 /* 使用指令 */
2 <div id="app">
3     <div v-dir1></div>
4     <div v-dir2></div>
5 </div>

2.Vue页面缓存:

详情:https://www.cnblogs.com/wangyunhui/p/8178392.html

3.钩子函数,那些会被触发一次?那些是多次?

beforeCreate   created

beforeMount   mounted

beforeUpdate   updated

beforeDestroy    destroyed

4.路由钩子

1.全局钩子

主要包括 beforeEachaftrEach,

beforeEach函数有三个参数:

to:router即将进入的路由对象
from:当前导航即将离开的路由
next:Function,进行管道中的一个钩子,如果执行完了,则导航的状态就是 confirmed (确认的);否则为false,终止导航。

afterEach函数不用传next()函数
这类钩子主要作用于全局,一般用来判断权限,以及以及页面丢失时候需要执行的操作,例如判断用户是否登录

2.单个路由里面的钩子

主要用于写某个指定路由跳转时需要执行的逻辑

 1                {
 2                     path: '/dashboard',
 3                     component: resolve =>    require(['../components/page/Dashboard.vue'], resolve),
 4                     meta: { title: '系统首页' },
 5                     beforeEnter: (to, from, next) => {
 6                         
 7                       },
 8                     beforeLeave: (to, from, next) => {
 9                         
10                     }
11                 
12                 },

3.组件路由

    主要包括 beforeRouteEnter和beforeRouteUpdate ,beforeRouteLeave,这几个钩子都是写在组件里面也可以传三个参数(to,from,next),作用与前面类似.

 1   beforeRouteEnter(to, from, next) {
 2     next(vm => {
 3       if (
 4         vm.$route.meta.hasOwnProperty('auth_key') &&
 5         vm.$route.meta.auth_key != ''
 6       ) {
 7         if (!vm.hasPermission(vm.$route.meta.auth_key)) {
 8           vm.$router.replace('/admin/noPermission')
 9         }
10       }
11     })
12   },

5.vue SSR

vue-server-renderer

详情:https://segmentfault.com/a/1190000015964813

6.路由有哪两种模式,url有哪两种?

hash 和 history 

7.Vue项目优化
  1. 对项目代码中的JS/CSS/SVG(*.ico)文件进行gzip压缩
  2. 对路由组件进行懒加载
  3. v-if 和 v-show选择调用
  4. 为item设置唯一key值,
  5. 细分vuejs组件
  6. 减少watch的数据
  7. loading(数据渲染加载动画)
 
8.Vue双向数据绑定实现原理?
Vue内部通过Object.defineProperty方法属性拦截的方式,把data对象里每个数据的读写转化成getter/setter,当数据变化时通知视图更新
getter函数里面返回一个value值,在setter函数中写入修改后的值并调用update方法更新视图的数据值
 
其他问题:
1.如何规划重构一个项目?
2.任职中自己的成长点在哪里?
原文地址:https://www.cnblogs.com/jun-qi/p/11190927.html