7.30复盘

1.AST

AST 是什么

抽象语法树 (Abstract Syntax Tree),简称 AST,它是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

AST 有什么用
  • 编辑器的错误提示、代码格式化、代码高亮、代码自动补全;
  • elintpretiier 对代码错误或风格的检查;
  • webpack 通过 babel 转译 javascript 语法;
AST 如何生成

1.读取 js 文件中的字符流

2.然后通过词法分析生成 token,之后再通过语法分析( Parser )生成 AST

整个解析过程主要分为以下两个步骤:

  • 分词:将整个代码字符串分割成最小语法单元数组
  • 语法分析:在分词基础上建立分析语法单元之间的关系

3.最后生成机器码执行。

为了兼容低版本浏览器 我们也通常会使用 webpack 打包编译我们的代码将 ES6 语法降低版本,比如箭头函数变成普通函数。将 const、let 声明改成 var 等等,他都是通过 AST 来完成的,只不过实现的过程比较复杂,精致。不过也都是这三板斧:

  1. js 语法解析成 AST;
  2. 修改 AST;
  3. AST 转成 js 语法;

2.git 如何解决代码冲突?

操作就是把自己修改的代码隐藏,然后把远程仓库的代码拉下来,然后把自己隐藏的修改的代码释放出来,让 git 自动合并。接着找 <<<<<<<, 哪里冲突哪里改。

3.git rebase, git merge 的区别?

merge 是合并,rebase 是变基.

git merge 会把公共分支和当前的commit合并在一起,形成一个新的commit提交。

git rebase 会把你当前分支的commit放到公共分支的最后面,所以叫变基。

4.GitFlow 基本流程和你的理解?

项目迭代通常是:开发=》提测=》Debug=》上线,这样的流程,中间会穿插进高优需求和hotfix。

  • 需要一个分支与生产环境完全对应,以随时处理hotfix(紧急bu g)的情况,使用master分支
  • 需要一个分支聚合日常开发的所有内容,保持最新的代码,以方便所有协作者同步代码,推荐develop分支
  • 线上出现紧急BUG,需要马上修复并上线,不适合直接在master分支上操作,需要一个临时分支,推荐hotfix
  • 每个人的日常开发分支,用到多个分支,以feature为单位,用完即删
  • 提测后的debug阶段,建议使用bugfix分支与feature分支进行区分,推荐使用bugfix分支
  • Debug后处于上线ready的状态下,需要一个专门上线的分支,推荐使用release分支

5.对原型链的理解:

在JavaScript中是使用构造函数来新建一个对象的,每一个构造函数的内部都有一个 prototype 属性,它的属性值是一个对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。当使用构造函数新建一个对象后,在这个对象的内部将包含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型。

当访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。原型链的尽头一般来说都是 Object.prototype 所以这就是新建的对象为什么能够使用 toString() 等方法的原因。

6.对闭包的理解:(举例很重要)

比如,函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包。

闭包是指有权访问另一个函数作用域中变量的函数。(定义)

主要作用:延伸了变量的作用范围

特点:1、让外部访问函数内部变量成为可能

2.可以避免使用全局变量,防止全局变量污染

3.会造成内存泄漏(有一块内存空间被长期占用,而不被释放)

7.什么是循环引用

当对象 1 中的某个属性指向对象 2,对象 2 中的某个属性指向对象 1 就会出现循环引用。

使用 JSON.decycle 可以去除循环引用。

8.map是什么?

map本质上就是键值对的集合,但是普通的Object中的键值对中的键只能是字符串。而ES6提供的Map数据结构类似于对象,但是它的键不限制范围,可以是任意类型,是一种更加完善的Hash结构。

WeakMap WeakMap 对象也是一组键值对的集合,其中的键是弱引用的。其键必须是对象,原始数据类型不能作为key值,而值可以是任意的。

WeakMap的设计目的在于,有时想在某个对象上面存放一些数据,但是这会形成对于这个对象的引用。一旦不再需要这两个对象,就必须手动删除这个引用,否则垃圾回收机制就不会释放对象占用的内存。

9.promise.all()和race()

.all()的作用是接收一组异步任务,然后并行执行异步任务,并且在所有异步操作执行完后才执行回调。

.race()的作用也是接收一组异步任务,然后并行执行异步任务,只保留取第一个执行完成的异步操作的结果,其他的方法仍在执行,不过执行结果会被抛弃。

