1.AST
AST 是什么
抽象语法树 (Abstract Syntax Tree),简称 AST,它是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
AST 有什么用
- 编辑器的错误提示、代码格式化、代码高亮、代码自动补全;
elint
、pretiier
对代码错误或风格的检查;webpack
通过babel
转译javascript
语法;
AST 如何生成
1.读取 js 文件中的字符流
2.然后通过词法分析生成 token
,之后再通过语法分析( Parser )生成 AST
整个解析过程主要分为以下两个步骤:
- 分词:将整个代码字符串分割成最小语法单元数组
- 语法分析:在分词基础上建立分析语法单元之间的关系
3.最后生成机器码执行。
为了兼容低版本浏览器 我们也通常会使用 webpack 打包编译我们的代码将 ES6 语法降低版本,比如箭头函数变成普通函数。将 const、let 声明改成 var 等等,他都是通过 AST 来完成的,只不过实现的过程比较复杂,精致。不过也都是这三板斧:
- js 语法解析成 AST;
- 修改 AST;
- 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()
的回调函数中。