前端面试可能会问到的知识点记录

vue数据响应式原理

  • 在vue初始化的时候,会对data当中的每一个属性进行遍历,创建对应的setter和getters,并创建与这个key对应的dep,这个dep是一个数组,然后当对模板进行编译的时候,发现有使用到这个data当中的某一个属性的时候,就会创建一个watcher并添加到与data当中属性对应的dep当中

  • 当数据发生变化的时候,observe监视到了,就会通知dep当的所有watcher,然后watcher触发更新

前端面试可能会问到的知识点记录_第1张图片

内联样式和其他优先级计算

样式类别

  • 行内样式(内联样式): 直接写在标签上的属性

    • <标签名 style="属性1:属性值1;属性2:属性值2;">内容
  • **内部样式:**将css样式写在style当中,比如下面代码


    


  • 外部样式: 通过link标签引入的
    • 比如

优先级

根据权重来计算出来的

  • 没有行内样式的情况下是权重来计算,比如通配符*的权重为1 , 标签选择器的权重为10,类选择器的权重为100,id选择器的权重为1000来进行计算的(权重值可能不一样,但是是这样子来算的),内联样式最高
  • !import会将至最高级!

数组的遍历

具体看mdnweb吧~这里只举例子

forEach

for循环

map

filter

for…of

for…in

vue的生命周期

  • VUE的生命周期钩子函数:就是指在一个组件从创建到销毁的过程自动执行的函数,在组件的整个生命周期内,钩子函数都是可被自动调用的,且生命周期函数的执行顺序与书写的顺序无关
  • vue2的生命周期
beforeCreate 创建前
created 创建后
beforeMount 挂载前
mounted 挂载后

beforeUpdate 更新前
updated 更新后
beforeDestroy 销毁前
destroyed 销毁后
  • 需要重点关注的4个生命周期

  • beforeCreate 创建前

    • vue实例刚刚被创建(刚执行new的操作),其他什么都没有做
  • created 创建后

    • 此时data,methods被绑定到了实例身上,但是依旧获取不到DOM
  • beforeMount

    • 此时DOM为虚拟的DOM,无法操作虚拟的DOM
  • mounted

    • 此时DOM已经生成并且被替换为了具体的数据,可以操作DOM了

v-mode的实现原理和在自定义组件中怎么实现

v-model原来写法

<input type="text" v-model="msg"/>

v-model拆解写法

<input type="text" :value="msg" @input="msg = $event.target.value"/>

v-model当中在自定义组件要怎么实现呢?

父组件当中:

<template>
  <div>
    <One v-model="money"></One>
    <div>父容器的money{{ money }}</div>
  </div>
</template>

<script>
export default {
  name: "",
  data() {
    return {
      money: 1000,
    };
  },
};
</script>

子组件当中:

<template>
  <div>
    <input :value="value" @input="$emit('input',$event.target.value)" />
    <div>儿子当中的值{{ value }}</div>
  </div>
</template>

<script>
export default {
  name: "One",
  props:["value"]
};
</script>

前端面试可能会问到的知识点记录_第2张图片

路由导航守卫

  • beforeEach: 全局前置守卫(在所有的路由到来之前都需要经过,所以是Each这个单词)
  • **beforeEnter:**路由独享守卫
  • 还有
    • **beforeResolve:**全局解析守卫
    • afterEach:全局后置钩子
  • 还有组件内的守卫
    • beforeRouteEnter:
    • beforeRouteUpdate:
    • beforeRouteLeave:
//官方版
const UserDetails = {
  template: `...`,
  beforeRouteEnter(to, from) {
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例 `this` !
    // 因为当守卫执行时,组件实例还没被创建!
  },
  beforeRouteUpdate(to, from) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
    // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from) {
    // 在导航离开渲染该组件的对应路由时调用
    // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
  },
}

