生命周期函数面试题
1.什么是 vue 生命周期;2.vue生命周期的作用是什么
vue中或箭头函数中,this的指向问题:
首先,在Vue所有的生命周期钩子方法(如beforeCreate,created,beforeMount,mounted,beforeUpdate, updated,beforeDestroy以及destroyed)里使用this,this指向调用它的Vue实例,即(new Vue)。
其次,箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象。
Vue每个组件都是独立的,每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁,这就是一个组件所谓的生命周期。在组件中具体的方法有:
data () {
return {
rendered: false,
}
(1)beforeCreate
在实例初始化之后,这个时候数据还没有挂载,只是一个空壳,无法访问数据和真实的DOM 一般不做操作
console.log(this.rendered); // undefined
(2)created
实例创建完成之后被调用,挂载数据 绑定事件 。 这个时候已经可以使用数据了,也可以更改数据,在这里更改数据不会出发updated,不会触发其他钩子函数,一般可以做初始化数据的获取
console.log(this.$el);//undefined
console.log(this.rendered); // false
(3)beforeMount
在挂载开始之前被调用,这个时候虚拟DOM已经创建完成,马上就要渲染,这里可以更改数据 ,不会触发updated,渲染前最后一个更改数据的机会,不会触发其他钩子函数,一般可以在这里做初始化数据的获取
console.log(this.$el);//undefined
(4)mounted
挂载到实例 渲染出真实的DOM,数据真实DOM都处理好了 ,事件已经挂载好了,可以在这里操作真实DOM
console.log(this.$el);
(5)beforeUpdate
数据更新时调用,发生在虚拟DOM重新渲染和补丁之前,当组件或实例的数据更改之后,会立即执行beforeUpdate,然后vue的虚拟dom机制会重新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比之后重新渲染,一般不做什么事儿
(6) updated
页面显示的数据和data中的数据已经保持同步了,都是最新的
(7) beforeDestroy
实例销毁之前调用,般在这里做一些善后工作,例如清除计时器、清除非指令绑定的事件等等
console.log(this.rendered);//false
console.log(this.$el);
(8) destroyed
实例销毁之后调用,组件的数据绑定、监听...去掉后只剩下dom空壳,这个时候,执行destroyed,在这里做善后工作也可以
console.log(this.$el);
console.log(this.rendered);
vue中内置的方法 属性和vue生命周期的运行顺序(methods、computed、data、watch、props)
3.第一次页面加载会触发哪几个钩子
第一次加载会触发 beforeCreate、created、beforeMount、mounted
4.简述每个周期具体适合哪些场景
beforecreate : 可以在这加个loading事件,在加载实例时触发
created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
mounted : 挂载元素,获取到DOM节点
updated : 如果对数据统一处理,在这里写上相应函数
beforeDestroy : 可以做一个确认停止事件的确认框 nextTick : 更新数据后立即操作dom
5.created和mounted的区别
created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。
6.vue获取数据在哪个周期函数
created异步获取数据
一般 created / beforeMount / mounted 皆可。
比如如果你要操作 DOM ,那肯定是 mounted 时候才能操作。
7.请详细说下你对vue生命周期的理解?
创建前/后(beforeCreate / created):在beforeCreated阶段,vue实例的挂载元素 $el和 数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,$el还没有。
载入前/后(beforeMount / mounted):在beforeMount阶段,vue实例的 $el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message 还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
更新前/后(beforeUpdate / updated):当data变化时,会触发beforeUpdate和updated方法。
销毁前/后():在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。
vue路由面试题
1.mvvm 框架是什么?
前端页面中使用MVVM的思想,即MVVM是整个视图层view的概念,属于视图层的概念。
MVVM是Model-View-ViewModel的简写。即模型-视图-视图模型。
模型指的是后端传递的数据。
视图指的是所看到的页面。
视图模型是mvvm模式的核心,它是连接view和model的桥梁。
它有两个数据传递方向:
一是将模型转化成视图,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。
二是将视图转化成模型,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。
以上两个方向都实现的,我们称之为数据的双向绑定。
2.vue-router 是什么?它有哪些组件
路由中有三个基本的概念 route, routes, router。
1, route,它是一条路由,由这个英文单词也可以看出来,它是单数, Home按钮 => home内容, 这是一条route, about按钮 => about 内容, 这是另一条路由。
2, routes 是一组路由,把上面的每一条路由组合起来,形成一个数组。[{home 按钮 =>home内容 }, { about按钮 => about 内容}]
3, router 是一个机制,相当于一个管理者,它来管理路由。因为routes 只是定义了一组路由,它放在哪里是静止的,当真正来了请求,怎么办? 就是当用户点击home 按钮的时候,怎么办?这时router 就起作用了,它到routes 中去查找,去找到对应的 home 内容,所以页面中就显示了 home 内容。
4,客户端中的路由,实际上就是dom 元素的显示和隐藏。当页面中显示home 内容的时候,about 中的内容全部隐藏,反之也是一样。客户端路由有两种实现方式:基于hash 和基于html5 history api.
(1)页面实现(html模版中)
在vue-router中, 我们看到它定义了两个标签<router-link> 和<router-view>来对应点击和显示部分。<router-link> 就是定义页面中点击的部分,<router-view> 定义显示部分,就是点击后,区配的内容显示在什么地方。所以 <router-link> 还有一个非常重要的属性 to,定义点击之后,要到哪里去, 如:<router-link to="/home">Home</router-link>
(2) js 中配置路由
首先要定义route, 一条路由的实现。它是一个对象,由两个部分组成: path和component. path 指路径,component 指的是组件。如:{path:’/home’, component: home}
3.active-class 是哪个组件的属性?
active-class是vue-router模块的router-link组件中的属性,用来做选中样式的切换
4.怎么定义 vue-router 的动态路由? 怎么获取传过来的值
可以通过query ,param两种方式
vue-router传递参数分为两大类
- 编程式的导航 router.push
- 声明式的导航 <router-link>
(1)编程式的导航
router.push
想要传递参数主要就是以对象的方式来写,分为两种方式:命名路由、查询参数,下面分别说明两种方式的用法和注意事项。
命名路由
特别注意:命名路由这种方式传递的参数,如果在目标页面刷新是会出错的
this.$router.push({ name: 'news', params: { userId: 123 }})
this.$route.params.userId
查询参数
注意:和name配对的是params\query,和path配对的只能是query
this.$router.push({ path: '/news', query: { userId: 123 }});
this.$route.query.userId
注意:通常我们在两个页面传数据时,一般会采用params,query,或者将数据用vuex,localStorage,sessionStorage存储起来,然后方便其他页面调用数据,但是params和vuex有一个不好的地方就是只要我们在接受数据的页面刷新一下,传过来的数据就会丢失,怎么解决这个问题,只需将params改为query即可
router.replace
跟 router.push
很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
声明式 | 编程式 |
---|---|
<router-link :to="..." replace> | router.replace(...) |
router.go(n)
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)
。
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
你也许注意到 router.push
、 router.replace
和 router.go
跟 window.history.pushState、 window.history.replaceState 和 window.history.go好像, 实际上它们确实是效仿 window.history
API 的。
(2)声明式的导航
字符串:<router-link to="news">click to news page</router-link>
命名路由:<router-link :to="{ name: 'news', params: { userId: 1111}}">click to news page</router-link>
查询参数:<router-link :to="{ path: '/news', query: { userId: 1111}}">click to news page</router-link>或<router-link :to="{ path: '/news', params: { userId: 1111}}">click to news page</router-link>
1.命名路由搭配params\query,刷新页面参数会丢失
2.查询参数只能搭配query,刷新页面数据不会丢失
3.接受参数使用this.$router后面就是搭配路由的名称就能获取到参数的值
5.vue-router 有哪几种导航钩子?
1、全局守卫: router.beforeEach
2、全局解析守卫: router.beforeResolve
3、全局后置钩子: router.afterEach
4、路由独享的守卫: beforeEnter
5、组件内的守卫: beforeRouteEnter、beforeRouteUpdate (2.2 新增)、beforeRouteLeave
导航
表示路由正在发生改变,vue-router 提供的导航守卫主要用来:
通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的
, 单个路由独享的
, 或者组件级的
。
注意:
参数或查询的改变并不会触发进入/离开的导航守卫。 你可以通过 观察 $route 对象 来应对这些变化,或使用 beforeRouteUpdate
的组件内守卫
1、全局守卫:
使用 router.beforeEach
注册一个全局前置守卫:
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
当一个导航触发时,全局前置守卫
按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前
一直处于等待中
。
每个守卫方法接收三个参数:
to: Route
: 即将要进入的目标 路由对象
from: Route
: 当前导航正要离开的路由
next: Function
: 一定要调用该方法来resolve
这个钩子。执行效果依赖 next 方法的调用
参数。
-
next()
: 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是confirmed
(确认的)。 -
next(false)
: 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由
对应的地址。 -
next('/') 或者 next({ path: '/' })
: 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向next 传递任意位置对象
,且允许设置诸如
replace: true、name: 'home' 之类的选项以及任何用在router-link
的to prop
或router.push
中的选项。 -
next(error)
: (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止
且该错误会被传递给router.onError()
注册过的回调。
确保要调用 next
方法,否则
钩子就不会被 resolved
。
2、全局解析守卫:
在 2.5.0+
你可以用 router.beforeResolve
注册一个全局守卫
。这和 router.beforeEach
类似,区别是:
在导航被确认之前
,同时在所有组件内守卫
和异步路由组件
被解析之后
,解析守卫就被调用
。
3、全局后置钩子
你也可以注册全局后置钩子
,然而和守卫不同的是
,这些钩子不会接受 next 函数
也不会改变导航本身
:
router.afterEach((to, from) => {
// ...
})
4、路由独享的守卫
你可以在路由配置上直接定义 beforeEnter
守卫:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
这些守卫与全局前置守卫
的方法参数是一样的
。
5、组件内的守卫
最后,你可以在路由组件内
直接定义以下路由导航守卫
:
beforeRouteEnter
beforeRouteUpdate
(2.2 新增)
beforeRouteLeave
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
//不过,你可以通过传一个回调给 next来访问组件实例。
//在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
注意:beforeRouteEnter
是支持给next 传递回调
的唯一守卫。对于beforeRouteUpdate
和 beforeRouteLeave
来说,this
已经可用了,所以不支持传递回调
,因为没有必要了:
beforeRouteUpdate (to, from, next) {
// just use `this`
this.name = to.params.name
next()
}
离开守卫beforeRouteLeave:
通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false)
来取消:
beforeRouteLeave (to, from , next) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}
6.route和router 的区别
1.router是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,他包含了所有的路由包含了许多关键的对象和属性。
举例:history对象
$router.push({path:'home'});本质是向history栈中添加一个路由,在我们看来是 切换路由,但本质是在添加一个history记录
方法:
$router.replace({path:'home'});//替换路由,没有历史记录
2.route是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象,可以获取对应的name,path,params,query等
$route.path
字符串,等于当前路由对象的路径,会被解析为绝对路径,如 "/home/news"
。
$route.params
对象,包含路由中的动态片段和全匹配片段的键值对
$route.query
对象,包含路由中查询参数的键值对。例如,对于 /home/news/detail/01?favorite=yes
,会得到$route.query.favorite == 'yes'
。
$route.router
路由规则所属的路由器(以及其所属的组件)。
$route.matched
数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
$route.name
当前路径的名字,如果没有使用具名路径,则名字为空。
$route.path, $route.params, $route.name, $route.query
这几个属性很容易理解,主要用于接收路由传递的参数
7.vue-router响应路由参数的变化
监测路由参数变化的方法(watch监听|导航守卫)
(1)watch监听
const User = {
template: '...',
watch: {
'$route' (to, from) {
// 对路由变化作出响应...
}
}
}
(2)导航守卫
const User = {
template: '...',
beforeRouteUpdate (to, from, next) {
// react to route changes...
// don't forget to call next()
}
}
8.vue-router传参(详见4)
9.vue-router的两种模式
hash模式和history模式
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来改变页面字体颜色,虽然没什么用,但是一定程度上说明了原理。
更关键的一点是,因为hash发生变化的url都会被浏览器记录下来,从而你会发现浏览器的前进后退都可以用了,同时点击后退时,页面字体颜色也会发生变化。这样一来,尽管浏览器没有请求服务器,但是页面状态和url一一关联起来,后来人们给它起了一个霸气的名字叫前端路由,成为了单页应用标配。
网易云音乐,百度网盘就采用了hash路由,看起来就是这个样子:
http://music.163.com/#/friend
https://pan.baidu.com/disk/home#list/vmode=list
history路由
随着history api的到来,前端路由开始进化了,前面的hashchange,你只能改变#后面的url片段,而history api则给了前端完全的自由
history api可以分为两大部分,切换和修改,参考MDN
切换历史状态
包括back
,forward
,go
三个方法,对应浏览器的前进,后退,跳转操作,有同学说了,(谷歌)浏览器只有前进和后退,没有跳转,嗯,在前进后退上长按鼠标,会出来所有当前窗口的历史记录,从而可以跳转(也许叫跳更合适):
history.go(-2);//后退两次
history.go(2);//前进两次
history.back(); //后退
hsitory.forward(); //前进
修改历史状态
包括了pushState
,replaceState
两个方法,这两个方法接收三个参数:stateObj,title,url
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模式怕啥
通过history api,我们丢掉了丑陋的#,但是它也有个毛病:
不怕前进,不怕后退,就怕刷新,f5,(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的,不玩虚的。
在hash模式下,前端路由修改的是#中的信息,而浏览器请求时是不带它玩的,所以没有问题.但是在history下,你可以自由的修改path,当刷新时,如果服务器中没有相应的响应或者资源,会分分钟刷出一个404来
10.vue-router实现路由懒加载( 动态加载路由 )
1 . vue异步组件技术 ==== 异步加载
vue-router配置路由 , 使用vue的异步组件技术 , 可以实现按需加载 .
但是,这种情况下一个组件生成一个js文件
结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。
第一步:定义一个能够被 Webpack 自动代码分割的异步组件。
- // 在src/router/index.js里面引入异步引入组件const index = () => import('../page/list/index.vue')
第二步:在路由配置中什么都不需要改变,只需要像往常一样使用 index。
- const router = new VueRouter({ routes: [ { path: '/index', component: index,name:"index" } ]})
第三步:在build/webpack.base.conf.js下的output属性,新增chunkFilename。
output: {
path: config.build.assetsRoot,
filename: '[name].js', //新增chunFilename属性
chunkFilename: '[name].js',
publicPath: process.env.NODE_ENV === 'production' ?
config.build.assetsPublicPath : config.dev.assetsPublicPath
},
非懒加载:
懒加载:
{
path: '/promisedemo',
name: 'PromiseDemo',
component: resolve => require(['../components/PromiseDemo'], resolve)
},
{
path: '/promisedemo1',
name: 'PromiseDemo1',
component: resolve => require(['../components/PromiseDemo1'], resolve)
}
2. es提案的import()
const 组件名=() => import('组件路径');
// 下面2行代码,没有指定webpackChunkName,每个组件打包成一个js文件。
const ImportFuncDemo1 = () => import('../components/ImportFuncDemo1')
const ImportFuncDemo2 = () => import('../components/ImportFuncDemo2')
// 下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。
// const ImportFuncDemo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo')
// const ImportFuncDemo2 = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo2')
export default new Router({
routes: [
{
path: '/importfuncdemo1',
name: 'ImportFuncDemo1',
component: ImportFuncDemo1
},
{
path: '/importfuncdemo2',
name: 'ImportFuncDemo2',
component: ImportFuncDemo2
}
]
})
3.webpack提供的require.ensure()
vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。
这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。
{
path: '/promisedemo',
name: 'PromiseDemo',
component: r => require.ensure([], () => r(require('../components/PromiseDemo')), 'demo')
},
{
path: '/hello',
name: 'Hello',
// component: Hello
component: r => require.ensure([], () => r(require('../components/Hello')), 'demo')
}
vue常见面试题
1.vue优点
2.vue父组件向子组件传递数据?prop
3.子组件像父组件传递事件
1.子组件需要使用this.$emit 将子组件的事件进行上级传递。
this.$emit('clickHeadAction',this.titleRight)
2. 父组件需要使用@事件名的方式,接收子组件的事件。
<div class="message">
<ActivityHead :titleLeft="msgLeft" :titleRight="msgRight" @clickHeadAction="clickChild"></ActivityHead>
</div>
methods: {
clickChild(msg){
console.log(msg);
}
}
4.v-show和v-if指令的共同点和不同点
1.共同点:都能控制元素的显示和隐藏。
2.不同点:实现本质⽅法不同,v-show本质就是通过控制css中的display设置为none,控制隐藏,只会编译⼀次;v-if是动态的向DOM树内添加或者删除DOM元素,若初始值为false,就不会编译了。⽽且v-if不停的销毁和创建⽐较消耗性能。
3.总结:如果要频繁切换某节点,使⽤v-show(切换开销⽐较⼩,初始开销较⼤)。如果不需要频繁切换某节点使⽤v-if(初始渲染开销较⼩,切换开销⽐较⼤)。
5.如何让CSS只在当前组件中起作用
当前组件<style>写成<style scoped>
6.<keep-alive></keep-alive>
的作用是什么?
<keep-alive></keep-alive>
包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。
大白话: 比如有一个列表和一个详情,那么用户就会经常执行打开详情=>返回列表=>打开详情…这样的话列表和详情都是一个频率很高的页面,那么就可以对列表组件使用<keep-alive></keep-alive>
进行缓存,这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染
7.如何获取dom
方法一:
直接给相应的元素加id,然后再document.getElementById("id");获取,然后设置相应属性或样式;
方法二:
使用ref,给相应的元素加ref=“name” 然后再this.$refs.name获取到该元素,并且可以直接调用子组件中定义的方法;
注意:
1、在获取相应元素之前,必须在mounted生命周期进行挂载,否则获取到的值为空;
2、如果是给子组件加id并修改自定义属性,则直接会加载该子组件对应的外层div上,并不会改变该子组件原本的自定义属性的值;
3、如果给子组件加ref,然后获取到该DOM元素之后改变相应的自定义属性的值,vue会报错
8.说出几种vue当中的指令和它的用法?
1、v-if:判断是否隐藏
2、v-for:数据循环
3、v-bind:class:绑定一个属性
4、v-model:实现数据双向绑定
5.这里重点说明一个v-if和v-show的区别:
共同点:都是通过判断绑定数据的true/false来展示的
不同点:v-if只有在判断为true的时候才会对数据进行渲染,如果为false代码删除。除非再次进行数据渲染,v-if才会重新判断。可以说是用法比较倾向于对数据一次操作。
v-show是无论判断是什么都会先对数据进行渲染,只是false的时候对节点进行display:none;的操作。所以再不重新渲染数据的情况下,改变数据的值可以使数据展示或隐藏
6.v-once:
7.v-html:
8.v-cloak:
9.vue-loader是什么?使用它的用途有哪些?
vue-loader会解析文件,提取出每个语言块,如果有必要会通过其他loader处理,最后将他们组装成一个commonjs模块;module.exports出一个vue.js组件对象;
1:< temlate>语言块
(1)默认语言:html
(2)每个.vue文件最多包含一个< template>块
(3)内容将被提取为字符串,将编译用作VUE组件的template选项;
2:< script>
(1)默认语言:JS(在监测到babel-loader或者buble-loader配置时,自动支持ES2015)
(2)每个.vue文件最多包含一个< script>块
(3)该脚本在类CommonJS环境中执行(就像通过webpack打包的正常JS模块)。所以你可以require()其他依赖。在ES2015支持下,也可以使用import跟export语法
(4)脚本必须导出Vue.js组件对象,也可以导出由VUE.extend()创建的扩展对象;但是普通对象是更好的选择;
3:< style>
(1)默认语言:css
(2)一个.vue文件可以包含多个< style>标签
(3)这个标签可以有 scoped 或者 module属性来帮助你讲样式封装到当前组件;具有不同封装模式的多个< style>标签可以在同一个组件中混合使用
(4)默认情况下,可以使用style-loader提取内容,并且通过< style>标签动态假如文档的< head>中,也可以配置webpack将所有的styles提取到单个CSS文件中;
4:自定义块
可以在.vue文件中添加额外的自定义块来实现项目的特殊需求;例如< docs>块;vue-loader将会使用标签名来查找对应的webpack loaders来应用到对应的模块上;webpack需要在vue-loader的选项loaders中指定;
vue-loader支持使用非默认语言,比如CSS预处理器,预编译的HTML模板语言,通过设置语言块的lang属性:
vue-loader作用:
解析和转换.vue文件。提取出其中的逻辑代码 script,样式代码style,以及HTML 模板template,再分别把他们交给对应的loader去处理。
用途:
js可以写es6,style样式可以写scss或less、template可以加jade等。
css-loader:加载由vue-loader提取出的CSS代码
vue-template-compiler:把vue-loader提取出的HTML模板编译成可执行的javascript代码
10.为什么使用key
因为vue组件高度复用,增加Key可以标识组件的唯一性,key的作用主要是为了高效的更新虚拟DOM,后续再原理给大家讲解
如何正确使用key:
不推荐:key="index"
推荐 :key="item.id"
最好的办法是使用数组中不会变化的那一项作为key
值,对应到项目中,即每条数据都有一个唯一的id
,来标识这条数据的唯一性;使用id
作为key
值
11.axios及安装
npm
12.axios解决跨域
【vue.config.js】
module.exports = {
devServer: {
proxy: {
'/api': {
// 此处的写法,目的是为了 将 /api 替换成 https://www.baidu.com/
target: 'https://www.baidu.com/',
// 允许跨域
changeOrigin: true,
ws: true,
pathRewrite: {
'^/api': ''
}
}
}
}
}
13.v-modal的使用
<!-- 在每次 input 事件触发后将输入框的值与数据进行同步,添加 lazy 修饰符,从而转变为使用 change 事件进行同步 -->
<input v-model.lazy="msg" >
<!--去除字符串首尾的空格-->
<input v-model.trim="msg">
<!--将数据转化为值类型-->
<input v-model.number="age" type="number">
14.scss的安装以及使用(无)
15.请说出vue.cli项目中src目录每个文件夹和文件的用法?
assets文件夹是放静态资源;
components是放组件;
router是定义路由相关的配置;view视图;
app.vue是一个应用主组件;
main.js是入口文件
相同点:资源在html中使用,都是可以的。
不同点:使用assets下面的资源,在js中使用的话,路径要经过webpack中file-loader编译,路径不能直接写。
assets中的文件会经过webpack打包,重新编译,推荐该方式。而static中的文件,不会经过编译。项目在经过打包后,会生成dist文件夹,static中的文件只是复制一遍而已。简单来说,static中建议放一些外部第三方,自己的放到assets,别人的放到static中。
注意:如果把图片放在assets与static中,html页面可以使用;但在动态绑定中,assets路径的图片会加载失败,因为webpack使用的是commenJS规范,必须使用require才可以,具体代码如下
16.分别简述computed和watch的使用场景
computed:
当一个属性受多个属性影响的时候就需要用到computed
最典型的栗子: 购物车商品结算的时候
watch:
当一条数据影响多条数据的时候就需要用watch
栗子:搜索数据
17.v-on可以监听多个方法吗
v-on可以监听多个方法
18.$nextTick的使用
vue中dom操作后,使用:
this.$nextTick(()=>{
最新dom数据
}),
this.$nextTick()将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。
假设我们更改了某个dom元素内部的文本,而这时候我们想直接打印出这个被改变后的文本是需要dom更新之后才会实现的,也就好比我们将打印输出的代码放在setTimeout(fn, 0)中;
在方法里直接打印的话, 由于dom元素还没有更新, 因此打印出来的还是未改变之前的值,而通过this.$nextTick()获取到的值为dom更新之后的值
19.vue组件中data为什么必须是一个函数
如果data是一个函数的话,这样每复用一次组件,就会返回一份新的data
,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data
,就会造成一个变了全都会变的结果。
所以说vue组件的data必须是函数。这都是因为js的特性带来的,跟vue本身设计无关。
js本身的面向对象编程也是基于原型链和构造函数,应该会注意原型链上添加一般都是一个函数方法而不会去添加一个对象了。
20.vue事件(event)对象的使用
@click="handleOpen"
默认第一个参数传入event对象;@click="handleOpen($event)"
,如果自己需要传入参数和event
对象,则需要使用$event
来获取event
对象并传入handleOpen
- 使用不带圆括号的形式,event 对象将被自动当做实参传入;
- 使用带圆括号的形式,我们需要使用 $event 变量显式传入 event 对象。
21. 组件间的通信
vue 组件间通信的几种方式,如 props、$emit
/$on
、vuex、$parent
/ $children
、$attrs
/$listeners
和 provide/inject
方法一、props
/$emit
父组件 A 通过 props 的方式向子组件 B 传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。
方法二、$emit
/$on
这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案 vuex
1.具体实现方式:
var Event=new Vue();
Event.$emit(事件名,数据);
Event.$on(事件名,data => {});
2.举个例子
假设兄弟组件有三个,分别是 A、B、C 组件,C 组件如何获取 A 或者 B 组件的数据
<div id="itany">
<my-a></my-a>
<my-b></my-b>
<my-c></my-c>
</div>
<template id="a">
<div>
<h3>A组件:{{name}}</h3>
<button @click="send">将数据发送给C组件</button>
</div>
</template>
<template id="b">
<div>
<h3>B组件:{{age}}</h3>
<button @click="send">将数组发送给C组件</button>
</div>
</template>
<template id="c">
<div>
<h3>C组件:{{name}},{{age}}</h3>
</div>
</template>
<script>
var Event = new Vue();//定义一个空的Vue实例
var A = {
template: '#a',
data() {
return {
name: 'tom'
}
},
methods: {
send() {
Event.$emit('data-a', this.name);
}
}
}
var B = {
template: '#b',
data() {
return {
age: 20
}
},
methods: {
send() {
Event.$emit('data-b', this.age);
}
}
}
var C = {
template: '#c',
data() {
return {
name: '',
age: ""
}
},
mounted() {//在模板编译完成后执行
Event.$on('data-a',name => {
this.name = name;//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
})
Event.$on('data-b',age => {
this.age = age;
})
}
}
var vm = new Vue({
el: '#itany',
components: {
'my-a': A,
'my-b': B,
'my-c': C
}
});
</script>
$on
监听了自定义事件 data-a 和 data-b,因为有时不确定何时会触发事件,一般会在 mounted 或 created 钩子中来监听。
方法三、vuex
3. Vuex 与 localStorage
vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在 vuex 里数据改变的时候把数据拷贝一份保存到 localStorage 里面,刷新之后,如果 localStorage 里有保存的数据,取出来再替换 store 里的 state。
let defaultCity = "上海"
try { // 用户关闭了本地存储功能,此时在外层加个try...catch
if (!defaultCity){
defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
}
}catch(e){}
export default new Vuex.Store({
state: {
city: defaultCity
},
mutations: {
changeCity(state, city) {
state.city = city
try {
window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
// 数据改变的时候把数据拷贝一份保存到localStorage里面
} catch (e) {}
}
}
})
这里需要注意的是:由于 vuex 里,我们保存的状态,都是数组,而 localStorage 只支持字符串,所以需要用 JSON 转换:
JSON.stringify(state.subscribeList); // array -> string
JSON.parse(window.localStorage.getItem("subscribeList")); // string -> array
方法四、$attrs
/$listeners
方法五、provide/inject
1. 简介
Vue2.2.0 新增 API,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。一言而蔽之:祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。
provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
2. 举个例子
假设有两个组件: A.vue 和 B.vue,B 是 A 的子组件
// A.vue
export default {
provide: {
name: '浪里行舟'
}
}
// B.vue
export default {
inject: ['name'],
mounted () {
console.log(this.name); // 浪里行舟
}
}
可以看到,在 A.vue 里,我们设置了一个 provide: name,值为 浪里行舟,它的作用就是将 name 这个变量提供给它的所有子组件。而在 B.vue 中,通过 inject
注入了从 A 组件中提供的 name 变量,那么在组件 B 中,就可以直接通过 this.name 访问这个变量了,它的值也是 浪里行舟。这就是 provide / inject API 最核心的用法。
需要注意的是:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的----vue 官方文档
所以,上面 A.vue 的 name 如果改变了,B.vue 的 this.name 是不会改变的,仍然是 浪里行舟。
方法六、$parent
/ $children
与 ref
ref
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例$parent
/$children
:访问父 / 子实例
需要注意的是:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。我们先来看个用 ref
来访问组件的例子
// component-a 子组件
export default {
data () {
return {
title: 'Vue.js'
}
},
methods: {
sayHello () {
window.alert('Hello');
}
}
}
// 父组件
<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.title); // Vue.js
comA.sayHello(); // 弹窗
}
}
</script>
不过,这两种方法的弊端是,无法在跨级或兄弟间通信。
总结
常见使用场景可以分为三类:
- 父子通信:
父向子传递数据是通过 props,子向父是通过 events($emit
);通过父链 / 子链也可以通信($parent
/$children
);ref 也可以访问组件实例;provide / inject API;$attrs/$listeners
- 兄弟通信:
Bus;Vuex - 跨级通信:
Bus;Vuex;provide / inject API、$attrs/$listeners
22.渐进式框架的理解(无)
23.Vue中双向数据绑定是如何实现的(无)
24.单页面应用和多页面应用区别及优缺点(无)
25.vue中过滤器有什么作用及详解
vue 过滤器filter的详解 - Panax - 博客园
1.代码运用的地方
<!-- 在双花括号中 -->
{{ date | formatDate}}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="data | formatDate"></div>
2.场景: 时间格式的转化
3. 注册过滤器函数
首先定义过滤器有两种方式,第一种是全局过滤器,我们可以直接在vue对象上使用filter方法注册过滤器,这种全局注册的过滤器在任何一个组件内都可以使用。第二种则是组件内部的过滤器,注册组件内部过滤器则只能在当前组件内使用,接下来我们使用这两种方式注册过滤器函数。
4. 过滤器可以串联:
{{ message | filterA | filterB }}
5. 接收参数
{{ message | filterA('arg1', arg2) }}
filterA
被定义为接收三个参数的过滤器函数。其中 message
的值作为第一个参数,普通字符串 'arg1'
作为第二个参数,表达式 arg2
的值作为第三个参数。
26.v-if和v-for的优先级
永远不要把 v-if
和 v-for
同时用在同一个元素上。
一般我们在两种常见的情况下会倾向于这样做:
-
为了过滤一个列表中的项目 (比如
v-for="user in users" v-if="user.isActive"
)。在这种情形下,请将users
替换为一个计算属性 (比如activeUsers
),让其返回过滤后的列表。 -
为了避免渲染本应该被隐藏的列表 (比如
v-for="user in users" v-if="shouldShowUsers"
)。这种情形下,请将v-if
移动至容器元素上 (比如ul
,ol
)。
当 Vue 处理指令时,v-for
比 v-if
具有更高的优先级,所以这个模板:
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
将会经过如下运算:
this.users.map(function (user) {
if (user.isActive) {
return user.name
}
})
因此哪怕我们只渲染出一小部分用户的元素,也得在每次重渲染的时候遍历整个列表,不论活跃用户是否发生了变化。
通过将其更换为在如下的一个计算属性上遍历:
computed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
}
}
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
我们将会获得如下好处:
- 过滤后的列表只会在
users
数组发生相关变化时才被重新运算,过滤更高效。 - 使用
v-for="user in activeUsers"
之后,我们在渲染的时候只遍历活跃用户,渲染更高效。 - 解藕渲染层的逻辑,可维护性 (对逻辑的更改和扩展) 更强。
27.assets和static的区别
assets里的文件编译过程中会被webpack处理理解为模块依赖,只支持相对路径的形式。assets放可能会变动的文件;
static里的文件不会被webpack解析,会直接被复制到最终的打包(默认是dist/static)下,必须使用绝对路径引用这些文件。static放不会变动的文件
28.列举常用的指令
v-model、v-for、v-show、v-if、v-hide、v-bind、v-on:click、v-text、v-html、v-bind:class、v-cloa、v-pre:把标签内部的元素原位输出
29.vue常用的修饰符
.lazy
在默认情况下,v-model
在每次 input
事件触发后将输入框的值与数据进行同步 。你可以添加 lazy
修饰符,从而转变为使用 change
事件进行同步:
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg" >
.number
如果想自动将用户的输入值转为数值类型,可以给 v-model
添加 number
修饰符
<input v-model.number="age" type="number">
.trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model
添加 trim
修饰符:
<div id='other'>
<input v-model.trim='trim'>
<p ref='tr'>{{trim}}</p>
<button @click='getStr'>获取</button>
</div>
事件修饰符
在事件处理程序中调用 event.preventDefault()
或 event.stopPropagation()
是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on
提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
注意:
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self
会阻止所有的点击,而 v-on:click.self.prevent
只会阻止对元素自身的点击。
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
按键修饰符
在监听键盘事件时,我们经常需要检查常见的键值。Vue 允许为 v-on
在监听键盘事件时添加按键修饰符:
<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` -->
<input v-on:keyup.13="submit">
记住所有的 keyCode
比较困难,所以 Vue 为最常用的按键提供了别名:
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">
全部的按键别名:
.enter
.tab
.delete
(捕获“删除”和“退格”键).esc
.space
.up
.down
.left
.right
系统修饰键
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
.ctrl
.alt
.shift
.meta
注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。
例如:
<!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
注意:
请注意修饰键与常规按键不同,在和 keyup
事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl
的情况下释放其它按键,才能触发 keyup.ctrl
。而单单释放 ctrl
也不会触发事件。如果你想要这样的行为,请为 ctrl
换用 keyCode
:keyup.17。
30.数组更新检测
1.变异方法
下列的数组方法是可以引起页面更新的(变异方法):
push() 尾部添加
pop() 尾部删除
shift() 头部删除
unshift() 头部添加
splice() 高级方法,删除、添加、替换
sort() 排序方法
reverse() 颠倒顺序
2.非变异方法
非变异方法与变异方法的区别就是,非变异方法不会改变原始数组,总是返回一个新数组,
当使用非变异方法时,可以用新数组替换旧数组,非变异方法大致有:filter()
, concat()
和 slice()
注意:下面两种方式修改数组是不会引起页面的更新的:
1. 直接通过下标的方式去修改数组: vm.fruits[1] = '我的天'
2. 直接通过修改数组的length属性的方式:vm.fruits.length = num
解决上面两个情况的方案:(Vue.set用法如下(vm.$set方法是Vue.set的别名,与其用法一致)
1.
(1). 使用 Vue.set(target, index, value) 静态方法
(2). 使用 实例对象的 $set() 实例方法 vm.$set(target, index, value)
Vue.set() 与 vm.$set() 语法和作用都是一样的。随你选择使用哪种方式
Vue.set(target, index, value)
target 要修改的数据源
index 要修改的下标
value 要修改成什么样
2. 直接换成 splice() 方法来操作即可
vm.fruits.splice(1)
总的来说:vue能不能检测到数组的变化并更新,取决于原生js的数组方法,
如果原生js方法(vue变异方法)能够修改原数组,那么vue就可以检测到变化并更新(例如push等方法)
如果原生js方法(vue非变异方法)不能够修改原数组,而是返回一个新数组,那么vue也可以检测到变化并更新,
前提是使用这些方法时要把新数组返回出来去替换掉旧数组
至于原生js的数组的两个坑,我们一定到注意,直接利用vue提供的方法来解决
31.Vue.set视图更新
Vue.set() 方法
调用方法:Vue.set( target, key, value )
target:要更改的数据源(可以是对象或者数组)
key:要更改的具体数据
value :重新赋的值
总结:
在vue文档中写着如果在实例创建之后添加新的属性到实例上,它不会触发视图更新
数据发生变化视图不更新 那就要Vue.set方法来设置
32.自定义指令详解(无)
Vue自定义指令详解_Liuqz2009的专栏-CSDN博客_自定义指令详解
33.vue的两个核心点
1、数据驱动:
在vue中,数据的改变会驱动视图的自动更新。传统的做法是需要手动改变DOM来使得视图更新,而vue只需要改变数据。
2、组件
组件化开发,优点很多,可以很好的降低数据之间的耦合度。将常用的代码封装成组件之后(vue组件封装方法),就能高度的复用,提高代码的可重用性。一个页面/模块可以由多个组件所组成。
34.vue和jQuery的区别
vue适用的场景:复杂数据操作的后台页面,表单填写页面
jquery适用的场景:比如说一些html5的动画页面,一些需要js来操作页面样式的页面
然而二者也是可以结合起来一起使用的,vue侧重数据绑定,jquery侧重样式操作,动画效果等,则会更加高效率的完成业务需求
src代码目录包含assets静态文件,components vue组件文件,plugins 插件文件(包含登录操作,http请求操作,过滤器,加解密操作,公共方法等),router 路由文件,store vuex文件,app.js vue相关配置,index.html主页面:
build目录为webpack打包文件,dist目录为打包后生成的文件,node_modules 引用的外部组件
35 引进组件的步骤
组件目录:
引入方式:
import BackToTop from '@/components/BackToTop'
使用组件:
components: { BackToTop },
使用组件:
<el-tooltip placement="top" content="tooltip">
<back-to-top :custom-style="myBackToTopStyle" :visibility-height="300" :back-position="50" transition-name="fade" />
</el-tooltip>
要注意下组件大小写命名的写法
36.Vue-cli打包命令是什么?打包后悔导致路径问题,应该在哪里修改
执行 npm run build 命令后,打包生成dist文件夹
在根目录下,新建 vue.config.js文件
37.三大框架的对比(无)
38.跨组件双向数据绑定(无)
39.delete和Vue.delete删除数组的区别
delete只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。
splice直接删除了数组 改变了数组的键值。
Vue.delete直接删除了数组 改变了数组的键值
40.SPA首屏加载慢如何解决
SPA(单页应用)首屏加载慢怎么处理?_超-CSDN博客_spa首屏加载慢如何解决
常见的几种解决方式
- 减小入口文件体积
- 静态资源本地缓存
- UI框架按需加载
- 图片资源压缩
- 开启GZip压缩
- 使用SSR
41.Vue-router跳转和location.href有什么区别
使用location.href='/url'来跳转,简单方便,但是刷新了页面;
使用history.pushState('/url'),无刷新页面,静态跳转;
引进router,然后使用router.push('/url')来跳转,使用了diff算法,实现了按需加载,减少了dom的消耗。
其实使用router跳转和使用history.pushState()没什么差别的,因为vue-router就是用了history.pushState(),尤其是在history模式下。
42.vue slot(无)
43.你们vue项目是打包了一个js文件,一个css文件,还是有多个文件?
根据vue-cli脚手架规范,一个js文件,一个CSS文件。
在打包之前,
- 代理的url
- build
assetsPublicPath: ‘./’
44.vue遇到的坑,如何解决的?
disableHostCheck: true
45.Vue里面router-link在电脑上有用,在安卓上没反应怎么解决?
Vue路由在Android机上有问题,babel问题,安装babel polypill插件解决
46.Vue2中注册在router-link上事件无效解决方法
使用 @click.native 。原因:router-link会阻止click事件,.native指直接监听一个原生事件。
47.RouterLink在IE和Firefox中不起作用(路由不跳转)的问题
方法一:只用a标签,不适用button标签;
方法二:使用button标签和Router.navigate方法
48.axios的特点有哪些
ajax缺点:
基于原生的XHR开发,XHR本身的架构不清晰
JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理
不符合关注分离(Separation of Concerns)的原则
配置和调用方式非常混乱,而且基于事件的异步模型不友好
axios优点:
从浏览器中创建 XMLHttpRequest
支持 Promise API
从 node.js 创建 http 请求
转换请求和响应数据
自动转换JSON数据
49.请说下封装 vue 组件的过程?
Vue组件封装过程
● 首先,使用Vue.extend()创建一个组件
● 然后,使用Vue.component()方法注册组件
● 接着,如果子组件需要数据,可以在props中接受定义
● 最后,子组件修改好数据之后,想把数据传递给父组件,可以使用emit()方法
50.vue 各种组件通信方法(父子 子父 兄弟 爷孙 毫无关系的组件)(无)
51.params和query的区别
52.vue mock数据
53 vue封装通用组件
54.vue初始化页面闪动问题
55.vue禁止弹窗后的屏幕滚动
56.vue更新数组时触发视图更新的方法
57.vue常用的UI组件库
58.vue如何引进本地背景图片
59.vue如何引进sass
60.vue修改打包后静态资源路径的修改
ES6面试题
ES6新增方法面试题
1.let const var比较
小结
- ES5中,使用var定义变量,变量的作用域有两种:全局作用域、函数作用域
- var定义变量存在变量提升,此外,先提升函数,后提升变量
ES6中新增了let关键字的同时,也新增了const关键字。
let与const有很多共同点:
- 都支持块级作用域
- 都不支持变量提升
- 都不支持重复声明
当然,const与let也有区别。const与let的区别在于:
- let声明变量时无需赋值,const声明变量时必须赋值
- let声明变量,变量可重新赋值,const声明变量,完成初始化后,值不得更改 (基本类型)
2.反引号(`)标识
1、可以解析变量
2、可以换行
3、可以调用函数
3.函数默认参数
1、函数参数的默认值
ES6则直接在参数定义里面设置函数参数的默认值,而且不用担心传入参数是0或者false会出错了:
2、箭头函数
箭头函数用 => 符号来定义。
箭头函数相当于匿名函数,所以采用函数表达式的写法。
左边是传入函数的参数,右边是函数中执行的语句。
相当于
上面是完整的写法,左边小括号,右边大括号,而下面的情况可以简写:
(1)当要执行的代码块只有一条return语句时,可省略大括号和return关键字:
(2)当传入的参数只有一个时,可以省略小括号:
相当于
(3)当不需要参数时,使用空的圆括号:
相当于
箭头函数在回调函数中是很简洁的,像这样:
在排序中:
需要注意的是, 箭头函数没有自己的this、arguments、super、new.target,它们分别指向外层函数的相应变量。
以前在ES5中使用this有点麻烦,这种问题很常见:
这就需要在嵌套函数外层使用that = this,然后内层使用that,就是下面这样子:
但是现在有了箭头函数,不再需要使用that = this或 _this = this 这种代码啦,因为箭头函数中的this直接就是外层函数中的this,代码更简单了:
4.箭头函数(详见上一条)
5.属性简写
在ES6中允许我们在设置一个对象的属性的时候不指定属性名。
不使用ES6:
const name='Ming', age='18', city='Shanghai';
const student ={
name:name,
age:age,
city:city
};
console.log(student);
使用ES6:
const name='Ming', age='18', city='Shanghai';
const student ={
name,
age,
city
};
console.log(student);
6.方法简写
a:function(){}可以简写为a(){}
7.Object.keys()方法,获取对象的所有属性名或方法名
8.Object.assign ()原对象的属性和方法都合并到了目标对象(待看)
Object.assign(target, …sources) target: 目标对象,sources: 源对象
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
注意:
1.如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性
2.Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标
对象的[[Set]],所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如
果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到
原型,应使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。
9.for...of 循环
for、for in、forEach、for of
深入了解 JavaScript 中的 for 循环 - 知乎
10.import和export
10、JS ES6中export和import详解 - 大牛半路出家 - 博客园
11.Promise对象
.then
可以添加多个回调函数,它们会按照插入顺序并且独立运行
12.解构赋值
13.set数据结构(可用于快速去重)
(1)set的属性
set的属性包括constructor(set原型)和size(用来记录set的大小)。
用法:Set.prototype.constructor
,Set.prototype.size
。
例如:
let set = new Set([1,2,3,2,2,2,1,2,34,4,45,523]);
console.log(set.constructor); // ƒ Set() { [native code] }
console.log(set.size); // 7
(2)set的操作方法
- add(val); 添加某个值
- delete(val); // 删除某个值,返回布尔值,表示结果。
- has(val); // 判断该值是否存在于Set中。
- clear(); // 清楚所有的值
(3)set的遍历方法
set
的键和值是同一个值。
- keys() 返回键名的方法
- values() 返回键值的方法
- entries() 返回键值对的方法
- forEach() 用回调函数遍历各个元素的方法
例如:
let set = new Set([2,2,2,1,5,34,45,33,23,32]);
for(let item of set.keys()) {
console.log(item); // // 2 1 5 34 45 33 23 32
}
for(let item of set.values()) {
console.log(item); // // 2 1 5 34 45 33 23 32
}
for(let item of set.entries()) {
console.log(item); // [2, 2] [1, 1] [5, 5] [34, 34] [45, 45] [33, 33] [23, 23] [32, 32]
}
set.forEach((value,key) => console.log(key+ '=' +value)); // 2=2 1=1 5=5 34=34 45=45 33=33 23=23 32=32
(4)应用
...
运算符和Array.from
以及map
和filter
都可以遍历set结构。
...
运算符
例如:
let set = new Set('123456654321');
console.log([...set]); // ["1", "2", "3", "4", "5", "6"]
...
和数组加起来可以去重
例如:
let arr = [1,2,3,4,6,3,3,1,3,4];
let removeRepeat = [...new Set(arr)];
console.log(removeRepeat); //[1, 2, 3, 4, 6]
map
方法
map和foreach的区别:
forEach()和map()的区别_xif3681的博客-CSDN博客_foreach和map的区别
例如:
let set = new Set([1,2,3,4]);
console.log(new Set([...set].map(x => x*2))); // {2, 4, 6, 8}
filter
方法
例如:
let set = new Set([1,2,3,4]);
console.log(new Set([...set].filter(x => x%2 === 0))); // {2, 4}
Array.from
例如:
let set = new Set([1,2,3,4]);
console.log(new Set(Array.from(set, x => x*2))); // {2, 4, 6, 8}
小案例
使用这个set
可以实现数学中的交集,并集效果。
例如:
let a = new Set([1,2,3]);
let b = new Set([3,4,5]);
let numa = new Set([...a,...b]);
console.log(numa); // {1, 2, 3, 4, 5}
//并集
let numb = new Set([...a].filter(x => b.has(x)));
console.log(numb); // {3}
14.Spread Operator 展开运算符(...)
展开运算符是ES6的特性,使用场景有以下:
一、函数调用中使用展开运算符 也就是三个点...
function test(a, b, c){}
let args = [0, 1, 2];
test(...args);
我们使用...展开运算符就把0,1,2分别传给了test()的参数a,b,c。
二、数组字面量使用展开运算符
1、通过加一个数组来合并数组
let arr1 = ['a', 'b', 'c'];
let arr2 = [...arr1, 'd', 'e'];//['a', 'b', 'c', 'd', 'e']
2、通过push来合并数组
let arr1 = ['a', 'b', 'c'];
let arr2 = ['d', 'e'];
arr1.push(...arr2);//['a', 'b', 'c', 'd', 'e']
三、用于解构赋值
let [arg1,arg2,...arg3] = [1, 2, 3, 4];
arg1 //1
arg2 //2
arg3 //['3','4']
注意:结构赋值中展开运算符只能用在最后。
四、类数组对象编程数组
let list = document,getElementsByTagName('div');
let arr = [...list];
五、对象展开运算符
let {x,y,...z}={x:1,y:2,a:3,b:4};
x; //1
y; //2
z; //{a:3,b:4}
15.字符串新增方法
1、JavaScript 只有indexOf
方法,可以用来确定一个字符串是否包含在另一个字符串中。如果不存在返回-1,如果存在返回字符串的位置。ES6 又提供了三种新方法。
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
2、repeat
方法返回一个新字符串,表示将原字符串重复n
次。
如下:
let str="hello"
str.repeat(2);//输出结果:hellohello
注意:如果是小数,那么取整,如果是0至-1之间的小数或者是0至1之间的小数,取整数。
3、字符串补全长度的功能。
(1)padStart()
用于头部补全
(2)padEnd()
用于尾部补全。
padStart()
和padEnd()
一共接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。
4、trimStart()
和trimEnd()方法,消除字符串头部的空格、消除尾部的空格。
ES2019对字符串实例新增了trimStart()
和trimEnd()
这两个方法。它们的行为与trim()
一致,trimStart()
消除字符串头部的空格,trimEnd()
消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。
浏览器还部署了额外的两个方法,trimLeft()
是trimStart()
的别名,trimRight()
是trimEnd()
的别名。
ES6数组面试题
1.forEach()
let phone=['小米','三星','苹果','一加','乐视','OPPO','VIVO','魅族','联想'];
phone.forEach((item,index,arr)=>{
console.log(item+index+arr)
})
可以使用foreach进行循环,然后再执行一个函数,函数的第一个参数是数组的每一项,第二个参数是数组的索引(index),第三表示改元素所在数组的全部数据(整个数组数据)
2.map()
map方法和forEach有点类似,但是这个map方法三个参数和forEach一样,但是map返回的数组不会替换掉原数组,可以使用新的变量名接收这个新生成的数组!
3.filter()
这个和forEach,map一样可以接收三个参数,和map一样返回的数组不会替换原数组,可以使用新数组接收,在return 可以设置返回的条件!
小结: forEach,map,filter都在对象内接收一个函数,这个函数都可以接收三个参数,第一个表示数组的子项,第二个表示数组的索引(index),第三表示遍历数组所在的数组全部数据!map,和filterreturn返回的新数组不会替换原数组,需要接收一个新变量存储新的数组!而filter的return可以设置筛选条件用于数组数据的筛选!
4.reduce()
ES6中的数组reduce()方法详解_Army-海军的博客-CSDN博客
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
第一个参数: callback函数
执行数组中每个值 (如果没有提供 initialValue则第一个值除外)的函数,包含四个参数:
accumulator
累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。
currentValue
数组中正在处理的元素。
index 可选
数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。
array可选
调用reduce()的原数组
第二个参数: initialValue可选
作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
5.some()
some()方法测试数组中的某个元素是否通过了由提供的函数实现的测试。
语法
array.some(callback[, thisObject]);
参数细节
-
callback - 测试每个元素的函数。
-
thisObject - 执行回调时用作此对象的对象。
返回值
如果某个元素通过了测试,则返回true,否则返回false。
例
function isBigEnough(element, index, array) {
return (element >= 10);
}
var retval = [2, 5, 8, 1, 4].some(isBigEnough);
console.log("Returned value is : " + retval );
var retval = [12, 5, 8, 1, 4].some(isBigEnough);
console.log("Returned value is : " + retval );
输出
Returned value is : false
Returned value is : true
6.every()
每个方法都测试数组中的所有元素是否都通过了提供的函数实现的测试。
句法
array.every(callback[, thisObject]);
参数
-
callback - 测试每个元素的函数。
-
thisObject - 执行回调时用作此对象的对象。
返回值
如果此数组中的每个元素都满足提供的测试函数,则返回true。
例
function isBigEnough(element, index, array) {
return (element >= 10);
}
var passed = [12, 5, 8, 130, 44].every(isBigEnough);
console.log("Test Value : " + passed );
产量
Test Value : false
some和every对比:
some 英语翻译为一些,every翻译为所有,每个,所以some方法 只要其中一个为true 就会返回true的,相反,every()方法必须所有都返回true才会返回true,哪怕有一个false,就会返回false;every()和 some()目的:确定数组的所有成员是否满足指定的测试
every:一假即假:
some:一真即真
7.all()方法(Promise.all()方法)
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。
js面试题
1.简述同步和异步的区别
js是一门单线程语言,所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。如果一个任务耗时过长,那么后面的任务就必须一直等待下去,会拖延整个程序,常见浏览器无反应,可能就是一段代码死循环,造成程序卡住在这个位置,无法继续
为了解决这个问题,js的执行模式分为两种:同步和异步。
"同步模式"就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;"异步模式"则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
具体来说,异步运行机制如下:
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
"异步模式"编程的4种方法
1.回调函数
2.事件监听,任务的执行不取决于代码的顺序,而取决于某个事件是否发生
3.发布订阅模式,也叫观察者模式
4.Promises对象
最基础的异步是setTimeout和setInterval函数,很常见,但是很少人有人知道其实这就是异步,因为它们可以控制js的执行顺序
2.怎么添加、移除、复制、创建、和查找节点
1)创建新节点(都要加引号)
createDocumentFragment() //创建一个DOM片段https://www.jianshu.com/p/8ae83364c09c
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点
2)添加、移除、替换、插入(都不需要加引号)
appendChild() //添加
removeChild() //移除
replaceChild() //替换
insertBefore() //插入
3)查找
getElementsByTagName() //通过标签名称
getElementsByName() //通过元素的Name属性的值
getElementById() //通过元素Id,唯一性
3.实现一个函数clone 可以对Javascript中的五种主要数据类型(Number、string、Object、Array、Boolean)进行复制
function clone(obj) {
var o;
switch (typeof obj) {
case "undefined":
break;
case "string":
o = obj + "";
break;
case "number":
o = obj - 0;
break;
case "boolean":
o = obj;
break;
case "object": // object 分为两种情况 对象(Object)或数组(Array)
if (obj === null) {
o = null;
} else {
if (Object.prototype.toString.call(obj).slice(8, -1) === "Array") {
o = [];
for (var i = 0; i < obj.length; i++) {
o.push(clone(obj[i]));
}
} else {
o = {};
for (var k in obj) {
o[k] = clone(obj[k]);
}
}
}
break;
default:
o = obj;
break;
}
return o;
}
4.如何消除一个数组里面重复的元素
js数组去重的10种方法_念念不忘的博客-CSDN博客_数组去重js
Methods 1: 思路:定义一个新数组,并存放原数组的第一个元素,然后将元素组一一和新数组的元素对比,若不同则存放在新数组中。
Methods 2: 思路:先将原数组排序,在与相邻的进行比较,如果不同则存入新数组。
Methods 3: 利用对象属性存在的特性,如果没有该属性则存入新数组。
Methods 4: 利用数组的indexOf下标属性来查询。
Methods 5: 利用数组原型对象上的includes方法。
Methods 6: 利用数组原型对象上的 filter 和 includes方法。
Methods 7: 利用数组原型对象上的 forEach 和 includes方法。
Methods 8: 利用数组原型对象上的 splice 方法。
Methods 9: 利用数组原型对象上的 lastIndexOf 方法。
Methods 10: 利用 ES6的set 方法。
5.写一个返回闭包的函数
1.闭包函数是指有权访问另一个函数作用域中的变量的函数
2.创建闭包函数最常见的方式是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量
3.闭包的特点:1函数嵌套函数,
2 函数内部可以引用外部的参数和变量
3 参数和变量不会被垃圾回收机制回收
4.闭包的优点:1 希望一个变量长期驻扎在内存中 *
2 避免全局变量的污染 *
3 私有变量存在
5.闭包的实现 1:函数嵌套函数 *
2 外层函数返回内层函数 *
3 外面有一全局变量接受外层函数
function fun1() {
var sum=0;
function fun2() {
sum++;
return sum
}
return fun2
}
var s=fun1();
console.log(s());
// 自执行函数 的闭包
//
var fun3=function () {
var a=3;
return{
b:7,
sum:function () {
return this.b+a;
}
}
}();
console.log(fun3.sum());
/*
* 把函数名当参数调用
* 回调函数
*
* */
function b() {
console.log("b");
}
function c() {
console.log("c");
}
function d(fun) {
fun();
}
d(b);
d(c);
// 循环
var num=0;
function a1(fun) {
fun(a1);
}
function a2(fun) {
num++;
console.log(num);
if(num>10) return;
fun(a2);
}
a1(a2);
// 事件函数 也是 回调函数
this.addEventListener("click",clickHandler);
function clickHandler(e) {
}
setInterval(animation,16);
function animation() {
}
6.使用递归完成1到100的累加
function sum(num) {
if( num==1 ){
return 1;
}
return num+sum(num-1);
}
console.log(sum(100))
7.Javascript有哪几种数据类型
javascript 六种数据类型 - 那些年的代码 - 博客园
7.Javascript有哪几种数据类型、8.如何判断数据类型 - 大牛半路出家 - 博客园
typeof null === ‘object’、+0 !=== -0
8.如何判断数据类型
7.Javascript有哪几种数据类型、8.如何判断数据类型 - 大牛半路出家 - 博客园
typeof,instanceof,Array.isArray()
9.console.log(1+'2')和console.log(1-'2')的打印结果
string、number
10.Js的事件委托是什么,原理是什么(无)
11.如何改变函数内部的this指针的指向
一、函数内部的this指向
调用方式 | this指向 |
普通函数调用 | window |
构造函数调用 | 实例对象 |
对象的方法调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件对象 |
定时器函数 | window |
立即执行函数 | window |
二、改变函数内部的this指向的三种方法
1、call方法
call()方法可以调用函数,还可以改变函数内的this指向,它的主要作用是可以实现继承
fun.call(thisArg,arg1,arg2,....)
这里将fun函数的this指向o
1 var o = {
2 name: 'Andy'
3 }
4
5 function fun(a, b) {
6 console.log(this);
7 console.log(a + b);
8 }
9 fun.call(o, 1, 2);
将父构造函数里的this指向子构造函数的实例对象(构造函数的this指向它的实例对象)
1 function Father(uname, age, sex) {
2 this.uname = uname;
3 this.age = age;
4 this.sex = sex;
5 }
6
7 function Son(uname, age, sex) {
8 Father.call(this, uname, age, sex);
9 }
10 var son = new Son('ldh', 18, 'male');
11 console.log(son);
2、apply方法
fun.apply(thisArg,[argsArray])(传递的值必须包含在数组里)
apply()方法可以调用函数,还可以改变函数内的this指向,但它的参数必须是数组(伪数组)
1 var arr = [1, 33, 22, 44, 12];
2 var max = Math.max.apply(Math, arr);
3 var min = Math.min.apply(Math, arr);
4 console.log(max, min);
3、bind方法(用的最多)
bind()方法不会调用函数,但是可以改变函数内部this指向
fun.bind(thisArg,arg1,arg2,....)
1 var o = {
2 name: 'Andy'
3 }
4
5 function fun(a, b) {
6 console.log(this);
7 console.log(a + b);
8 }
9 var f = fun.bind(o, 1, 2);
10 f();
下面的小案例是点击按钮后按钮禁用,直到三秒后开启按钮
虽然定时器函数里的this指向window,但是bind(this)里面的this是在定时器函数的外面绑定的,这个this又在btns[i].onclick这个函数里面,所以这个this指向的就是btns对象
为什么这里的this.disabled的this不用btns[i]呢,因为定时器是在3秒后执行,而for循环立马执行,这里有三个按钮,则i为3
1 var btns = document.querySelectorAll('button');
2 for (var i = 0; i < btns.length; i++) {
3 btns[i].onclick = function() {
4 this.disabled = true;
5 setTimeout(function() {
6 this.disabled = false;
7 }.bind(this), 3000);
8 }
9 }
12.列举几种解决跨域问题的方式,且说明原理
解决跨域的方式主要有两种
- 跨域资源共享 CORS
- jsonp
13.谈谈垃圾回收机制的方式及内存管理(无)
14.写一个function ,清除字符串前后的空格
function trim(str) {
if (str && typeof str === "string") {
return str.replace(/(^\s*)|(\s*)$/g,""); //去除前后空白符
}
}
15.js实现继承的方法有哪些
16.判断一个变量是否是数组,有哪些办法
1、instanceof
function isArray (obj) {
return obj instanceof Array;
}
2、Array对象的 isArray方法
function isArray (obj) {
return Array.isArray(obj);
}
3、Object.prototype.toString
function isArray (obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
17.let ,const ,var 有什么区别
1.let/const定义的变量不会出现变量提升,而var定义的变量会提升
2.相同作用域中,let和const不能出现重复声明。而var就可以
3.const声明变量时必须设置初始值
4.const声明一个只读的常量,这个常量不可改变。
在这里有个非常重要的点:JS中,复杂数据类型,存储在栈中的是堆内存的地址,存在栈中的这个地址是不变的,但是存在堆中的值是可以变得
18.箭头函数与普通函数有什么区别
- 更简洁的语法
- 没有this
- 不能使用new 构造函数
- 不绑定arguments,用rest参数...解决
- 使用call()和apply()调用
- 捕获其所在上下文的 this 值,作为自己的 this 值
- 箭头函数没有原型属性
- 不能简单返回对象字面量
- 箭头函数不能当做Generator函数,不能使用yield关键字
- 箭头函数不能换行
19.随机取1-10之间的整数
js获取0-1之间的随机数,获取1-10之间的随机数
1.js
//获取0-1之间的随机数
var num = Math.random();
-
console.log(num);
-
//获取1-10之间的随机数
-
var num = Math.floor(Math.random() * 10+ 1);
-
console.log(num);
2.获取两个数之间的随机整数
function getRandomNumberByRange(start, end) {
return Math.floor(Math.random() * (end - start) + start)
}
getRandomNumberByRange(0,100)
效果:
20.new操作符具体干了什么
21.Ajax原理
22.模块化开发怎么做
23.异步加载Js的方式有哪些
24.xml和 json的区别
25.webpack如何实现打包的
26.常见web安全及防护原理
27.用过哪些设计模式
28.为什么要同源限制
29.offsetWidth/offsetHeight,clientWidth/clientHeight与scrollWidth/scrollHeight的区别
30.javascript有哪些方法定义对象
31.说说你对promise的了解
32.谈谈你对AMD、CMD的理解
33.web开发中会话跟踪的方法有哪些
34.介绍js有哪些内置对象?
35.说几条写JavaScript的基本规范?
36.javascript创建对象的几种方式?
37.eval是做什么的?
38.null,undefined 的区别?
在JavaScript中,null
和 undefined
几乎相等
在 if
语句中 null
和 undefined
都会转为false两者用相等运算符比较也是相等
console.log(null==undefined); //true 因为两者都默认转换成了false
console.log(typeof undefined); //"undefined"
console.log(typeof null); //"object"
console.log(null===undefined); //false "==="表示绝对相等,null和undefined类型是不一样的,所以输出“false”
null
和 undefined
基本同义,二者又有什么区别呢?
null
表示没有对象,即该处不应该有值
1) 作为函数的参数,表示该函数的参数不是对象
2) 作为对象原型链的终点
undefined
表示缺少值,即此处应该有值,但没有定义
1)定义了形参,没有传实参,显示undefined
2)对象属性名不存在时,显示undefined
3)函数没有写返回值,即没有写return,拿到的是undefined
4)写了return,但没有赋值,拿到的是undefined
null和undefined转换成number数据类型
null
默认转成 0
undefined
默认转成 NaN