每日十题 金三银四面试题(六)
共 4809字,需浏览 10分钟
·
2021-03-08 01:22
一、单例模式(设计模式)
参考解答:
单例对象是一种常用的设计模式。单例对象只有一个实例。JavaScript的应用可以写弹框组件。
class Person() {
constructor(name, sex) {
this.name = name
this.sex = sex
}
}
const Singleton = () => {
let instance;
const SingleTon = (name, sex) => {
if(!instance) instance = new Person(name, sex)
return instance;
}
return Singleton()
}
二、说一下 computed 和 watch 的区别
参考解答:
computed 是计算属性,依赖其他属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容。返回的是一个属性值可以理解为返回一个数据。
watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作。可以理解为执行某个方法。
从使用场景上说,computed 适用一个数据被多个数据影响,而 watch 适用一个数据影响多个数据。
四、谈谈你对this对象的理解
参考解答:
this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在实际开发中,this 的指向可以通过四种调用模式来判断。
1. 第一种是函数调用模式,当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。
2. 第二种是方法调用模式,如果一个函数作为一个对象的方法来调用时,this 指向这个对象。
3. 第三种是构造器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
4. 第四种是 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。也就是说,在使用 call() 方法时,传递给函数的参数必须逐个列举出来。bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。
这四种方式,使用构造器调用模式的优先级最高,然后是 apply 、 call 和 bind 调用模式,然后是方法调用模式,然后是函数调用模式。
五、实现一个寄生组合式继承。
1. 子类Student,父类Person
2. 父类属性name,原型方法sayName打印name;
3. 子类属性grade和继承父类属性,原型方法有sayGrade和继承自父类的方法。
参考解答:
// 定义父类 Person
function Person(name) {
this.name = name;
}
// 父类原型方法
Person.prototype.sayName = function() {
console.log("My name is " + this.name + ".")
}
// 定义子类 Student
function Student(name, grade) {
// 执行父类构造函数,继承name属性
Person.call(this, name);
this.grade = grade;
}
// 继承父类原型方法
Student.prototype = Object.create(Person.prototype);
// 原型构造器修正
Student.prototype.constructor = Student;
// 定义子类原型方法
Student.prototype.sayGrade = function() {
console.log(`My grade is ${this.grade}.`)
}
六、什么是MVVM? MVC? MVP?
参考解答:
MVC、MVP 和 MVVM 是三种常见的软件架构设计模式,主要通过分离关注点的方式来组织代码结构,优化我们的开发效率。
1. MVC 通过分离 Model、View 和 Controller 的方式来组织代码结构。其中 View 负责页面的显示逻辑,Model 负责存储页面的业务数据,以及对相应数据的操作。并且 View 和 Model 应用了观察者模式,当 Model 层发生改变的时候它会通知有关 View 层更新页面。Controller 层是 View 层和 Model 层的纽带,它主要负责用户与应用的响应操作,当用户与页面产生交互的时候,Controller 中的事件触发器就开始工作了,通过调用 Model 层,来完成对 Model 的修改,然后 Model 层再去通知 View 层更新。
2. MVP 模式与 MVC 唯一不同的在于 Presenter 和 Controller。在 MVC 模式中我们使用观察者模式,来实现当 Model 层数据发生变化的时候,通知 View 层的更新。这样 View 层和 Model 层耦合在一起,当项目逻辑变得复杂的时候,可能会造成代码的混乱,并且可能会对代码的复用性造成一些问题。MVP 的模式通过使用 Presenter 来实现对 View 层和 Model 层的解耦。MVC 中的Controller 只知道 Model 的接口,因此它没有办法控制 View 层的更新,MVP 模式中,View 层的接口暴露给了 Presenter 因此我们可以在 Presenter 中将 Model 的变化和 View 的变化绑定在一起,以此来实现 View 和 Model 的同步更新。这样就实现了对 View 和 Model 的解耦,Presenter 还包含了其他的响应逻辑。
3. MVVM 模式中的 VM,指的是 ViewModel,它和 MVP 的思想其实是相同的,不过它通过双向的数据绑定,将 View 和 Model 的同步更新给自动化了。当 Model 发生变化的时候,ViewModel 就会自动更新;ViewModel 变化了,View 也会更新。这样就将 Presenter 中的工作给自动化了。我了解过一点双向数据绑定的原理,比如 vue 是通过使用数据劫持和发布订阅者模式来实现的这一功能。
七、说说 $route 和 $router 的区别
参考解答:
$route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。
$router 是“路由实例”对象包括了路由的跳转方法,钩子函数等。
八、Vue生命周期的各个阶段是什么?
参考解答:
Vue 一共有8个生命阶段,分别是创建前、创建后、加载前、加载后、更新前、更新后、销毁前和销毁后,每个阶段对应了一个生命周期的钩子函数。
(1)beforeCreate 钩子函数,在实例初始化之后,在数据监听和事件配置之前触发。因此在这个事件中我们是获取不到 data 数据的。
(2)created 钩子函数,在实例创建完成后触发,此时可以访问 data、methods 等属性。但这个时候组件还没有被挂载到页面中去,所以这个时候访问不到 $el 属性。一般我们可以在这个函数中进行一些页面初始化的工作,比如通过 ajax 请求数据来对页面进行初始化。
(3)beforeMount 钩子函数,在组件被挂载到页面之前触发。在 beforeMount 之前,会找到对应的 template,并编译成 render 函数。
(4)mounted 钩子函数,在组件挂载到页面之后触发。此时可以通过 DOM API 获取到页面中的 DOM 元素。
(5)beforeUpdate 钩子函数,在响应式数据更新时触发,发生在虚拟 DOM 重新渲染和打补丁之前,这个时候我们可以对可能会被移除的元素做一些操作,比如移除事件监听器。
(6)updated 钩子函数,虚拟 DOM 重新渲染和打补丁之后调用。
(7)beforeDestroy 钩子函数,在实例销毁之前调用。一般在这一步我们可以销毁定时器、解绑全局事件等。
(8)destroyed 钩子函数,在实例销毁之后调用,调用后,Vue 实例中的所有东西都会解除绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
当我们使用 keep-alive 的时候,还有两个钩子函数,分别是 activated 和 deactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 actived 钩子函数。
九、为什么0.1 + 0.2 != 0.3 ? 如何解决该问题?
参考解答:
当计算机计算 0.1+0.2 的时候,实际上计算的是这两个数字在计算机里所存储的二进制,0.1 和 0.2 在转换为二进制表示的时候会出现位数无限循环的情况。js 中是以 64 位双精度格式来存储数字的,只有 53 位的有效数字,超过这个长度的位数会被截取掉这样就造成了精度丢失的问题。这是第一个会造成精度丢失的地方。在对两个以 64 位双精度格式的数据进行计算的时候,首先会进行对阶的处理,对阶指的是将阶码对齐,也就是将小数点的位置对齐后,再进行计算,一般是小阶向大阶对齐,因此小阶的数在对齐的过程中,有效数字会向右移动,移动后超过有效位数的位会被截取掉,这是第二个可能会出现精度丢失的地方。当两个数据阶码对齐后,进行相加运算后,得到的结果可能会超过 53 位有效数字,因此超过的位数也会被截取掉,这是可能发生精度丢失的第三个地方。
对于这样的情况,我们可以将其转换为整数后再进行运算,运算后再转换为对应的小数,以这种方式来解决这个问题。
我们还可以将两个数相加的结果和右边相减,如果相减的结果小于一个极小数,那么我们就可以认定结果是相等的,这个极小数可以
使用 es6 的 Number.EPSILON
十、如何编写高性能的JavaScript?
参考解答:
1.使用位运算代替一些简单的四则运算。
2.避免使用过深的嵌套循环。
3.不要使用未定义的变量。
4.当需要多次访问数组长度时,可以用变量保存起来,避免每次都会去进行属性查找。