//注释版
beforeRouteEnter(to,from,next){
    console.log("路由进入之前",to,from);
    //手动调用next()方法,确保路由跳转继续执行
    next();
}
beforeRouteUpdate(to,from,next){
    console.log("路由更新之前",to,from);
    next();
}
beforeRouteLevae(to,from,next){
    console.log("路由离开之前",to,from);
    next();
}

伪类和伪元素

  • 伪类作用对象是整个元素,比如整个链接,整个段落,容器div等
a:link {
	color: #111
}

a:hover {
	color: #222
}

div:first-child {
	color: #333
}

div:nth-child(3) {
	color: #444
}
等等
  • 而伪元素作用于元素的一部分,一个段落的第一行或者第一个字母
p::first-line {
	color: #555
}

p::first-letter {
	color: #666
}
a::after {
	content: "helloWorld"
}

a::before {
	content: "helloWorld"
}

用promise写个ajax(仿照axios)

  • 首先了解下xhr发送时候的状态码
    • 0 代表还没有调用open
    • 1 代表还没有调用send方法
    • 2 代表还没有收到响应(刚刚发送)
    • 3 代表收到了部分数据
    • 4 代表收到了完整的数据
  • xhr的onreadystatechange是异步的~(因为有回调函数)

代码:

<script>
//类似于axios({...})
const axios = (options) => {
	//获取method 和 url
	let {
		method,
		url
	} = options;
	if (method && url) {
		var returnPromise = new Promise((resolve, reject) => {
			let xhr = new XMLHttpRequest();
			xhr.open(method, url);
			xhr.send();
			xhr.onreadystatechange = function () {
				if (xhr.readyState == 4 && xhr.status >= 200 & xhr.status < 300) {
					resolve(xhr.response);
				}
			};
			//监听错误的
			xhr.onerror = function () {
				reject(new Error(xhr.statusText))
			}
		});
		return returnPromise;
	} else {
		return Promise.reject("请输入完整的参数");
	}
}
axios({
	method: "get",
	url: "https://api.oick.cn/dog/ap1i.php"
}).then(data => {
	console.log(data);
});
</script>

css画一个梯形和三角形

基本原理都是利用边框

三角形:

@在线演示

关键是设置width为0

DOCTYPE html>
<head>

    <style>
        *{
            margin: 0;
            padding: 0;
        }
        #app{
            width: 0;
            border-top: 100px solid transparent;
            border-bottom: 100px solid green;
            border-left: 100px solid transparent;
            border-right: 100px solid transparent;
        }
    style>
head>
<body>
    <div id="app">div>
body>
html>

梯形:

@在线演示

关键是设置width和height的值

DOCTYPE html>
<head>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        #app{
            width: 50px;
            height: 50px;
            border-top: 100px solid transparent;
            border-bottom: 100px solid green;
            border-left: 100px solid transparent;
            border-right: 100px solid transparent;
        }
    style>
head>
<body>
    <div id="app">div>
body>
html>

前端面试可能会问到的知识点记录_第3张图片

http和https响应的状态码有哪些

  • 200~300一般是请求成功的
    • 204代表服务器处理了请求,但没有返回任何数据(也就是没有内容)
  • 404 (not found) 找不到页面
  • 403 服务器拒绝了请求
  • 503 服务不可用

cookie localStorage sessionStorage区别

  • cookie: 存储的数据比较小,4kb左右,在有效期之前一直存在,一般用于验证用户,比如说token,并且每次都会携带在HTTP请求头中
  • localStorage: 可以长期存储在用户电脑并且所有的网页都可以访问,存储大小大概5m
  • sessionStorage: 有效期存储在当前页面,页面关闭后sessionStorage就失效(不违法同源策略的情况下),存储大小大概5m

TCP三次握手,四次挥手

