听说成年人的自尊都是工资给的,我已经没有自尊两个多月了,哈哈哈
疫情影响很多小伙伴都被破离职了,下面整理了一些常见面试题,可以大家一起分享,程序员小白一个,如有问题,欢迎大佬们随时指点呀~~
1.前端优化可以做什么?
2.http和https的区别?
3.ajax的理解和优缺点?
4.什么是MVVM?
5.v-if和v-show的区别?
6.总结生命周期钩子函数
7.vuex的五个核心概念
8.vue路由钩子函数
9.什么情况会造成跨域,以及怎么解决跨域?
10.hash与history的区别?
11.vue的内容分发
12.深拷贝和浅拷贝的区别?
13.v-text、v-html(重点) 、v-pre 区别及用法
14.vue双向绑定原理
15.for循环结构遍历数组与key的作用分析
16.钩子函数参数
17.如何自定义指令指令?
18.父子组件传值以及非父子组件传值
19.computed计算属性与method方法的区别?
20.渐进增强和优雅降级之间的不同吗?
21.请描述一下cookies,sessionStorage和localStorage的区别?
22.清除浮动
23.什么是闭包,如何使用它,为什么要使用它?
24.JavaScript宿主对象和原生对象的区别?
25.call和.apply的区别是什么?
26.==和===有什么不同?
27.http和https有什么区别?
28.节流防抖
29.es6的新语法你了解哪些?
30.get和post的区别?
一、前端优化可以做什么?
1.减少http请求:
合并js和css文件(使用webpack或其它打包工具打包)
小图片使用雪碧图(需要的图片和ui说下,交给ui做就可以)
使用base64表示简单的图片
2.用内容分发网络cdn
cdn主要用于静态文件,如css,js和flash。
cdn,自动寻找最近的物理机服务器下载web组件
3.简js和css
使用jsmint和gzip精简文件,css精简技术点:#660066 优化 #606 ,提取 css ,js 公共方法,使用0代替0px
4.资源体积减少
gzip压缩
js混淆
css压缩
图片压缩
5.图片加载处理
图片懒加载
首屏加载时进度条的显示
懒加载详细:最初给图片的src设置一个比较简单的图片,然后将图片的真实地址设置给自定义的属性,做一个占位,然后给图片设置监听事件,一旦图片到达视口范围,从图片的自定义属性中获取出真是地址,然后赋值给src,让其进行加载。
首屏加载时进度条的显示:往对于首屏优化后的数据量并不满意的话,同时也不能进一步缩短首屏包的长度了,就可以使用进度条的方式,来提醒用户进行等待。
6.移动端优化
长列表滚动优化
函数防抖和函数节流
使用touchstart、touchend代替click
HTML的viewport设置
长列表滚动:ios尽量局部滚动,Android尽量全局滚动,同时需要给body添加上-webkit-overflow-scrolling: touch来优化移动段的滚动
防抖:止频繁滑动或者重复点击,当调用动作过了n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。
节流:预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期。
touchstart、touchend代替click:click在移动端会有300ms延时,事件执行顺序是touchstart->touchmove->touchend->click
HTML的viewport设置:可以防止页面的缩放,来优化性能。
二、http和https的区别?
1.https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2.http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3.http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4.http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
三、ajax的理解和优缺点?
ajax是无须进行刷新页面就可以请求后台的数据的一种方法,通过XmlHttpRequest对象来向服务器发送异步请求,从服务器中获取数据,然后通过js进行操作dom,以此来更新页面内容。
实现过程:
1.创建一个XmlHttpRequest的对象
2.设置响应HTTP请求的回调函数
3.创建一个HTTP请求,指定响应的请求方法、url、参数等
4.发送HTTP请求
5.获取服务端返回的数据
6.使用js操作dom更新页面的内容
缺点:
1.对搜索引擎不友好
2.要实现Ajax下的前后退功能成本较大
3.跨域问题限制
四、什么是MVVM?
M、V、VM 分别代表什么?
m model
数据层 Vue 中 数据层 都放在 data 里面
v view 视图
Vue 中 view 即 我们的HTML页面
vm (view-model) 控制器 将数据和视图层建立联系
vm 即 Vue 的实例 就是 vm
总结:
1、mvvm是把整个项目划分为三大块
2、vue实例vm是数据模型data和视图模板view的桥梁
3、当数据改变的时候,vm会通知视图进行改变
4、当视图改变的时候,如果用的是v-model,它会通知数据改变
5、总结:mvvm写代码的原则
(1) 只关心数据,不关心视图的DOM结构
(2) 数据和视图是一一对应的
(3) 不要去操作DOM,而是去操作数据
(4) 因为数据是响应式的
(5) 只要数据变了,视图自然就会跟着变
MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑
MVVM模式和以前的MVC模式一样,主要目的是分离(View)和模型(Model),有几大优点:低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。
可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
五、v-if和v-show的区别?
v-if 是“真正的”条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下, v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说, v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件不太可能改变,则使用 v-if 较好。
六、总结生命周期钩子函数
1.beforeCreate:在实例初始化之后,**数据观测(data observer) ** 和 event/watcher事件配置 之前被调用,注意是 之前,此时data、watcher、methods统统滴没有。
这个时候的vue实例还什么都没有,但是$route对象是存在的,可以根据路由信息进行重定向之类的操作。
2.created:在实例已经创建完成之后被调用。在这一步,实例已完成以下配置:数据观测(data observer) ,属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el属性目前不可见。
此时 this.$data 可以访问,watcher、events、methods也出现了,若根据后台接口动态改变data和methods的场景下,可以使用。
3.beforeMount:在挂载开始之前被调用,相关的 render 函数 首次被调用。但是render正在执行中,此时DOM还是无法操作的。我打印了此时的vue实例对象,相比于created生命周期,此时只是多了一个$el的属性,然而其值为undefined,
页面渲染时所需要的数据,应尽量在这之前完成赋值。
4.mounted:在挂载之后被调用。在这一步 创建vm.$el并替换el,并挂载到实例上。
此时元素已经渲染完成了,依赖于DOM的代码就放在这里吧~比如监听DOM事件。
5.beforeUpdate:$vm.data更新之后,虚拟DOM重新渲染 和打补丁之前被调用。
你可以在这个钩子中进一步地修改$vm.data,这不会触发附加的重渲染过程。
6.updated:虚拟DOM重新渲染 和打补丁之后被调用。
当这个钩子被调用时,组件DOM的data已经更新,所以你现在可以执行依赖于DOM的操作。但是不要在此时修改data,否则会继续触发beforeUpdate、updated这两个生命周期,进入死循环!
7.beforeDestroy:实例被销毁之前调用。在这一步,实例仍然完全可用。
实例要被销毁了,赶在被销毁之前搞点事情吧哈哈~
8.destroyed:Vue实例销毁后调用。此时,Vue实例指示的所有东西已经解绑定,所有的事件监听器都已经被移除,所有的子实例也已经被销毁。
这时候能做的事情已经不多了,只能加点儿提示toast之类的东西吧。
注:beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed这几个钩子函数,在服务器端渲染期间不被调用。
七、Vuex的五个核心概念
VueX 是一个专门为 Vue.js 应用设计的状态管理架构,统一管理和维护各个vue组件的可变化状态(你可以理解成 vue 组件里的某些 data )。
Vue有五个核心概念,state, getters, mutations, actions, modules。下面将对这个五个核心概念进行梳理。
state => 基本数据
getters => 从基本数据派生的数据
mutations => 提交更改数据的方法,同步!
actions => 像一个装饰器,包裹mutations,使之可以异步。
modules => 模块化Vuex
State
state即Vuex中的基本数据!
state提供唯一的公共数据源,所有共享的数据都要统一放到store的state中进行存储
Vuex使用单一状态树,即用一个对象就包含了全部的状态数据。state作为构造器选项,定义了所有我们需要的基本状态参数。
在Vue组件中获得Vuex属性
我们可以通过Vue的Computed获得Vuex的state,如下:
每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。
mapState辅助函数
当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
对象展开运算符
mapState 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。但是自从有了对象展开运算符,我们可以极大地简化写法:
对象运算符
... 展开运算符(spread operator)允许一个表达式在某处展开。展开运算符在多个参数(用于函数调用)或多个元素(用于数组字面量)或者多个变量(用于解构赋值)的地方可以使用。
展开运算符不能用在对象当中,因为目前展开运算符只能在可遍历对象(iterables)可用。iterables的实现是依靠[Symbol.iterator]函数,而目前只有Array,Set,String内置[Symbol.iterator]方法,而Object尚未内置该方法,因此无法使用展开运算符。不过ES7草案当中已经加入了对象展开运算符特性。
例子:
ES7草案中的对象展开运算符
ES6中还不支持对对象的展开运算符,但是ES7中将支持。对象展开运算符符可以让我们更快捷地操作对象,如下例子:
组件仍然保有局部状态
使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。
如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。
组件访问state中数据的两种方式
第一种方式:
第二种方式:
getters
即从store的state中派生出的状态。
getters接收state作为其第一个参数,接受其他getters 作为第二个参数,如不需要,第二个参数可以省略如下例子:
与state一样,我们也可以通过Vue的Computed获得Vuex的getters。
mapGetters 辅助函数仅仅是将 store 中的 getters 映射到局部计算属性,与state类似
如果你想将一个 getter 属性另取一个名字,使用对象形式:
mutations
提交mutation是更改Vuex中的store中的状态的唯一方法。
mutation必须是同步的,如果要异步需要使用action。
每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,提交载荷作为第二个参数。(提交荷载在大多数情况下应该是一个对象),提交荷载也可以省略的。
你不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:
对象风格的提交方式
我们也可以使用这样包含 type 属性的对象的提交方式。
Mutations 需遵守 Vue 的响应规则
最好提前在你的 store 中初始化好所有所需属性。
当需要在对象上添加新属性时,你应该
使用 Vue.set(obj, 'newProp', 123), 或者
以新对象替换老对象。例如,利用对象展开运算符我们可以这样写state.obj = {...state.obj, newProp: 123 }
mapMutations 辅助函数
与其他辅助函数类似,你可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。
actions
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
我们用如下例子来结束actions:
注意:Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
*分发actions
Action 通过 store.dispatch 方法触发:
*其他与mutations类似的地方
Actions 支持同样的载荷方式和对象方式进行分发:
*mapActions辅助函数
你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store):
import { mapActions } from 'vuex'
export default {
//..
methods: {
模块的局部状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态,对于模块内部的 getter,根节点状态会作为第三个参数:
同样,对于模块内部的 action,context.state 是局部状态,根节点的状态是 context.rootState
模块的局部状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态,对于模块内部的 getter,根节点状态会作为第三个参数:
}
}
Modules
使用单一状态树,导致应用的所有状态集中到一个很大的对象。但是,当应用变得很大时,store 对象会变得臃肿不堪。
为了解决以上问题,Vuex 允许我们将 store 分割到模块(module)。每个模块拥有自己的 state、mutation、action、getters、甚至是嵌套子模块——从上至下进行类似的分割:
模块的局部状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态,对于模块内部的 getter,根节点状态会作为第三个参数:
同样,对于模块内部的 action,context.state 是局部状态,根节点的状态是 context.rootState:
总结下stateMutationActionGetter
state :所有的共享数据都要统一放到store的state中进行存储
mutation : 用于修改store中的数据
action :因为在mutations中不能写异步代码,所以需要action,它是用于处理异步任务的
Getter : 用于对store中的数据进行加工处理新的数据,注意它并不会改变state中的数据,只是起包装作用,相当于计算属性
vuex 是处理复杂的父子通信时,需要用到的技术。
state中可以定义一些常量,getters中写触发state改变的方法,通常用到getters比较少。
还有两个可以存放方法的地方是actions和mutations,一个是异步,一个是同步,我们一般都用mutations存放一些方法,在页面中通过 this.$store.commit()方法来触发mutations,从而改变state中的值。
八、vue路由的钩子函数
vue-router 提供的导航钩子主要用来拦截导航,让它完成跳转或取消。
路由钩子分为三类: 全局的、单个路由独享的、或者组件级的
全局钩子: 主要包括beforeEach和afterEach,这类钩子主要作用于全局,一般用来判断权限,以及页面丢失时候需要执行的操作。一般写在main.js里面
beforeEach函数有三个参数:to:router即将进入的路由对象
from:当前导航即将离开的路由
next:Function,进行管道中的一个钩子,如果执行完了,则导航的状态就是 confirmed (确认的);否则为false,终止导航。
可以使用router.beforeEach注册一个全局的 before 钩子
router.beforeEach((to, from, next) => { // ... })
after 钩子没有 next 方法,不能改变导航
router.afterEach(route => { // ...})
在项目中用到:
1.全局的钩子
2.单个路由里面的钩子
主要用于写某个指定路由跳转时需要执行的逻辑
3.组件内的钩子 (用到的不多)
主要包括 beforeRouteEnter和beforeRouteUpdate ,beforeRouteLeave,这几个钩子都是写在组件里面也可以传三个参数(to,from,next),作用与前面类似.
九、什么情况会造成跨域,以及怎么解决跨域?
造成跨域的四字真言:同源策略
跨域问题是这是浏览器为了安全实施的同源策略导致的,同源策略限制了来自不同源的document、脚本,同源的意思就是两个URL的域名、协议、端口要完全相同
解决跨域
1.JSONP
JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。
核心思想:网页通过添加一个
1)原生实现:
2)vue.js实现:
2.CORS跨域(目前用的最多的)
普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置
若带cookie跨域请求:前后端都需要进行设置
【前端设置】
根据xhr.withCredentials字段判断是否带有cookie
1)原生ajax实现
2)vue-resource
3)axios
【服务端设置】
服务器端对于CORS的支持,主要是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
3.设置document.domain解决无法读取非同源网页的 Cookie问题
因为浏览器是通过document.domain属性来检查两个页面是否同源,因此只要通过设置相同的document.domain,两个页面就可以共享Cookie(此方案仅限主域相同,子域不同的跨域应用场景。)
4.跨文档通信 API:window.postMessage()
调用postMessage方法实现父窗口 http://test1.com向子窗口http ://test2.com发消息(子窗口同样可以通过该方法发送消息给父窗口)
它可用于解决以下方面的问题:
页面和其打开的新窗口的数据传递
多窗口之间消息传递
页面与嵌套的iframe消息传递
上面三个场景的跨域数据传递
调用message事件,监听对方发送的消息
十、hash与history的区别?
SPA需要在不刷新页面的情况下做页面更新的能力,这就需要引入前端路由,实际上,前端路由是利用了浏览器的hash或history属性。
hash :(兼容性更强)
hash (url中#后面的部分)虽然出现在 URL 中,但不会被包含在 http 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
当hash改变时,会触发hashchange事件,监听该事件,对页面进行更新。
window.onhashchange = function() {
document.getElementById("demo").innerHTML = x; //更新页面内容
......
};
history :
history 利用了 html5 history interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器记录栈,
在当前已有的 back、forward、go 基础之上,它们提供了对历史记录 修改的功能(pushState将传入url直接压入历史记录栈,replaceState将传入url替换当前历史记录栈)。
只是当它们执行修改时,虽然改变了当前的 URL ,但浏览器不会立即向后端发送请求。
pushState()、replaceState() 方法接收三个参数:stateObj、title、url
history.pushState({color: "red"}, "red", "red");//设置状态
window.onpopstate = function(event) { //监听状态
if(event.state && event.state.color === "red") {
document.body.style.color = "red"; //更新页面内容
}
}
// 改变状态 *在使用history API改变浏览器的url时,仍需要额外的步骤去触发 popstate 事件,例如调用 history.back() 和 history.forward() 等方法
history.back();
history.forward();
history 对比 hash
history优势:
pushState 设置的 url 可以是同源下的任意 url ;而 hash 只能修改 # 后面的部分,因此只能设置当前 url 同文档的 url。
pushState 设置的新的 url 可以与当前 url 一样,这样也会把记录添加到栈中;hash 设置的新值不能与原来的一样,一样的值不会触发动作将记录添加到栈中。
pushState 通过 stateObject 参数可以将任何数据类型添加到记录中;hash 只能添加短字符串。
pushState 可以设置额外的 title 属性供后续使用。
history劣势:
history 在刷新页面时,如果服务器中没有相应的响应或资源,就会出现404。因此,如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
hash 模式下,仅 # 之前的内容包含在 http 请求中,对后端来说,即使没有对路由做到全面覆盖,也不会报 404。
十一、vue的内容分发
将父组件的内容放到子组件指定的位置叫做内容分发或者叫做slot(插槽)
组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力
1.单个插槽:
2.多个插槽也叫具名插槽:具名插槽就是将某个名字的内容插到子组件对应名字里面去
可以实现内容不同样式不同,通过slot中的name属性绑定元素
具名插槽和匿名插槽的区别:
具名插槽有名字
匿名插槽没有名字
具名插槽用法
具有名字的插槽
使用 中的 "name" 属性绑定元素
通过slot属性来指定, 这个slot的值必须和下面slot组件得name值对应上 如果没有匹配到 则放到匿名的插槽中
需要注意的是 相同的name值的互相替换,没有用slot指定名字所以会替换掉子组件里没有name属性的slot标签
3.作用域插槽
作用插槽允许你将模板传递给插槽,而不是传递一个已呈现的元素。它被称为作用域的插槽,因为尽管模板在父范围内呈现,但它可以访问某些子数据。
作用域插槽使用场景
当子组件做循环显示列表 或 某一部分由外部传递进来 时,则使用 作用域插槽
父组件对子组件加工处理
既可以复用子组件的slot,又可以使slot内容不一致
十二、深拷贝和浅拷贝
浅拷贝和深拷贝都只针对于像Object, Array这样的复杂对象,
区别:浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制
简单来说,深拷贝主要是将另一个对象的属性值拷贝过来之后,另一个对象的属性值并不受到影响,因为此时它自己在堆中开辟了自己的内存区域,不受外界干扰。
浅拷贝主要拷贝的是对象的引用值,当改变对象的值,另一个对象的值也会发生变化。
十三、v-text、v-html(重点) 、v-pre 区别及用法
v-text 输出的是纯文本,浏览器不会对其再进行html解析
v-html 会将其当html标签解析后输出
v-pre 显示原始信息跳过编译过程
一些静态的内容不需要编译加pre指令可以加快渲染
十四、vue双向绑定原理
重点是Object.defineProperty(obj,'msg',{})下面的get方法和set方法
十五、for循环结构遍历数组时key的作用
1)key来给每个节点做一个唯一标识
2)key的作用主要是为了高效的更新虚拟DOM,提高性能
提示:v-if和v-for不推荐一起使用,当使用的时候v-for具有比v-if更高的优先级。
十六、钩子函数参数
到了时间点不用我们操作,自己就会触发
有生命周期的
十七、自定义指令vue内置的指令可能在开发的时候不够用,这时候,我们可以考虑自己自定义指令
语法
比如自定义属性
十八、父子组件传值以及非父子组件传值
父组件向子组件传值--基本用法
例子:
子组件向父组件传值--基本用法
子组件获取到父组件中的函数值方法一:
子组件获取到父组件中的函数值方法二:
子组件向父组件传值:(在$emit('父组件中的函数名',想传的值),在父组件中的methods中的函数fn中使用传过来的值
非父子组件间传值
兄弟之间如何通讯
兄弟之间传递数据需要借助于事件中心,通过事件中心传递数据
提供事件中心 var hub = new Vue()
传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)
接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
(要改变谁,在谁里面写mounted,这样通过this,就能改变谁里面的数据)
销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
如果触发事件传递值了,监听事件参数中要有(形参)与之对应
销毁事件函数,需要定义在兄弟的父亲methods中
十九、computed 计算属性与method方法的区别
computed中的计算属性可以写在method中,区别就是method调用要加()而computed不用
computed中必须要加return
computed里面的方法不是通过事件去触发的,而是当属性(必须是data中的属性)发生改变的时候那么当前函数就会被触发
最大的区别是,computed中有缓存,相同的值会直接拿已经缓存的,提高性能,但是method没有缓存,一样的值还是会重新获取
二十、渐进增强和优雅降级之间的不同
渐进增强
针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
优雅降级
一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。
区别:
1.优雅降级是从复杂的现状开始,并试图减少用户体验的供给
2.渐进增强则是从一个非常基础的,能够起作用的版本开始,并不断扩充,以适应未来环境的需要
3.降级(功能衰减)意味着往回看;而渐进增强则意味着朝前看,同时保证其根基处于安全地带
二十一、请描述一下cookies,sessionStorage和localStorage的区别?
sessionStorage和localStorage是HTML5 Web Storage API提供的,可以方便的在web请求之间保存数据。有了本地数据,就可以避免数据在浏览器和服务器间不必要地来回传递。sessionStorage、localStorage、cookie都是在浏览器端存储的数据,其中sessionStorage的概念很特别,引入了一个“浏览器窗口”的概念。sessionStorage是在同源的同窗口(或tab)中,始终存在的数据。也就是说只要这个浏览器窗口没有关闭,即使刷新页面或进入同源另一页面,数据仍然存在。关闭窗口后,sessionStorage即被销毁。同时“独立”打开的不同窗口,即使是同一页面,sessionStorage对象也是不同的cookies会发送到服务器端。其余两个不会。
cookies:每个特定的域名下最多生成的cookie个数有限制,IE和Opera会清理近期最少使用的cookie,Firefox会随机清理cookie
sessionStorage:用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁,是会话级别的存储。
localStorage:用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。
二十二、清除浮动
1.使用空标签清除浮动。这种方法是在所有浮动标签后面添加一个空标签定义css clear:both.弊端就是增加了无意义标签。
2.使用overflow。给包含浮动元素的父标签添加css属性overflow:auto;zoom:1;zoom:1用于兼容IE6。
3.使用after伪对象清除浮动。该方法只适用于非IE浏览器。使用中需注意以下几点。一、该方法中必须为需要清除浮动元素的伪对象中设置height:0,否则该元素会比实际高出若干像素;二、content属性是必须的,但其值可以为空,content属性的值设为”.”,空亦是可以的。
4.浮动外部元素
二十三、什么是闭包,如何使用它,为什么要使用它?
包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。闭包可以用在许多地方。它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
使用闭包的注意点:由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
(关于闭包,详细了解请看[JavaScript之作用域与闭包详解]( https://www.cnblogs.com/syfwh... )
二十四、JavaScript宿主对象和原生对象的区别?
原生对象
ECMA-262 把本地对象(native object)定义为“独立于宿主环境的 ECMAScript 实现提供的对象”。
“本地对象”包含哪些内容:Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError。
由此可以看出,简单来说,本地对象就是 ECMA-262 定义的类(引用类型)。
内置对象
ECMA-262 把内置对象(built-in object)定义为“由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现”。这意味着开发者不必明确实例化内置对象,它已被实例化了。
同样是“独立于宿主环境”。根据定义我们似乎很难分清“内置对象”与“本地对象”的区别。而ECMA-262 只定义了两个内置对象,即 Global 和 Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)。如此就可以理解了。内置对象是本地对象的一种。
宿主对象
何为“宿主对象”?主要在这个“宿主”的概念上,ECMAScript中的“宿主”当然就是我们网页的运行环境,即“操作系统”和“浏览器”。
所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境提供的对象。 所有的BOM和DOM都是宿主对象。 因为其对于不同的“宿主”环境所展示的内容不同。其实说白了就是, ECMAScript官方未定义的对象都属于宿主对象,因为其未定义的对象大多数是自己通过ECMAScript程序创建的对象。
二十五、call和apply的区别是什么?
call方法:
语法:call(thisObj,Object)
定义:调用一个对象的一个方法,以另一个对象替换当前对象。
说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。 如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
apply方法:
语法:apply(thisObj,[argArray])
定义:应用某一对象的一个方法,用另一个对象替换当前对象。
说明:如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递任何参数。
对于apply和call两者在作用上是相同的,但两者在参数上有以下 区别 :
对于第一个参数意义都一样,但对第二个参数:apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。如 func.call(func1,var1,var2,var3)对应的apply写法为:func.apply(func1,[var1,var2,var3])同时使用apply的好处是可以直接将当前函数的arguments对象作为apply的第二个参数传入。
二十六、==和===有什么不同?
== equality 等同,=== identity 恒等。 ==, 两边值类型不同的时候,要先进行类型转换,再比较。 ===,不做类型转换,类型不同的一定不等。
二十七、http和https有什么区别?
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
二十八、节流防抖
防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时;典型的案例就是输入搜索:输入结束后n秒才进行搜索请求,n秒内又输入的内容,就重新计时。
节流:规定在一个单位时间内,只能触发一次函数,如果这个单位时间内触发多次函数,只有一次生效; 典型的案例就是鼠标不断点击触发,规定在n秒内多次点击只有一次生效。
二十九、es6的新语法你了解哪些?
箭头函数:
var foo = function(){return 1;};
//等价于
let foo = () => 1;
*箭头函数中的 this 指的不是window,是对象本身。
let、const
ES6 推荐在函数中使用 let 定义变量
const 用来声明一个常量,但也并非一成不变的
let 和 const 只在最近的一个块中(花括号中)有效
let
let关键字声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域特性。
1)不存在变量提升
2)暂时性死区
const
声明常量,常量就是值(内存地址)不能变化的量
1)声明常量时必须赋值 ,必须声明后马上赋值
2)既然是常量不能重新进行赋值,如果是基本数据类型,不能更改值,如果是复杂数据类型,不能更改地址值
下面来讲讲var let const 的区别:使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象
使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升
使用 const 声明的是常量,声明时候必须要给定值,在后面出现的代码中不能再修改该常量的值
解构赋值
解构赋值是ES6中推出的一种高效、简洁的赋值方法
模版字符串
ES6中提供了用反引号`来创建字符串,里面可包含${…}等
for…of && for…in
for…of 遍历(数组)
for…in 遍历对象中的属性
PromiseES6 对 Promise 有了原生的支持,一个 Promise 是一个等待被异步执行的对象,当它执行完成后,其状态会变成 resolved 或者 rejected
Promises是处理异步操作的一种模式,之前在很多三方库中有实现,比如jQuery的 deferred 对象。当你发起一个异步请求,并绑定了.when(), .done()等事件处理程序时,其实就是在应用promise模式
展开运算符
使你能够将字面量对象展开为多个元素
async/await
async 函数返回的是一个 Promise 对象,所以在最外层不能用 await 获取其返回值的情况下,我们当然应该用原来的方式: ```then()``` 链来处理这个 Promise 对象,就像这样
await 可以用于等待一个 async 函数的返回值
如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
看到上面的阻塞一词,心慌了吧……放心,这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。
set 和 map
set
它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成 Set 数据结构
const s = new Set();
map
对象是键值对集合,和 JSON 对象类似,但是 key 不仅可以是字符串还可以是对象
三十、get和post的区别?
其实,GET和POST本质上两者没有任何区别。他们都是HTTP协议中的请求方法:底层实现都是基于TCP/IP协议。所谓区别,只是浏览器厂家根据约定,做得限制不同而已。
(1)使用Get请求时,参数在URL中显示,而使用Post请求,则不会显示出来;
(2)Post传输的数据量大,可以达到2M,而Get方法由于受到URL长度的限制,只能传递大约1024字节.
(3)Get请求请求需注意缓存问题,Post请求不需担心这个问题;
(4)Post请求必须设置Content-Type值为application/x-form-www-urlencoded;
(5)发送请求时,因为Get请求的参数都在url里,所以send函数发送的参数为null,而Post请求在使用send方法时,却需赋予其参数;
(6)GET方式请求的数据会被浏览器缓存起来,因此其他人就可以从浏览器的历史记录中读取到这些数据,例如账号和密码等。在某种情况下,GET方式会带来严重的安全问题。而POST方式相对来说就可以避免这些问题。