乐趣区

关于前端:前端必会面试题指南

计算属性和 watch 有什么区别? 以及它们的使用场景?

// 区别
  computed 计算属性:依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值产生扭转,下一次获取 computed 的值时才会从新计算 computed 的值。watch 侦听器:更多的是察看的作用, 无缓存性, 相似与某些数据的监听回调, 每当监听的数据变动时都会执行回调进行后续操作
// 使用场景
  当须要进行数值计算, 并且依赖与其它数据时, 应该应用 computed, 因为能够利用 computed 的缓存属性, 防止每次获取值时都要从新计算。当须要在数据变动时执行异步或开销较大的操作时, 应该应用 watch, 应用 watch 选项容许执行异步操作(拜访一个 API), 限度执行该操作的频率,并在失去最终后果前,设置中间状态。这些都是计算属性无奈做到的。

如何依据设计稿进行挪动端适配?

挪动端适配次要有两个维度:

  • 适配不同像素密度, 针对不同的像素密度,应用 CSS 媒体查问,抉择不同精度的图片,以保障图片不会失真;
  • 适配不同屏幕大小, 因为不同的屏幕有着不同的逻辑像素大小,所以如果间接应用 px 作为开发单位,会使得开发的页面在某一款手机上能够精确显示,然而在另一款手机上就会失真。为了适配不同屏幕的大小,应依照比例来还原设计稿的内容。

为了能让页面的尺寸自适应,能够应用 rem,em,vw,vh 等绝对单位。

JS 数据类型

根本类型:Number、Boolean、String、null、undefined、symbol(ES6 新增的),BigInt(ES2020)
援用类型:Object,对象子类型(Array,Function)

Vue 通信

1.props 和 $emit
2. 地方事件总线 EventBus(根本不必)
3.vuex(官网举荐状态管理器)
4.$parent 和 $children
当然还有一些其余方法,但根本不罕用,或者用起来太简单来。介绍来通信的形式,还能够扩大说一下应用
场景,如何应用,注意事项之类的。

new 一个构造函数,如果函数返回 return {}return nullreturn 1return true 会产生什么状况?

如果函数返回一个对象,那么 new 这个函数调用返回这个函数的返回对象,否则返回 new 创立的新对象

宏工作和微工作别离有哪些

  • 微工作包含:promise 的回调、node 中的 process.nextTick、对 Dom 变动监听的 MutationObserver。
  • 宏工作包含:script 脚本的执行、setTimeout,setInterval,setImmediate 一类的定时事件,还有如 I/O 操作、UI 渲染等。

参考 前端进阶面试题具体解答

ES6 新个性

1.ES6 引入来严格模式
    变量必须申明后在应用
    函数的参数不能有同名属性, 否则报错
    不能应用 with 语句 (说实话我根本没用过)
    不能对只读属性赋值, 否则报错
    不能应用前缀 0 示意八进制数, 否则报错 (说实话我根本没用过)
    不能删除不可删除的数据, 否则报错
    不能删除变量 delete prop, 会报错, 只能删除属性 delete global[prop]
    eval 不会在它的外层作用域引入变量
    eval 和 arguments 不能被从新赋值
    arguments 不会主动反映函数参数的变动
    不能应用 arguments.caller (说实话我根本没用过)
    不能应用 arguments.callee (说实话我根本没用过)
    禁止 this 指向全局对象
    不能应用 fn.caller 和 fn.arguments 获取函数调用的堆栈 (说实话我根本没用过)
    减少了保留字(比方 protected、static 和 interface)2. 对于 let 和 const 新增的变量申明

3. 变量的解构赋值

4. 字符串的扩大
    includes():返回布尔值,示意是否找到了参数字符串。startsWith():返回布尔值,示意参数字符串是否在原字符串的头部。endsWith():返回布尔值,示意参数字符串是否在原字符串的尾部。5. 数值的扩大
    Number.isFinite()用来查看一个数值是否为无限的(finite)。Number.isNaN()用来查看一个值是否为 NaN。6. 函数的扩大
    函数参数指定默认值
7. 数组的扩大
    扩大运算符
8. 对象的扩大
    对象的解构
9. 新增 symbol 数据类型