TCP三次握手

  • 目的就是为了确认双方的接收能力和发送能力是否正常

  • 客户端向服务端发送一个SYN(同步序列),等待服务器确认

  • 服务端收到客户端的SYN后进行确认客户端的SYN包,然后也发送一个自己的SYN包,发送(SYN+ACK)包给客户端

  • 客户端收到SYN+ACK包后,向服务端发送确认包ACK,发送完成则建立连接,开始传输数据

四次挥手

  • 客户端发出连接释放的报文,并 进入终止等待1 状态
  • 服务器收到连接释放报文,发出确认报文,并且服务器进入关闭等待状态
  • 客户端收到服务器确认请求后,进入 终止等待2 状态,(管子里面还有数据,要流完!)
  • 服务器将最后的数据传输完成的时候,就告诉客户端,向客户端发送连接释放报文,并且服务器进入最后确认状态
  • 客户端收到服务器的连接释放报文后,必须发出确认,此时客户端进入时间等待状态,注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态
  • 服务器收到客户端发送的确认,立即进入关闭状态

大概过程

前端面试可能会问到的知识点记录_第4张图片

for … in 和for … of

简单来说一句话的博主

for…in是es5的用于遍历key

for…of是es6的用于遍历value

  • 先有in,再由of
<script>
	var arr = ["李白", "诗人", "陋室铭"];
	for (var key in arr) {
		//0 1 2 
		console.log(key);
	}

	for (var value of arr) {
		//李白 诗人 陋室铭
		console.log(value);
	}
</script>

一个比较神奇的例子:

Object.prototype.objCustom = function () {}; 
Array.prototype.arrCustom = function () {};

let iterable = [3, 5, 7];
iterable.foo = "hello";

for (let i in iterable) {
  console.log(i); //  0, 1, 2, "foo", "arrCustom", "objCustom"
}
//arrCustom是继承自Array的属性,objCustom是继承自Object的属性。

for (let i of iterable) {
  console.log(i); // 3, 5, 7
}

nginx正向代理,反向代理

正向代理:

​ 面向客户,帮助客户解决问题,比如说我要访问YouTube,配置nginx后就可以访问了,这就是正向代理

前端面试可能会问到的知识点记录_第5张图片

反向代理:

​ 面向服务器,帮助服务器解决问题,比如配置代理转发请求,原来是本地的请求转发到远程

前端面试可能会问到的知识点记录_第6张图片

css九宫格

弹性盒布局笔记

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Documenttitle>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        #wrap{
            width: 306px;
            height: 304px;
            display: flex;
            border: 1px solid red;
            /* 关键是设置弹性盒是否换行,不然的话就会压缩 */
            flex-wrap: wrap;
        }
        #wrap div{
            width: 100px;
            height: 100px;
            border: 1px solid blue;
        }
    style>
head>
<body>
    <div id="wrap">
        <div id="one">div>
        <div id="two">div>
        <div id="three">div>
        <div id="four">div>
        <div id="five">div>
        <div id="six">div>
        <div id="seven">div>
        <div id="eight">div>
        <div id="nine">div>
    div>
body>
html>

效果图

前端面试可能会问到的知识点记录_第7张图片

微任务和宏任务

宏任务:定时器

微任务:promise

微任务的优先级大于宏任务

computed和methods区别

  • computed是带缓存的,只有当引用的数据发生变化的时候才会重新计算,而methods是每次调用都会重新执行

  • computed是响应式的,methods不是响应式的

    • 当computed当中靠依赖的值计算出来的结果发生改变的时候,会引发computed重新计算,而methods不会
  • computed可以具有getter和setter方法,因此可以赋值,而methods是不行的。

    • 比如computed
data(){
	return {
		name:"李",
		lastName:"白"
	}
},
computed:{
	fullName(){
        return this.name+this.lastName;
    },
    //等同于
    fullName:{
        get(){
            return this.name+this.lastName
        }
    },
    //写全点这样子写
    fullName:{
        get(){
            return this.name+this.lastName;
        },
        set(newValue){
            //处理newValue
        }
    }
}

