面试题-期中测试答案

1.创建代理对象,通过代理对象访问属性时,
 抛出错误 Property "${key}" does not exist

const man = {
name: 'jscoder',
age: 22
}
const pMan = new Proxy(man, {
get(target, key){
if (key in target) {
return target[key]
} else {
throw new Error(`Property "${key}" does not exist`)
}
}
})


2.红灯3秒亮一次,绿灯1秒亮一次,黄灯2秒亮一次
 如何让三个灯不断交替重复亮灯? (用Promise实现)三个亮灯函数已经存在:
const man = {
name: 'jscoder',
age: 22
}
const pMan = new Proxy(man, {
get(target, key){
if (key in target) {
return target[key]
} else {
throw new Error(`Property "${key}" does not exist`)
}
}
})
function red(){
console.log('red');
}
function green(){
console.log('green');
}
function yellow(){
console.log('yellow');
}
var light = function(timmer, cb){
return new Promise(function(resolve, reject) {
setTimeout(function() {
cb();
resolve();
}, timmer);
});
};
var step = function() {
Promise.resolve().then(function(){
return light(3000, red);
}).then(function(){
return light(2000, green);
}).then(function(){
return light(1000, yellow);
}).then(function(){3.答案解析
在浏览器环境下打印结果是
result 2 undefined result 1 undefined
result 2 中执行的函数getCount()没有执行主体,里面函数的 this 是window,所以打印undefined
result 1 中执行的方法getCount()前面的执行者是action,而action中没有count熟悉,所以打印结果是
undefined
4.你觉得 TypeScript 和 JavaScript 有什么区别
 TS是JS的超集
 TS在JS的基础上添加类型系统以及完全的支持ES6+语法
 Angular, Vue.js3.0将直接支持TS
 TS需要编译,JS基本直接被浏览器解析执行
 
 2)TypeScript 你都用过哪些类型
 基本类型,数组类型,函数类型
 元组类型
 枚举类型
 
 3)TypeScript 中type和interface的区别
5.对 async/await 的理解,分析内部原理
Promise解决了回调地狱的问题,但是如果遇到复杂的业务,代码里面会包含大量的 then 函数,使得代
码依然不是太容易阅读。
基于这个原因,ES7 引入了 async/await,这是 JavaScript 异步编程的一个重大改进,提供了在不阻塞
主线程的情况下使用同步代码实现异步访问资源的能力,并且使得代码逻辑更加清晰,而且还支持 trycatch 来捕获异常,非常符合人的线性思维。
async/await,这种方式能够彻底告别执行器和生成器,实现更加直观简洁的代码。根据 MDN 定义,
async 是一个通过异步执行并隐式返回 Promise 作为结果的函数。可以说async 是Generator函数的语
法糖,并对Generator函数进行了改进
step();
});
}
step();
type 可以声明基本类型别名,联合类型,元组等类型
type 语句中还可以使用 typeof 获取实例的 类型进行赋值
interface 能够声明合并他的重点是自带了执行器,相当于把我们要额外做的(写执行器/依赖co模块)都封装了在内部
6.async/await如果右边方法执行出错该怎么解决
方式2:
async function test() {
let res = await 异步().then().catch()
}
7.说一下Event Loop的过程?promise 定义时传入的函数什么时候执
行?(小米 三面)
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event
Loop(事件循环)。
用两个队列来处理异步任务。
以setTimeout为代表的任务放到被称为macrotask,放到Macrotask queue中,
而以Promise 为代表的任务放到Microtask queue中。
eventloop对这两个队列的处理逻辑也不一样。
执行过程如下:
JavaScript引擎首先从macrotask queue中取出第一个任务,
执行完毕后,将microtask queue中的所有任务取出,按顺序全部执行(全部执行不仅指开始执行时队
列里的microtask,在这一步执行过程中产生的新的microtask,也要在这里执行)
然后再从macrotask queue中取下一个, 执行完毕后,再次将microtask queue中的全部取出; 循环往
复,直到两个queue中的任务都取完。
换句话说,一次eventloop循环会处理一个macrotask和所有这次循环中产生的microtask
 2)Promise定义时传入的函数什么时候执行的?
 定义时的函数称为 执行器函数, 它是同步的,会立即执行