10.promise一些奇特的手写题:

1.使用Promise实现每隔1秒输出1,2,3

可以用Promise配合着reduce不停的在promise后面叠加.then

const arr = [1, 2, 3]
arr.reduce((p, x) => {
  return p.then(() => {
    return new Promise(r => {
      setTimeout(() => r(console.log(x)), 1000)
    })
  })
}, Promise.resolve())
2. 使用Promise实现红绿灯交替重复亮

红灯3秒亮一次,黄灯2秒亮一次,绿灯1秒亮一次;如何让三个灯不断交替重复亮灯?

实现思路:

1.我们需要用到一个定时器函数接收俩个参数,亮灯颜色和亮灯时间。

2.由于Promise是只有发生状态响应才会继续执行后续操作,那么我们先在Promise中将颜色打印出来。

3.然后通过一个定时器设置时间延时,延时相当于就是亮灯的时间,因为我们亮灯了,然后延时,就会造成一种灯在这段时间一直亮着的效果

function red() {
  console.log("red");
}
function green() {
  console.log("green");
}
function yellow() {
  console.log("yellow");
}
const light = function (timer, cb) {
  return new Promise(resolve => {
    setTimeout(() => {
      cb()
      resolve()
    }, timer)
  })
}
const step = function () {
  Promise.resolve().then(() => {
    return light(3000, red)
  }).then(() => {
    return light(2000, green)
  }).then(() => {
    return light(1000, yellow)
  }).then(() => {
    return step()
  })
}

step();
3. 实现mergePromise函数

实现mergePromise函数,把传进去的数组按顺序先后执行,并且把返回的数据先后放到数组data中。

const time = (timer) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve()
    }, timer)
  })
}
const ajax1 = () => time(2000).then(() => {
  console.log(1);
  return 1
})
const ajax2 = () => time(1000).then(() => {
  console.log(2);
  return 2
})
const ajax3 = () => time(1000).then(() => {
  console.log(3);
  return 3
})

function mergePromise () {
  // 在这里写代码
}

mergePromise([ajax1, ajax2, ajax3]).then(data => {
  console.log("done");
  console.log(data); // data 为 [1, 2, 3]
});

// 要求分别输出
// 1
// 2
// 3
// done
// [1, 2, 3]

思路:

  • 定义一个数组data用于保存所有异步操作的结果
  • 初始化一个const promise = Promise.resolve(),然后循环遍历数组,在promise后面添加执行ajax任务,同时要将添加的结果重新赋值到promise上。
function mergePromise (ajaxArray) {
  // 存放每个ajax的结果
  const data = [];
  let promise = Promise.resolve();
  ajaxArray.forEach(ajax => {
  	// 第一次的then为了用来调用ajax
  	// 第二次的then是为了获取ajax的结果
    promise = promise.then(ajax).then(res => {
      data.push(res);
      return data; // 把每次的结果返回
    })
  })
  // 最后得到的promise它的值就是data
  return promise;
}
4.封装一个异步加载图片的方法

只需要在图片的onload函数中,使用resolve返回一下就可以了。

function loadImg(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = function() {
      console.log("一张图片加载完成");
      resolve(img);
    };
    img.onerror = function() {
    	reject(new Error('Could not load image at' + url));
    };
    img.src = url;
  });
5. 限制异步操作的并发个数并尽可能快的完成全部

有8个图片资源的url,已经存储在数组urls中。

urls`类似于`['https://image1.png', 'https://image2.png', ....]

而且已经有一个函数function loadImg,输入一个url链接,返回一个Promise,该Promise在图片下载完成的时候resolve,下载失败则reject

但有一个要求,任何时刻同时下载的链接数量不可以超过3个。请写一段代码实现这个需求,要求尽可能快速地将所有图片下载完成。

思路:

既然题目的要求是保证每次并发请求的数量为3,那么我们可以先请求urls中的前面三个(下标为0,1,2),

并且请求的时候使用Promise.race()来同时请求,三个中有一个先完成了(例如下标为1的图片),

我们就把这个当前数组中已经完成的那一项(第1项)换成还没有请求的那一项(urls中下标为3)。

直到urls已经遍历完了,然后将最后三个没有完成的请求(也就是状态没有改变的Promise)用Promise.all()来加载它们。