闭包和原型和原型链

闭包

  • 内部函数有对上层作用域的引用
  • 内部函数在所在定义域外保持引用并进行访问
  • 这位博主写的很详细@地址
<script>
    function father(){
    var a = 100;
    var b = function(){
        //内部函数有对上层作用域的引用
        console.log(a);
        a++;
    }
    return b;
}
// 内部函数在所在定义域外保持引用并进行访问
var temp = father();
temp();
temp();
temp();
</script>

原型和原型链

原型

  • 每一个函数(比如说构造函数),都具有一个属性叫prototype,这个属性指向的是一个对象,我们叫这个对象叫原型对象
  • 每一个实例化对象,都具有一个属性叫__proto__,这个属性指向其构造函数的prototype属性,并且有如下关系(实例化对象.__proto__ === 构造函数.prototype)

原型链

  • 当对象在自身找不到被调用的属性或者方法的时候,就会去原型链身上寻找
  • 以下代码寻找属性sex过程
    • 1.在自身上寻找sex属性,如果有,就返回,没有就接着下一步寻找
    • 2.在其构造函数身上寻找是否有sex属性(通过实例化对象.__proto__来访问),有就返回,没有就下一步寻找
    • 3.在其构造函数身上的上一层再次寻找(因为原型对象也是一个对象,是一个实例对象),通过实例化对象.__proto__.__proto__来进行访问并寻找是否有sex属性
    • 发现没有,就返回undefined,
    <script>
      function Dog(name, color) {
        this.name = name;
        this.color = color;
      }
      //为原型链上添加一个属性'sex';
      Dog.prototype.sex = "未知";
      //建立一个实例化对象
      var xiaobai = new Dog("小白", "白色");
      //输出结果为 '未知'
      console.log(xiaobai.age);
    </script>
证明在原型链上寻找
  • hasOwnProperty查找某一个对象自身是否有某一个属性,不会去查找他的原型链
    <script>
      function Dog(name, color) {
        this.name = name;
        this.color = color;
      }
      //为原型链上添加一个属性'sex';
      Dog.prototype.sex = "未知";
      //建立一个实例化对象
      var xiaobai = new Dog("小白", "白色");
      //搜索实例化对象身上是否有属性'sex'
      var result = xiaobai.hasOwnProperty("sex");
      //输出结果为false
      console.log(result);
    </script>

说一说new会发生什么?

  1. 创建一个新的实例化对象

  2. 该构造函数的this指向新的实例化对象

  3. 执行该构造函数体

  4. 返回这个this(如果没有返回值的情况下)

  5. 创建一个新对象,并在内存当中开辟一个新对象

  6. 将新对象的__proto__(隐式原型链)指向构造函数的prototype(显示原型链)

  7. 将构造函数的this指向新的实例化对象

  8. 返回这个新对象

项目中的难点

  1. 编程式路由往同一地址跳转时会报错的情况,很莫名其妙,虽然不影响,但是控制台总是会报错,找github找百度才解决

router配置文件中添加如下代码

