关于js的面试题:
1.undefined 和 null 有什么区别?
答:目前,null和undefined基本是同义的,只有一些细微的差别。
null表示"没有对象",即该处不应该有值。典型用法是:
(1) 作为函数的参数,表示该函数的参数不是对象。 (2) 作为对象原型链的终点。
Object.getPrototypeOf(Object.prototype) // null
undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。 (3)对象没有赋值的属性,该属性的值为undefined。 (4)函数没有返回值时,默认返回undefined。
var i; i // undefined
function f(x){console.log(x)} f() // undefined
var o = new Object(); o.p // undefined
var x = f(); x // undefined
2.什么是事件传播?
答:当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。 在“冒泡阶段”中,事件冒泡或向上传播至父级,祖父母,祖父母或父级,直到到达window
为止;而在“捕获阶段”中,事件从window
开始向下触发元素 事件或event.target
。
- 捕获阶段–事件从
window
开始,然后向下到每个元素,直到到达目标元素。 - 目标阶段–事件已达到目标元素。
- 冒泡阶段–事件从目标元素冒泡,然后上升到每个元素,直到到达
window
。
3.什么是冒泡事件?
答:当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。 在冒泡阶段,事件冒泡,或者事件发生在它的父代,祖父母,直到到达window
为止。触发子代的方法父代的方法也会触发.
阻止冒泡事件的发生,在子代中添加e.stopPropagation()事件。
4.事件的捕获?
答:事件捕获指的是从document到触发事件的那个节点,即自上而下的去触发事件。
<div id="parent"> <div id="child" class="child"></div> </div> document.getElementById("parent").addEventListener("click",function(e){ alert("parent事件被触发,"+e.target.id); },true) document.getElementById("child").addEventListener("click",function(e){ alert("child事件被触发,"+e.target.id) },true) 结果: parent事件被触发,parent child事件被触发,child 结论:先parent,然后child。事件触发顺序变更为自外向内,这就是事件捕获。
5.event.preventDefault() 和 event.stopPropagation()方法之间有什么区别?
答:event.preventDefault()
方法可防止元素的默认行为。 如果在表单元素中使用,它将阻止其提交。 如果在锚元素中使用,它将阻止其导航。 如果在上下文菜单中使用,它将阻止其显示或显示。 event.stopPropagation()
方法用于阻止捕获和冒泡阶段中当前事件的进一步传播。
6.== 和 === 有什么区别?
答:==
用于一般比较,===
用于严格比较,==
在比较的时候可以转换数据类型,===
严格比较,只要类型不匹配就返回flase
。
假设我们要比较x == y
的值。
- 如果
x
和y
的类型相同,则 JS 会换成===
操作符进行比较。 - 如果
x
为null
,y
为undefined
,则返回true
。 - 如果
x
为undefined
且y
为null
,则返回true
。 - 如果
x
的类型是number
,y
的类型是string
,那么返回x == toNumber(y)
。 - 如果
x
的类型是string
,y
的类型是number
,那么返回toNumber(x) == y
。 - 如果
x
为类型是boolean
,则返回toNumber(x)== y
。 - 如果
y
为类型是boolean
,则返回x == toNumber(y)
。 - 如果
x
是string
、symbol
或number
,而y
是object
类型,则返回x == toPrimitive(y)
。 - 如果
x
是object
,y
是string
,symbol
则返回toPrimitive(x) == y
。 - 剩下的 返回
false
全等运算符 ===
全等和不全等操作符遵循以下基本规则(IEA规则):
- 如果两个操作数有不同的类型,它们不是严格相等的
- 如果两个操作数都为
null
,则它们是严格相等的 - 如果两个操作数都为
undefined
,它们是严格相等的 - 如果一个或两个操作数都是
NaN
,它们就不是严格相等的 - 如果两个操作数都为
true
或都为false
,它们是严格相等的 - 如果两个操作数都是
number
类型并且具有相同的值,则它们是严格相等的 - 如果两个操作数都是
string
类型并且具有相同的值,则它们是严格相等的 - 如果两个操作数都引用相同的对象或函数,则它们是严格相等的
- 以下所有其他情况下操作数都不是严格相等的。
7.为什么在 JS 中比较两个相似的对象时返回 false?
答:
let a = { a: 1 }; let b = { a: 1 }; let c = a; console.log(a === b); // 打印 false,即使它们有相同的属性 console.log(a === c); // true
console.log
语句返回false
,而第二个console.log
语句返回true
。a
和c
有相同的引用地址,而a
和b
没有。8.!! 运算符能做什么?
答:!!
运算符可以将右侧的值强制转换为布尔值。console.log(!!null); // false console.log(!!undefined); // false console.log(!!''); // false console.log(!!0); // false console.log(!!NaN); // false console.log(!!' '); // true console.log(!!{}); // true console.log(!![]); // true console.log(!!1); // true console.log(!![].length); // false
9.什么是作用域?
答:JavaScript 中的作用域是我们可以有效访问变量或函数的区域。JS 有三种类型的作用域:全局作用域、函数作用域和块作用域(ES6)。
10.什么是闭包?
答:闭包就是一个函数在声明时能够记住当前作用域、父函数作用域、及父函数作用域上的变量和参数的引用,直至通过作用域链上全局作用域,基本上闭包是在声明函数时创建的作用域。
举个栗子:
function makeFunc() { var name = "Mozilla"; function displayName() { alert(name); } return displayName; } var myFunc = makeFunc(); myFunc();
11.Js中的虚值是什么?
答:在 JS 中,虚值有 false
, 0
,''
, null
, NaN
, undefined
。简单的来说虚值就是是在转换为布尔值时变为 false
的值。
12.JavaScript 中 `this` 值是什么?
答:this
指的是当前正在执行或调用该函数的对象的值。this
值的变化取决于我们使用它的上下文和我们在哪里使用它。谁调用他就指向谁,注意箭头函数会使this的指向发生改变.
13.对象的 prototype(原型) 是什么?
答:在JavaScript中,prototype对象是实现面向对象的一个重要机制。
14.Function.prototype.call
方法的用途是什么?
答:call()
方法使用一个指定的 this
值和单独给出的一个或多个参数来调用一个函数。
const details = { message: 'Hello World!' }; function getMessage(){ return this.message; } getMessage.call(details); // 'Hello World!'
call()
作用与 apply()
方法类似,只有一个区别,就是 call()
方法接受的是一个参数列表,而 apply()
方法接受的是一个包含多个参数的数组。
15.什么是arguments?
答:arguments
对象是函数中传递的参数值的集合。它是一个类似数组的对象,因为它有一个length属性,我们可以使用数组索引表示法arguments[1]
来访问单个值,但它没有数组中的内置方法,如:forEach
、reduce
、filter
和map等方法.
arguments
对象是所有(非箭头)函数中都可用的局部变量。你可以使用arguments
对象在函数中引用函数的参数.
16.var
,let
和const
的区别是什么?
答:1.const定义的变量不可以修改,而且必须初始化。
2.var
声明变量存在变量提升,let
和const
不存在变量提升。
3.var定义的变量可以修改,如果不初始化会输出undefined,不会报错。
4.let
和const
声明形成块作用域,函数内部使用let定义后,对函数外部无影响。
5.同一作用域下let
和const
不能声明同名变量,而var
可以。
17.对象的解构.
答:对象析构是从对象或数组中获取或提取值的一种新的、更简洁的方法。假设有如下的对象:
const employee = { firstName: "Marko", lastName: "Polo", position: "Software Developer", yearHired: 2017 }; 从对象获取属性,早期方法是创建一个与对象属性同名的变量。这种方法很麻烦,因为我们要为每个属性创建一个新变量。假设我们有一个大对象,它有很多属性和方法,用这种方法提取属性会很麻烦。 var firstName = employee.firstName; var lastName = employee.lastName; var position = employee.position; var yearHired = employee.yearHired;
18.什么是promise?
答:Promise 是异步编程的一种解决方案:从语法上讲,promise
是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。promise
有三种状态:pending(等待态)
,fulfiled(成功态)
,rejected(失败态)
;状态一旦改变,就不会再变。创造promise
实例后,它会立即执行。
了解详细请看另一篇文章:地址
19.js基础类型
答:基础类型:String、Number、null、undefined、Boolean、Symbol、Bigint
引用类型:Object、Function
20.js中基础数据类型和引用数据类型的区别?
答:基础数据类型值保存在栈内存中,赋值时互相不影响
引用类型保存在堆内存中,不可以直接访问堆内存空间中的位置和操作堆内存空间,只能操作栈内存只用的引用地址;实际上改变的是堆内存中的数据;
详情请看连接:地址
21.原型链的理解?
答:当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。
详情请看另一篇连接:地址
22.什么是防抖和节流?
答:防抖:当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
节流:当持续触发事件时,保证一定时间段内只调用一次事件处理函数。连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。
详情请看另一篇文章连接:地址
23.如何实现浅拷贝和深拷贝?
答:浅拷贝:只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,指向同一个内存,只是拷贝了引用的地址,当改对象发生改变,拷贝的对象也随之发生改变。
深拷贝:深拷贝就是完完整整的将一个对象从内存中拷贝一份出来,拷贝出来的内存和原对象内存的指向都是不同的,修改不会相互影响
详情请看另一篇文章连接:地址
24.实现数组扁平化?
答:将多维数组变为一维数组,如:arr = [1,2,[3,[4,5]]]
var arr = [1,2,[3,[4,5]]] function flatten(arr) { while (arr.some(item => Array.isArray(item))) { arr = [].concat(...arr); } return arr; } console.log(arr); //arr = [1,2,3,4,5]
25.如何理解BigInt?
答:BigInt是一种新的数据类型,用于当整数值大于Number数据类型支持的范围时。这种数据类型允许我们安全地对大整数
执行算术操作,表示高分辨率的时间戳,使用大整数id,等等,而不需要使用库。BigInt
是一种内置对象,它提供了一种方法来表示大于 253 - 1
的整数。这原本是 Javascript中可以用 Number
表示的最大数字。BigInt
可以表示任意大的整数。
在JS中,所有的数字都以双精度64位浮点格式表示,那这会带来什么问题呢?这导致JS中的Number无法精确表示非常大的整数,它会将非常大的整数四舍五入;
如:可以用在一个整数字面量后面加 n
的方式定义一个 BigInt
console.log(BigInt(999999999999999)); //=>999999999999999n
使用 typeof
测试时, BigInt
对象返回 "bigint" :
typeof 1n === 'bigint'; // true typeof BigInt('1') === 'bigint'; // true
使用 Object
包装后, BigInt
被认为是一个普通 "object" :
typeof Object(1n) === 'object'; // true
详细介绍请看:地址
26.js中的类型转换有几种?
- 转换成数字
- 转换成布尔值
- 转换成字符串
27.typeof监测的类型?instanceof呢?
答:typeof 主要用于监测基本类型。
typeof undefined;//=> undefined typeof 'a';//=> string typeof 1;//=> number typeof true;//=> boolean typeof {};//=> object typeof [];//=> object typeof function() {};//=> function typeof null;//=> object
instanceof:主要用于检测引用类型(左边是对象,右边是函数.根据对象的原形链往上找,如果原形链上有右边函数.prototype,返回true;否则返回false)
var obj = {}; obj instanceof Object; //=> true; var arr = []; arr instanceof Array; //=> true; var fn = function() {}; fn instanceof Function; //=> true;
实现原理:
Object.proptotype.__proto__
),仍然不等于B.prototype,那么返回false,否则返回true.function instance_of(L, R) {//L 表示左表达式,R 表示右表达式 let prototype = R.prototype; while (true) { if(L === null) {//已经找到原型链的顶端 return false; } else if(L.__proto__ === prototype) { return true; } L = L.__proto__;//继续向上一层原型链查找 } }
28.判断一个变量是不是数组?
答:1.使用Array.isArray判断,返回true则说明该变量是数组;
2.使用instanceof Array判断,返回true则说明该变量是数组;
3.使用Object.prototype.toString.call判断,如果值为[object Arrat], 说明该变量是数组;
4.通过constructor判断,如果是数组,那么arr.constructor === Array (不准确)
29.数组与类数组的区别?
答:1.拥有数组的length属性,其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理)
2.不具有数组所具有的方法;
3.类数组是一个普通对象,而真实的数组Array类型。
常见的类数组有:函数的参数arguments,DOM对象列表(比如通过document。querySelectorAll得到的列表),JQuery对象(比如$('div'))
类数组转化为数组:
//第一种方法 Array.prototype.slice.call(arrayLike, start); //第二种方法 [...arrayLike]; //第三种方法: Array.from(arrayLike);
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象。
30.ES6有什么新特性?
答:1.新增了块级作用域(let,const);
2.提供了定义类的语法糖(class);
3.新增了一种基本数据类型(Symbol);
4.新增了变量的解构赋值;
5.数组新增了一些API,如isArray、form、of方法,数组实例新增了entries(),keys() 和 values() 等方法;
6.函数参数允许设置默认值,引入了rest参数,新增了尖头函数;
7.对象和数组新增了拓展运算符;
8.新增了模块化(import、export),新增了set和map数据解构;
9.ES6原生提供了Proxy构造函数,用来生成Proxy实例;
10.新增了生成器(Generator)和遍历器(Iterator)
31.什么是函数柯里化?实现sun(1)(2)(3)返回结果是1,2,3之和?
答:函数柯里化就是接受多个参数变化成接受一个单一参数的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
案例:1.简单粗暴:
function sum(a) { return function(b) { return function(c) { return a+b+c; } } } console.log(sum(1)(2)(3)); // 6
2.实现一个curry函数,将普通函数进行柯里化:
function curry(fn, args = []) { return function(){ let rest = [...args, ...arguments]; if (rest.length < fn.length) { return curry.call(this,fn,rest); }else{ return fn.apply(this,rest); } } } //test function sum(a,b,c) { return a+b+c; } let sumFn = curry(sum); console.log(sumFn(1)(2)(3)); //6 console.log(sumFn(1)(2, 3)); //6
32.async/await的用法和理解!
答: async 是一个修饰符,async 定义的函数会默认的返回一个Promise对象resolve的值,因此对async函数可以直接进行then操作,返回的值即为then方法的传入函数
async function fun0() { console.log(1) return 1 } fun0().then( x => { console.log(x) }) // 输出结果 1, 1,
async function funp() { console.log('Promise') return new Promise(function(resolve, reject){ resolve('Promise') }) } funp().then( x => { console.log(x) }) // 输出promise promise
await 也是一个修饰符,await 关键字 只能放在 async 函数内部, await关键字的作用 就是获取 Promise中返回的内容, 获取的是Promise函数中resolve或者reject的值
如果await 后面并不是一个Promise的返回值,则会按照同步程序返回值处理
// await 关键字 只能放在 async 函数内部, await关键字的作用 就是获取 Promise中返回的内容, 获取的是Promise函数中resolve或者reject的值 // 如果await 后面并不是一个Promise的返回值,则会按照同步程序返回值处理,为undefined const bbb = function(){ return 'string'} async function funAsy() { const a = await 1 const b = await new Promise((resolve, reject)=>{ setTimeout(function(){ resolve('time') }, 3000) }) const c = await bbb() console.log(a, b, c) } funAsy() // 运行结果是 3秒钟之后 ,输出 1, time , string,
// 2.如果不使用promise的方法的话 function log2(time) { setTimeout(function(){ console.log(time) return 1 }, time) } async function fun1() { const a = await log2(5000) const b = await log2(10000) const c = log2(2000) console.log(a) console.log(1) } fun1() // 以上运行结果为: 立刻输出undefined 立刻输出1 2秒后输出2000 5秒后输出5000 10秒后输出10000
关于vue面试题
1.什么是MVVM?
答:MVVM是Model-View-ViewModel的缩写。MVVM是一种设计思想。Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
2.Vue全家桶?
答:ue(整体架构) + vuex(状态管理) + vue-router(路由) + vue_resource || axios(ajax请求) + mint-UI(移动端UI框架库) || antd-vue(PC端UI框架库)
3.Vue的生命周期?
答:1..beforeCreate --创建前,触发的行为:vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。在此阶段可以做的事情:加loading事件。
2.created --创建后,触发的行为:vue实例的数据对象data有了,$el还没有在此阶段可以做的事情:解决loading,请求ajax数据为mounted渲染做准备。
3.beforeMount --渲染前,触发的行为:vue实例的$el和data都初始化了,但还是虚拟的dom节点,具体的data.filter还未替换在此阶段可以做的事情。
4.mounted --渲染后,触发的行为:vue实例挂载完成,data.filter成功渲染在此阶段可以做的事情:配合路由钩子使用。
5.beforeUpdate --更新前,触发的行为:data更新时触发,在此阶段可以做的事情。
6.updated —更新后,触发的行为:data更新时触发,在此阶段可以做的事情:数据更新时,做一些处理(此处也可以用watch进行观测)。
7.beforeDestroy —销毁前,触发的行为:组件销毁时触发,在此阶段可以做的事情:可向用户询问是否销毁。
8.destroyed —销毁后触发的行为:组件销毁时触发,vue实例解除了事件监听以及和dom的绑定(无响应了),但DOM节点依旧存在,在此阶段可以做的事情:组件销毁时进行提示
4.active-class是哪个组件的属性?有何作用?
答:属于vue-router的样式方法,当routerlink标签被点击时将会应用这个样式。
5.vue的优点是什么?
答:低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
6.v-model的实现原理?
答:1.需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。
2.compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。
3.Watcher订阅者是Observer和Compile之间通信的桥梁,在自身实例化时往属性订阅器(dep)里面添加自己,自身必须有一个update()方法,待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
4.MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
总之:vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
7.请说出v-if和v-show的区别。
答:v-show指令是通过修改元素的display的CSS属性让其显示或者隐藏。
v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果。
8.vue-router有哪几种导航钩子?
答:三种,一种是全局导航钩子:router.beforeEach(to,from,next),作用:跳转前进行判断拦截。
第二种:组件内的钩子;
第三种:单独路由独享组件
9.指令v-el的作用是什么?
答:提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标.可以是 CSS 选择器,也可以是一个 HTMLElement 实例。
10.请说出几种vue当中的指令和它的用法?
答:v-if 判断是否隐藏,添加或删除元素;v-show 控制的隐藏出现,控制css的显示隐藏;v-for 进行列表和表格的渲染; v-bind 绑定属性,使用:替代 有三个修饰符:sync,once,camel将绑定的特性的名称转化为驼峰法则命名;v-model 双向数据绑定;v-html 获取html标签;v-text不解释标签;v-html解释标签;
11.vue-loader是什么?使用它的用途有哪些?
答:解析.vue文件的一个加载器,跟template/js/style转换成js模块。用途:js可以写es6、style样式可以scss或less、template可以加jade等
12.请说出vue.cli项目中src目录每个文件夹和文件的用法?
答:assets文件夹是放静态资源;components是放组件;router是定义路由相关的配置;view视图;app.vue是一个应用主组件;main.js是入口文件。
13.第一次加载页面会触发哪几个钩子?
答:会触发beforeCrate(创建前)、created(创建后)、beforeMount(载入前)、mounted(载入后)。
14.created和mounted的区别?
答:created是在模板渲染成htnl前调用的,即通常初始化某些值,然后渲染成视图;mounted是在模板渲染成html后调用,通常是初始化页面之后,再对html的dom节点进行一些需要的操作。
15.vue-router是什么?它有哪些组件?
答:vue用来写路由的一个插件。router-link、router-view。
16.为什么使用key?
答:当有相同标签名的元素切换时,需要通过 key 特性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。
17.VNode是什么?虚拟 DOM是什么?
答:Vue在 页面上渲染的节点,及其子节点称为“虚拟节点 (Virtual Node)”,简写为“VNode”。“虚拟 DOM”是由 Vue 组件树建立起来的整个 VNode 树的称呼。
18.vuex是什么?怎么使用?哪种功能场景使用它?
答:Vue框架中状态管理。在main.js引入store,注入。新建一个目录store,….. export 。场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车等。
19.vuex有哪几种属性?
答:State、 Getter、Mutation 、Action、 Module。
vuex的State特性:
Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于一般Vue对象里面的data
state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
它通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中
vuex的Getter特性:
getters 可以对State进行计算操作,它就是Store的计算属性
虽然在组件内也可以做计算属性,但是getters 可以在多组件之间复用
如果一个状态只在一个组件内使用,是可以不用getters
vuex的Mutation特性
Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作。
20.vue中$router和$route的区别?
答:1、$route是一个对象:可以获取当前页面的路由的路径query、params、meta等参数;
2、$router是VueRouter的一个实例对象:在options中可以获取路由的routes配置参数,使用this.$router可以不在每个独立需要封装路由的组件中都导入路由。
21.vue-router的两种模式的区别?
答:1.hash模式:hash模式背后的原理是onhashchange
事件,可以在window
对象上监听这个事件:
window.onhashchange = function(event){ console.log(event.oldURL, event.newURL); let hash = location.hash.slice(1); document.body.style.color = hash; }
因为hash发生变化的url都会被浏览器记录下来,从而你会发现浏览器的前进后退都可以用了,同时点击后退时,页面字体颜色也会发生变化。
2.history路由:随着history api的到来,前端路由开始进化了,前面的hashchange,你只能改变#后面的url片段,而history api则给了前端完全的自由切换路由,
切换历史状态,包括back
,forward
,go
三个方法。
history.go(-2);//后退两次 history.go(2);//前进两次 history.back(); //后退 hsitory.forward(); //前进
修改历史状态:
history.pushState({color:'red'}, 'red', 'red'}) window.onpopstate = function(event){ console.log(event.state) if(event.state && event.state.color === 'red'){ document.body.style.color = 'red'; } } history.back(); history.forward();
通过pushstate把页面的状态保存在state对象中,当页面的url再变回这个url时,可以通过event.state取到这个state对象,从而可以对页面状态进行还原,这里的页面状态就是页面字体颜色,其实滚动条的位置,阅读进度,组件的开关的这些页面状态都可以存储到state的里面。
通过history api,我们丢掉了丑陋的#,但是它也有个毛病:不怕前进,不怕后退,就怕刷新,f5,(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的,不玩虚的。
在hash模式下,前端路由修改的是#中的信息,而浏览器请求时是不带它玩的,所以没有问题.但是在history下,你可以自由的修改path,当刷新时,如果服务器中没有相应的响应或者资源,会跳转到404页面。
22.vue组件中data为什么必须是一个函数?
答:组件中的data
写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data
,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data
,就会造成一个变了全都会变的结果。
23.Vue组件中写name选项有除了搭配keep-alive还有其他作用么?你能谈谈你对keep-alive了解么?
答:1.组件中写name选项有什么作用:
1.1.项目中使用keep-alive时,可搭配name进行过滤。
1.2.DOM做递归组件时需要调用自身的name。
1.3.vue-devtools 调试工具里显示组件名称是由vue文件中的name值决定的。
2.keep-alive的使用
2.1.keep-alive 是vue内置的一个组件,可以使被包含的组件保留状态,避免重新渲染。
2.2.一般结合路由和动态组件一起使用,用于缓存组件。
2.3.提供include 和 exclude 属性,两者都支持字符串或正则表达式, include表示只有名称匹配的组件会被缓存,exclude表示任何名称匹配的组件都不会被缓存,其中exclude的优先级比exclude高。
2.4.对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。
24.Vue 为什么要用 vm.$set() 解决对象新增属性不能响应的问题?
答:1.Vue使用了Object.defineProperty实现双向数据绑定
2.在初始化实例时对属性执行 getter/setter 转化
3.属性必须在data对象上存在才能让Vue将它转换为响应式的(这也就造成了Vue无法检测到对象属性的添加或删除)
所以Vue提供了Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value);
webpack面试题
1.谈谈你对webpack的看法
答:webpack是一个模块打包工具,可以使用它管理的项目中的模块依赖,并编译输出模块所需的静态页面。它可以很好的管理、打包开发中所用HTML、css、javascript和静态文件等,让开发更有效率。对于不同类型的依赖,webpack有对应的加载器,而且会分析模块间的依赖关系,最后合并生成优化的静态资源。
2.webpack的基本功能和工作原理?
答:1.代码转换:Typescript编译成javascript、scss编译成css等;
2.文件优化:压缩Javascript、css、html代码,压缩合并图片等;
3.代码分割:提取多个页面的公共代码,提取首屏不需要执行部分的代码让其进行异步加载;
4.代码合并:在采用模块的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件;
5.自动刷新:监听本地代码的变化,自动构建刷新浏览器;
6.代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过;
7.自动发布:更新代码后,自动构建出线上发布代码并传输给发布系统;
3.webpack构建过程?
答:1.从entry里配置的module开始递归解析entry依赖的所有module;
2.每找到一个module就会根据配置的loader去找出对应的转换规则;
3.对module进行转换后,再解释出当前的module依赖的module;
4.这些模块会以entry为单位分组,一个entry和其所有依赖的module被分到一个组chunk;
5.最后webpack会把所有的chunk转换成文件输出;
6.在整个流程中webpack会在恰当的时机执行plugin里定义的逻辑;
4.与webpack类似的工具还有哪些?谈谈你为什么最终选择(或放弃)使用webpack?
.答:同样基于入口的打包工具还有几个主流的:webpack、rollup、parcel
从应用场景看:
1.webpack适用于大型复杂的前端站点构建;
2.rollup适用于基础库的打包,如vue、react;
3.parcel适用于简单的实验性项目,他可以满足低门槛的快速看到效果。由于parcel在打包过程中给出的调试信息十分有限,所以一旦打包出错难以调试,所以不建议复杂的项目使用parcel。
5.什么是loader?什么是plugin?
答:loader:模块转换器,用于把模块原内容按照需求转换成新内容;通过使用不同的Loader,Webpack可以要把不同的文件都转成JS文件,比如CSS、ES6/7、JSX等;
plugin:扩展插件,在webpack构建流程中的特定时间注入扩展逻辑来改变构建结果或做你想要的事情;一个是插件含有apply方法的一个方法,通过这个方法可以参与到整个webpack打包流程;
loader是用来对模块的源代码进行转换,而插件目的在于解决 loader 无法实现的其他事
因为plugin可以在任何阶段调用,能够跨Loader进一步加工Loader的输出;
6.有哪些常见的Loader?
答:1.file-loader:把文件输出到一个文件夹中,在代码中通过相对的url去引用输出的文件;
2.url-loader:和file-loader类似,但是能在文件很小的情况下以base64的放肆将文件内容注入到代码中;
3.image-loader:加载并压缩图片;
4.babel-loader:把ES6转化成ES5;
5.css-loader:加载css,支持模块化、压缩、文件等特性;
6.style-loader:把css代码注入到javascript中,通过DOM操作去加载CSS;
7.eslint-loader:检查javascript;
8.source-map-loader:加载额外的Source Map文件,以方便断点调试;
7.分别介绍bundle,chunk,module是什么?
答:bundle:是由webpack打包出来的文件;
chunk:代码块,一个chunk是由多个组合围城,用于代码的合并和分割;
module:是开发中的单个模块,一个模块对应一个文件,webpack会从配置中的entry中递归找出所以依赖的模块;
8.webpack热更新是如何做到的?
答:webpack的热更新又称热替换(Hot Module Replacement),缩写为HMR。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
首先要知道server端和client端都做了哪些处理工作
1.webpack的watch模式下,文件系统中某一个文件发生修改,webpack监听到文件的变化,根据配置文件对模块重新进行打包,并将打包后的代码通过简单的javascript对象保存在内存中。
2.webpack-dev-server和webpack之间的接口交互,而在这一步,主要是dev-server的中间件webpack-dev-middleware和webpack之间的交互,webpack-dev-middleware调用webpack暴露的API对代码变化进行监控,并且告诉webpack,将代码打包到内存中。
3.对webpack-dev-server对文件变化的一个监控,不同于第一步,并不是监控代码变化重新打包。当我们在配置文件中配置了devServer。watchContentBase为true的时候,Server会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器对应用进行live reload。这里是浏览器的刷新,和HMR是两个概念。
4.通过sockjs(webpack-dev-server的依赖)在浏览器和服务端建立一个websocket长连接,将webpack编译打包的各个阶段的状态信息告知浏览器,同时也包括上一步中Server监听静态文件变化的信息。浏览器根据这些socket消息进行不同的操作,服务器传递的最主要信息还是新模块的hash值,后面的步骤是根据hash值来进行模块热替换。
5.webpack-dev-server/client端并不能够请求更新的代码,也不会执行热更模块操作,而是把这些工作又交给了webpack,webpack/hot/dev-serve的工作就是根据webpack-dev-server/client传给它的信息以及dev-server的配置决定是刷新浏览器还是进行模块热更新,如果是刷新浏览器就没有后面的步骤了。
6.HotModuleReplacement.runtime是客户端HMR的中枢,它接受上一步传递给他新模块的hash值,通过JsopMainTemplate.runtime向server端发送ajax请求,服务端返回一个json,该json包含了多月需要更新的模块的hash值,获取到更新列表后,该米快再次通过jsonp请求,获取到最新的模块代码。
7.下图中第10步市决定HMR成功的关键,HotModulePlugin将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。
8.最后,当HMR失败后,回退到live reload操作,也就是进行浏览器刷新来获取最新打包代码。
9.如何利用webpack来优化前端性能?
答:1.压缩代码。删除多余的代码、注释、简化代码的写法等等,可以利用webpack的UglifyJsPlugin和ParalleUglifyPlugin来压缩js文件,利用css-loader来压缩css;
2.利用cdn加速,在构建过程找中,将引用的静态资源修改为cdn对应的路径。可以利用webpack对于output和各loader的publicPath参数来修改资源路径;
3.删除死代码,将无用的代码删除,可以通过启动webpack时追加参数--optimize-minimize来实现。
4.提取公共代码,压缩文件图片等。
其他面试题:
1.HTML语义化
答:让开发者阅读和写出更优雅的代码,让浏览器的爬虫和机器很好的解析。
如:header、footer、nav、aside、hgroup等
2.css3新特性
答:过度(transition)、动画(animation)、选择器、阴影、边框、背景、渐变、弹性布局等等
3.重绘和回流
答:重绘:当元素的一部分属性发生变化,如外观背景色不会引起布局变化而需要重新渲染的过程叫做重绘(改变样式)。
注意:回流必将引起重绘,而重绘不一定会引起回流
回流:当render树中的一部分或者全部因为大小边距等问题发生改变而需要重建的过程叫做回流(改变大小)。
重绘发生条件:元素的属性或者样式发生变化。
回流发生条件:
1.添加或者删除可见的DOM元素;
2.元素位置改变;
3.元素尺寸改变——边距、填充、边框、宽度和高度
4.内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;
5.页面渲染初始化;
6.浏览器窗口尺寸改变——resize事件发生时;
小结:
后续会有持续更新...