10.Set 和 Map 数据结构 
    ES6 提供了新的数据结构 Set。它相似于数组,然而成员的值都是惟一的,没有反复的值。Set 自身是一个构造函数,用来生成 Set 数据结构。Map 它相似于对象,也是键值对的汇合,然而“键”的范畴不限于字符串,各种类型的值(包含对象)都能够当作键。11.Proxy
    Proxy 能够了解成,在指标对象之前架设一层“拦挡”,外界对该对象的拜访
    都必须先通过这层拦挡,因而提供了一种机制,能够对外界的拜访进行过滤和改写。Proxy 这个词的原意是代理,用在这里示意由它来“代理”某些操作,能够译为“代理器”。Vue3.0 应用了 proxy
12.Promise
    Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更正当和更弱小。特点是:对象的状态不受外界影响。一旦状态扭转,就不会再变,任何时候都能够失去这个后果。13.async 函数 
    async 函数对 Generator 函数的区别:(1)内置执行器。Generator 函数的执行必须靠执行器,而 async 函数自带执行器。也就是说,async 函数的执行,与一般函数截然不同,只有一行。(2)更好的语义。async 和 await,比起星号和 yield,语义更分明了。async 示意函数里有异步操作,await 示意紧跟在前面的表达式须要期待后果。(3)失常状况下,await 命令前面是一个 Promise 对象。如果不是,会被转成一个立刻 resolve 的 Promise 对象。(4)返回值是 Promise。async 函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象不便多了。你能够用 then 办法指定下一步的操作。14.Class 
    class 跟 let、const 一样:不存在变量晋升、不能反复申明...
    ES6 的 class 能够看作只是一个语法糖,它的绝大部分性能
    ES5 都能够做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。15.Module
    ES6 的模块主动采纳严格模式,不论你有没有在模块头部加上 "use strict";。import 和 export 命令以及 export 和 export default 的区别

代码输入后果

var length = 10;
function fn() {console.log(this.length);
}

var obj = {
  length: 5,
  method: function(fn) {fn();
    arguments[0]();}
};

obj.method(fn, 1);

输入后果:10 2

解析:

  1. 第一次执行 fn(),this 指向 window 对象,输入 10。
  2. 第二次执行 arguments[0],相当于 arguments 调用办法,this 指向 arguments,而这里传了两个参数,故输入 arguments 长度为 2。

代码输入后果

Promise.reject('err!!!')
  .then((res) => {console.log('success', res)
  }, (err) => {console.log('error', err)
  }).catch(err => {console.log('catch', err)
  })

输入后果如下:

error err!!!

咱们晓得,.then函数中的两个参数:

  • 第一个参数是用来解决 Promise 胜利的函数
  • 第二个则是解决失败的函数

也就是说 Promise.resolve('1') 的值会进入胜利的函数,Promise.reject('2')的值会进入失败的函数。

在这道题中,谬误间接被 then 的第二个参数捕捉了,所以就不会被 catch 捕捉了,输入后果为:error err!!!'

然而,如果是像上面这样:

Promise.resolve()
  .then(function success (res) {throw new Error('error!!!')
  }, function fail1 (err) {console.log('fail1', err)
  }).catch(function fail2 (err) {console.log('fail2', err)
  })

then 的第一参数中抛出了谬误,那么他就不会被第二个参数不活了,而是被前面的 catch 捕捉到。

对事件委托的了解

(1)事件委托的概念

事件委托实质上是利用了 浏览器事件冒泡 的机制。因为事件在冒泡过程中会上传到父节点,父节点能够通过事件对象获取到指标节点,因而能够把子节点的监听函数定义在父节点上,由父节点的监听函数对立解决多个子元素的事件,这种形式称为事件委托(事件代理)。

应用事件委托能够不必要为每一个子元素都绑定一个监听事件,这样缩小了内存上的耗费。并且应用事件代理还能够实现事件的动静绑定,比如说新增了一个子节点,并不需要独自地为它增加一个监听事件,它绑定的事件会交给父元素中的监听函数来解决。

(2)事件委托的特点

  • 缩小内存耗费

如果有一个列表,列表之中有大量的列表项,须要在点击列表项的时候响应一个事件:

<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
  ......
  <li>item n</li>
</ul>