const originalPush = VueRouter.prototype.push;
//解决重复提交相同链接报错
VueRouter.prototype.push = function push(location, onResolve, onReject) {
    if (onResolve || onReject)
        return originalPush.call(this, location, onResolve, onReject)
    return originalPush.call(this, location).catch((err) => {
        if (VueRouter.isNavigationFailure(err)) {
            // resolve err
            return err
        }
        // rethrow error
        return Promise.reject(err)
    })
}
const originalReplace = VueRouter.prototype.replace;
VueRouter.prototype.replace = function replace(location, onResolve, onReject) {
    if (onResolve || onReject){
        //回调函数里面会用到this的指向,所以就要使用call
        return originalReplace.call(this, location, onResolve, onReject)
    }
    return originalReplace.call(this, location).catch((err) => {
        if (VueRouter.isNavigationFailure(err)) {
            //如果为相同链接引发的错误,返回错误原因,promise状态为resolve
            // resolve err
            return err
        }
        // rethrow error
        return Promise.reject(err)
    })
}
  1. 轮播图问题,数据显示正常,但是轮播图好像有问题,后面发现是数据在到达之前就设置swiper轮播图,所以添加$nextTick即可,保证数据有了后,dom被渲染了后在执行swiper初始化操作
 watch:{
    /* 监视bannerList数据更新 */
    bannerList(){
      /* 等待页面更新完成后执行回调 */
      this.$nextTick(()=>{
          //不应该使用类选择器的,这样子后期生成会选择所有相同的类!!
         var mySwiper = new Swiper(this.$refs.mySwiper, {
          // direction: 'vertical', // 垂直切换选项
          loop: true, // 循环模式选项
          // 如果需要分页器
          pagination: {
            el: ".swiper-pagination",
          },
          autoplay: {
            //触碰后不会停止自动切换
            disableOnInteraction: false,
          },
          // 如果需要前进后退按钮
          navigation: {
            nextEl: ".swiper-button-next",
            prevEl: ".swiper-button-prev",
          },
        });
      });
    }
  },

数组的去重

  • set构造函数去重
    • 调用set构造函数转换为一个集合,在进行转换回去数组(扩展运算符…或者Array.from)就实现了去重
<script>
	var tempArray = [1,2,3,4,5,5,6,7];
	//转化为set
	var tempSet = new Set(tempArray);
	//set转换回来数组 - 方法1
	var tempAfterArray1 = [...tempSet];
	///set转换回来数组 - 方法2
	var tempAfterArray2 = Array.from(tempSet);
	//[1, 2, 3, 4, 5, 6, 7]
	console.log(tempAfterArray1);
	//[1, 2, 3, 4, 5, 6, 7]
	console.log(tempAfterArray2);
</script>
  • 普通方法去重1-双重for循环
    • 通过双重for循环,第一层循环为i的时候,第二层循环就从i+1位置开始遍历,如果发现相同的,则通过splice来删除,(splice会改变原数组)
for(var i = 0;i<tempArray.length;i++){
	for(var j = i+1;j<tempArray.length;j++){
		if(tempArray[i] == tempArray[j]){
			tempArray.splice(j,1);
			j--;
		}
	}
}
  • 普通方法去重-filter和indexOf结合
    • indexOf在数组中,返回该数组中第一个找到的索引位置,若未找到,则返回-1
var tempArray = [1, 2, 5, 5, 6, 6, 7];

//item为当前遍历的项
//index为当前遍历项的索引
var a = tempArray.filter((item, index) => {
    return tempArray.indexOf(item) == index;
})
//[1, 2, 5, 6, 7]
console.log(a);

//遍历过程
item = 1,index=0
tempArray.indexOf(item) 返回 0
return 0 == 0 ;//为true,存储'1'

item = 2,index=1
tempArray.indexOf(item) 返回 1
return 1 == 1 ;//为true,存储'2'


item = 5,index=2
tempArray.indexOf(item) 返回 2
return 2 == 2 ;//为true,存储'5'

item = 5,index=3
tempArray.indexOf(item) 返回 2
return 2 == 3 ;//为false,不存储


item = 6,index=4
tempArray.indexOf(item) 返回 4
return 4 == 4 ;//为true,存储'6'

item = 6,index=5
tempArray.indexOf(item) 返回 4
return 4 == 5 ;//为false,不存储

item = 7,index=6
tempArray.indexOf(item) 返回 6
return 6 == 6 ;//为true,存储'7'
  • 使用indexOf
let temp = [];
tempArray.forEach(item=>{
	if(temp.indexOf(item)===-1){
		temp.push(item);
	}
})

ES6+的常见语法

let

特点:

  • 没有变量提升的功能
  • 块级作用域
  • 不可以重复声明
    • 不可以出现let a = 100;然后又出现let a = 90;但是var变量可以
  • 具有暂时性死锁
    • 具体可以看看这个

