1.在代码块中举列说明js中this指针的绑定规则和绑定优先级
(1)默认绑定
(2)隐式绑定
(3)显式绑定
(4)new绑定
用一句话总结this的指向规则:
this的指向,是在执行函数时根据执行上下文所动态决定的。
(调用函数会创建新的属于函数自身的上下文,执行上下文的调用创建阶段会决定this的指向。)
一般来说,this 绑定的优先级: new > 显示绑定 > 隐式绑定 > 默认绑定。
(1)默认绑定
```
var a = 'global'
function fn1() {
console.log(this)
}
fn1(); //global
```
函数在全局环境调用执行时,this指向全局对象,如果使用严格模式,this绑定到undefined。
(2)隐式绑定
```
var a = 'globalA';
var obj = {
a: 'objA',
test
}
function test () {
console.log(this.a)
}
obj.test(); // objA
```
函数作为对象的方法被调用时,this指向方法运行时所在的当前对象,即obj。
```
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
function test() {
console.log(this.a);
}
obj1.obj2.foo(); // 42
```
如果是多重对象的调用,this 也还是绑定在上一级对象上,即obj2上。对象属性引用链中只有最后一层会影响调用位置。
(2.1) 隐式丢失
```
var a = 'global'
const zhangsan = {
name: "张三",
sayHi () {
console.log(this);
},
wait() {
function foo1() {
console.log(this.a);
}
foo1();
}
};
zhangsan.wait(); //global
```
这里的函数调用比上面的程序多了一层函数的嵌套,绑定的 this 就发生了变化,变成了 winodw 全局对象。
解决办法:
```
var a = 'globalA';
var obj = {
a: 'objA',
test
}
function test () {
console.log(this.a)
}
obj.test(); // objA
```
(3)显示绑定
使用call,apply,bind改变函数的调用对象,第一个参数就是改变后的调用这个函数的对象
```
function fn1() {
console.log(this)
}
fn1.call({x:100}); //{x: 100}
```
使用call,apply,bind方法调用时,第一个参数就是this指向的对象{x:100}。
call,apply,bind的区别
call、apply、bind的作用是改变函数运行时this的指向,所以它们的第一个参数是要绑定给this的值,区别在于
```
fn.call({x:100},p1,p2,p3) //call后面的参数是一个参数列表,一个一个传进去的
fn.apply({x:100},argument) //apply里面的第二个参数是一个参数数组
fn.bind({x:100},p1,p2,p3) //bind和call相似,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数,没有立即执行。
```
(4)new绑定
var x = "global!";
function obj(){
this.x = "obj!";
}
var o = new obj();
console.log(o.x); //obj!
2.在代码块中举列说明js原型链如何实现继承
其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
function Main () {}
Main.prototype.sex = '男' ;
Main.prototype.eat = function () {
console.log('Main eat ...')
}
function Personal () {}
Personal.prototype.name = 'hwk';
Personal.prototype.sayName = function () {
console.log('Personal name')
}
// 继承
Personal.prototype = new Main();
Personal.prototype.constructor = Personal;
var p = new Personal();
console.log(p.sex ) ; // 男
console.log(p.name) ; // undefined
p.eat(); // Main eat ...
p.sayName (); // Uncaught TypeError:p.sayName is not a function
3.在代码块中举例说明vue实现路由拦截的俩种方式
beforeEach钩子函数有3个参数,to,from,next。
每一次路由改变之后页面加载之前执行。
afterEach 钩子不会接受 next 函数也不会改变导航本身,只接收(to, from)
每一次路由改变之后页面加载之后执行。
to : 从哪来
from:到哪去
next:放行
//全局守卫
router.beforeEach((to, from, next)=> {
// 判断是否登录
if(sessionstorage.getItem("isLogin") == "1") {
next()
}else {
// 如果没有登录,但是跳转登录页面也放行
if(to.name == 'login') {
next()
}else {
// 如果没有登录,也不是去登录页,就拦截,让它跳转登录页
next('/login')
}
}
);
router.afterEach((to, from) => {
// ...
do something;
})
4.简述强缓存/协商缓存的原理、特点和可能形成的问题
1、基本原理
1)浏览器在加载资源时,根据请求头的expires和cache-control判断是否命中强缓存,是则直接从缓存读取资源,不会发请求到服务器。
2)如果没有命中强缓存,浏览器一定会发送一个请求到服务器,通过last-modified和etag验证资源是否命中协商缓存,如果命中,服务器会将这个请求返回,但是不会返回这个资源的数据,依然是从缓存中读取资源
3)如果前面两者都没有命中,直接从服务器加载资源
2、相同点
如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;
3、不同点
强缓存不发请求到服务器,协商缓存会发请求到服务器。
特点
强缓存:直接从本地副本比对读取,不去请求服务器,返回的状态码是200。
协商缓存:会去服务器比对,若没改变才直接读取本地缓存,返回的状态码是304。
可能形成的问题(缓存混乱)
使用强缓存时,浏览器不会发送请求到服务端,根据设置的缓存时间浏览器一直从缓存中获取资源,在这期间若资源产生了变化,浏览器就在缓存期内就一直得不到最新的资源
Expires
只要发送请求时间是在Expires之前,那么本地缓存始终有效,则在缓存中读取数据,
由于失效的时间是一个绝对时间,所以当服务器与客户端时间偏差较大时,就会导致缓存混乱