前端面试题一
1. Vue 的双向数据绑定原理是什么?
Vue 采用数据劫持+订阅发布模式实现双向绑定。通过 Object.defineProperty()方法来为组件中 data 的每个属性添加 get 和 set 方法,在数据变动时,触发 set 里相应的监听回调函数,将变动信息发布给订阅者。主要有以下步骤:
- 组件初始化时:
- 创建一个dep 对象作为观察者(依赖收集、订阅发布的载体);
- 通过Object.defineProperty()方法对 data 中的属性及子属性对象的属性,添加 getter 和 setter 方法; 调用 getter 时,便去 dep 里注册函数。调用 setter 时,便去通知执行刚刚注册的函数。
- 组件挂载时:
- compile解析模板指令,将其中的变量替换成数据。然后初始化渲染页面视图,并将每个指令对应的节点绑定上更新函数、监听函数。后续一旦数据发生变化,便会更新页面。页面发生变化时也会相应发布变动信息;
- 组件同时会定义一个watcher 类作为订阅者,watcher 可以视作 dep 和组件之间的桥梁。其在实例化时会向 dep 中添加自己,同时自身又有一个 update 方法,待收到 dep 的变动通知时,便会调用自己的 update 方法,触发 compile 中的相应函数完成更新。
2. 组件通讯
1. 父传子
父传子利用props
2. 子传父
- 利用on和emit 即子组件利用一个事件来触发$emit来传递出去一个事件及参数,然后在父组件里绑定这个事件,然后处理这个事件,获取传递的参数。
- 子组件改变父组件传递的props(你会发现通过props可传递复杂类型数据,可以通过子组件改变数据内容,不推荐使用,因为vue是规定props是单向绑定)。
3. 兄弟通信
创建一个空的vue实例,然后挂载到当前的vue实例的原型上,然后一个组件进行emit 传 递 事 件 以 及 需 要 传 递 的 数 据 。 在 另 一 个 组 件 那 里 就 可 以 进 行 使 用 emit传递事件以及需要传递的数据。在另一个组件那里就可以进行使用emit传递事件以及需要传递的数据。在另一个组件那里就可以进行使用on来接受这个事件并处理这个传递参数
3. vue中v-if和v-for那个优先级高?
1、v-for优先于v-if
2、如果同时出现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,浪费了性能
3、要避免出现这种情况,则再外层嵌套template,再这一层进行v-if判断,然后在内部进行v-for循环
4. $nextTick 是什么?
Vue 实现响应式并不是在数据发生后立即更新 DOM,使用 vm.$nextTick 是在下次 DOM 更新循环结束之后立即执行延迟回调。在修改数据之后使用,则可以在回调中获取更新后的 DOM。
5. v-for 中 key 的作用是什么?
key 是 Vue 使用 v-for 渲染列表时的节点标识。使用了 key 之后,当列表项发生变化时,Vue 会基于 key 的变化而重新排列元素顺序,并且移除 key 不存在的元素,提升运行效率。
6. 如何动态更新对象或数组的值?
如何动态更新对象或数组的值?因为 Object.defineProperty()的限制,Vue 无法监听到对象或数组内部某个属性值的变化,因此在直接设置以上两类数据的值时,页面不会实时更新。此时可以通过 this.$set 方法来解决:
this.$set(要改变的数组/对象,要改变的位置/key,要改成的value) this.$set(this.arr, 0, "OBKoro1"); // 改变数组 this.$set(this.obj, "c", "OBKoro1"); // 改变对象 数组原生方法造成的数据更新,可以被 Vue 监听到。如 splice()push()pop()等。
7. Vue-router 路由有哪些模式?
1. hash 模式:后面的 hash 值的变化,浏览器既不会向服务器发出请求,浏览器也不会刷新,每次 hash 值的变化会触发 hashchange 事件。
2. history 模式:利用了 HTML5 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
8. 宏任务和微任务
宏任务
分类:setTimeout、setInterval、 requestAnimationFrame
特点:
1. 宏任务所处的队列即为宏任务队列
2. 第一个宏任务队列只有一个任务,执行主线程中的js代码
3. 宏任务队列可以有多个
4. 当宏任务队列中的任务执行完毕之后,会查看是否有微任务队列,有则执行微任务 队列中的任务,没有则查看是否有宏任务队列
微任务
分类:Promise的回调函数、 process.nextTick
特点:
1. 微任务所处的队列即为微任务队列
2. 只有一个微任务队列
3. 在上一个宏任务队列执行完毕之后如果有微任务队列就会执行微任务队列中的所有任务
9. 什么是防抖和节流?有什么区别?如何实现?
防抖
场景:在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。这些需求都可以通过函数防抖动来实现。尤其是第一个需求,如果在频繁的事件回调中做复杂计算,很有可能导致页面卡顿,不如将多次计算合并为一次计算,只在一个精确点做操作。
一般的防抖会有immediate选项,表示是否立即调用。
1. 对于按钮防点击来说的实现:如果函数是立即执行的,就立即调用,如果函数是延迟执行的,就缓存上下文和参数,放到延迟函数中去执行。一旦我开始一个定时器,只要我定时器还在,你每次点击我都重新计时。一旦你点累了,定时器时间到,定时器重置为 null,就可以再次点击了。
2.对于延时执行函数来说的实现:清除定时器ID,如果是延迟调用就调用函数节流
防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间行。
高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。
思路:每次触发事件时都判断当前是否有等待执行的延时函数。
10. webpack的使用
webpack是一个打包web项目的工具 ,可以实现css,js,less,cass,html的混淆加密,minify,结合webpack-dev-server热部署,非常方便前端页面和Nodejs的开发。
webpack安装方法
npm install webpack --save-dev
npm install webpack-dev-server --save-dev执行webpack需要在项目目录有一个配置文件webpack.config.js。
const path = require('path')
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "main.js",
publicPath: "dist/"
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', "css-loader"],
},
],
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: 'img/[name].[hash:8].[ext]'
},
},
],
},
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
},
],
},
resolve: {
alias: {
"vue$": "vue/dist/vue.esm.js",
}
}
}
上面的 entry 表示入口文件,webpack会自动关联出此js文件引用的其他js文件。可以设置为一个数组,表示多个入口。
自己写的代码打包存放的位置在output中指定了相对路径./bin/app.bundle.js。
vendor 指出其中使用的第三方js,
便于下方plugins使用CommonsChunkPlugin把自己编写的代码和第三方代码分开,此插件构造函数中指定了是哪个vendor,以及处理后的保存位置。
modules 中的配置的loaders用于执行顺序从右到左,类似管道依次处理test参数匹配到的js文件、css文件。
webpack好用的的参数
-p 或者 --optimize-minimize 实现去空格压缩
-d 生成js.map文件,便于对应源码位置
-- hot 启用热部署,不用刷新网页
--watch 观察文件变化自动重新webpack ,启动webpack-dev-server时不使用这个参数也可以自动触发webpack
11. new做了什么?
1. 创建一个空对象。(var obj = new Object())
2. 将空对象的原型赋值给构造函数的原型。(Person.prototype = obj.proto)
3. 执行构造函数的代码,为空对象添加属性(Person.call(obj)),也可以理解为将构造函数内部的this指针指向新建的空对象。
- 返回添加后的对象。
12. 箭头函数中this指向问题
“箭头函数”的this
,总是指向定义时所在的对象,而不是运行时所在的对象。
bind方法不会立即执行,返回一个改变了上下文的this后的函数, 便于稍后调用; apply, call则是立即执行。
除此外, 在 ES6 的箭头函数下, call 和 apply 将失效, 对于箭头函数来说:
箭头函数体里面的 this 对象, 是定义时所在的对象,
箭头函数不能用作构造函数,也就是说不能使用 new 命令
箭头函数不可以使用 arguments 对象