function limitLoad(urls, handler, limit) {
  let sequence = [].concat(urls); // 复制urls
  // 这一步是为了初始化 promises 这个"容器"
  let promises = sequence.splice(0, limit).map((url, index) => {
    return handler(url).then(() => {
      // 返回下标是为了知道数组中是哪一项最先完成
      return index;
    });
  });
  // 注意这里要将整个变量过程返回,这样得到的就是一个Promise,可以在外面链式调用
  return sequence
    .reduce((pCollect, url) => {
      return pCollect
        .then(() => {
          return Promise.race(promises); // 返回已经完成的下标
        })
        .then(fastestIndex => { // 获取到已经完成的下标
        	// 将"容器"内已经完成的那一项替换
          promises[fastestIndex] = handler(url).then(
            () => {
              return fastestIndex; // 要继续将这个下标返回,以便下一次变量
            }
          );
        })
        .catch(err => {
          console.error(err);
        });
    }, Promise.resolve()) // 初始化传入
    .then(() => { // 最后三个用.all来调用
      return Promise.all(promises);
    });
}
limitLoad(urls, loadImg, 3)
  .then(res => {
    console.log("图片全部加载完毕");
    console.log(res);
  })
  .catch(err => {
    console.error(err);
  });

11.事件循环

JavaScript 是一门单线程语言同步和异步任务分别进入不同的执行环境,同步的进入主线程,即主执行栈,异步的进入任务队列。主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。 上述过程的不断重复就是我们说的 Event Loop (事件循环)。

node

node在处理各种事件时,首先从上往下将同步代码诶个进行处理,当遇到异步代码时,就会进入node的事件队列中去。

node的事件循环主要从timers、poll、check、nextTick、和promise这几个来进行监听与处理.

**timer:**主要负责计时器线程,负责监听计时器是否到执行时间,到了的话,就会执行计时器里面的函数。列如setTimeout和setInterval都是在该线程执行的。

**poll:**当进入poll队列时,即使没有要执行的事件,poll也会一直等下去,一直等到有需要执行的事件,或者其他事件队列里面有需要执行的事件时才会进行下一步到check队列。(最费时间的是在poll时)

check:check主要用于监听setImmediate事件setImmediate事件会直接进入check队列,不会像timers那样只有时间到了才会进入队列

当在event loop判断完后,实际上第一步应该是查看nextTick然后查看promise,之后才是timers.nextTick的优先级高于promise,所以nextTick一定是最先去处理的

两个特点:

nextTick和promise,可以说就是js里面的微队列,而timers、poll、check都是处于宏队列中

setImmediate的执行顺序本来应该要高于setTimeout的,但因为计算机的执行速度,导致可能setTimeout比setImmediate更快的执行

12.postion属性?

static 是 position 的默认值,就是没有定位,元素处于现在正常的文档流中

relative 是相对定位,指的是给元素设置相对于自己原本位置的定位,元素并不脱离文档流

absolute 是绝对定位,是的指让元素相对于 static 定位之外的第一个父元素进行定位

fixed 是一种特殊的绝对定位,也会脱离文档流,只不过 fixed 的元素是固定相对与 body 来定位的

inherit 就是继承父元素的 position 属性

sticky!!!

网站滚动到一定高度的时候,让一部分内容作为navbar,也就是置顶显示,我们一般会使用js监听scroll事件来实现,但是新增的css属性position:sticky可以简单实现

position:sticky 使用条件

1.父元素不能overflow:hidden或者overflow:auto属性。

2.必须指定top、bottom、left、right4个值之一,否则只会处于相对定位

3.父元素的高度不能低于sticky元素的高度

4、sticky元素仅在其父元素内生效

箭头函数与普通函数的区别

(1)箭头函数比普通函数更加简洁

(2)箭头函数没有自己的this

(3)箭头函数继承来的this指向永远不会改变

(4)箭头函数不能作为构造函数使用

(5)箭头函数没有自己的arguments

13.readyState

每当 readyState 改变时,就会触发 onreadystatechange 事件。

readyState 属性存有 XMLHttpRequest 的状态信息。

0 (未初始化)对象已建立,但是尚未初始化(尚未调用open方法)
1 (初始化)已调用send()方法,正在发送请求
2 (发送数据)send方法调用完成,但是当前的状态及http头未知
3 (数据传送中)已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误,
4 (完成)数据接收完毕,此时可以通过通过responseBody和responseText获取完整的回应数据

14.get和post的区别