解构赋值

数组的解构赋值

var str = "动感超人&18";
let[name,age] = str.split("&");
console.log(name,age);//动感超人 18

也可以跳过接收
var str = "动感超人&18";
let[,age] = str.split("&");
console.log(age);// 18

对象的解构赋值

var objName = {
    name:"李白",
    age:2000,
}
let {name,age}=objName;
console.log(name,age);//李白 2000

箭头函数

  • this始终指向函数声明时所在作用域下的this的值(在什么环境下,什么this执行,不会受其他改变)

Promise

promise常用的方法

  • Promise.reject(reason)方法返回一个带有拒绝原因的Promise对象。
  • Promise.resolve(value)方法返回一个以给定值解析后的Promise 对象
  • Promise.all(iterable)方法获取这个可迭代对象的promise的结果,
    • 当iterable有一个是reject,那么就会触发catch,返回的值就是这个reject传递过来的值
    • 当iterable全部为resolve,那么就会触发then,返回的值就是由resolve传递过来的值组成的数组
    • 一句话,有错误立即输出错误的reject,没有错误就输出resolve返回的值
//iterable中有一个是reject
<script>
	var p1 = Promise.resolve("promise1");
	var p2 = Promise.reject("promise2");
	var p3 = Promise.resolve("promise3");
	Promise.all([p1, p2, p3]).then((data) => {
        //不会被执行
		console.log(data);
	}).catch(function (error) {
		//catch方法将会被执行,输出结果为:"promise2"
		console.log(error);
	});
</script>

//iterable均为resolve
<script>
	var p1 = Promise.resolve("promise1");
	var p2 = Promise.resolve("promise2");
	var p3 = Promise.resolve("promise3");
	Promise.all([p1, p2, p3]).then((data) => {
        //输出['promise1', 'promise2', 'promise3']
		console.log(data);
	}).catch(function (error) {
		//不会被执行
		console.log(error);
	});
</script>

  • Promise.race(iterable)
    • 一句话概括:这个iterable竞争,只要有一个完成了,那么这个Promise.race的返回值就是这个第一个完成的值,不管结果本身是成功状态还是失败状态。

class类

可选链操作符

  • ?. 为可选链操作符
  • 有一段数据,我们是通过服务器来请求的,但是可以这段数据会嵌套很多层,有时候为了不报错,我们不得不去判断是否有数据才会去接着寻找下一层
  • 比如下面这层嵌套
var a = {
	b:{
		c:{
			d:{
				name:"李白的师傅"
			}
		}
	}
}
  • 在这之前,我们需要找寻字段d的数据,就要这样子,才不会发生报错
var dataD = a && a.b && a.b.c && a.b.c.d && a.b.c.d.name
  • 有了可选链操作符,就不用这么麻烦了,当某一段/某一层数据不存在的时候,会返回undefined
var dataD = a?.b?.c?.d?.name

ES6模块化

  • 分为默认暴露
  • 分别暴露
  • 整体暴露
  • 具体可以看这个文章

async await

  • 一句话概括作用:使得异步的代码像同步一样实现

async的返回值

  • async函数的返回值都是promise
  • 如果返回一个Promise类型的对象,由这个返回的Promise对象决定是resolve或者reject
<script>
	async function getResult(){
		return Promise.resolve("解决了");
	}
	console.log(getResult());
</script>

<script>
	async function getResult(){
		return Promise.reject("失败了");
	}
	console.log(getResult());
</script>

resolve-输出一个状态为pending的promise

resolve-输出一个状态为pending的promise

reject-输出一个状态为pending的promise 并引发报错提示

前端面试可能会问到的知识点记录_第8张图片

  • 如果返回一个非Promise类型,返回的结果js会自动帮助封装成为一个promise对象
