面试答案1
vuex的属性
- state:声明定义数据
- getters:存放有依赖关系的数据,类似于computed计算属性
- mutation:同步的修改state中的数据
- actions:异步修改数据
- modules:让每一个模块都有自己的state,getters,mutation,actions
vue双向绑定的原理
Vue实现双向绑定的原理就是利用了Object.defineProperty()这个方法重新定义了对象获取属性值(get)和设置属性值(set)的操作来实现的。
vue全家桶有什么(❎)
- Vue-cli项目构建工具
- vue-router 路由
- vuex状态管理
- axios http 请求工具
- webpack
vue常见的一些指令
- v-for 现在要配合 :key使用
- v-if 是否渲染这个标签
- v-bind 简写:
- v-show display控制 none/block
- v-else 配合v-if使用
- v-on 简写@
vue中的data为什么是返回的函数
因为一个组件是可以共享的,但他们的data是私有的,所以每个组件都要return一个新的对象,返回一个唯一的对象,不要和其他组件共用一个对象
合并数组的方法
- concat
var a = [1,2,3]
var b = [4,5,6]
var c = a.concat(b) //c = [1,2,3,4,5,6]
- apply
var a = [1,2,3]
var b = [4,5,6]
var c = a.push.apply(a,b)
- 数组合并去重
let arr1 = [1,2,3]
let arr2 = [2,3,4]
let arr = arr1.concat(arr2) //合并数组
let arrNew = new Set(arr) //通过set集合去重
Array.from(arrNew) //将set集合转化为数组
垂直居中的方式(❎)
- absolute+transform:绝对定位+转换
.parent {
position: relative;
}
.child {
position: absolute;
left: 50%;
rigth: 50%;
transfrom: translate(-50%,-50%)
}
- flex + justify-content + align-items
.parent {
display: flex;
justify-content: center; //水平居中
align-items: center; //垂直居中
}
面试第二天
call,apply,bind的区别,实现以下方法(❎)
- call和apply都是为了解决改变this的指向。作用都相同,只是传参的方式不同。除了第一个参数外,call可以接受一个参数的列表,apply只接受一个参数的数组
call的实现思路
//call的实现思路
Function.prototype.myCall = function (context){
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var context = context || window
//给context添加一个属性
context.fn = this
//通过参数伪数组将context后面的参数取出来
var args = [...arguments].slice(1)
var result = context.fn(...args)
//删除 fn
delete context.fn
return result
}
apply的实现思路
//apply的实现思路
Function.prototype.myApply = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var context = context || window
//为context添加一个属性
context.fn = this
var result
//判断是否存在第二个参数
//如果存在就将第二个参数也展开
if(arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
bind的实现思路:bind返回了一个函数,对于函数来说有两种调用方式,一种是直接的调用,一种是通过new的方式
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
const args = [...arguments].slice(1)
//返回一个函数
return function F () {
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context,args.concat(...arguments))
}
}
vue中为什么使用axios
vue是虚拟DOM操作的,JQuery.ajax和都需要操作DOM,官方不推荐,而且axios本身就可以解决回调地狱的问题
cookie和session和window.localstore的区别(❎)
loaclStorage声明周期是永久的,存放数据一般为5MB;sessionStorage仅在当前会话下有效,关闭页面或者浏览器后被清除,存放数据大小一般为5MB;cookie具有极高的扩展性和可用性,存放数据大小为4k左右,有个数限制,一般不能超过20个。 localStorage、sessionStorage、Cookie共同点:都是保存在浏览器端,且同源的。
new操作的过程(❎)
在调用new的过程中会发生四件事: 1. 新生成了一个对象 2. 链接到原型 3. 绑定this 4. 返回新对象 其实我们完全可以自己实现一个new的过程
function createNew() {
let obj = {}
let Sunday = [].shift.call(arguments)
obj.__proto__ = Sunday.prototype
let result = Sunday.apply(obj,arguments)
return result instanceof Object ? result : obj
}
vue-router的原理(❎)
vue-router路由提供了两种路由模式:hash模式和history模式。 - hash模式:ue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。 - history模式:如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面
vue组件通信
组件通信一般分为三种: 1. 父子组件通信 - 父组件通过props传递给子组件数据,子组件通过$emit发送数据传递数据,子组件不能修改父组件的数据,父子组件通信属于典型的单向数据流。 2. 兄弟组件通信 - 对于这种情况可以通过查找父组件中的子组件实现,也就是 this.$parent.$children,在 $children 中可以通过组件 name 查询到需要的组件实例,然后进行通信。 3. 跨多层级组件通信 待定
面试第三天
跨域
- JSONP
//jsonp的原理很简单,就是利用 <script> 标签没有跨域的限制的漏洞。当需要通讯时,通过 <script> 标签指向一个需要访问的地址,并提供一个回调函数来接收数据。
//JSONP使用简单并且兼容性不错,但是只限于get请求
<script src="http://sunday/api?param8=a¶m2=b&callback=jsonp"></script>
<script>
function jsonp(data) {
console.log(data)
}
</script>
- CORS
cors需要浏览器和后端同时支持。IE 8 和 9 需要通过XDonmainRequest来实现。
浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端,只要后端实现了 CORS ,就实现了跨域。
服务端设置 Access-C动态容量—Allow-Origin 就可以开启CORS。此属性表示哪些域名可以访问资源。
- document.domain
这种方式只能用于主域名相同的情况下,例如sunday.test.com 和 zhaojiujiu.test.com 适用于该方式。
只需要给页面添加 document.domain = 'test.com'表示主域名都相同就可以实现跨域
- postMessage
// 发送消息端
window.parent.postMessage('message','http://sunday.com')
//接收消息端
var zhaojiujiu = new MessageChannel()
zhaojiujiu.addEventListener('message',event => {
var origin = even.origin || event.originalEvent.origin
if(origin === 'http://sunday.com') {
console.log('验证通过')
}
})
函数的节流和防抖
首先我们先明确一下函数节流和防抖的定义
- 函数节流:在指定时间间隔内只会执行一次任务
- 函数防抖:任务频繁触发的情况下,只有任务触发的时间间隔超过指定间隔的时候,任务才会执行
函数节流(throttle)
最形象的例子就是,当我们在滚动页面的时候,判断是否滚动到底部,在不考虑函数节流和代码性能的情况下,就是通过监听==window.scorll==的事件
$(window).on('scroll',function () {
//判断滚动条是否滚动到页面的底部
let pagHeight = $('body').height(),
scroolTop = $(window).scrollTop(),
winHeight = $(window).height(),
thresold = pageHeight - scorllTop - winHeight;
if(thresold > -100 && thresold <= 20) {
console.log('bottom');
}
});
//这种情况会非常消耗性能的,在滚动时,浏览器会时刻的监听是否满足滚动的条件,但是在实际的开发中我们是每隔一段时间去执行一次,所以就出现了函数节流的概念
下面试解决函数节流的解决方案:
function throttle(fn) {
let tag = true;
return function() {
if (!tag) return;
tag = false;
setTimeout(() => {
fn.apply(this,arguments);
tag = true;
},500)
}
}
函数防抖(debounce)
这种情况也非常常见,在我们做搜索功能是,在input中输入数据时,每输入一个数据就会被触发一次请求,在我们输完搜索内容时,会发生几次请求,不仅用户体验不好,就连对服务器的的压力也会变大。 我们大多数的解决方案就是引入一个函数防抖(debounce)
//函数防抖的原理
function debounce (fn) {
let tag = null;
return function () {
clearTimeout(tag);
tag = setTimeout(() => {
fn.apply(this, arguments);
},500);
};
}
其实函数节流和函数防抖的目的,就是节约计算机资源。
闭包
-
- 闭包的定义:
闭包就是能够读取其他函数内部变量的函数,在js中可以将闭包理解为“函数中的函数” - 闭包的用途:
1.读取函数内部的变量
2.让变量始终保持在内存中,延长变量的生命周期 - 闭包的条件:
1.有外层函数和子函数
2.外层函数有局部变量
3.子函数能操控外层函数的局部变量
4.子函数与外部函数有关联
function A() {
let a = 1
window.B = function () {
console.log(a)
}
}
A()
B() // 1
- 闭包的存在意义:
// 使用闭包的方式解决var定义函数问题
for (var i = 0;i<=3;i++){
(function(j){
setTimeout(function timer() {
console.log(j)
},j*1000)
})(i)
}
面试第四天
原型继承和class继承
原型继承
- 如何实现原型继承?
先更改子类的原型prototype指向一个New父类()对象。
子类.prototype = new 父类()
再给子类的原型设置一个constructor指向子类
子类.prototype.constructor = 子类
- 代码
// 人类 → 父类
function Person() {
this.name = '名字';
this.age = 10;
this.gender = '男';
}
Person.prototype.sayHi = function () { console.log('你好'); };
Person.prototype.eat = function () { console.log('我会吃。。。'); };
Person.prototype.play = function () { console.log('我会玩'); };
// 学生类 → 子类
function Student() {
this.stuId = 1000;
}
// 子类的原型prototyp指向父类的一个实例对象
Student.prototype = new Person();
// 添加一个constructor成员
Student.prototype.constructor = Student;
// 如何实现原型继承:
// 给子类的原型prototype重新赋值为父类的一个实例对象。
// 利用了原型链上属性或方法的查找规则。
// 创建一个学生对象
var stu1 = new Student();
console.log(stu1.constructor)
class继承
- 在es6中可以使用class去实现继承,并且实现起来很简单
class Parent {
constructor(value) {
this.val = value
}
getValue() {
console.log(this.val)
}
}
class Child extends Parent {
constructor(value) {
super(value)
this.val = value
}
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
- class 实现继承的核心在于使用 extends 表明继承自哪个父类,并且在子类构造函数中必须调用 super,因为这段代码可以看成 Parent.call(this, value)