当你一层一层的把get和post剖析到底,你会发现他们的本质就是tcp连接,没有啥区别,只是由于http协议规定和浏览器或者服务器的限制,导致他们在应用过程中体现形式不同。

但是它们中还有一个较大的区别:

1.get在请求时发送一个数据包,会将header和data一起发送过去,而post会产生两个数据包先发送header,服务器返回100,然后在发送data,服务器返回200

2.最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。

3.post能发送更多的数据类型(get只能发送ASCII字符) post发送的数据更大(get有url长度限制)

4.post更安全(不会作为url的一部分,不会被缓存、保存在服务器日志、以及浏览器浏览记录中)

5.post用于修改和写入数据,get一般用于搜索排序和筛选之类的操作(淘宝,支付宝的搜索查询都是get提交),目的是资源的获取,读取数据

15.对BFC的理解,如何创建BFC

块格式化上下文是Web页面的可视化CSS渲染的一部分。BFC是一个独立的布局环境,可以理解为一个容器,在这个容器中按照一定规则进行物品摆放,并且不会影响其它环境中的物品。如果一个元素符合触发BFC的条件,则BFC中的元素布局不受外部影响。

BFC的作用:

解决margin的重叠问题:由于BFC是一个独立的区域,内部的元素和外部的元素互不影响,将两个元素变为两个BFC,就解决了margin重叠的问题。

解决高度塌陷的问题:在对子元素设置浮动后,父元素会发生高度塌陷,也就是父元素的高度变为0。解决这个问题,只需要把父元素变成一个BFC。常用的办法是给父元素设置overflow:hidden

创建自适应两栏布局:可以用来创建自适应两栏布局:左边的宽度固定,右边的宽度自适应。

创建BFC的条件:

  • 根元素:body;
  • 元素设置浮动:float 除 none 以外的值;
  • 元素设置绝对定位:position (absolute、fixed);
  • display 值为:inline-block、table-cell、table-caption、flex等;
  • overflow 值为:hidden、auto、scroll;

16.伪元素和伪类?

核心区别在于,是否创造了“新的元素”

伪元素:

1、本质上是创建了一个有内容的虚拟容器;

2、只能同时使用一个伪元素

3、不存在DOM文档中,是虚拟的元素,是创建新元素;(代表某个元素的子元素,这个子元素虽然在逻辑上存在,但却并不实际存在于文档树中。)

4、如::before 、::after

伪类:

1、本质上是为了弥补常规CSS选择器的不足;

2、可以同时使用多个伪类;

3、存在DOM文档中(元标签找不到,只有符合触发条件时才看到),逻辑上存在,但在文档树中却无须标识的“幽灵”分类。

17.vuex

“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样可以方便地跟踪每一个状态的变化。

各模块在核心流程中的主要功能:

  • Vue Components∶ Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
  • dispatch∶操作行为触发方法,是唯一能执行action的方法。
  • actions∶ 操作行为处理模块。负责处理Vue Components接收到的所有交互行为。包含同步/异步操作。该模块提供了Promise的封装,以支持action的链式触发。
  • commit∶状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
  • mutations∶状态改变操作方法。是Vuex修改state的唯一推荐方法,其他修改方式在严格模式下将会报错。该方法只能进行同步操作,且方法名只能全局唯一。
  • state∶集中存储Vuecomponents中data对象的零散数据。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
  • getters∶ state对象读取方法。图Vue Components通过该方法读取全局state对象。

18.$nextTick 原理及作用

nextTick 的核心是利用了如 Promise 、MutationObserver、setImmediate、setTimeout的原生 JavaScript 方法来模拟对应的微/宏任务的实现,本质是为了利用 JavaScript 的这些异步回调任务队列来实现 Vue 框架中自己的异步回调队列。

比如:

有时候,可能遇到这样的情况,DOM1的数据发生了变化,而DOM2需要从DOM1中获取数据,那这时就会发现DOM2的视图并没有更新,这时就需要用到了nextTick了。

所以,在以下情况下,会用到nextTick:

  • 在数据变化后执行的某个操作,而这个操作需要使用随数据变化而变化的DOM结构的时候,这个操作就需要方法在nextTick()的回调函数中。
  • 在vue生命周期中,如果在created()钩子进行DOM操作,也一定要放在nextTick()的回调函数中。

因为在created()钩子函数中,页面的DOM还未渲染,这时候也没办法操作DOM,所以,此时如果想要操作DOM,必须将操作的代码放在nextTick()的回调函数中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值