面试题详解
一 数据类型判断
1 typeof
可以判断数据类型,它返回表示数据类型的字符串(返回结果只能包括number,boolean,string,function,object,undefined);
可以使用typeof判断变量是否存在(如if(typeof a!="undefined"));
Typeof 运算符的问题是无论引用的对象是什么类型 它都返回object
2 instanceof
因为A instanceof B 可以判断A是不是B的实例,返回一个布尔值,由构造类型判断出数据类型
console.log(
123 instanceof Number, //false
'dsfsf' instanceof String, //false
false instanceof Boolean, //false
[1,2,3] instanceof Array, //true
{a:1,b:2,c:3} instanceof Object, //true
function(){console.log('aaa');} instanceof Function, //true
undefined instanceof Object, //false
null instanceof Object, //false
new Date() instanceof Date, //true
/^[a-zA-Z]{5,20}$/ instanceof RegExp, //true
new Error() instanceof Error //true
)
还需要注意null和undefined都返回了false,这是因为它们的类型就是自己本身,并不是Object创建出来它们,所以返回了false。
**3 constructor **
利用实例化对象的constructor属性指向构造函数自己
一般用法为 A.constructor
var num = 12;
console.log([].constructor); //Array
console.log('string'.constructor); //string
console.log(num.constructor); //number
console.log(new Object().constructor); //object
但如果声明了一个构造函数,并且把他的原型指向改变了,这种情况下,constructor 也不能准确的判断
**4 **toString
所有数据类型的父类都是Object, toString为Object的原型prototype的方法,返回格式为[object xxx],其中Object对象返回的是[Object object],其他类型需通过call/apply来调用
Object.prototype.tostring.call();
var toString = Object.prototype.toString;
toString.call(123); //"[object Number]"
toString.call('abcdef'); //"[object String]"
toString.call(true); //"[object Boolean]"
toString.call([1, 2, 3, 4]); //"[object Array]"
toString.call({name:'wenzi', age:25}); //"[object Object]"
toString.call(function(){ console.log('this is function'); }); //"[object Function]"
toString.call(undefined); //"[object Undefined]"
toString.call(null); //"[object Null]"
toString.call(new Date()); //"[object Date]"
toString.call(/^[a-zA-Z]{5,20}$/); //"[object RegExp]"
toString.call(new Error()); //"[object Error]"
使用Object.prototype.toString.call()的方式来判断一个变量的类型是最准确的方法。
二 原型和原型链
原型的作用就是实现属性和方法的共享
概念
JavaScript 的所有对象中都包含了一个 [proto] 内部属性,这个属性所对应的就是自身的原型
JavaScript 的函数对象,除了原型 [proto] 之外,还有 prototype 属性,当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型 [proto]
原型链 (JS原型与原型链继承)
实例对象与原型之间的连接,叫做原型链。proto( 隐式连接 )
JS在创建对象的时候,都有一个叫做proto的内置属性,用于指向创建它的函数对象的原型对象prototype。
内部原型(proto)和构造器的原型(prototype)
1、每个对象都有一个proto属性,原型链上的对象正是依靠这个属性连结在一起
2、作为一个对象,当你访问其中的一个属性或方法的时候,如果这个对象中没有这个 方法或属性,那么Javascript引擎将会访问这个对象的proto属性所指向上一个对 象,并在那个对象中查找指定的方法或属性,如果不能找到,那就会继续通过那个对象 的proto属性指向的对象进行向上查找,直到这个链表结束。
隐式原型 (proto):隐式原型的作用是用来构成原型链,实现基于原型的继承
显式原型(prototype):用来实现基于原型的继承与属性的共享
三 闭包
定义
定义 当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数的内部变量,且返回的那个函数在外部被执行,就产生了闭包.
三个特性
1:函数套函数
2:内部函数可以直接访问外部函数的内部变量或参数
3:变量或参数不会被垃圾回收机制回收 GC
优点:
变量长期存储在内存中。
避免全局变量的污染。
私有成员的存在。
缺点
常驻内存,增加内存使用量。
使用不当会很容易造成内存泄露。
用途
匿名自执行函数
<script type="text/javascript">
(function (a) {
console.log(a)
})(3) // 3
</script>
防抖节流
应用场景主要是用户频繁操作后,通过定时请求。可以减少服务器的请求次数。
防抖debounce
function debounce(fn,delay){
let timer = null;
return function(){
let context = this
let args = arguments
clearTimeout(timer)
timer = setTimeout(function(){
fn.apply(context,args)
},delay)
}
}
let flag = 0
function foo(){
flag++
console.log('Number of calls:%d',flag)
}
document.addEventListener('click',debounce(foo,1000))
节流 throttle
相对于防抖更加宽松,防抖主要是用户触发最后一次事件后,延迟一段事件触发,而节流会规定的时间内触发一次事件。
function throttle(fn,delay){
let timer = null;
let startTime = Date.now()
return function(){
let curTime = Date .now()
let remaining = delay - (curTime -startTime)
const context = this
const args = arguments
clearTimeout(timer)
if(remaining<=0){
fn.apply(context,args)
startTime = Date.now();
}else{
timer = setTimeout(fn,remaining)
}
}
}
function xxx(){
console.log('1')
}
document.addEventListener('click', throttle(xxx,5000))
四 js继承
1 原型链继承
父类的实例作为子类的原型
function Woman(){
}
Woman.prototype= new People();
Woman.prototype.name = 'haixia';
let womanObj = new Woman();
优点:简单易于实现,父类的新增的实例与属性子类都能访问
缺点:可以在子类中增加实例属性,如果要新增加原型属性和方法需要在new 父类构造函数的后面
无法实现多继承
创建子类实例时,不能向父类构造函数中传参数
2.借用构造函数继承(伪造对象、经典继承)
复制父类的实例属性给子类
function Woman(name){
//继承了People
People.call(this); //People.call(this,'wangxiaoxia');
this.name = name || 'renbo'
}
let womanObj = new Woman();
优点:解决了子类构造函数向父类构造函数中传递参数
可以实现多继承(call或者apply多个父类)
缺点:方法都在构造函数中定义,无法复用
不能继承原型属性/方法,只能继承父类的实例属性和方法
3.实例继承(原型式继承)
function Wonman(name){
let instance = new People();
instance.name = name || 'wangxiaoxia';
return instance;
}
let wonmanObj = new Wonman();
优点:不限制调用方式,简单,易实现
缺点:不能多次继承
4.组合式继承
调用父类构造函数,继承父类的属性,通过将父类实例作为子类原型,实现函数复用
function People(name,age){
this.name = name || 'wangxiao'
this.age = age || 27
}
People.prototype.eat = function(){
return this.name + this.age + 'eat sleep'
}
function Woman(name,age){
People.call(this,name,age)
}
Woman.prototype = new People();
Woman.prototype.constructor = Woman;
let wonmanObj = new Woman(ren,27);
wonmanObj.eat();
缺点:由于调用了两次父类,所以产生了两份实例
优点:函数可以复用,不存在引用属性问题
可以继承属性和方法,并且可以继承原型的属性和方法
5.寄生式继承
寄生式继承是与原型式继承紧密相关的一种思路。寄生式继承的思路 与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数, 该函数在内部已某种方式来增强对象,最后再像真地是它做了所有工作一 样返回对象。
function createAnother(original) {
var clone = object(original);
clone.sayHi = function () {
alert("hi");
};
return clone;
}
var person = {
name:"EvanChen",
friends:["Shelby","Court","Van"];
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi();///"hi"
基于 person 返回了一个新对象 -—— person2,新对象不仅具有person 的所有属性和方法,而且还有自己的 sayHi() 方法。在考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。
6 寄生组合继承
通过寄生的方式来修复组合式继承的不足,完美的实现继承
//父类
function People(name,age){
this.name = name || 'wangxiao'
this.age = age || 27
}
//父类方法
People.prototype.eat = function(){
return this.name + this.age + 'eat sleep'
}
//子类
function Woman(name,age){
//继承父类属性
People.call(this,name,age)
}
//继承父类方法
(function(){
// 创建空类
let Super = function(){};
Super.prototype = People.prototype;
//父类的实例作为子类的原型
Woman.prototype = new Super();
})();
//修复构造函数指向问题
Woman.prototype.constructor = Woman;
let womanObj = new Woman();
7 es6实现继承
//class 相当于es5中构造函数
//class中定义方法时,前后不能加function,全部定义在class的protopyte属性中
//class中定义的所有方法是不可枚举的
//class中只能定义方法,不能定义对象,变量等
//class和方法内默认都是严格模式
//es5中constructor为隐式属性
class People{
constructor(name='wang',age='27'){
this.name = name;
this.age = age;
}
eat(){
console.log(`${this.name} ${this.age} eat food`)
}
}
//继承父类
class Woman extends People{
constructor(name = 'ren',age = '27'){
//继承父类属性
super(name, age);
}
eat(){
//继承父类方法
super.eat()
}
}
let wonmanObj=new Woman('xiaoxiami');
wonmanObj.eat();
五 什么是深拷贝 浅拷贝 如何实现
浅拷贝
只是将数据中所有的数据引用下来,依旧指向同一个存放地址,拷贝之后的数据修改之后,也会影响到原数据的中的对象数据
深拷贝
将数据中所有的数据拷贝下来,对拷贝之后的数据进行修改不会影响到原数据
实现深拷贝
1 JSON.stringify() 、 JSON.parse()
是否支持深拷贝多层引用类型嵌套:支持
不可以拷贝 undefined , function, RegExp 等类型的数据
**展开运算符... **
是否支持深拷贝多层引用类型嵌套:不支持
Object.assign(target, source)
是否支持深拷贝多层引用类型嵌套:不支持
递归拷贝 - 深拷贝函数
是否支持深拷贝多层引用类型嵌套:支持
实现思路
将要拷贝的数据 obj 以参数的形式传参
声明一个变量 来储存我们拷贝出来的内容
判断 obj 是否是引用类型数据,如果不是,则直接赋值即可( 可以利用 obj instanceof Type 来进行判断),
由于用 instanceof 判断array 是否是object的时候,返回值为true, 所以我们在判断的时候,直接判断obj 是否是Array 就可避免这个问题
根据判断的不同类型,再给之前的变量赋予不同的类型: [ ] : { }
循环obj 中的每一项,如果里面还有复杂数据类型,则直接利用递归再次调用copy函数
最后 将 这个变量 return 出来即可
var obj = { //原数据,包含字符串、对象、函数、数组等不同的类型
name:"test",
main:{
a:1,
b:2
},
fn:function(){
},
friends:[1,2,3,[22,33]]
}
function copy(obj){
let newobj = null; //声明一个变量用来储存拷贝之后的内容
//判断数据类型是否是复杂类型,如果是则调用自己,再次循环,如果不是,直接赋值即可,
//由于null不可以循环但类型又是object,所以这个需要对null进行判断
if(typeof(obj) == 'object' && obj !== null){
//声明一个变量用以储存拷贝出来的值,根据参数的具体数据类型声明不同的类型来储存
newobj = obj instanceof Array? [] : {};
//循环obj 中的每一项,如果里面还有复杂数据类型,则直接利用递归再次调用copy函数
for(var i in obj){
newobj[i] = copy(obj[i])
}
}else{
newobj = obj
}
return newobj; //函数必须有返回值,否则结构为undefined
}
var obj2 = copy(obj)
obj2.name = '修改成功'
obj2.main.a = 100
console.log(obj,obj2)
六 Axios 拦截做过哪些
Axios 拦截分为请求拦截和响应拦截。
请求拦截
就是在你请求的时候会进行触发!只要是你发送一个 axios 请求就会触发!所以我们主要用它做我们的loading
加载和数据的权限验证,包括我们所有的数据预加载也可以实现。
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
响应拦截
主要是我们在 loading
加载,和做所有数据加载需要整体的结束,这个时候的结束就需要在数据马上发给前端的时候进行隐藏和结束,包括我们的请求头的设置,后端数据已经发送过来的时候,我们为了确保请求头的传递就必须在看看header
里面是否有你需要的请求,如果有的话,再次进行设置!
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
七 图片懒加载的实现原理
定义
当打开一个有很多图片的页面时,先只加载页面上看到的图片,等滚动到页面下面时,再加载所需的图片。这就是图片懒加载。
作用
减少或延迟请求数,缓解浏览器的压力,增强用户体验。
实现方法
1、设置图片src属性为同一张图片,同时自定义一个data-src属性来存储图片的真实地址
2、 页面初始化显示的时候或者浏览器发生滚动的时候判断图片是否在视野中
3、 当图片在视野中时,通过js自动改变该区域的图片的src属性为真实地址
具体代码
html部分
<div class="container">
<img src="http://smashinghub.com/wp-content/uploads/2014/08/cool-loading-animated-gif-3.gif" alt="1" data-src="http://cdn.jirengu.com/book.jirengu.com/img/1.jpg">
.
.
.
<img src="http://smashinghub.com/wp-content/uploads/2014/08/cool-loading-animated-gif-3.gif" alt="20" data-src="http://cdn.jirengu.com/book.jirengu.com/img/20.jpg">
</div>
<style>
.container {
max-width: 800px;
margin: 0 auto;
}
.container:after{
content: '';
display: block;
clear: both;
}
.container img {
float: left;
width: 50%;
}
h1{
clear: both;
}
/*注:img都是浮动,如果不清除浮动,h1的值高度就相当于container里面最高的,不是实际的数值*/
</style>
js部分
<script>
start() // 一开始没有滚动,也需要触发一次
$(window).on('scroll', function(){// 滚动时,显示对应图片
start()
})
function start(){
$('.container img').not('[data-isLoaded]').each(function(){
var $node = $(this)
if( isShow($node) ){
loadImg($node)
}
})
}
function isShow($node){ // 判断图片是否在视野中
return $node.offset().top <= $(window).height() + $(window).scrollTop()
}
function loadImg($img){
$img.attr('src', $img.attr('data-src'))
$img.attr('data-isLoaded', 1) // 区别图片是否被加载过,防止重新加载
}
八 瀑布流原理
瀑布流
瀑布流,又称瀑布流式布局。 是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。
原理
瀑布流布局要求要进行布置的元素等宽,然后计算元素的宽度与浏览器宽度之比,得到需要布置的列数。
创建一个数组,长度为列数,里面的值为已布置元素的总高度(最开始为0)
然后将未布置的元素依次布置到高度最小的那一列,就得到了瀑布流布局。
代码
js实现
九 解构赋值
作用: 解构赋值是针对针对赋值运算符的扩展 ,是一种针对,对象数组进行模式匹 配,后对其中的变量进行赋值。
原理 解构赋值是ES6提供的语法糖,其内在是针对可迭代对象的interator接口,通过遍历器按顺序获取对应的值进行赋值
(Iterator概念)Iterator是一种接口。为各种不一样的数据解构提供统一的访问机制。任何数据解构只要有Iterator
接口,就能通过遍历操作,依次按顺序处理数据结构内所有成员。
用于 对数组跟对象进行解构
1.可以交换变量的值
-
从函数返回多个值
-
提取JSON数据
var jsonData = { id: 42, status: "OK", data: [867, 5309] }; let { id, status, data: number } = jsonData; console.log(id, status, number);
十 async和await
作用: 是一套关于异步的解决方案
await有两个作用,一是作为求值关键字,二是将异步操作变成同步操作;如果方法中使用了await,那么在方法前面必须加上async
当await作为求值关键字时 后面可以跟Promise或表达式,可以直接获取Promise中的值或表达式的值
跟Promise
app.use(async (ctx, next) => {
// next()返回的是Promise,a的值是"hello, world!"
const a = await next();
const b = next();
b.then((res) => {
console.log(res); // 打印出"hello, world!"
})
});
app.use((ctx, next) => {
return "hello, world!";
})
跟表达式
const a = await 100*100;
async的作用是将方法的返回值封装成Promise
async function t() {
return "hello";
}
console.log(t()); // 打印出 Promise{"hello"}
十一 generator
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function
关键字与函数名之间有一个星号;二是,函数体内部使用yield
表达式,定义不同的内部状态
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next
方法,就会返回一个有着value
和done
两个属性的对象。value
属性表示当前的内部状态的值,是yield
表达式后面那个表达式的值;done
属性是一个布尔值,表示是否遍历结束。
十二 Ajax 是什么 以及如何创建
是什么:主要用来实现客户端与服务器端的异步通信效果,实 现页面的局部刷新
如何创建
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息
(3)设置响应HTTP请求状态变化的函数
(4)发送HTTP请求
(5)获取异步调用返回的数据
(6)使用JavaScript和DOM实现局部刷新
十三prop 验证 和默认值
作用:用于接收来自父组件的数据(子组件期待获得的数据)
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 验证
我们可以为组件的 prop 指定需求。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 匹配任何类型)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组且一定会从一个工厂函数返回默认值
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
十四 vue 自定义指令如何使用
*自定义指令*
指令的注册方式和「过滤器」、「混入」、「组件」注册的方式一样都分为两种:一是全局注册,二是局部注册。
1.全局注册
Vue.directive('name', {})
2.局部注册
directives: {
name: {}
}
然后在模版中直接使用即可。
你好,六哥在这
Vue 提供了自定义指令的几个钩子函数:
bind:指令第一次绑定到元素时调用,只执行一次。
inserted:被绑定的元素,插入到父节点的 DOM 中时调用。
update:组件更新时调用。
componentUpdated:组件与子组件更新时调用。
unbind:指令与元素解绑时调用,只执行一次。
除update 与 componentUpdated 钩子函数之外,每个钩子函数都含有 el、binding、vnode 这三个参数。
oldVnode 只有在 update 与 componentUpdated 钩子中生效。
参数el 就是指令绑定的 DOM 元素,而binding是一个对象,它包含一下属性:name、value、oldValue、expression、arg、modifiers。
另外值得注意的一点是,除了 el 之外,binding、vnode 属性都是只读的。
注:Vue自定义指令也可以写修饰符和传参
十五 vue 常用修饰符
Vue提供了一些修饰符,这些修饰符在使用起来非常方便,比如阻止默认事件、冒泡等。
.lazy:
v-modeil不用多说,输入框改变,这个数据就会改变,lazy这个修饰符会在光标离开input框才会更新数据:
.trim:
输入框过滤首尾的空格:
.number:
先输入数字就会限制输入只能是数字,先字符串就相当于没有加number,注意,不是输入框不能输入字符串,是这个数据是数字:
.stop:
阻止事件冒泡,相当于调用了event.stopPropagation()方法。这个应该不需要解释:
<button @click.stop="test">test
.prevent:
阻止默认行为,相当于调用了event.preventDefault()方法,比如表单的提交、a标签的跳转就是默认事件:
<a @click.prevent="test">test
.self:
只有元素本身触发时才触发方法,就是只有点击元素本身才会触发。比如一个div里面有个按钮,div和按钮都有事件,我们点击按钮,div绑定的方法也会触发,如果div的click加上self,只有点击到div的时候才会触发,变相的算是阻止冒泡:
.once:
只能用一次,无论点击几次,执行一次之后都不会再执行:
.capture:
事件的完整机制是捕获-目标-冒泡,事件触发是目标往外冒泡,比如:
顺序是2 1,capture的作用就是让这个顺序相反:
先1 后2。
.passive:
其实我不怎么理解,官网解释说可以提升移动端的性能,查了查,大概解释就是每次滚动都会有一个默认事件触发,加了这个就是告诉浏览器,不需要查询,不需要触发这个默认事件preventDefault:
.native:
组件绑定当前组件的事件是不会触发的,需要用native才能触发:
<My-component @click="shout(3)">
鼠标.left、.reight、.middle:
就是鼠标点击的时候就触发:
<button @click.right="test">test
.keyCode:
监听按键的指令,具体可以查看vue的键码对应表:
<input type="text" @keyup.enter="test(1)">
<button @click.enter="test(1)">test
注意,只有你点击过一次或者聚焦到这个输入框才能使用键盘触发。
.exact:
系统修饰键,只有按着这个键然后用鼠标点击才会触发,官网解释:
<button @click.ctrl="onClick">A
<button @click.ctrl.exact="onCtrlClick">A
但是我试了一下没有用。
.sync
对prop进行双向绑定,个人暂时用不习惯:
//父组件
//子组件
this.$emit('update:fatest,sontest);
十六 keep-alive 的作用
作用: keep-alive用来缓存组件,避免多次加载相应的组件,减少性能消耗
如果需要频繁切换路由,这个时候就可以考虑用keep-alive了,来达到避免数据的重复请求的目的。
<keep-alive>
<component>
<!-- 该组件将被缓存! -->
</component>
</keep-alive>
如果你需要缓存部分页面或者组件,可以使用如下方法:
1.使用router. meta属性
export default [
{
path: '/',
name: 'home',
component: Home,
meta: {
keepAlive: true // 需要被缓存
}
}, {
path: '/:id',
name: 'edit',
component: Edit,
meta: {
keepAlive: false // 不需要被缓存
}
}
]
<keep-alive>
<router-view v-if="$route.meta.keepAlive">
<!-- 这里是会被缓存的视图组件,比如 Home! -->
</router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive">
<!-- 这里是不被缓存的视图组件,比如 Edit! -->
</router-view>
2.使用新增属性inlcude/exclude
<keep-alive include="组件的name名称">
<component>
<!-- name 为 a 的组件将被缓存! -->
</component>
</keep-alive>可以保留它的状态或避免重新渲染
<keep-alive exclude="a">
<component>
<!-- 除了 name 为 a 的组件都将被缓存! -->
</component>
</keep-alive>可以保留它的状态或避免重新渲染
Props:
include
- 字符串或正则表达式。只有名称匹配的组件会被缓存。exclude
- 字符串或正则表达式。任何名称匹配的组件都不会被缓存。max
- 数字。最多可以缓存多少组件实例。
十七 Object.defineProperty()方法有何用
作用:会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
<script>
// Object.defineProperty阔以用于给对象添加更新属性
let obj = {}
// 该方法中包含以下参数:需要添加属性的对象,你需要加的属性,配置项
Object.defineProperty(obj, 'name', {
// getter函数
get() {
// get函数中,一定要return当前这个新添加进去的属性作为返回值
console.log('你当前获取到的值是', name) // 相当于获取:obj.name
return name
},
// setter函数, 这个函数中包含一个参数,这个参数表示当前设置的这个属性的新的值, 相当于:obj.name = 'itcast'
set(newName) {
name = newName
console.log('这里你给name传递了新的值', newName);
}
})
</script>
参数
Object.defineProperty( obj, prop, desc );
obj 要在其上定义属性的对象。
prop 要定义或修改的属性的名称。
descriptor 将被定义或修改的属性描述符。
返回值
返回被操作的对象,即返回 obj 参数
注意点
configurable : 当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
enumerable : 当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。
value : 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
writable : 当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false
用途
vue 通过 getter-setter 函数来实现双向绑定
俗称属性挂载器
专门监听对象数组变化的 Object.observe()(es7)也用到了该方法
存取修饰符具有以下两个可选键值
get : 一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。
默认为 undefined。
set : 一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。
默认为 undefined。
十八 什么是虚拟DOM,和diff算法
是什么: 它是一个Object对象模型,用来模拟真实dom节点的结构
作用 用来高效的渲染页面,减少不必要的DOM操作 提高渲染效率
diff 算法 :
作用:用来做比对两次vdom结构,
1 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
2 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异 通知视图进行更新
十九 vue 中数组中的某个对象的属性发生变化,视图不更新如何解决
产生原因 :vue实现响应式渲染更新原理,在于监听data里的数据,但针对于对象、数组这类结构较深的数据无法完全监测
方案一:利用Vue.set(object,key,val)
例:Vue.set(vm.obj,'k1','v1')
方案二:利用this.$set(this.obj,key,val)
this.$set(this.obj,'k1','v1')
方案三:利用Object.assign({},this.obj)创建新对象
例: this.obj.k1='v1'; this.obj = Object.assign({}, this.obj) 或 this.obj = Object.assign({}, this.obj,{'k1','v1'})
二十 vue3.0 与 vue2.0 的区别
vue3.0 的发布与 vue2.0 相比,优势主要体现在:更快、更小、更易维护、更易于原生、让开发者更轻松;
1 默认进行懒观察(lazy observation)。
在 2.x 版本里,不管数据多大,都会在一开始就为其创建观察者。当数据很大 时,这可能会在页面载入时造成明显的性能压力。3.x 版本,只会对「被用于渲染初始可见部分的数据」创建观察者,而且 3.x 的观察者更高效。
2更精准的变更通知
比例来说:2.x 版本中,使用 Vue.set 来给对象新增一个属性时,这个对象的所有 watcher 都会重新运行;3.x 版本中,只有依赖那个属性的 watcher 才会重新运行。
3 3.0 新加入了 TypeScript 以及 PWA 的支持
4 部分命令发生了变化:
- 下载安装 npm install -g vue@cli
- 删除了vue list
- 创建项目 vue create
- 启动项目 npm run serve
5 默认项目目录结构也发生了变化:
移除了配置文件目录,config 和 build 文件夹
移除了 static 文件夹,新增 public 文件夹,并且 index.html 移动到 public 中
在 src 文件夹中新增了 views 文件夹,用于分类 视图组件 和 公共组件
二十一路由导航守卫有几种,如何实现
作用:当我们路由切换到一个组件里面,如果没有权限,不让进入,有权限可以进入
1、全局守卫: router.beforeEach
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
3、全局后置钩子: router.afterEach
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`
}
}
每个守卫方法接收三个参数:
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()
注册过的回调。
评论区