如果给每个列表项一一都绑定一个函数,那对于内存耗费是十分大的,效率上须要耗费很多性能。因而,比拟好的办法就是把这个点击事件绑定到他的父层,也就是 ul 上,而后在执行事件时再去匹配判断指标元素,所以事件委托能够缩小大量的内存耗费,节约效率。

  • 动静绑定事件

给上述的例子中每个列表项都绑定事件,在很多时候,须要通过 AJAX 或者用户操作动静的减少或者去除列表项元素,那么在每一次扭转的时候都须要从新给新增的元素绑定事件,给行将删去的元素解绑事件;如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和指标元素的增减是没有关系的,执行到指标元素是在真正响应执行事件函数的过程中去匹配的,所以应用事件在动静绑定事件的状况下是能够缩小很多反复工作的。

// 来实现把 #list 下的 li 元素的事件代理委托到它的父层元素也就是 #list 上:// 给父层元素绑定事件
document.getElementById('list').addEventListener('click', function (e) {
  // 兼容性解决
  var event = e || window.event;
  var target = event.target || event.srcElement;
  // 判断是否匹配指标元素
  if (target.nodeName.toLocaleLowerCase === 'li') {console.log('the content is:', target.innerHTML);
  }
});

在上述代码中,target 元素则是在 #list 元素之下具体被点击的元素,而后通过判断 target 的一些属性(比方:nodeName,id 等等)能够更准确地匹配到某一类 #list li 元素之上;

(3)局限性

当然,事件委托也是有局限的。比方 focus、blur 之类的事件没有事件冒泡机制,所以无奈实现事件委托;mousemove、mouseout 这样的事件,尽管有事件冒泡,然而只能一直通过地位去计算定位,对性能耗费高,因而也是不适宜于事件委托的。

当然事件委托不是只有长处,它也是有 毛病 的,事件委托会影响页面性能,次要影响因素有:

  • 元素中,绑定事件委托的次数;
  • 点击的最底层元素,到绑定事件元素之间的 DOM 层数;

在必须应用事件委托的中央,能够进行如下的解决:

  • 只在必须的中央,应用事件委托,比方:ajax的部分刷新区域
  • 尽量的缩小绑定的层级,不在 body 元素上,进行绑定
  • 缩小绑定的次数,如果能够,那么把多个事件的绑定,合并到一次事件委托中去,由这个事件委托的回调,来进行散发。

用过 TypeScript 吗?它的作用是什么?

为 JS 增加类型反对,以及提供最新版的 ES 语法的反对,是的利于团队合作和排错,开发大型项目

代码输入后果

const promise1 = new Promise((resolve, reject) => {setTimeout(() => {resolve('success')
  }, 1000)
})
const promise2 = promise1.then(() => {throw new Error('error!!!')
})
console.log('promise1', promise1)
console.log('promise2', promise2)
setTimeout(() => {console.log('promise1', promise1)
  console.log('promise2', promise2)
}, 2000)

输入后果如下:

promise1 Promise {<pending>}
promise2 Promise {<pending>}

Uncaught (in promise) Error: error!!!
promise1 Promise {<fulfilled>: "success"}
promise2 Promise {<rejected>: Error: error!!}

正向代理和反向代理的区别

  • 正向代理:

客户端想取得一个服务器的数据,然而因为种种原因无奈间接获取。于是客户端设置了一个代理服务器,并且指定指标服务器,之后代理服务器向指标服务器转交申请并将取得的内容发送给客户端。这样实质上起到了对实在服务器暗藏实在客户端的目标。实现正向代理须要批改客户端,比方批改浏览器配置。

  • 反向代理:

服务器为了可能将工作负载分不到多个服务器来进步网站性能 (负载平衡)等目标,当其受到申请后,会首先依据转发规定来确定申请应该被转发到哪个服务器上,而后将申请转发到对应的实在服务器上。这样实质上起到了对客户端暗藏实在服务器的作用。
个别应用反向代理后,须要通过批改 DNS 让域名解析到代理服务器 IP,这时浏览器无奈察觉到真正服务器的存在,当然也就不须要批改配置了。

正向代理和反向代理的构造是一样的,都是 client-proxy-server 的构造,它们次要的区别就在于两头这个 proxy 是哪一方设置的。在正向代理中,proxy 是 client 设置的,用来暗藏 client;而在反向代理中,proxy 是 server 设置的,用来暗藏 server。