<script>
	async function getResult(){
		return "看看我是什么"
	}
	console.log(getResult());
</script>

返回一个状态为fulfiled的promise

前端面试可能会问到的知识点记录_第9张图片

  • 抛出错误 ,返回的结果是一个失败的Promise对象
<script>
	async function getResult(){
		return new Error('出错啦')
	}
	console.log(getResult());
</script>

抛出错误的async返回值

await

有时候在项目当中,经常使用await来发送ajax请求获取数据后的操作,因为await可以阻塞进程,等待这个promise有结果后才开始后面的代码!!!

await遇上成功的promise(也就是resolve了)
<script>
	function testAwait() {
		return new Promise((resolve, reject) => {
			resolve("成功解决问题")
		})
	}

	async function getResult() {
		var result;
		try {
			result = await testAwait();
			console.log("成功了,结果为-", result);
		} catch (error) {
			console.log("捕捉到失败,原因为-", error);
		}
		console.log("我可不可以执行到这里")
	}
	console.log(getResult());
</script>

前端面试可能会问到的知识点记录_第10张图片

await遇上失败的promise(也就是reject了)

例子1: 遇上失败的promise并且使用try...catch捕捉

<script>
	function testAwait() {
		return new Promise((resolve, reject) => {
			reject("失败了")
		})
	}

	async function getResult() {
		var result;
		try {
			result = await testAwait();
			console.log("成功了,结果为",result);
		} catch (error) {
			console.log("捕捉到失败,原因为",error);
		}
        //输出结果证明可以
        console.log("我可不可以执行到这里")
	}
	console.log(getResult());
</script>

例子1输出结果

前端面试可能会问到的知识点记录_第11张图片

例子2: 遇上失败的promise没有使用,没有使用try..catch捕捉

<script>
	function testAwait() {
		return new Promise((resolve, reject) => {
			reject("失败了")
		})
	}

	async function getResult() {
		var result = await testAwait();;
		console.log("获取到的结果为",result);
		console.log("我可不可以执行到这里");//输出结果证明不会执行到这里
		// try {
		//     result = await testAwait();
		//     console.log("成功了,结果为",result);
		// } catch (error) {
		//     console.log("捕捉到失败,原因为",error);
		// }
	}
	console.log(getResult());
</script>

例子2输出结果

会发现报错了,并且console.log("我可不可以执行到这里") 没有执行,因为promise失败导致程序中断!

前端面试可能会问到的知识点记录_第12张图片

总结: await遇上失败的promise(也就是reject了)
  • await一旦遇到reject,后面代码就不会被执行了
  • 可以使用try...catch来解决,并且catch捕捉到的原因为reject传递的参数值

模板字符串

tab上面按键的符号 里面可以用${变量名}来使用变量

`你的名字为${name},年龄为${age}`

什么是执行上下文

​ 执行上下文是指函数调用时在执行栈中产生的当前函数的执行环境,该环境如隔绝外部世界的容器边界,保管可访问的变量、this对象等。(说通俗点就是函数执行时候的一个环境)

执行上下文

分类

  • 分为全局执行上下文(我理解为刚执行js脚本时候的一些初始化操作,比如我们没有写window对象我们却可以使用window对象,并且却可以直接输出this)
  • 函数执行上下文

注意

不管是全局执行上下文,还是函数执行上下文,上下文的创建过程都是如下

  • 第一步:创建函数上下文,压入栈
    1. 收集变量,形成变量对象(里面也包含了this这个变量)
    2. 确定this的指向(全局执行上下文的this指向的是window)
    3. 确定作用域链(作用域链,查找变量的过程,如果在作用域链上找不到就报错),作用域链是数组的形式
  • 第二步: 执行上下文(也就是执行里面的代码)

使用这一段代码来作为全局执行上下文和函数执行上下文的例子

<script>
function Dog(name,age){
    this.name = name;
    this.age = age;
}
var xiaobai = new Dog("小白",10);
</script>