8.说一下防抖函数的应用场景,并简单说下实现方式 (滴滴)
答:
应用场景:输入框搜索自动补全事件,频繁操作点赞和取消点赞等等
实现方式:
var timer = null;
function click(){
 clearTimeout(timer);
 timer = setTimeout(()=>{
 ajax(...);
 },500)
}
实现原理:如果在500ms内频繁操作,则每次都会清除一次定时器然后重新创建一个。直到最后一次操
作,然后等待500ms后发送ajax
方式1:
async function test() {
let res = await 异步()
}
test().catch()9.说一下V8的垃圾回收机制 (小米)
主要思路:
1. 新生代内存区分为两个等大小空间,使用空间为From,空闲空间为To
2. 将所有对象存储于From空间(包括活动对象和非活动对象)
3. 当From空间应用到一定程度后会触发GC机制,标记整理后将活动对象拷贝至To
4. From完成释放(From和To交换空间)
10.performance API 中什么指标可以衡量首屏时间
答:参考地址 https://www.cnblogs.com/longm/p/7382163.html
 https://juejin.im/post/6844904020482457613
11.说下暂时性死区
暂时性死区是ECMAScript与作用域相关的一个新语义模块, 在ES2015(又叫ES6)中引入
借鉴
https://sinaad.github.io/xfe/2016/02/26/temporal-dead-zone-tdz-demystified/
12.观察者和发布订阅的区别
在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模
式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模
式大多数时候是异步的(使用消息队列)。
观察者 模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式
13.gulp自己写过任务吗?说一下它的构建流程(阿里2018)
答:
gulp是基于Node开发环境运行的,所以要先确认好是否有Node开发环境
安装好Node以后,运行npm init创建package.json文件
安装gulp以及你的任务中要使用的依赖
创建并编写gulpfile.js文件
运行程序及打包
gulp的构建有三个核心概念,分别是读取流、转换流和写入流,我们通过读取流把需要转换的文件读取
出来,然后通过转换流的转换逻辑,转换成我们想要的结果,再通过写入流去写入到指定的文件位置。
这样的一个过程就完成了我们日常在构建当中所需要的工作。gulp的官方定义就是基于流的构建系统。
gulp希望实现一个构建管道的概念,这样的话,我们在后续去做一些扩展插件的时候就可以有一个很统
一的方式。14.package-lock.json 有什么作用,如果项目中没有它会怎么样,
举例说明
作用:package-lock.json 是在 npm install 时候生成一份文件,用来记录当前状态下实际安装的各个
npm package的具体来源和版本号。需要上传到git时,保证大家的依赖包一致。
解决了package.json缺点:原来package.json文件只能锁定大版本,也就是版本号的第一位,并不能锁
定后面的小版本,你每次npm install都是拉取的该大版本下的最新的版本,为了稳定性考虑我们几乎是
不敢随意升级依赖包的,这将导致多出来很多工作量,测试/适配等,所以package-lock.json文件出来
了,当你每次安装一个依赖的时候就锁定在你安装的这个版本。
15.webpack 常用配置项,并说明用途 (跟谁学 2020)
entry:打包的入口文件,一个字符串或者一个对象
output:配置打包的结果,一个对象
fileName:定义输出文件名,一个字符串
path:定义输出文件路径,一个字符串
module:定义对模块的处理逻辑,一个对象
loaders:定义一系列的加载器,一个数组
test:正则表达式,用于匹配到的文件
loader/loaders:字符串或者数组,处理匹配到的文件。如果只需要用到一个模块加载器则使

