面试题
- 3.9日面试题背诵
- 3.10面试题
- 3.11面试题:
- 3.17面试题:
- 3.18日面试题
- 3.19日面试题
- 3.23 面试题
- 3.25 面试题
- 3.26 面试题
- 3.29 面试题
- 3.30 面试题
- 1. [js中数组常用的方法总结,包括ES6](https://blog.csdn.net/wang729506596/article/details/83019131)
- 2.怎么增加、移除、移动、复制、创建和查找节点
- 3. input 点击,获取取值
3.9日面试题背诵
1. vue的特点是什么
- 国人开发的一个轻量级框架
- 双向数据绑定,在数据操作方面更为简单
- 视图、数据、结构分离,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作
- 组件化:方便封装和复用
- 虚拟DOM:dom操作是非常耗费性能的,不再使用原生的dom操作节点,极大解放dom操作
2. vue中父子组件时如何传值的
1.路由配置
使用children 属性实现路由嵌套,嵌套的组件关系就是父子组件关系
{
path: '/father',
name: 'father',
component: father,
children: [
{
path: 'son',
name: 'son',
component: son
}
]
}
2. 传递静态数据
3. 传递动态数据
父组件中传递的数据来自 data 或者计算属性
v-bind 指令的作用是:告诉vue 值是一个js表达式,而不是一个普通字符串,既然是js表达式,就会进行js解析,所以才可以传递动态数据
传值方式 有 :父组件向子组件传值,子组件向父组件传值,非关系组件传值。
引用官网的一句话:父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息,如下图所示:
4. 传递静态或动态 Prop
像这样,你已经知道了可以像这样给 prop 传入一个静态的值:
<blog-post title="My journey with Vue"></blog-post>
你也知道 prop 可以通过 v-bind
动态赋值,例如:
<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>
<!-- 动态赋予一个复杂表达式的值 -->
<blog-post
v-bind:title="post.title + ' by ' + post.author.name"
></blog-post>
在上述两个示例中,我们传入的值都是字符串类型的,但实际上任何类型的值都可以传给一个 prop。
父组件向子组件传值:
-
父组件向子组件传递方法
-
- 父组件向子组件传递方法使用事件绑定机制,当自定义一个事件属性后,子组件就可以调用传递的这个方法
- 使用$emit(‘父组件中的方法’)触发父组件传过来的方法
- $emit(‘父组件中的方法’,)中的第二个参数往后都为需要传递的参数
- 子组件可以通过$emit传递自身的数据,父组件也可以将接收的子组件的数据存到自身的data属性中
-
<div id='app'> <!-- 父组件可以在引用子组件的同时,通过属性绑定的形式把父组件的数据传递给子组件 --> <com1 :parentmsg="msg"></com1> </div> <script> let app = new Vue({ el: '#app', data: { msg: '这是父组件的数据', }, //创建一个子组件 components: { com1: { template: '<h1>这是子组件--{{parentmsg}}</h1>', data() { return { } }, // 把父组件传递过来的数据,先在props数组中定义一下,这样才能使用这个数据 props: ['parentmsg'] } } }); </script>
-
父组件向子组件传值(data与props的区别)
-
子组件默认无法访问到父组件中data上的数据和methods中的方法
-
父组件可以在引用子组件的同时通过属性绑定(v-bind)的形式把需要传递给子组件的数据传给子组件
-
父组件传过来的属性需要先在props数组中定义一下(与属性的名字相同),这样才能使用这个数据
-
组件中的所有props中的数据都是通过父组件传递给子组件的
-
props中的数据都是只读的无法重新赋值
-
子组件中的data数据并不是父组件传递过来的,是子组件私有的,例如:子组件通过ajax请求回来的数据,可以放到data身上
-
子组件接收的父组件的值分为引用类型和普通类型两种:
- 普通类型:字符串(String)、数字(Number)、布尔值(Boolean)、空(Null)
- 引用类型:数组(Array)、对象(Object)
-
基于 vue 的单向数据流,即组件之间的数据是单向流通的,子组件是不允许直接对父组件传来的值进行修改的,所以应该避免这种直接修改父组件传过来的值的操作,否则控制台会报错
-
如果传过来的值是简单数据类型,是可以在子组件中修改,也不会影响其他兄弟组件内同样调用了来自该父组件的值。
-
具体操作是可以先把传过来的值重新赋值给data中的一个变量,然后再更改那个变量
// 子组件 export default { props: ['myName'], data() { return { name : this.myName // 把传过来的值赋值给新的变量 } }, watch: { myName(newVal) { this.name = newVal //对父组件传过来的值进行监听,如果改变也对子组件内部的值进行改变 } }, methods: { changeName() { this.name = 'Lily' // 这里修改的只是自己内部的值,就不会报错了 }, } }
-
- **注:**如果不使用 watch 来监听父组件传递的 myName 值,子组件中的 name 值是不会随着父组件的 myName 值进行改变,因为 data 中 name: this.myName 仅仅只是定义了一个初始值。
- 如果引用类型的值,当在子组件中修改后,父组件的也会修改,因其数据是公用的,其他同样引用了该值的子组件也会跟着被修改。可以理解成父组件传递给子组件的值,就相当于复制了一个副本,这个副本的指针还是指向父组件中的那个,即共享同一个引用。所以除非有特殊需要,否则不要轻易修改。
-
父传子的实现方式就是通过props属性,子组件通过props属性接收从父组件传过来的值,而父组件传值的时候使用 v-bind 将子组件中预留的变量名绑定为data里面的数据即可
子组件向父组件传值
1.子组件绑定一个事件,通过 this.$emit() 来触发
- 父组件的方法被传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,当作参数传递过去;
- 子组件内部通过**this.$emit(‘方法名’, 要传给父元素的值)**方式,来调用父组件中的方法,同时把数据传递给父组件使用
- 通过在子组件注册这个事件 然后父组件在方法里面来接受注册的这个事件等于的方法 。父组件方法接受的参数就是子组件传过来的参数
子组件中需要以某种方式例如点击事件的方法来触发一个自定义事件;子组件给父组件传参用this.$emit(‘事件名’,携带的内容),父组件在相应的位置监听事件
2.通过 callback 函数
先在父组件中定义一个callback函数,并把 callback 函数传过去
// 父组件
<child :callback="callback"></child>
methods: {
callback: function(name) {
this.name = name
}
}
在子组件中接收,并执行 callback 函数
// 子组件
<button @click="callback('Jack')">改变父组件的name</button>
props: {
callback: Function,
}
3.通过 $parent / $children 或 $refs 访问组件实例
这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。
// 子组件
export default {
data () {
return {
title: '子组件'
}
},
methods: {
sayHello () {
console.log('Hello');
}
}
}
// 父组件
<template>
<child ref="childRef" />
</template>
<script>
export default {
created () {
// 通过 $ref 来访问子组件
console.log(this.$refs.childRef.title); // 子组件
this.$refs.childRef.sayHello(); // Hello
// 通过 $children 来调用子组件的方法
this.$children.sayHello(); // Hello
}
}
</script>
注:这种方式的组件通信不能跨级。
非父子组件进行传值
- 创建一个空的 Vue 实例作为事件总线bus
- 使用 bus.$emit(‘名称’,数据) 进行传递数据
- 使用 **bus.$on(名称’,function(val){val即为接收到的数据})**接收数据
3. v-show 和 v-if 指令的共同点和不同点
共同点:都能控制元素的显示和隐藏
不同点:实现本质方法不同
v-show
本质就是通过控制css中的 display 设置为 none,控制隐藏,只会编译一次;
v-if
是动态地向 DOM 树内添加或者删除 DOM 元素,若初始值为 false,就不会被编译了
v-if
是不停的不停销毁和创建比较消耗性能
总结:
如果要频繁切换某节点,使用 v-show
(切换开销比较小,初始开销比较大)。
如果不需要频繁切换某节点,使用 v-if
(初始渲染开销比较小,切换开销比较大)
3.10面试题
1.说出几种vue当中的指令和它的作用
- v-mode : 便捷的获取和设置表单元素的值(双向数据绑定)
- v-for :根据数据生成列表结构
- v-if 和 v-show :显示和隐藏
- v-on :为元素绑定事件
- v-once :只绑定一次
- v-text : 设置标签的文本值
- v-html : 设置标签的innerHTML
- v-bind :为元素绑定属性
2.vue-loader 是什么? 使用他的用途有哪些?
vue 文件的一个加载器,将 template
/ js
/ style
转换成 js
模块
用途:
- js可以写成 ES6
- style 样式可以写成 scss 或 less
- template 可以加 jade
3.axios 是什么?怎么使用?
axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征:
- 从浏览器中创建 XMLHttpRequest
- 从 node.js 发出 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求和响应数据
- 取消请求
- 自动转换JSON数据
一句话来说就是:axios 是 请求后台资源的模块d
安装命令
npm install axios --save
在 js 文件中使用 import 引进来,然后使用 get 或者 post ,如果成功返回在 .then函数中,
失败则返回在 catch 函数中
执行GET请求
// 向具有指定ID的用户发出请求
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// 也可以通过 params 对象传递参数
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
执行POST请求
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
执行多个并发请求
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
//两个请求现已完成
}));
4.单页面应用和多页面应用的区别及优点
单页面:
单页面(SPA),通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多应用于pc端。
多页面:
多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新。
优点:
单页面的优点:
1,用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小
2,前后端分离
3,页面效果会比较炫酷(比如切换页面内容时的专场动画)
多页面的优点
1,与单个页面相反,它具有更强的可伸缩性,可以添加更多的子页面,也可以添加层级,三级或者四级,加上搜索框。
2,SEO功能和更突出的优势,它比单页面承载的内容更多,包括搜索引擎和营销策略。
缺点:
单页面缺点:
1,不利于seo
2,导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理)
3,初次加载时耗时多
4,页面复杂度提高很多
多页面缺点
这就是工作量的问题,早期页面的设计、前端框架的构建和后端的开发,还有后期维护,工作量相当大。
3.11面试题:
1. vue常用的修饰符和作用
1.v-model的修饰符
.lazy
(类贼)修饰符 :v-model
默认是在input
事件中同步输入框的数据的,也就是说,一旦有数据发生改变,对应的data
中的数据就会自动发生改变,lazy
修饰符可以让数据失去焦点或者回车时才会更新- .
number
修饰符 :在输入框中,无论我们输入的是数字还是字母,都会被当做字符串类型进行处理 .trim
(垂木)修饰符 : 去除input
值两边空格,
2.事件修饰符
stop
: 阻止了事件冒泡,相当于调用了event.stopPropagation
方法prevent
: 阻止了事件的默认行为,相当于调用了event.preventDefault
方法self
(赛奥服): 只当在event.target
是当前元素自身时触发处理函数once
:绑定了事件以后只能触发一次,第二次就不会触发capture
:使事件触发从包含这个元素的顶层开始往下触发passive
(趴ser屋) :在移动端,当我们在监听元素滚动事件的时候,会一直触发onscroll
事件会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给onscroll
事件整了一个.lazy
修饰符native
(内体屋): 让组件变成像html
内置标签那样监听根元素的原生事件,否则组件上使用v-on
只会监听自定义事件
3.修饰符的应用场景
.stop
:阻止事件冒泡.native
:绑定原生事件.once
: 事件只执行一次.self
: 将事件绑定在自身身上,相当于阻止事件冒泡.prevent
:阻止默认事件.caption
:用于事件捕获.once
:只触发一次.keyCode
:监听特定键盘按下.left
- (2.2.0) : 只当点击鼠标左键时触发。.right
- (2.2.0) : 只当点击鼠标右键时触发。.middle
- (2.2.0) : 只当点击鼠标中键时触发。.passive
- (2.3.0) : 以{ passive: true }
模式添加侦听器
注意:
- 使用串联修饰符时,顺序很重要,其效果按串联顺序产生:
v-on:click.prevent.self
会阻止所有的点击,而v-on:click.self.prevent
只会阻止对元素自身的点击。
4.按键修饰符
@click.ctrl
:All
或shif
被一同按下时才会触发@click.ctrl.exact
:只有在Ctrl
被按下的时候才会触发@click.exact
:没有任何系统修饰符被按下的时候触发2.
2.谈谈你对MVVM开发模式的理解
MVVM 分为 Model、View、ViweModel 三者
-
Model :代表数据模型、数据和业务逻辑都在Model层中定义
-
View:代表UI视图、负责数据的展示
-
ViewModel:负责监听Model中数据的改变并且控制使徒的更新,处理用户交互操作
Model和view并无直接关联,而是通过viewModel来进行联系的,Model和viewMode1之间有着双向数据绑定的联系。因此当Model中的数据改变时会触发view层的刷新,View中由于用户交互操作而改变的数据也会在Mode1中同步。
这种模式实现了Model和View的数据自动同步,因此开发者只需要专注对数据的维护操作即可,不需要自己操作
3.前端如何优化网站性能?
1、减少 HTTP 请求数量
在浏览器与服务器进行通信时,主要是通过 HTTP 进行通信。浏览器与服务器需要经过三次握手,每次握手需要花费大量时间。而且不同浏览器对资源文件并发请求数量有限(不同浏览器允许并发数),一旦 HTTP 请求数量达到一定数量,资源请求就存在等待状态,这是很致命的,因此减少 HTTP 的 请求数量可以很大程度上对网站性能进行优化。
可以通过精灵图、合并css和js文件、懒加载等方式来减少http请求。
CSS Sprites
国内俗称CSS精灵,这是将 多张图片合并成一张图片达到减少HTTP请求的一种解决方案,可以通过CSS的background属性来访问图片内容。这种方案同时还可以减少图片总字节数。
合并 CSS 和 JS 文件
现在前端有很多工程化打包工具,如:grunt、gulp、webpack等。为了减少 HTTP 请求数量,可以通过这些工具再发布前将多个CSS或者多个JS合并成一个文件。`
采用 lazyLoad
俗称懒加载,可以控制网页上的内容在一开始无需加载,不需要发请求,等到用户操作真正需要的时候立即加载出内容。这样就控制了网页资源一次性请求数量。
2、控制资源文件加载优先级
浏览器在加载HTML内容时,是将HTML内容从上至下依次解析,解析到link或者script标签就会加载href或者src对应链接内容,为了第一时间展示页面给用户,就需要将CSS提前加载,不要受 JS 加载影响。
一般情况下都是CSS在头部,JS在底部。
3、利用浏览器缓存
浏览器缓存是将网络资源存储在本地,等待下次请求该资源时,如果资源已经存在就不需要到服务器重新请求该资源,直接在本地读取该资源。
4、减少 DOM 操作(vue这种减少操作DOM)
5、图标使用 IconFont 替换image标签
4. vue中样式绑定语法
1.对象方法
v-bind:class="{'orange':isRipe,'green':isNotRipe}"
2.数组方法
v-bind:class="[class1,class2]"
3.行内
v-bind:style="{color:color,fontSize:fontSize+'px'}"
3.17面试题:
1.简述vue中每个生命周期的具体适合哪些场景
vue的生命周期及使用场景
1. 创建前:beforeCreate(){}
在 new 一个 vue 实例后,只有一些默认的生命周期钩子和默认事件,其他东西还都没有创建,此时还访问不到data
中的属性以及 methods
中的属性和方法,不能再这个阶段使用 data
中的数据和 methods
中的方法,我们可以在当前生命周期创建一个 loading
事件,在页面加载完成之后将loading
移除。
2. 创建完成:created(){}
created [kriˈeɪtɪd]
data
和 methods
都以将被初始化好了,如果要调用 methods
中的方法,或者操作 data 中的内容,最早可以在这个阶段中操作
当前生命周期执行的时候会遍历data中所有的属性,给每一个属性都添加一个getter、setter方法,将data中的属性变成一个响应式属性;当前生命周期执行的时候会遍历data&&methods身上所有的属性和方法,将这些属性和方法代理到vue的实例身上,在当前生命周期中我们可以进行前后端数据的交互(注:在vue项目中,我们在进行前后端交互时一般不用ajax,因为我们只是想单纯的使用ajax,但是却要引入整个jQuery,在一定程度上降低了性能,再者vue本身设计就是尽量减少DOM元素的操作,但jQuery却是注重DOM元素的操作,这就有点不合理,所以,一般用axios)。
拓展:axios与ajax的区别及优缺点
区别:axios是通过Promise实现对ajax技术的一种封装,就像jquery对ajax的封装一样,简单来说就是ajax技术实现了局部数据的刷新,axios实现了对ajax的封装,axios有的ajax都有,ajax有的axios不一定有,总结一句话就是 axios是ajax,ajax不止axios
ajax:
1、本身是针对MVC编程,不符合前端MVVM的浪潮;
2、基于原生XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案,jquery整个项目太大,单纯使用ajax却要引入整个jquery非常不合理(采取个性化打包方案又不能享受cdn服务);
3、ajax不支持浏览器的back按钮;
4、安全问题ajax暴露了与服务器交互的细节;
5、对搜索引擎的支持比较弱;
6、破坏程序的异常机制;
7、不容易调试。
axios:
1、从node.js创建http请求;
2、支持Promise API;
3、客户端防止CSRF(网站恶意利用);
4、自动转化json数据;
5、提供了一些并发请求的接口。
3. 挂载前:beforeMount(){}
mount [maʊnt]
执行这个钩子的时候你在内存中已经编译好了模板,模板与数据进行结合,但是还没有挂载到页面上,因此我们可以在当前生命周期中进行数据的修改。
4. 挂载完成:mounted(){}
mounted ['maʊntɪd]
执行到这个钩子的时候,表示Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。如果我们想要通过插件操作页面上的DOM节点,最早可以在这个阶段中执行
当前生命周期数据和模板进行相结合,并且已经挂载到页面上了,因此我们可以在当前生命周期中获取到真实的DOM元素,还可以在当前生命周期中做方法的实例化,给元素添加一个ref属性,且值必唯一,通过:this.$refs.属性获取DOM元素。
5. 更新前:beforeUpdate(){}
当执行这个钩子的时,页面中的显示的数据还是旧的,data中的数据是更新后的,页面还没有和最新的数据保持同步
当数据发生改变的时候当前生命周期就会执行,因此我们可以通过当前生命周期来检测数据的变化,当前生命周期执行的时候会将更新的数据与模板进行相结合,但是并没有挂载到页面上,因此我们可以在当前生命周期中做更新数据的最后修改。
6. 更新完成:updated(){}
页面显示的数据和data中的数据已经保持同步,都是最新的
数据与模板进行相结合,并且将更新后的数据挂载到了页面上。因此我们可以在当前生命周期中获取到最新的DOM结构(注:在当前生命周期中如果做实例化操作的时候须做条件判断)。
7. 销毁前:beforeDestroy(){}
当前生命周期中我们需要做事件的解绑,监听的移除,定时器的清除等操作。
这个时间上所有的 data 和 methods,指令,过滤器。。。。处于可用状态。还没有真正被销毁
8. 销毁完成:destroyed(){}
destroy [dɪ’strɔɪ]
当前生命周期中我们需要做事件的解绑,监听的移除,定时器的清除等操作。(注:被keep-alive包裹的组件,不会被销毁,而是被缓存起来)。
这个时候上所有的data 和 methods ,指令 ,过滤器。。。。。。都是出于不可用状态。组件已经被销毁了
第一次页面加载会触发beforeCreate、created、beforeMount、mounted, mounted说明dom渲染完毕
Vue生命周期在真实场景下的业务应用
created:进行ajax请求异步数据的获取、初始化数据
mounted:挂载元素dom节点的获取
nextTick:针对单一事件更新数据后立即操作dom
updated:任何数据的更新,如果要做统一的业务逻辑处理
watch:监听数据变化,并做相应的处理
一段代码
首先,我们运行一段代码,看看这段代码会发生什么,然后再分析这段代码输出的结果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue生命周期</title>
</head>
<body>
<div id="app">
<p>{{message}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data() {
return {
message: 'vue生命周期'
}
},
beforeCreate() {
console.group('-----beforeCreate-----');
console.log("%c%s", "color:green", "el :" + this.$el);
console.log("%c%s", "color:green", "data :" + this.$data);
console.log("%c%s", "color:green", "message :" + this.message);
},
created() {
console.group('-----created-----');
console.log("%c%s", "color:green", "el: :" + this.$el);
console.log("%c%s", "color:green", "data :" + this.$data);
console.log("%c%s", "color:green", "message :" + this.message);
},
beforeMount() {
console.group('-----beforeMount-----');
console.log("%c%s", "color:green", "el: :" + this.$el);
console.log(this.$el);
console.log("%c%s", "color:green", "data :" + this.$data);
console.log("%c%s", "color:green", "message :" + this.message);
},
mounted() {
console.group('-----mounted-----');
console.log("%c%s", "color:green", "el: :" + this.$el);
console.log(this.$el);
console.log("%c%s", "color:green", "data :" + this.$data);
console.log("%c%s", "color:green", "message :" + this.message);
},
beforeUpdate() {
console.group('-----beforeUpdate-----');
console.log("%c%s", "color:green", "el: :" + this.$el);
console.log(this.$el);
console.log("%c%s", "color:green", "data :" + this.$data);
console.log("%c%s", "color:green", "message :" + this.message);
},
updated() {
console.group('-----updated-----');
console.log("%c%s", "color:green", "el: :" + this.$el);
console.log(this.$el);
console.log("%c%s", "color:green", "data :" + this.$data);
console.log("%c%s", "color:green", "message :" + this.message);
},
befogreenestroy() {
console.group('-----befogreenestroy-----');
console.log("%c%s", "color:green", "el: :" + this.$el);
console.log(this.$el);
console.log("%c%s", "color:green", "data :" + this.$data);
console.log("%c%s", "color:green", "message :" + this.message);
},
destroyed() {
console.group('-----destroyed-----');
console.log("%c%s", "color:green", "el: :" + this.$el);
console.log(this.$el);
console.log("%c%s", "color:green", "data :" + this.$data);
console.log("%c%s", "color:green", "message :" + this.message);
}
})
</script>
</body>
</html>
结果分析
由上图的结果我们可以看到,vue
实例在创建过程中总共调用了四个钩子函数。
beforeCreate
和created
在beforeCreate
这个生命周期期间,初始化事件,并对数据进行观测,刚开始所有的数据,属性包括dom
节点都不可用,都为undefined
。可以看到在created
的时候此时数据已经和data
属性进行绑定,并且能打印出具体的值。在created
到beforeMount
这个阶段,首先会判断对象是否有el选项,如果有的话就继续向下编译,如果没有el选项,就会停止编译,也就意味着会停止生命周期,直到在该vue实例上调用vm.$mount(el)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue生命周期</title>
</head>
<body>
<div id="app">
<p>{{message + '这是外部html中的'}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
template: "<p>{{message + '这是在template中的'}}</p>",
data() {
return {
message: 'vue生命周期'
}
}
})
</script>
</body>
</html>
template参数选项的有无对生命周期的影响,如果Vue实例对象中有template参数选项,则将其作为模板编译成render函数;如果没有template选项,则将外部的HTML作为模板编译;根据上述代码执行的结果,可以发现template中的模板优先级要高于外部HTML的优先级。这也是为什么el的判断要在template之前了,因为vue需要通过el找到对应的外部template。另外vue对象中还有一个render函数,它是以createElement作为参数,然后做渲染操作,并且直接可以嵌入JSX。
render函数选项的优先级是高于template选项的。
beforeMount
和mounted
beforeMount
这个阶段给vue实例对象添加$el成员,并且替换掉了DOM元素。在这之前控制台打印的还是undefined。
mounted
这个阶段页面内容已经发生了变化,数据已经页面上渲染完毕。
beforeUpdate
和updated
当vue发现data中的数据发生了改变,会触发对应组件的重新渲染,会先后调用者两个钩子函数。如果在mounted
这个阶段改变数据值,是不会触发beforeUpdate
,beforeUpdate
是针对视图层的,视图层的数据发生改变才会触发这个钩子函数。
如果视图层数据被改变后,会触发beforeUpdate
,如果在beforeUpdate
里再一次改变数据,此时会再一次触发beforeUpdate
吗?答案是不会的。原因是:mounted
改变了数据,视图层的数据发生变化,此时触发beforeUpdate
,尽管beforeUpdate
再次改变了数据,但此时mounted
改变过后的数据还没有更新到视图层,因此在beforeUpdate
里再次变化数据的是没有更新到视图层的数据,当然不会再次触发beforeUpdate
。
那假如在updated
里改变数据呢?此时的由mounted
改变的数据已经跟新至视图层,此时在updated
改变数据就会触发beforeUpdate
。
####beforeDestroy
和destroyed
beforeDestroy
钩子函数在vue实例销毁之前调用,在这一步,实例仍然完全可用。
destroyed
钩子函数在vue实例销毁后调用。调用后,vue实例指示的所有东西都会解绑,所有的事件监听器会被移除,所有的子实例也会被销毁
2. 如何避免回调函数
1.模块化
将回调函数转换为独立的函数
2.使用流程控制库
例如 aync
等
3.使用Promis
4.使用aync/await
3.使用 NPM 有哪些好处
1. 对于项目方面
通过NPM,可以安装和管理项目的依赖,并且能够指明依赖项的具体版本号
2. 对于 Node开发应用而言
你可以通过 package.json
文件来管理项目信息,配置脚本,以疾指明依赖项的的具体版本
3. npm i xx -S 和 npm i xx -D的命令含义
npm i module_name -S = > npm install module_name --save //写入到 dependencies 对象
npm i module_name -D => npm install module_name --save-dev //写入到 devDependencies 对象
npm i module_name -g //全局安装
i 是install 的简写
-S就是–save的简写
-D就是–save-dev
用–save-dev安装的包的名称及版本号就会存在package.json的devDependencies这个里面 ,
而–save会将包的名称及版本号放在dependencies里面
我们在使用npm install 安装模块或插件的时候,有两种命令把他们写入到 package.json 文件里面去,比如:
–save-dev
–save
在 package.json 文件里面提现出来的区别就是,使用 --save-dev 安装的 插件,被写入到 devDependencies 对象里面去,而使用 --save 安装的插件,责被写入到 dependencies 对象里面去。
那 package.json 文件里面的 devDependencies 和 dependencies 对象有什么区别呢?
devDependencies 里面的插件只用于开发环境,不用于生产环境 ,而 dependencies 是需要发布到 生产环境 的。
补充:npm i与npm install的区别
一、背景
最近接手别人的vue项目,进行npm i 来安装依赖的时候一直出错,直到使用npm install 重新安装才成功,一直觉得疑惑,网上查阅,才知道它们之间是有些区别的。
二、区别
1、npm i 安装的模块及依赖,使用npm uninstall是没有办法删除的,必须使用npm uninstall i才可以删除
2、npm i 会帮助检测与当前node最匹配的npm的版本号,并匹配出相互依赖的npm包应该升级的版本号
3、npm i 安装的一些包,在当前的node版本下是没有办法使用的,必须使用建议版本
4、npm i安装出现问题是不会出现npm-debug.log文件的,但npm install 安装出现问题是有这个文件的。
5.npm uninstall xx的含义
卸载本地软件包
npm uninstall <package>
:从node_modules目录中移除一个包。
npm uninstall --save <package>
:从package.json的dependencies中移除一个包。
npm uninstall --save-dev <package>
:从package.json的devDependencies中移除一个包。
npm uninstall -g <package>
:卸载全局软件包。
实际操作时,发现使用npm uninstall <package>
不仅会在node_modules目录下删除该包,还会将该包在package.json中dependencies或devDependencies里面的信息删除。
总结:本地命令加上-g就是全局命令。
6.项目转换的时候,不会把node_modules包发过去,那么你拿到没有nodeModules目录的项目怎么让它跑起来
npm install 安装所有的依赖
3.18日面试题
1. 一般来说是针对不同的浏览器写不同的css,就是 CSS Hack
CSS hack由于不同厂商的浏览器,比如Internet Explorer,Safari,Mozilla Firefox,Chrome等,或者是同一厂商的浏览器的不同版本,如IE6和IE7,对CSS的解析认识不完全一样,因此会导致生成的页面效果不一样,得不到我们所需要的页面效果。 这个时候我们就需要针对不同的浏览器去写不同的CSS,让它能够同时兼容不同的浏览器,能在不同的浏览器中也能得到我们想要的页面效果。
简单的说,CSS hack的目的就是使你的CSS代码兼容不同的浏览器。当然,我们也可以反过来利用CSS hack为不同版本的浏览器定制编写不同的CSS效果。
css hack的作用是:解决浏览器的兼容性问题(IE).
css hack的原理是: 通过选择器和样式属性的优先级来解决问题。
IE浏览器 Hack 一般又分为三种,条件Hack、选择符Hack(详细参考CSS文档:css文档)。例如:
// 1. 条件Hack
<!-- [if IE]>
<style>
.test{color: red;}
</style>
<![endif] -->
// 2.属性Hack
.test{
color:#090; /* For IE8 */
*color:#f00; /* For IE7 and earlier */
_color:#ff0;/* For IE6 and earlier */
}
// 3.选择符Hack
* html .test{color:#090;} /* For IE6 and earlier */
* + html .test{color:#ff0;} /* For IE7 */css
条件Hack
语法:
<!--[if <keywords>? IE <version>?]>
HTML代码块
<![endif]-->
取值:
<keywords>
if条件共包含6种选择方式:是否、大于、大于或等于、小于、小于或等于、非指定版本
是否:
指定是否IE或IE某个版本。关键字:空
大于:
选择大于指定版本的IE版本。关键字:gt(greater than)
大于或等于:
选择大于或等于指定版本的IE版本。关键字:gte(greater than or equal)
小于:
选择小于指定版本的IE版本。关键字:lt(less than)
小于或等于:
选择小于或等于指定版本的IE版本。关键字:lte(less than or equal)
非指定版本:
选择除指定版本外的所有IE版本。关键字:!
说明:
用于选择IE浏览器及IE的不同版本
-
if条件Hack是HTML级别的(包含但不仅是CSS的Hack,可以选择任何HTML代码块)
-
如不想在非IE中看到某区域,可这样写:
<!--[if IE]> <p>你在非IE中将看不到我的身影</p> <![endif]-->
-
上述p代码块,将只在IE中可见。
属性Hack
语法:
selector{
<hack>?property:value<hack>?;
}
取值:
_:
选择IE6及以下。连接线(中划线)(-)亦可使用,为了避免与某些带中划线的属性混淆,所以使用下划线(_)更为合适。
*:
选择IE7及以下。诸如:(+)与(#)之类的均可使用,不过业界对(*)的认知度更高
\\9:
选择IE6+
\\0:
选择IE8+和Opera
[;property:value;];:
选择webkit核心浏览器(Chrome,Safari)。IE7及以下也能识别。中括号内外的3个分号必须保留,第一个分号前可以是任意规则或任意多个规则
[;color:#f00;]; 与 [color:#f00;color:#f00;]; 与 [margin:0;padding:0;color:#f00;]; 是等价的。生效的始终是中括号内的最后一条规则,所以通常选用第一种写法最为简洁。
说明:
选择不同的浏览器及版本
-
尽可能减少对CSS Hack的使用。Hack有风险,使用需谨慎
-
通常如未作特别说明,本文档所有的代码和示例的默认运行环境都为标准模式。
-
一些CSS Hack由于浏览器存在交叉认识,所以需要通过层层覆盖的方式来实现对不同浏览器进行Hack的。如下面这个例子:
如想同一段文字在IE6,7,8显示为不同颜色,可这样写:
.test{ color:#090\\9; /* For IE8+ */ *color:#f00; /* For IE7 and earlier */ _color:#ff0; /* For IE6 and earlier */ }
-
上述Hack均需运行在标准模式下,若在怪异模式下运行,这些Hack将会被不同版本的IE相互识别,导致失效。
选择符级Hack
语法:
<hack> selector
{ sRules }
说明:
选择不同的浏览器及版本
- 尽可能减少对CSS Hack的使用。Hack有风险,使用需谨慎
- 通常如未作特别说明,本文档所有的代码和示例的默认运行环境都为标准模式。
- 一些CSS Hack由于浏览器存在交叉认识,所以需要通过层层覆盖的方式来实现对不同浏览器进行Hack的。
- 简单列举几个:
* html .test{color:#090;} /* For IE6 and earlier */
* + html .test{color:#ff0;} /* For IE7 */
.test:lang(zh-cn){color:#f00;} /* For IE8+ and not IE */
.test:nth-child(1){color:#0ff;} /* For IE9+ and not IE */
补充:
2、如何消除一个数组里面重复的元素
var arr1 = [1,2,2,2,3,3,3,4,5,6],
arr2 = [];
for(var i = 0, len=arr1.length;i<len;i++){
if(arr2.indexOf(arr1[i]) < 0){
arr2.push(arr1[i])
}
}
document.write(arr2); // 1,2,3,4,5,6
数组去重其他方法
关于数组其他知识点
js常见算法(一):排序,数组去重,打乱数组,统计数组各个元素出现的次数, 字符串各个字符的出现次数,获取地址链接的各个参数
3、写一个 function,清除字符串前后的空格。(兼容所有浏览器)
function trim(str){
if(str & typeof str === "string"){
return str.replace(/(^s*)|(s*)$/g,'');//去除前后空白符
}
}
其他方法:
4. 一次完整的HTTP事务是怎样的一个过程
基本流程:
a. 域名解析
b. 发起 TCP 的3次握手
c. 建立 TCP 连接后发起的 http 请求
d. 服务器端响应 http 请求,浏览器得到 html 代码
e. 浏览器解析 html 代码,并请求 html 代码中的资源
f. 浏览器对页面进行渲染呈现给用户
5.JS中的函数声明提升和变量声明提升
变量声明提升
1、变量定义
可以使用var定义变量,变量如果没有赋值,那变量的初始值为undefined。
var a //声明一个变量,但是没有赋值,则其值和数据类型都是 undefined
console.log(a);
a = 10
2、变量作用域
变量作用域指变量起作用的范围。变量分为全局变量和局部变量。全局变量在全局都拥有定义;而局部变量只能在函数内有效。
在函数体内,同名的局部变量或者参数的优先级会高于全局变量。也就是说,如果函数内存在和全局变量同名的局部变量或者参数,那么全局变量将会被局部变量覆盖。
所有不使用var定义的变量都视为全局变量
函数的声明方式
函数声明有三种方式:函数声明,函数表达式(又称函数字面量声明),函数对象的声明(使用率很低)
方式一:函数声明
function 函数名(形参列表){
//方法体
}
方式二:函数表达式(又称函数字面量声明)
var 变量名=function 函数名(形参列表){ 备注:这个函数名有点特别,它只能在它自己这个函数体内被调用。不能被外部访问,有兴趣的自己测试测试。
//方法体
}
方式三:函数对象的声明
var 方法名 =new Function("形参1","形参2","形参3", "方法体");
只有函数声明才会函数声明提升,其他两种不会函数声明提升。
三种不同的函数声明方式
<script>
function test0 (){
return 0
}
var test1;
var test2 ;
alert(test0)//function (){return 1}
alert(test1)//undefined
alert(test2)//undefined
test1 = function (){
return 1
}
test2 = new Function ( "return 2")
</script>
上面的代码才是真正的js执行顺序。这里面的test1和test2提升是作为变量提升的。
函数声明提升和变量声明提升
-
当先使用变量,后定义变量时,变量的声明会提升
var b = 10 function b () { console.log('b'); } console.log(b)
-
当函数名称与变量名称重名时,函数的优先级更高
console.log(b)
var b = 10
function b () {
console.log('b');
}
解决提升为问题的最好方法直接使用 let
方法声明函数,让js代码变成严格模式
3.19日面试题
1. 说一下自己常用的es6的功能
1. let / const
es6 以前,都是用 var 关键字来标识,这样有个变量提升的坑。在 es6 中,添加了 let 和 const 两个关键字,let 定义变量,const 定义常量,并且添加了块级作用域。看下用法:
let:
let a = 1
a = 100
编译结果:
var a = 1;
a = 100;
const:
const b = 2
编译结果:
var b = 2;
再看一个:
const b = 2
b = 200
出错:
SyntaxError: es6-demo.js: "b" is read-only
说 b 是只读,说明 const 定义的是常量。在实际项目中,如有不希望别人改的变量,就可以用 const 定义,也是很方便了。
顺便看一下var的变量提升:
console.log(a); // undefined
var a = 100
在定义之前输出变量 a ,是 undefined,他其实相当于这样子写:
var a
console.log(a);
a = 100
es6 以前,js 引擎将所有的变量都提到最前面,初始化为 undefined。
2. 多行字符串 / 模板变量
在 es6 之前,字符串的拼接是这样的:
var name = "李四"
var age = 18
var myIntro = ''
myIntro += 'my name is ' + name + ', '
myIntro += 'my age is ' + age
在 es6 中,可以使用模板字符串 `` 和模板变量 ${ } :
const name = "李四"
const age = 18
const myIntro = `my name is ${name},
my age is ${age}
`
这样使用起来很方便,避免字符串拼接中出现的不必要的错误,而且更简单简洁,最重要的是人易懂!需要注意的是 ${ } 要和 `` 一起使用不然不会被解析。
3. 解构赋值
顾名思义,就是先解构,再赋值!
比如先定义一个对象和数组:
var obj = {
a: 1,
b: 2,
c: 3
}
var arr = [1,2,3]
在 es6 以前,这样获取属性的值:
obj.a
obj.b
arr[i]
在 es6 中:
const {a,c} = obj
const [x,y,z] = arr
很简单就可以获取对象的属性和数组的值,看下编译得结果:
var obj = {
a: 1,
b: 2,
c: 3
};
var a = obj.a,
c = obj.c;
var arr = [1, 2, 3];
var x = arr[0],
y = arr[1],
z = arr[2];
看明白了吗?
就是使用 const {a,c} = obj 这种方式获取对象的属性的方法时,大括号中的变量对象对象的属性,使用 const [x,y,z] = arr 这种方式获取数组的元素,中括号中的变量的索引对应真正数组的索引,即:x 是第一个,对应 arr 数组中的第一个,z 是数组的第三个,对应 arr 数组的第三个。
4. 块级作用域
在 es6 以前:
var obj = {
a: 1,
b: 2,
c: 3
}
for (var item in obj) {
console.log(item);
}
console.log(item);
变量 item 其实是在循环外部,咱们预想是访问不到的,但是实际是可以访问到的,以上写法相当于将 var item 提到最前面,就好理解了,这样子的话变量 item 相当于是在全局都可以访问的,这与我们的预期是相违背的。
再来看看 es6 中:
const obj = {
a: 1,
b: 2,
c: 3
}
for (let key in obj) {
console.log(key);
}
console.log(key);
因为有块级作用域的概念,所以循环中的 key 变量只在循环中能使用,咱们编译一下:
var obj = {
a: 1,
b: 2,
c: 3
};
for (var _key in obj) {
console.log(_key);
}
console.log(key);
很明显,循环里面的 key 和 外面的 key 不是同一个东西!
5. 函数默认参数
首先来设定一个场景:有一个函数 test ,可能给它传一个参数,也可能传两个,传一个参数时,第二个参数给个默认值,在 es6 以前这样判断:
function test (a, b) {
if (b == null) {
b = 0
}
}
但是在 es6 中写法非常简单:
function test (a, b = 0) {
// ...
}
咱们编译一下,康康他到底是个啥:
function test(a) {
// ...
var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
}
原来如此!es6的写法也就是在内部做了一个判断:如果参数的个数大于1,并且第二个参数不是undefined,那么就将第二个参数赋值给 b,否则b = 0。这个跟上面的 es6 之前的判断是一致的,这种写法特别简单、易读!
6. 箭头函数
用 map 遍历数组
es6 以前:
var arr = [100, 200, 300]
arr.map(function (item) {
return item + 1
})
es6 写法:
const arr = [100, 200, 300]
arr.map(item => item + 1)
当函数只有一个参数,并且函数体中只有一行代码时,可以简写成上面的形式,参数有多个,函数体中超过一行这样写:
arr.map((item,index) => {
console.log(index, item);
return item + 1
})
编译看一下:
var arr = [100, 200, 300];
arr.map(function (item) {
return item + 1;
});
arr.map(function (item, index) {
console.log(index, item);
return item + 1;
});
好吧,,其实就是把箭头函数转换成普通函数。。
你以为箭头函数到这儿就完了???
this 指向:
运行下面脚本:
function test () {
console.log('test', this);
}
test.call({a: 100})
结果:
test {a: 100}
在其中加一个map:
function test () {
console.log('test', this);
var arr = [1,2,3]
arr.map(function (item) {
console.log(item, this);
})
}
test.call({a: 100})
结果:
很明显,map 中的 this 是 window 对象。这是 js 的一个大坑,在这个情况下,通过 call 绑定了一个 this 是 { a: 100 } 这个对象,但是 map 中的 this 却是 window 。这是因为 this 的取值是在其调用时决定的,而 map 中的函数相当于调用一个普通函数,而普通函数的作用域在全局,其 this 就指向了 window 。而箭头函数韩浩的解决了这个问题,箭头函数中的 this 与其上一个作用域的 this ,在此处中,map 中的 上一级作用域是 test 函数,而 test 函数的 this 是 { a: 100 } 对象。
function test () {
console.log('test', this);
var arr = [1,2,3]
arr.map( item => {
console.log(item, this);
})
}
test.call({a: 100})
结果:
7. 模块化
在现在多个人开发同一个项目很常见,每个人负责不同的模块,还有可能会几个人使用同一个模块,在这种情况下,模块化就很重要!其实使用起来也很简单,比如说有模块A、B、C三个 js 文件,各自在其中定义好自己的代码,使用 export 关键字导出自己的东西,别人使用时用 import 关键字引用即可,模块化的处理工具有 webpack、rollup 等。如果你有兴趣,详情可以看看这一篇博客ES6 模块化如何使用,开发环境如何打包,欢迎指正。
8. class
标题是 class, 但是实际上应该说是构造函数:
es6以前,js 构造函数:
function MathPlus (x, y) {
this.x = x
this.y = y
}
MathPlus.prototype.getAddResult = function () {
return this.x + this.y
}
var testAdd = new MathPlus(1,2)
console.log(testAdd.getAddResult()); // 3
es6 中 class 写法:
class MathPlus {
constructor(x, y) {
this.x = x
this.y = y
}
getAddResult() {
return this.x + this.y
}
}
const testAddEs6 = new MathPlus(3,4)
console.log(testAddEs6.getAddResult()); // 7
js 定义构造函数和 es6 中的 class 的所实现目的一样的,但是 class 就看起来更加清晰、易懂!在 js 的构造函数中,在其原型上定义方法从而实现,而 class 中直接在 { } 中写函数就可以直接实现这个类中有这个函数了。
本质:
其实 class 的本质其实是一个语法糖:
理解起来就是,二者实现的东西是一样的,只是有个的写法更简洁、易读、易懂。对应一下,其实 class 所实现的东西和 js 的构造函数是一样的,class 的实现原理和 js 构造函数是一样的,都是通过原型实现的。
console.log(typeof MathPlus); // function
console.log( MathPlus.prototype === testAddEs6.__proto__ ); // true
console.log( MathPlus === MathPlus.prototype.constructor ); // true
以上的结果和 js 的构造函数是一致的,MathPlus 这个 class 的本质是一个function ,其实例 testAddEs6 有一个隐式原型 proto ,并且和 MathPlus 的 prototype 三等。
关于继承:
js 实现继承:
function Math (x, y) {
this.x = x
this.y = y
}
Math.prototype.getSubResult = function () {
return this.x - this.y
}
// 通过绑定原型,实现继承
MathPlus.prototype = new Math()
var testAdd = new MathPlus(1,2)
// console.log(testAdd.getAddResult());
console.log(testAdd.getSubResult()); // -1
class 继承:
class Math {
constructor(x, y) {
this.x = x
this.y = y
}
getSubResult() {
return this.x - this.y
}
}
class MathPlus extends Math {
constructor(x, y) {
super(x, y) // 重点!!!自动实现父类的 constructor
this.x = x
this.y = y
}
getAddResult() {
return this.x + this.y
}
}
const testAddEs6 = new MathPlus(3,4)
console.log(testAddEs6.getAddResult()); // 7
console.log(testAddEs6.getSubResult()); // -1
console.log(testAddEs6.__proto__ === MathPlus.prototype); // true
console.log(MathPlus.prototype.__proto__ === Math.prototype); // true
看到结果其实就更能体现 class 实际上是通过原型实现的!
9. promise
在项目中涉及到网络请求资源时,就要调用后端提供的接口,常用的jQuery、axios、fetch等,我用的最多的就是 axios ,jQuery 也用过,不过很少。。,说正题!调用接口之后就有回调函数,成功与否都有相应的回调函数,这个是异步的,但是当请求比较复杂时,会出现回调地狱(callback hell),比如说:根据接口1 获取 data1 ,在其中调用接口2 获取data 3…,用 promise 就很简单,至少看起来不会那么复杂。
首先我们先封装一个根据接口 URL 获取数据的通用方法 getData :
function getData(url) {
return new Promise((resolve, reject) => {
$.ajax({
url,
success: (data) => {
resolve(data)
},
error: (data) => {
reject(data)
}
})
})
}
getData 函数中,返回了一个 promise 实例,resolve 表示成功时调用的回调函数,reject 表示失败时调用的回调函数。那么,对于上面比较复杂的情况就可以写成:
getData(url1).then(data1 => {
console.log(data1);
return getData(url2)
}).then(data2 => {
console.log(data2);
return getData(url3)
}).catch(err => {
console.log(err);
})
是不是很清晰?每一次回调 then 中的 data 来自上一层的数据返回。
原文链接:https://zhuanlan.zhihu.com/p/334705702
2. link和@import的区别
两者都是外部引用css的方式,但是存在一定的区别:
- link 是 XHTML 标签,除了能够加载 CSS,还可以定义RSS等其他事务;而@import属于 CSS 范畴,只能加载CSS
- link 引入 CSS 时,在页面载入时同时加载;@import 需要页面完成载入以后再加载
- link 是 XHTML 标签,无兼容问题;@import 则是 CSS2.1提出的,低版本的浏览器不支持
- link 支持使用 JavaScript 控制 DOM 改变样式,而 @import 不支持
3. 请描述下 cookies,sessionStorage 和 localStorage 的区别
cookie 是网站为了表示用户身份而储存在用户本地终端(client Side)上的数据 (通常经过加密)
cookie数据始终在同源的 http 请求中携带(即使不需要),机会再浏览器和服务其间来回传递
sessionStorage 和 localStorage 不会自动把数据发给服务器,尽在本地保存
存储大小:
- cookie 数据大小不能超过 4k
- sessionStorage 和 localStorage,虽然也有存储大小的限制,但比 cookie 大得多,可以达到5M或更大
有效时间:
- localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据
- sessionStorage 数据在当前浏览器窗口关闭后自动删除
- cookie 设置的cookie 过期时间之前一直有效,即使窗口或浏览器关闭
3.23 面试题
css 面试题
1.介绍一下标准的CSS的盒子模型?与低版本IE的盒子模型有什么不同的?
标准盒子模型:宽度 = 内容的宽度 (content) + border + padding + margin
低版本 IE盒子模型:宽度 = 内容宽度 (content + border + padding) + margin
用来控制元素的盒子模型的解析模式,默认为content-box
content-box: W3C 的标准盒子模型,设置元素的 height/width 属性指的是 content 部分的 高/宽
border-box: IE 传统盒子模型。设置元素的 height/width 属性指的是 border + padding + content 部分的 高/宽
2.CSS选择器有哪些?哪些属性可以继承?
CSS 选择器无疑是其核心之一,对于基础选择器以及一些常用伪类必须掌握。下面列出了常用的选择器。 想要获取更多选择器的用法可以看 MDN CSS Selectors。
基础选择器
- 标签选择器:
h1
- 类选择器:
.checked
- ID 选择器:
#picker
- 通配选择器:
*
属性选择器
[attr]
:指定属性的元素;[attr=val]
:属性等于指定值的元素;[attr*=val]
:属性包含指定值的元素;[attr^=val]
:属性以指定值开头的元素;[attr$=val]
:属性以指定值结尾的元素;[attr~=val]
:属性包含指定值(完整单词)的元素(不推荐使用);[attr|=val]
:属性以指定值(完整单词)开头的元素(不推荐使用);
组合选择器
- 相邻兄弟选择器:
A + B
- 普通兄弟选择器:
A ~ B
- 子选择器:
A > B
- 后代选择器:
A B
继承性
在 CSS 中有一个很重要的特性就是子元素会继承父元素对应属性计算后的值。比如页面根元素 html 的文本颜色默认是黑色的,页面中的所有其他元素都将继承这个颜色,当申明了如下样式后,H1 文本将变成橙色。
body {
color: orange;
}
h1 {
color: inherit;
}
设想一下,如果 CSS 中不存在继承性,那么我们就需要为不同文本的标签都设置一下 color,这样一来的后果就是 CSS 的文件大小就会无限增大。
CSS 属性很多,但并不是所有的属性默认都是能继承父元素对应属性的,那哪些属性存在默认继承的行为呢?一定是那些不会影响到页面布局的属性,可以分为如下几类:
- 字体相关:
font-family
、font-style
、font-size
、font-weight
等; - 文本相关:
text-align
、text-indent
、text-decoration
、text-shadow
、letter-spacing
、word-spacing
、white-space
、line-height
、color
等; - 列表相关:
list-style
、list-style-image
、list-style-type
、list-style-position
等; - 其他属性:
visibility
、cursor
等;
对于其他默认不继承的属性也可以通过以下几个属性值来控制继承行为:
inherit
:继承父元素对应属性的计算值;initial
:应用该属性的默认值,比如 color 的默认值是#000
;unset
:如果属性是默认可以继承的,则取inherit
的效果,否则同initial
;revert
:效果等同于unset
,兼容性差。
3.css3有哪些新特性
- background-image
- background-origin(content-box/padding-box/border-box)
- background-size
- background-repeat
- word-wrap(对 长的不可分割单词换行) word-wrap: break-word
- 文字阴影: text-shadow: 5px 5px 5px #FF0000; (水平阴影,垂直阴影,模糊距离,阴影颜色)
- font-face 属性:定义自己的字体
- 圆角 (边框半径) : border-radius 属性用于创建圆角
- 边框图片:border-image: url(border.png) 30 30 round
- 盒阴影:box-shadow: 10px 10px 5px #888888
- 媒体查询:定义两套 CSS,当浏览器的尺寸变化时会采用不同的属性
4.px和em的区别
px 和 em 都是长度单位
区别是:
- px 的值是固定的,指定是多少就是多少,计算比较容易。
- em 的值是不固定的,并且 em 汇集成父元素的字体大小
- 浏览器的默认字体高都是16px。所以未经调整的浏览器都符合:1em = 16px。那么 12px=0.75em,10px=0.625em
3.25 面试题
js 面试题
更多js面试题请看 Javascript前端经典的面试题及答案
1.JavaScript 的 typeof 返回哪些数据类型?
alert(typeof null); // object
alert(typeof undefined); // undefined
alert(typeof NaN); // number
alert(NaN == undefined); // false
alert(NaN == NaN); // false
alert(NaN === NaN); // false
var str = "123abc";
alert(typeof str++); // number
alert(str); // NaN
var a = [];
var b = a;
alert(a==b);//true
b = [];
alert(a==b);//false
// 批注:每次使用 [] 都是新建一个数组对象。当数组比较的时候其实比较的是他们的引用。[] == []的时候,从值上尽管两边都是[]但是从引用上两边是不相等的。
JavaScript 中的基本类型为:
- Undefined
- Null
- Boolean
- Number
- String
在此对 NaN 做个扩展
NaN 是一个特殊数值,和 0 平等。在 IEEE754 规范里它表示非实数, 表明非数值,这个数值用于保存一个本来要返回数值的操作数未返回数值的情况
String 则是一个函数,JavaScript 里的构造器都是函数。
NaN 有个独一无二的特性:它不等同于任何值,包括它自己。
它有两个特点:
- 任何涉及 NaN 的操作都会返回 NaN
- NaN 与任何值都不相等,包括它本身
NaN === NaN // false
基于这两个特点,ECMAScript 定义了 isNaN() 函数,这个函数接受一个参数,返回结果表示参数是否为 NaN ,任何不能被转换成数值的值都会导致这个函数返回 true
另一个比较坑的东西是 isNaN 这个函数,和 NaN 这个值本身不是一回事:
isNaN('abc') // true
isNaN(123) // false
isNaN(NaN) // true
isNaN(undefined) // true
// 如果字符串是合法的数字表达式则会返回 false,很有用
isNaN('123') // false
isNaN('0.1') // false
isNaN('6e3') // false
// 但是...
isNaN(true) // false
isNaN(false) // false
isNaN(null) // false
对此题做一个扩展
JS中 [] == ![]结果为true,而 {} == !{}却为false
console.log( [] == ![] ) // true
console.log( {} == !{} ) // false
在比较字符串、数值和布尔值的相等性时,问题还比较简单。但在涉及到对象的比较时,问题就变得复杂了。最早的ECMAScript中的相等和不相等操作符会在执行比较之前,先将对象转换成相似的类型。后来,有人提出了这种转换到底是否合理的质疑。最后,ECMAScript的解决方案就是提供两组操作符:
相等和不相等——先转换再比较 (==)
全等和不全等——仅比较而不转换 (===)
ECMAScript中相等操作符由两个等于号(==)表示,如果两个操作数相等,则返回true,这种操作符都会先转换操作数(通常称为强制转型),然后再比较它们的相等性
在转换不同的数据类型时,对于相等和不相等操作符:在JS高程中一书中给出如下的基本转换规则
-
如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1;
-
如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值
-
如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较
这两个操作符在进行比较时则要遵循下列规则。
-
null 和undefined 是相等的
-
要比较相等性之前,不能将null 和 undefined 转换成其他任何值
-
如果有一个操作数是NaN,则相等操作符返回 false ,而不相等操作符返回 true。重要提示:即使两个操作数都是NaN,相等操作符也返回 false了;因为按照规则, NaN 不等于 NaN
-
如果两个操作数都是对象,则比较它们是不是同一个对象,如果两个操作数都指向同一个对象,则相等操作符返回 true;否则, 返回false
这里说一下题外话 :[] 和 {} 都是属于引用类型,引用类型是存放在堆内存中的 ,而在栈内存中会有一个或者多个地址来指向这个堆内存相对应的数据。所以在使用 == 操作符的时候,对于引用类型的数据,比较的是地址,而不是真实的值。
现在来探讨 [] == ! [] 的结果为什么会是true
①、根据运算符优先级 ,! 的优先级是大于 == 的,所以先会执行 ![]
!可将变量转换成boolean类型,null、undefined、NaN以及空字符串(’’)取反都为true,其余都为false。
所以 ! [] 运算后的结果就是 false
也就是 [] == ! [] 相当于 [] == false
②、根据上面提到的规则(如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1),则需要把 false 转成 0
也就是 [] == ! [] 相当于 [] == false 相当于 [] == 0
③、根据上面提到的规则(如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较,如果对象没有valueOf()方法,则调用 toString())
而对于空数组,[].toString() -> ‘’ (返回的是空字符串)
也就是 [] == 0 相当于 ‘’ == 0
④、根据上面提到的规则(如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值)
Number(’’) -> 返回的是 0
相当于 0 == 0 自然就返回 true了
总结一下:
[] == ! [] -> [] == false -> [] == 0 -> ‘’ == 0 -> 0 == 0 -> true
那么对于 {} == !{} 也是同理的
关键在于 {}.toString() -> NaN(返回的是NaN)
根据上面的规则(如果有一个操作数是NaN,则相等操作符返回 false)
总结一下:
{} == ! {} -> {} == false -> {} == 0 -> NaN == 0 -> false
原文链接:https://blog.csdn.net/magic_xiang/article/details/83686224
2.列举至少 3 中强制类型转换和 2 中隐式类型转换
-
强制类型转换:明确调用内置函数,强制把一种类型的值转换为另一种类型。强制类型转换主要有:
Boolean、Number、String、parseInt、parseFloat
-
隐式类型转换:在使用算数运算符时:运算符两边的数据类型可以是任意的,比如,一个字符串可以和数字相加。之所以不同的数据类型之间可以做运算,是因为 JavaScript 引擎在运算之前会悄悄的把他们进行隐式类型转换。隐式类型转换主要有: +、-、==、!
3.JavaScript 的事件流模型都有什么?
事件流描述的是从页面中接收事件的顺序。 DOM 结构是树形结构,当页面中的某一个元素触发了某个一个事件,事件会从最顶层的 window 对象开始,向下传播到目标元素,途径的祖先节点都会触发对应的事件,如果当前节点的该事件绑定了事件处理函数的话,则会执行该函数当事件达到目标元素并执行绑定函数(如果有绑定的话)后,事件又会向上传播到 window 元素,途径的祖先节点都会触发对应的事件(如果绑定事件处理函数的话)
事件流包含三个阶段:
-
事件捕捉阶段
-
处于目标阶段
-
事件冒泡阶段
事件捕捉阶段:事件开始由顶层对象触发,然后逐级向下传播,直到目标的元素;
处于目标阶段:处在绑定事件的元素上;
事件冒泡阶段:事件由具体的元素先接收,然后逐级向上传播,直到不具体的元素;
3.26 面试题
1. BOM对象有哪些,列举 window 对象?
- Window 对象 是 JS 的最顶层对象,其他的 BOM 对象都是 Window 对象的属性
- document 对象 :文档对象
- location 对象:浏览器当前 URL 信息
- navigator 对象:浏览器本身信息
- screen 对象:客户端屏幕信息
- history 对象:浏览器访问历史信息
2. 请简述 AJAX 及基本步骤?
简述 AJAX :
AJAX 即 “Asynchronous JavaScript And XML” (异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新
AJAX 基本步骤:
- 初始化 ajax 对象
- 链接地址,准备数据
- 发送请求
- 接收数据(正在接收,尚未完成)
- 接受数据完成
// 初始化 ajax对象
var xhr = new XMLHttpRequest();
// 链接地址,准备数据
xhr.open("方式","地址",是否为异步);
// 接受数据完成触发的事件
xhr.onload = function(){};
// 发送数据
xhr.send();
3. HTTP 状态消息 200 302 304 404 500 分别表示什么?
- 200:请求已成功,请求所希望的响应头或数据体将随此响应返回
- 302:请求的资源临时从不同的 URL 响应请求 。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在 Cache-Contor 或者 Expires 中进行了指定的情况下,这个响应才是课缓存的
- 304:如果客户端发送了一个待条件的 GET 请求且该请求已被允许, 而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。304响应禁止包含消息体,因此始终以消息开头的第一个空行结束
- 403:服务器已经理解请求,但是 拒绝执行 它
- 404: 请求失败 ,请求所希望的到的资源未被在服务器上发现
- 500:服务器遇到了一个 未曾预料的状况 ,导致了它 无法完成对请求的处理 。一般来说,这个问题都会在服务器端的源代码出现错误时出现
3.29 面试题
1. GET 和 POST 的区别,何时使用 POST
GET 和 POST 的区别:
GET:一般用于查询数据,使用URL传递参数,由于浏览器对地址栏长度有限制,所以对使用 get 方式所发送信息的数量有限制,同时浏览器会记录(历史记录,缓存)中会保留请求地址的信息,包括地址后面的数据。get只能发送普通格式(URL 编码格式)的数据
POST: 一般用于向服务器发送数据,对所发送的数据的大小理论上是没有限制,浏览器会缓存记录地址,但是不会记录 post 提交的数据。post 可以发送纯文本、URL编码格式、二进制格式的字符串,形式多样
在以下情况中,使用 POST 请求:
- 以提交为目的的请求(类似语义化,get表示请求,post 表示提交)
- 发送私密类数据(用户名、密码)(因为浏览器缓存记录特性)
- 向服务器发送大量数据(数据大小限制区别)
- 上传文件图片时(数据类型区别)
2. js 字符串操作函数
函数 | 作用 |
---|---|
concat() | 将两个或多个字符串的文本组合起来,返回一个新的字符串 |
indexOf() | 返回字符串中一个子串第一处出现的索引,如果没有匹配项,返回-1 |
charAt() | 返回指定位置的字符 |
lastIndexOf() | 返回字符串中一个子串最后一处出现的索引,如果没有匹配项,返回-1 |
substr() | 可在字符串中抽取从 start 下标开始的指定数目的字符 |
slice() | 提取字符串中的一部分,并返回一个新字符串 |
replace() | 用来查找匹配一个正则表达式的字符串,然后使用新字符串代替匹配的字符串 |
split() | 通过将字符串划分为子串,将一个字符串做成一个字符串数组 |
length | 返回字符串的长度,所谓字符串的长度指其包含的字符的个数 |
toLowerCase() | 将整个字符串转成小写字母 |
toUpperCase() | 将整个字符串转成大写字母 |
3.30 面试题
1. js中数组常用的方法总结,包括ES6
1.push() 后增
push()方法可以向数组后添加一个新的元素,并返回新数组的长度。
末尾添加,返回长度,改变原数组
var a = [1,2,3]
var b = a.push(4)
console.log(a) // [1,2,3,4]
console.log(b) // 4
2.unshift() 前增
unshift()可以向数组前添加一个或多个元素,并返回新的长度
首部添加,返回长度,改变原数组
var a = [2,3,4]
var b = a.unshift(0,1)
console.log(a) // [0,1,2,3,4]
console.log(b) // 5
3.pop() 后删
pop() 用于删除并返回最后一个元素。
尾部删除,返回被删除的元素,改变原数组
var a = [1,2,3]
var b = a.pop()
console.log(a) // [1,2]
console.log(b) // 3
4.shift() 前删
shift() 用于删除并返回首个元素
删除首部元素,返回被删元素,改变原数组
var a = [1,2,3]
var b = a.shift()
console.log(a) // [2,3]
console.log(b) // 1
5. splice() 修该删除
splice(index,length,增加的元素1,增加的元素2…,增加的元素N) 表示从index开始删除length个元素,并从index开始新增元素1~N,放回被删除的元素组成的数组
对数组进行删除修改,返回被删除的元素组成的数组,改变原数组
var a = [1,2,3]
var b = a.splice(1,1,3,[2,3,4],5)
console.log(a) // [1,3,[2,3,4],5,3]
console.log(b) // [2]
6. concat() 拼接
concat() 方法用来合并两个或多个数组
合并两个或多个数组,返回新数组,不会改变原数组
var a = [1,2,3]
var b = [4,5]
var c = a.concat(b)
console.log(a) // [1,2,3]
console.log(b) // [4,5]
console.log(c) // [1,2,3,4,5]
7. slice() 剪切
slice(startIndex,endIndex) 返回从startIndex开始(包括),到endIndex(不包括)之间的原属组成的数组
返回新数组,不改变原数组
var a = [1,2,3]
var b = a.slice(0,1)
// 不填参数则表示剪切整个数组
var c = a.slice()
console.log(a) // [1,2,3]
console.log(b) // [1]
console.log(c) // [1,2,3]
console.log(a===c) // false // 注意 a !== c
// 负数表示从后往前数
var d = a.slice(-1,-2)
console.log(d) // [] 从左向右截取,所以说为[]
var e = a.slice(-1)
console.log(e) // [3]
8.join()
join() 方法用来将数组转换为字符串
不改变原数组,返回转换后的字符串
var a = [1,2,3,4,5]
console.log(a.join(',')) // 1,2,3,4,5
console.log(a) // [1,2,3,4,5]
9.sort() 排序
按ascii码排序
改变原数组,返回排序后的数组
var a = ['a','b','d','c']
console.log(a.sort()) // ['a','b','c','d']
console.log(a) // ['a','b','c','d']
10.reverse() 颠倒顺序
reverse() 方法用于颠倒数组中元素的顺序。
返回的是颠倒后的数组,会改变原数组。
var a = [1,3,2,7,6]
console.log(a.reverse()) // [6,7,2,3,1]
console.log(a) // [6,7,2,3,1]
11.indexOf()和lastIndexOf()
indexOf(某元素,startIndex) 从 startIndex 开始,查找某元素在数组中的位置,若存在,则返回第一个位置的下标,否则返回-1
lastIndexOf(某元素,startIndex) 和indexOf()相同,区别在于 从尾部向首部查询
不会改变原数组,返回找到的index,否则返回-1
若不使用下标,则一般通过includes()方法代替indexOf()
var a = [1,2,4,3,4,5]
console.log(a.indexOf(4)) // 2
console.log(a.indexOf(4,3)) // 4
12.filter() 过滤
filter() 方法返回数组中满足条件的元素组成的新数组,原数组不变
filter()的参数是一个方法
var a = [1,2,3,4,11]
// 第一个参数为一个方法,有三个参数,current:当前值 index:当前值下标 array:这个数组对象
var b = a.filter(function(current,index,array){
return current < 10
})
console.log(b) // [1,2,3,4]
console.log(a) // [1,2,3,4,11]
13.map() 格式化数组
map() 方法来根据需求格式化原数组,返回格式化后的数组。原数组不变
var a = [1,2,3,4,5]
// 参数同filter方法
var b = a.map(function(current,index,array){
return current + 1
})
console.log(b) // [2,3,4,5,6]
console.log(a) // [1,2,3,4,5]
14.every()
对数组的每一项都运行给定的函数,若每一项都返回 ture,则返回 true
var a = [1,2,3,4,5]
var b = a.every(function(current,index,array){
return current < 6
})
var c = a.every(function(current,index,array){
return current < 3
})
console.log(b) // true
console.log(c) // false
15.some()
对数组的每一项都运行给定的函数,若存在一项或多项返回 ture,则返回 true
var a = [1,2,3,4,5]
var b = a.some(function(current,index,array){
return current > 4
})
var c = a.some(function(current,index,array){
return current > 5
})
console.log(b) // true
console.log(c) // false
16.forEach() 数组遍历
遍历整个数组,中途不能中断
var arr = ['a','b','c']
var copy = []
arr.forEach(function(item){
copy.push(item)
})
console.log(copy)
ES6新增的方法
1.find()
找到数组中第一次满足条件的元素,并返回,若找不到则返回undefined。不改变原数组。
和filter()方法的区别在于:filter返回值是所有满足条件的元素组成的数组,
一般在需要使用找到的元素时,用find()方法
var a = [1,2,3,4]
// b在下面需要使用,则一般用find
var b = a.find(function(ele,index,array){
return ele == 1
})
var c = 3
var d = b + c
console.log(a) // [1,2,3,4]
console.log(b) // 1
console.log(d) // 4
// 若只需判断元素是否存在
// 若果是简单数组(非对象数组),则一般使用Array.includes(value)方法
// 如果为对象数组,则可以使用Array.some()方法
var a = [1,2,3]
console.log(a.includes(1)) // true
var a = [{"name": "xiaoming" },{"name": "xiaohong"}]
console.log(a.some(function(ele){
return ele.name == 'xiaoming'
})) // true
2.findIndex()方法
findIndex()的作用同indexOf(),返回第一个满足条件的下标,并停止寻找。
区别是findIndex() 的参数为一个回调函数,且一般用于对象数组
var a = [1,2,3,4]
var b = a.findIndex(function(ele,index,array){
return ele === 2
})
var c = a.indexOf(2)
console.log(a) // [1,2,3,4]
console.log(b) // 1
console.log(c) // 1
3.includes()
includes()方法,返回一个布尔值。 参数是一个value,一般用于简单数组。
对于复杂数组,则可以使用some()方法替代includes()方法
var a = [1,2,3]
console.log(a.includes(1)) // true
4.Array.isArray()方法
用来判断一个元素是否为数组
Array.isArray([]) // true
Array.isArray({}) // false
2.怎么增加、移除、移动、复制、创建和查找节点
1. 创建节点
createElement() //创建一个具体的元素
2. 添加、移除
appendChild() //添加
removeChild() //移动
3.查找
方法 | 作用 |
---|---|
document.getElementsByTagName() | 通过标签名称 获取的是数组 |
document.getElementsByName() | 通过元素的Name属性的值 |
document.getElementsById() | 通过元素Id,唯一性 |
document.querySelector(’#id’) | 查找元素 唯一性 |
document.querySelectorAll(’#id’) | 查找元素 获取的是数组 |
3. input 点击,获取取值
<input type="button" id="text" value="点击一下" />
<script type="text/javascript">
var btn = document.getElementById("text");
btn.onclick = function(){
alert(this.value); // 此处的 this 是按钮元素
}
</script>