行内元素有哪些?块级元素有哪些?空 (void) 元素有那些?

  • 行内元素有:a b span img input select strong
  • 块级元素有:div ul ol li dl dt dd h1 h2 h3 h4 h5 h6 p

空元素,即没有内容的 HTML 元素。空元素是在开始标签中敞开的,也就是空元素没有闭合标签:

  • 常见的有:<br><hr><img><input><link><meta>
  • 鲜见的有:<area><base><col><colgroup><command><embed><keygen><param><source><track><wbr>

说一下 SPA 单页面有什么优缺点?

长处:1. 体验好,不刷新,缩小 申请  数据 ajax 异步获取 页面流程;2. 前后端拆散

3. 加重服务端压力

4. 共用一套后端程序代码,适配多端

毛病:1. 首屏加载过慢;2.SEO 不利于搜索引擎抓取

说一下怎么取出数组最多的一项?

// 我这里只是一个示例
const d = {};
let ary = ['赵', '钱', '孙', '孙', '李', '周', '李', '周', '周', '李'];
ary.forEach(k => !d[k] ? d[k] = 1 : d[k]++);
const result = Object.keys(d).sort((a, b) => d[b] - d[a]).filter((k, i, l) => d[k] === d[l[0]]);
console.log(result)

代码输入后果

const promise = new Promise((resolve, reject) => {console.log(1);
  console.log(2);
});
promise.then(() => {console.log(3);
});
console.log(4);

输入后果如下:

1 
2 
4

promise.then 是微工作,它会在所有的宏工作执行完之后才会执行,同时须要 promise 外部的状态发生变化,因为这里外部没有发生变化,始终处于 pending 状态,所以不输入 3。

代码输入后果

var a = 10
var obj = {
  a: 20,
  say: () => {console.log(this.a)
  }
}
obj.say() 

var anotherObj = {a: 30} 
obj.say.apply(anotherObj) 

输入后果:10 10

我么晓得,箭头函数时不绑定 this 的,它的 this 来自原其父级所处的上下文,所以首先会打印全局中的 a 的值 10。前面尽管让 say 办法指向了另外一个对象,然而仍不能扭转箭头函数的个性,它的 this 依然是指向全局的,所以依旧会输入 10。

然而,如果是一般函数,那么就会有齐全不一样的后果:

var a = 10  
var obj = {  
  a: 20,  
  say(){console.log(this.a)  
  }  
}  
obj.say()   
var anotherObj={a:30}   
obj.say.apply(anotherObj)

输入后果:20 30

这时,say 办法中的 this 就会指向他所在的对象,输入其中的 a 的值。

闭包

首先阐明什么是闭包,闭包简略来说就是函数嵌套函数,外部函数援用来内部函数的变量,从而导致垃圾回收
机制没有把以后变量回收掉,这样的操作带来了内存透露的影响,当内存透露到肯定水平会影响你的我的项目运行
变得卡顿等等问题。因而在我的项目中咱们要尽量避免内存透露。

原型

构造函数是一种非凡的办法,次要用来在创建对象时初始化对象。每个构造函数都有 prototype(原型)(箭头函数以及 Function.prototype.bind()没有)属性,这个 prototype(原型)属性是一个指针,指向一个对象,这个对象的用处是蕴含特定类型的所有实例共享的
属性和办法,即这个原型对象是用来给实例对象共享属性和办法的。每个实例对象的__proto__都指向这个
构造函数 / 类的 prototype 属性。面向对象的三大个性:继承 / 多态 / 封装

对于 new 操作符:1. new 执行的函数, 函数外部默认生成了一个对象

2. 函数外部的 this 默认指向了这个 new 生成的对象

3. new 执行函数生成的这个对象, 是函数的默认返回值

ES5 例子:function Person(obj) {
    this.name = obj.name
    this.age= obj.age
}
// 原型办法
Person.prototype.say = function() {console.log('你好,', this.name)
}
// p 为实例化对象,new Person()这个操作称为构造函数的实例化
let p = new Person({name: '番茄', age: '27'})
console.log(p.name, p.age)
p.say()

ES6 例子:class Person{constructor(obj) {
      this.name = obj.name
        this.age= obj.age
  }
  say() {console.log(this.name)
  }
}

let p = new Person({name: 'ES6- 番茄', age: '27'})
console.log(p.name, p.age)
p.say()
退出移动版