loader:string,如果要使用多个模块加载器,则使用loaders:array
include:字符串或者数组,指包含的文件夹
exclude:字符串或者数组,指排除的文件夹
resolve:影响对模块的解析,一个对象
extensions:自动补全识别后缀,是一个数组
plugins:定义插件,一个数组
16.阐述 webpack css-loader 作用 和 原理? (跟谁学)
css-loader只是帮我们解析了css文件里面的css代码,
默认webpack是只解析js代码的,所以想要应用样式我们要把解析完的css代码拿出来加入到
style标签中。
实现原理:
{
test: /.css$/,
loader: 'css-loader',
exclude: /(node_modules|bower_components)/
}
const postcss = require('postcss');
const Tokenizer = require('css-selector-tokenizer');
const loaderUtils = require('loader-utils');
// 插件,用来提取url
function createPlugin(options) {
return function(css) {const { importItems, urlItems } = options;
// 捕获导入,如果多个就执行多次
css.walkAtRules(/^import$/, function(rule) {
// 拿到每个导入
const values = Tokenizer.parseValues(rule.params);
// console.log(JSON.stringify(values));
// {"type":"values","nodes":[{"type":"value","nodes":
[{"type":"string","value":"./base.css","stringType":"'"}]}]}
// 找到url
const url = values.nodes[0].nodes[0]; // 第一层的第一个的第一个
importItems.push(url.value);
});
// 遍历规则,拿到图片地址
css.walkDecls(decl => {
// 把value 就是 值 7.5px solid red
// 通过Tokenizer.parseValues,把值变成了树结构
const values = Tokenizer.parseValues(decl.value);
values.nodes.forEach(value => {
value.nodes.forEach(item => {
/*
{ type: 'url', stringType: "'", url: './bg.jpg', after: ' ' }
{ type: 'item', name: 'center', after: ' ' }
{ type: 'item', name: 'no-repeat' }
*/
if (item.type === 'url') {
const url = item.url;
item.url = `_CSS_URL_${urlItems.length}_`;
urlItems.push(url); // ['./bg.jpg']
}
});
});
decl.value = Tokenizer.stringifyValues(values); // 转回字符串
});
return css;
};
}
// css-loader是用来处理,解析@import "base.css"; url('./assets/logo.jpg')
module.exports = function loader(source) {
const callback = this.async();
// 开始处理
const options = {
importItems: [],
urlItems: []
};
// 插件转化,然后把url路径都转化成require('./bg.jpg'); // ...
const pipeline = postcss([createPlugin(options)]);
// 1rem 75px
pipeline
// .process("background: url('./bg.jpg') center no-repeat;")
.process(source)
.then(result => {
// 拿到导入路径,拼接
const importCss = options.importItems
.map(imp => {
// stringifyRequest 可以把绝对路径转化成相对路径
return `require(${loaderUtils.stringifyRequest(this, imp)})`; // 拼接
})17.webpack中loader和plugin的区别 (字节跳动 搜狐)
什么是loader
loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起
打包到指定的文件中
1. 处理一个文件可以使用多个loader,loader的执行顺序和配置中的顺序是相反的,即最后一个
loader最先执行,第一个loader最后执行
2. 第一个执行的loader接收源文件内容作为参数,其它loader接收前一个执行的loader的返回值作为
参数,最后执行的loader会返回此模块的JavaScript源码
什么是plugin
在webpack运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过
webpack提供的API改变输出结果。
loader和plugin的区别
对于loader,它是一个转换器,将A文件进行编译形成B文件,这里操作的是文件,比如将A.scss转换为
A.css,单纯的文件转换过程
plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并
不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务
18.webpack、rollup、parcel优劣?
webpack
为处理资源管理和分割代码而生,可以包含任何类型的文件。灵活,插件多。
rollup
.join('\n'); // 拿到一个个import
let cssString = JSON.stringify(result.css); // 包裹后就是"xxx" 双引号
cssString = cssString.replace(/@import\s+?["'][^'"]+?["'];/g, '');
cssString = cssString.replace(/_CSS_URL_(\d+?)_/g, function(
matched,
group1
) {
// 索引拿到,然后拿到这个,替换掉原来的_CSS_URL_0_哪些
const imgURL = options.urlItems[+group1];
// console.log('图片路径', imgURL);
// "background: url('"+require('./bg.jpg')+"') center no-repeat;"
return `"+require('${imgURL}').default+"`;
}); // url('_CSS_URL_1_')
// console.log(JSON.stringify(options));
// console.log(result.css);
callback(
null,
`
${importCss}
module.exports = ${cssString}
`
);
});
};用标准化的格式(es6)来写代码,通过减少死代码尽可能地缩小包体积。
parcel
超快的打包速度,多线程在多核上并发编译,不用任何配置。
对比
配置
webpack和rollup都需要配config文件,指明entry, output, plugin,transformations。二者的细微区
别在于:
rollup 有对import/export所做的node polyfills,webpack没有
rollup支持相对路径,而webpack没有,所以得使用path.resolve/path.join。
parcel则是完全开箱可用的,不用配置。
入口文件
webpack只支持js文件作为入口文件,如果要以其他格式的文件作为入口,比如html文件为入口,如要
加第三方Plugin。
rollup可以用html作为入口文件,但也需要plugin,比如rollup-plugin-html-entry。
parcel可以用index.html作为入口文件,而且它会通过看index.html的script tag里包含的什么自己找到
要打包生成哪些js文件。
transformations
transformations指的是把其他文件转化成js文件的过程,需要经过transformation才能够被打包。
webpack使用Loaders来处理。
rollup使用plugins来处理。
parcel会自动去转换,当找到配置文件比如.babelrc, .postcssrc后就会自动转。
摇树优化
摇树优化是webpack的一大特性。需要1,用import/export语法,2,在package.json中加副作用的入
口,3,加上支持去除死代码的缩小器(uglifyjsplugin)。
rollup会统计引入的代码并排除掉那些没有被用到的。这使您可以在现有工具和模块的基础上构建,而
无需添加额外的依赖项或膨胀项目的大小。
parcel不支持摇树优化。
dev server
webpack用webpack-dev-server。
rollup用rollup-plugin-serve和rollup-plugin-livereload共同作用。
parcel内置的有dev server。
热更新
webpack的 wepack-dev-server支持hot模式。
rollup不支持hmr。
parcel有内置的hmr。
代码分割webpack通过在entry中手动设置,使用CommonsChunkPlugin,和模块内的内联函数动态引入来做代
码分割。
rollup有实验性的代码分割特性。它是用es模块在浏览器中的模块加载机制本身来分割代码的。需要把
experimentalCodeSplitting 和 experimentalDynamicImport 设为true。
parcel支持0配置的代码分割。主要是通过动态improt。
19.babel.config.js 和 .babelrc 有什么区别
它们是Babel 有两种并行的配置文件格式,可以一起使用,也可以分开使用。
babel.config.js 文件是项目范围的配置,加载规则是按目录加载的,是只针对自己的代码。按照
commonjs 导出对象,可以写js的逻辑。具有不同的拓展名(json、js、html)。
.babelrc 文件是相对文件的配置,config的配置针对了第三方的组件和自己的代码内容。一般有了
babel.config.js 就不会在去执行.babelrc的设置。
20.webpack 中 tree shaking 的用途和原理是什么?
答:
tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于
ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015
模块打包工具 rollup。
新的 webpack 4 正式版本,扩展了这个检测能力,通过 package.json 的 "sideEffects" 属性作为标记,
向 compiler 提供提示,表明项目中的哪些文件是 "pure(纯的 ES2015 模块)",由此可以安全地删除文件
中未使用的部分。
tree shaking的概念在1990年就提出了,但是直到ES6的ES6-style模块出现以后才真正被利用起来。这
是因为tree shaking只能在静态modules下工作。ECMAScript 6 模块加载是静态的,因此整个依赖树可
以被静态地推导出解析语法树。所以在ES6中使用tree shaking是非常容易的。而且,tree shaking不仅
支持import/export级别,还支持statement(声明)级别。
21.阐述一下 VUE中 eventbus 的原理 (猿辅导)
EventBus是消息传递的一种方式,基于一个消息中心,订阅和发布消息的模式,称为发布订阅者模式。
1. on('name', fn)订阅消息,name:订阅的消息名称, fn: 订阅的消息
2. emit('name', args)发布消息, name:发布的消息名称 , args:发布的消息
实现
class Bus {
constructor () {
this.callbacks = {}
}
$on(name,fn) {
this.callbacks[name] = this.callbacks[name] || []
this.callbacks[name].push(fn)
}
$emit(name,args) {
if(this.callbacks[name]){
//存在遍历所有callback
this.callbacks[name].forEach(cb => cb(args))
}
}
}使用
22.vue-loader 的实现原理是什么?
答:vue-loader就是将 *.vue 文件变成 *.bundle.js,然后放入浏览器运行。而在这个过程当中,其实调
用了三个内部loader(lib/style-compiler、lib/template-compiler和lib/selector)和多个外部
loader(babel-loader、vue-style-loader、css-loader等等)。
JS部分:selector(参数type=script) 的处理结果是将 *.vue 中的 script 抽出来之后交给babel-loader
去处理,最后生成可用的 JavaScript。
HTML部分:selector (参数type=template) 的处理结果是将 .vue 中的 template 抽出来之后交给
template-compiler 处理,最终输出成可用的 HTML。
Style部分:selector (参数type=style) 的处理结果是将.vue 中的 style 抽出来之后交给 style-compiler 处
理成设置好的样式(less、sass、css), 然后交给对应的 loader 处理生成 module, 之后通过 vue-styleloader或者style-loader 将 css 放在
const EventBus = new EventBusClass()
EventBus.on('fn1', function(msg) {
alert(`订阅的消息是:${msg}`);
});
EventBus.emit('fn1', '你好,世界!')

Guess you like

Origin blog.csdn.net/weixin_40918145/article/details/117818973