1.先全局执行上下文

第一步:创建全局执行上下文,压入栈

1.收集变量,形成变量对象

前端面试可能会问到的知识点记录_第13张图片

2.确定this的指向

前端面试可能会问到的知识点记录_第14张图片

3.确定作用域链

前端面试可能会问到的知识点记录_第15张图片

第二步:执行全局上下文 比如说赋值

要执行的代码如下,注意注释

  • 执行可以执行的代码,比如说函数的调用,变量的赋值之类的
<script>
//函数定义,不执行
function Dog(name,age){
    this.name = name;
    this.age = age;
}
//执行
var xiaobai = new Dog("小白",10);
</script>

执行var xiaobai = new Dog("小白",10);代码的过程 这里是在调用函数的过程,所以可以理解为在调用函数执行上下文,所以我们跳转到函数执行上下文的过程

2.函数执行上下文

第一步:创建函数执行上下文

1.收集变量,形成变量对象,2.确定this的指向 3.确定作用域链

前端面试可能会问到的知识点记录_第16张图片

第二步:执行函数上下文

function Dog(name,age){
    //执行里面的代码
    this.name = name;
    this.age = age;
}

前端面试可能会问到的知识点记录_第17张图片

注意: 执行函数上下文完成,所属的栈会被丢弃!

但是,丢弃之前返回了this指向,并存储在了变量xiaobai当中

function Dog(name,age){
    //执行里面的代码,执行完成后销毁
    this.name = name;
    this.age = age;
    //你不添加系统也会自动加一个
    return this;
}

var xiaobai = new Dog("小白",10);

如图,销毁前返回了this并保存在了变量xiaobai当中

前端面试可能会问到的知识点记录_第18张图片

数据绑定原理

需要解决的2个问题

  • 如何知道data的属性发生了变化的 (重要)
    • 也就通过observer为data当中的每一个属性通过数据劫持添加gettersetter
    • 原来是没有gettersetter的,通过数据劫持去添加了set,get
  • 那么要如何知道当前这个数据变化要更新哪些节点呢?
    • 订阅者/发布者模式

前端面试可能会问到的知识点记录_第19张图片

1.发布者observer

  • 给data中的所有层次的属性都添加settergetter(也就是数据劫持)
  • 同时为data当中的每一个属性创建一个对应的dep对象
  • dep对象data当中的每一个属性为一一对应的对象

1.5中间还需要一个dep(订阅器),去通知订阅者

  • dep和watcher的关系是初始化的时候就建立起来的

2.订阅者(watcher)

  • 解析每一个模板语法都创建一个watcher(比如模板当中有一个标签使用了插值语法,那就会创建一个watcher,v-bind也会触发创建watcher)
  • 并且创建watcher的时候最后一个参数为用于更新节点的回调函数
  • 订阅者需要知道数据变了,从而去更新界面

webpack

说说看什么是webpack

  • webpack就是一个模块化打包工具,它将所有的文件看成是模块,它会分析项目目录下的模块(比如说less,sass等),并将其转换和打包为合适的格式供浏览器使用

什么是loader,什么又是plugin

  • loader: 是文件加载器,可以加载资源文件,并对这些文件进行一些处理,比如:编译,压缩等,
  • plugin:webpack在运行的生命周期会广播出许多事件,plugin可以监听这些事件,在合适的时机中通过webpack提供的api改变输出结果

区别:

  • loader是将A文件进行编译形成B文件,这里操作的是文件,A.less => A.css
  • plugin是用于在webpack打包编译过程里,在对应的事件节点里执行自定义操作,比如资源管理、bundle文件优化等操作

有哪些常见的Loader,他们都是解决什么问题的

  • 原本的webpack只能处理js/json的模块,有了loader或者plugin后,就可以处理更多的模块了

常见的模块

  1. css-loader: 加载.css文件(也就是可以使用css模块了)
  2. style-loader:使用