1、什么是MVVM,MVC模型
MVC
即Model-View-Contorller(模型-视图-控制器)是项目的一种分层开发思想,它把复杂的业务逻辑抽离为单一的小模块,每个模块看似相互独立,其实又各自有相互依赖的关系。它的好出是:能保证模块的单一性,方便程序的开发、维护、耦合度低。
MVVM
即Model-View-ViewModel(模型-视图-控制器)是一种双向数据绑定的模式,用ViewModel来建立起Model数据层和View视图层的连接,数据改变会影响视图,视图改变也会影响数据。
2、vue双向数据绑定的原理?
vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
具体步骤:
第一步
需要observe的数据对象进行递归遍历,包括子属性对象的属性,都家上setter和getter这样的话,给这个对象的某个值赋值,就会触发setter,那么就监听到了数据的变化。
第二步
compile解析模块指令,将模块中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。
vue实例从创建到销毁的过程就是生命周期
也就是从开始创建、初始化数据、编译模板、挂载dom-->渲染、更新-->渲染、准备销毁、销毁中等一系列过程。
vue的生命周期常见的主要分为4大阶段8大钩子函数
一、创建前后
beforeCreate:data和method还没初始化。
created:data和method初始化完成。
二、渲染前后
beforeMount:已经编译好了模板字符串、但没有真正渲染到页面中。
mounted:已经渲染完成,可以看到页面。
三、数据更新前后
beforeUpdate:已经可以拿到最新的数据,但没有渲染到视图中。
updated:已经把更新后的数据渲染到视图中。
四、销毁前后
beforeDestroy:实例进去准备销毁阶段,此时data、methods、指令等还是可用状态。
destroyed:实例已经完成销毁,此时的data、methods、指令等已不可用。
3、v-if和v-show的区别
v-if
是真正的条件渲染,因为它会确保在切换过程中 条件块内的事件监听器和子组件适当地被销毁和重建,操作地实际上是dom元素地创建和销毁。
v-show
不管初始条件的true/false元素会一直被渲染,知道页面实例被销毁,只是简单地操作css(display:none/block)来切换这个dom是否显示。
总结:
v-if有更高的切换开销,v-show有更高的初始渲染开销,因此如果非常频繁地切换,使用v-show会更好,交互起来更流畅,如果运行时使用条件很少改变,则使用v-if
4、async await是什么?它有哪些作用?
async await是ES7的新语法,它的作用就是async用于申明一个function是异步的,而await用于等待一个异步方法执行完成,它可以很好地替代promise中的then
async函数返回的是一个promise对象,可以使用then方法添加回调函数,当函数执行的时候,一旦遇到await就先返回,等到异步操作完成之后再执行函数内后面的语句。
5、常用的数组方法
concat()
合并两个或多个数组,此方法不会更改数组,会返回一个新的数组:let arrA = arrB.concat(arrC) 。
find()
方法返回数组中提供的测试函数(函数内判断)第一个元素的值,否则返回undefined。
array.find(function(currentValue, index, arr),thisValue)
var ages = [3, 10, 18, 20];
function checkAdult(age) {
return age >= 18;
}
function myFunction() {
document.getElementById("demo").innerHTML = ages.find(checkAdult);
}
// 18
findIndex()
方法返回数组中满足提供测试函数(函数内判断)第一个元素的索引,否则返回-1。
array.findIndex(function(currentValue, index, arr), thisValue)
includes()
方法用来判断数组是否包含一个指定的值,根据情况,返回true/false。
string.includes(searchvalue, start) // start可选,开始的索引(默认0)
indexOf()
方法返回在数组中可以找到一个指定值的第一个索引,不存在则返回-1。
string.indexOf(searchvalue,start) // start可选,开始的索引(默认0)
join()
方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串,如果数组长度为1,返回的字符串不使用分隔符。
array.join(separator) // separator可选。指定要使用的分隔符。如果省略该参数,则使用逗号作为分隔符。
-----------------
pop()
方法从数组中删除最后一个元素,并返回该元素的值,改变数组。
array.pop()
const sites = ['Google', 'Runoob', 'Taobao', 'Zhihu', 'Baidu'];
console.log(sites.pop());
// 输出结果为: "Baidu"
console.log(sites);
// 输出结果为: ['Google', 'Runoob', 'Taobao', 'Zhihu']
sites.pop();
console.log(sites);
// 输出结果为: ["Google", "Runoob", "Taobao"]
push()
方法将一个或多个元素添加到数组末尾,并返回该数组的新长度,改变数组。
array.push(item1, item2, ..., itemX)
var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.push("Kiwi","Lemon","Pineapple")
// Banana,Orange,Apple,Mango,Kiwi,Lemon,Pineapple
shift()
方法从数组中删除 第一个元素,并返回该元素的值,改变数组。
array.shift()
var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.shift()
// Orange,Apple,Mango
unshift()
方法将一个或多个元素添加到数组头部,并返回该数组的新长度,改变数组。
array.unshift(item1,item2, ..., itemX)
var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.unshift("Lemon","Pineapple");
// Lemon,Pineapple,Banana,Orange,Apple,Mango
splice()
方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容,改变数组。
array.splice(index,howmany,item1,.....,itemX)
// index 必需。规定从何处添加/删除元素。该参数是开始插入和(或)删除的数组元素的下标,必须是数字。
// howmany 可选。规定应该删除多少个元素。必须是数字,但可以是 "0"则不删除。如果未规定此参数,则删除从 index 开始到原数组结尾的所有元素。
// item1, ..., itemX 可选。要添加到数组的新元素
var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.splice(2,0,"Lemon","Kiwi");
let a = [1,2,3,4];
let b = a.splice(2,1,"B");
console.log(b) // [3]
console.log(a) // [1, 2, "B", 4]
let c = a.splice(1,2);
console.log(a) // [1,4]
// Banana,Orange,Lemon,Kiwi,Apple,Mango
reverse()
方法将数组中元素的位置颠倒,并返回该数组,改变数组。
array.reverse()
sort()
方法用原地算法对数组的元素进行排序,并返回数组,默认排序顺序是将元素转换为字符串,然后比较它们的UTF-16代码单元格序列时构建的。
array.sort(sortfunction)
// sortfunction: 排序的方法
/***
* 根据对象字段排序方法 type = 'reverse' || 'order'
* data.sort(util.compare('Y', 'reverse'))
* 'Y': 字段名
* 'reverse': 排序方式
*/
function compare(property, type) {
return function (a, b) {
var value1 = a[property];
var value2 = b[property];
return type == 'reverse' ? (value2 - value1) : (value1 - value2);
}
}
6、数组 有哪几种循环方式?分别有什么用?
every
方法测试一个数组的所有元素是否能通过某个指定函数的测试,它返回一个布尔值。
array.every(function)
some()
方法测试是否至少有一个元素通过被提供的测试函数,返回一个布尔值。
array.some(function)
filter()
方法创建一个新数组,起包含通过所提供测试函数的所有元素。
let newArr = array.filter(function) // 过滤
forEach()
方法对数组的每个月拿苏执行一次提供的函数。
array.forEach(item => {
// 执行操作
})
7、常用的字符串方法有哪些?
chartAt()
方法从一个字符串中返回指定字符。
string.charAt(index) // index必需,表示字符串中某个位置的数字,即字符在字符串中的位置。
concat()
方法将一个或多个字符串与源字符串连接合并,形成一个新的字符串并返回
let strA = 'ABC',
strB = 'CDE'
let srtC = strA.concat(strB) // 'ABCCDE'
includes
方法用于判断一个字符串是否包含在另一个字符串中,返回true/false
string.includes(searchvalue, start) // start可选,开始的索引(默认0)
indexOf()
方法返回调用它的string对象中给i第一次出现指定值的索引,从start处进行搜索,如果未找到该值,则返回-1
string.indexOf(searchvalue,start) // start可选,开始的索引(默认0)
match()
方法检索返回第一个字符串匹配正则表达式的结果
string.match(regexp) // regexp,必需。规定要匹配的模式的 RegExp 对象。如果该参数不是 RegExp 对象,则需要首先把它传递给 RegExp 构造函数,将其转换为 RegExp 对象。
// 在字符串中查找 "ain":
var str="The rain in SPAIN stays mainly in the plain";
var n=str.match(/ain/g);
// n 输出数组结果值: ain,ain,ain
replace()
方法返回一个由替换之(replacement)替换一些或所有匹配的模式(pattern)后的新字符串,模式可以是一个字符串或者一个正则表达式,替换值可以是一个字符串或者一个每次匹配都要调用的回调函数
方法不会改变原字符串,而是返回一个新字符串
string.replace(searchvalue,newvalue)
// searchvalue 必需。规定子字符串或要替换的模式的 RegExp 对象。请注意,如果该值是一个字符串,则将它作为要检索的直接量文本模式,而不是首先被转换为 RegExp 对象
// newvalue 必需。一个字符串值。规定了替换文本或生成替换文本的函数。
padStart()&padEnd()
方法用另一个字符串填充当前字符串(重复,如果需要的话),一边产生的字符串达到给定的长度。填充从当前字符串的开始应用的,常用于时间补0。
返回新的字符串,表示用参数字符串从头部(左/右侧)补全原字符串。
console.log("h".padStart(5,"o")); // "ooooh"
console.log("h".padEnd(5,"o")); // "hoooo"
console.log("h".padStart(5)); // " h"
slice()
方法截取某个字符串的一部分,并返回一个新的字符串,不会改变原字符串
string.slice(start,end)
// start 必须。 要抽取的片断的起始下标,第一个字符位置为 0。如果为负数,则从尾部开始截取。
// end 可选。 紧接着要截取的片段结尾的下标。若未指定此参数,则要提取的子串包括 start 到原字符串结尾的字符串。如果该参数是负数,那么它规定的是从字符串的尾部开始算起的位置。slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。
split()
方法使用指定的分隔符字符串将一个String对象分割成字符串数组,返回一个字符串数组,不改变原字符串
string.split(separator,limit)
// separator 可选。字符串或正则表达式,从该参数指定的地方分割 string Object。
// limit 可选。该参数可指定返回的数组的最大长度。如果设置了该参数,返回的子串不会多于这个参数指定的数组。如果没有设置该参数,整个字符串都会被分割,不考虑它的长度。
trim()
方法会从一个字符串的两端删除空白字符。在这个上下文中的空白字符是所有的空白字符(space,tab,no-break space等)以及所有行终止字符(LF,CR)。
8、什么是原型链
每一个实例对象上都有一个proto属性,指向构造函数的原型对象,构造函数的原型对象也是一个对象,也有proto属性,这样一层一层往上找的过程就成了原型链
9、什么是闭包?手写一个闭包函数?闭包有哪些优缺点?
闭包(closure)指有权访问另一个函数作用域中变量的函数。简单理解就是,一个作用域可以访问另一个函数内部的局部变量
function fn() {
var num = 10
function fun() {
console.log(num)
}
return fun
}
var f = fn()
f()
作用:延长变量作用域、在函数的外部可以访问函数内部的局部变量,容易造成内层泄露,因为闭包中的局部变量永远不会被回收
10、常见的继承有哪些?
一、原型链继承
特点:1、实例可继承的属性有:实例的构造函数的属性,父类构造函数的属性,父类原型的属性。 (新实例不会继承父类实例的属性)。
缺点:1、新实例无法向父类构造函数传参
2、继承单一
3、所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会呗修改)
二、借用构造函数继承
重点:用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))【
特点:1、只继承了父类构造函数的属性,没有继承父类原型的属性
2、解决了原型链继承缺点1、2、3
3、可以继承多个构造函数属性(call多个)
缺点:1、只能继承父类构造函数的属性
2、无法实线构造函数的复用
3、每个新实例都有父类构造函数的副本,冗余臃肿
三、组合继承(组合原型链继承和接用构造函数继承)-- 常用
重点:结合了两种模式的优点,传参和复用。
特点:1、可以继承父类原型上的属性,可以传参,可以复用。
2、每个新实例引入的构造函数属性是私有的。
缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
四、原型式继承
重点:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了可以随意增添属性的实例或对象,object.create()就是这个原理
特点:类似于复制一个对象,用函数来包装。
缺点:1、所有实例都会继承原型上的属性。
2、无法实现复用。(新实例属性都是后面添加的)
五、class类实线继承
通过extends和super来实现继承
六、寄生式继承
重点:就是给原型式继承外面套了个壳子。
优点:没有创建自定义类型,因为只是套了个壳子返回对象,这个函数顺理成章就成了创建的对象。
缺点:没有原型,无法复用
11、后台管理系统中权限管理怎么实线的?
登录:当用户填写完账号密码后向服务端验证是否正确,验证通过之后