简答类题目

1、typescript和javascript的区别

关系:typescript是基于javascript基础之上的编程语言,是javascript的超集。

语言类型:javascript是弱类型语言,typescript是强类型语言。

TS需要编译,JS基本直接被浏览器解析执行。

主要区别就是 typescript 扩展的功能:

  • 类型系统
    • 类型检查
    • 类型注解
  • 支持ES6新特性(可以编译为指定版本的ES)
  • 增加了其他概念:
    • 扩展类class的一些概念
      • 如抽象类
      • public
      • private
      • protected
    • 枚举 enum
    • 接口 interface
    • 类型声明 @type
    • 泛型

2、typescript 都有哪些类型

  • 基本类型
    • boolean 布尔类型
    • number 数值类型
    • string 字符串类型
    • null / undefined
    • void 空值类型
    • any 任意值
    • never 永远不会有返回值
    • array 数组
    • tuple 元组
    • enum 枚举
    • object 对象
    • 函数类型
      • 函数声明
      • 函数表达式
  • 联合类型 - 多种类型组合使用
  • 接口 interface - 定义对象的类型
  • 泛型 - 定义函数、接口或类的时候不预先指定具体的类型,而是在使用的时候再指定类型

3、typescript中type和interface的区别

interface 除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。

type 用于创建类型别名,可以作用于基本类型(原始值),联合类型,元组以及其它任何你需要手写的类型。

相同点

  • 都可以描述一个对象或函数

  • 都允许扩展(extends)

    • interface 使用 extends
    • type 使用交叉类型实现 interface 的 extends 行为

不同点

  • type 可以声明基本类型别名,联合类型,元组等类型
  • type 语句中可以使用 typeof 获取实例的类型进行赋值
  • interface 可以合并同名接口的声明

参考:

  • https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases
  • https://www.typescriptlang.org/docs/handbook/advanced-types.html#interfaces-vs-type-aliases

总结:

  • 由于 interface 通过开放扩展更紧密地映射 JavaScript 对象的工作方式,因此我们建议尽可能在类型别名上使用接口。
  • 另一方面,如果您无法使用 interface 表达某种形状,而需要使用联合类型或元组类型,则通常使用类型别名。

4、对 async/await 的理解,分析内部原理

回答:

async 关键字声明的函数表示是异步函数,函数执行返回的是一个 promise。

async 内部原理就是将函数执行包裹在一个promise中,并返回这个promise。

当函数执行完毕,变更promise的状态。

如果函数有返回值,就作为promise的值返回。

await 用于等待一个异步函数执行完成。

相当于生成器函数(Generator)中 的yield,它会等待后面的异步函数执行完,并返回其处理结果。

其他官方答案:

Promise解决了回调地狱的问题,但是如果遇到复杂的业务,代码里面会包含大量的 then 函数,使得代码依然不是太容易阅读。

基于这个原因,ES7 引入了 async/await,这是 JavaScript 异步编程的一个重大改进,提供了在不阻塞主线程的情况下使用同步代码实现异步访问资源的能力,并且使得代码逻辑更加清晰,而且还支持 try-catch 来捕获异常,非常符合人的线性思维。

async/await,这种方式能够彻底告别执行器和生成器,实现更加直观简洁的代码。根据 MDN 定义,async 是一个通过异步执行并隐式返回 Promise 作为结果的函数。可以说async 是Generator函数的语法糖,并对Generator函数进行了改进

他的重点是自带了执行器,相当于把我们要额外做的(写执行器/依赖co模块)都封装了在内部

5、async/await 如果右边方法执行出错该怎么办?

  • async 函数内部出错可以使用 try-catch捕获错误
  • await 右边的方法会返回promise,可以使用catch捕获错误

注意:

// try catch 只能捕获同步异常 和 async 中的 await Promise 的异常
// try catch 不能直接捕获 Promise 的异常

async function e() {
  throw new Error('异常')
}

function syncFn() {
  try {
    const p = e()

    // 尝试注释 promise 的 catch 方法
    p.catch(err => {
      console.log('Promise catch:', err.message)
    })
  } catch (err) {
    console.log('try catch:', err.message)
  }
}
syncFn()

async function asyncFn() {
  try {
    const p = await e()
  } catch (err) {
    console.log('try catch:', err.message)
  }
}
asyncFn()

6、说一下 event loop 的过程?promise 定义时传入的函数什么时候执行?

回答:

JS执行时将数据存储在堆栈内存中,在执行栈执行任务。

JS任务开始处理,将代码逐行压入执行栈。

任务处理完,清空执行栈,压入下一个任务的代码。

任务存储在任务队列中。

按照进入队列的顺序,依次压入执行栈处理。

JS会先执行同步任务,然后执行微任务,最终执行宏任务。

整个过程就是事件循环 Event Loop机制。

内容可以分为:执行代码、收集和处理任务、以及执行队列中的任务。

  • 执行代码:运行执行栈中的代码
  • 收集和处理任务:收集任务,判断任务执行顺序
  • 执行队列中的任务:执行栈清空,查询任务队列是否有任务,有则将下一个任务取出压入执行栈去执行。这个顺序依据队列的先进先出原则。

其他官方答案:

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为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

promise 定义时传入的函数称为 执行器函数,它是同步的,是立即执行的。

7、说一下防抖函数的应用场景,并简单说下实现方式

回答:

防抖是优化高频率执行js代码的一种手段。

多用于频繁的触发一个事件,但只需要保证执行一次即可,之前的触发都可以忽略的场景。

例如:

  1. select下拉框支持远程查询(异步请求获取数据作为选项列表)
  2. 用户注册时,手机号、email等格式校验(监听input事件)

实现方式可以分为两种:

  • 立即执行:保证立即执行,屏蔽频繁操作(不执行)
    • 触发事件后立即执行函数,开始计时,在n秒内再次触发事件,不执行函数,并重新计时,n秒后触发事件,立即执行函数,并开始计时
  • 非立即执行:延迟执行,频繁操作不断覆盖,保证执行最后的一次
    • 触发事件后开始计时,在n秒后执行函数,如果在n秒内再次触发事件,则重新计时,保证等待n秒才执行函数。

8、观察者模式和发布订阅模式的区别

回答:

  • 观察者模式 是由具体目标调度,比如当事件触发,目标对象就会去调用观察者的方法,所以观察者模式的订阅者与发布者之间是存在依赖的。
  • 发布/订阅模式 由统一调度中心(事件中心)调用,因此发布者和订阅者不需要知道对方的存在,减少二者依赖关系,这样会变得更灵活。
  • 观察者模式大多数时候是同步的,比如当事件触发,目标就会调用观察者的方法。而发布订阅模式大多数时候是异步的(使用消息队列)。
  • 观察者模式需要在单个应用程序地址空间中实现,而发布订阅模式更像交叉应用模式。

9、gulp自己写过任务吗?说一下它的构建流程

回答:

gulp定义任务

入口文件gulpfile.js,通过导出函数成员的方式定义任务。

任务接收的第一个参数是一个函数,用于标记任务结果。

当任务完成之后,需要调用这个函数或其他方式标记任务已经完成。

// foo任务
exports.foo = done => {
  // ...任务内容
  done() // 标记任务完成
}

也可以通过返回一个promise标记任务状态。

resolve表示成功,reject表示失败。

其他官方答案:

gulp 是基于Node开发环境运行的,所以要先确认好是否有Node开发环境

安装好Node以后,运行npm init创建package.json文件

安装gulp以及你的任务中要使用的依赖

创建并编写gulpfile.js文件

运行程序及打包

gulp构建流程

构建过程大多数是将文件读出来,进行转换,最后写入到另外的位置。

  1. 输入:读取流
  2. 加工:转换流
  3. 输出:写入流

gulp的构建有三个核心概念,分别是读取流、转换流和写入流。

我们通过读取流把需要转换的文件读取出来,然后通过转换流的转换逻辑,转换成我们想要的结果,再通过写入流去写入到指定的文件位置。

这样的一个过程就完成了我们日常在构建当中所需要的工作。

gulp的官方定义就是基于流的构建系统。

gulp希望实现一个构建管道的概念,这样的话,我们在后续去做一些扩展插件的时候就可以有一个很统一的方式。

10、package-lock.json 有什么作用,如果项目中没有它会怎么样,举例说明

回答:

当开发者根据package.json安装依赖时,文件中声明的依赖版本号不是具体的,而是向新兼容下载的(下载主版本最新的版本)。

package-lock.json 是在 npm install时候生成一份文件,用来记录当前状态下实际安装的各个npm package的具体来源和版本号。

当项目中其他开发者或一个新的环境,或新的下载源,重新安装依赖时,目录下包含这个文件,就会锁定安装时依赖的来源和版本号,保证每个开发者安装的依赖都是一样的。

解决了package.json的缺点:原来package.json文件只能锁定大版本,也就是版本号的第一位,并不能锁定后面的小版本,你每次npm install都是拉取的该大版本下的最新的版本,为了稳定性考虑我们几乎是不敢随意升级依赖包的,这将导致多出来很多工作量,测试/适配等,所以package-lock.json文件出来了,当你每次安装一个依赖的时候就锁定在你安装的这个版本。

11、webpack 常用配置项有哪些,并说明用途

回答:

  • entry 配置入口文件
  • output 配置打包输出
  • mode 配置webpack工作模式
    • 默认 production 生产模式,自动启动一些优化打包结果的配置
    • development 开发模式,会自动优化打包速度,添加一些调试当中用到的辅助
    • none 运行最原始状态的打包,不会做额外的处理
  • module.rules 配置资源模块加载器 loader
    • 通过loader可以加载任何类型的资源
  • plugins 配置插件,可以解决 loader(资源模块加载) 解决不了的其他自动化工作
  • devServer 配置 webpack-dev-server 启动的web服务
    • proxy API代理
    • hot 开启 hmr 热替换
  • devtool 配置SourceMap
  • optimization 配置优化功能
  • resolve 用于设置模块如何被解析
    • alias 路径别名
    • extensions 自动解析的扩展
    • modules 指定webpack解析模块时搜索的目录

其他官方答案(没有分层级):

entry:打包的入口文件,一个字符串或者一个对象
output:配置打包的结果,一个对象
fileName:定义输出文件名,一个字符串
path:定义输出文件路径,一个字符串
module:定义对模块的处理逻辑,一个对象
loaders:定义一系列的加载器,一个数组
test:正则表达式,用于匹配到的文件
loader/loaders:字符串或者数组,处理匹配到的文件。如果只需要用到一个模块加载器则使用                 
loader:string,如果要使用多个模块加载器,则使用loaders:array
include:字符串或者数组,指包含的文件夹
exclude:字符串或者数组,指排除的文件夹
resolve:影响对模块的解析,一个对象
extensions:自动补全识别后缀,是一个数组
plugins:定义插件,一个数组

12、阐述 webpack css-loader 的作用 和 原理?

回答:

css-loader 用于将js文件中导入的css文件内容转换成js代码,用于 style-loader或其他插件插入到页面中。

原理:将css文件转换称一个js模块(将css代码push到一个数组当中)

可以通过 style-loader 将css-loader 转换的结果通过 style 标签的形式,追加到页面上

官方答案:

 {
      test: /.css$/,
      loader: 'css-loader',
      exclude: /(node_modules|bower_components)/
}

css-loader只是帮我们解析了css文件里面的css代码,
默认webpack是只解析js代码的,所以想要应用样式我们要把解析完的css代码拿出来加入到
style标签中。

实现原理:

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)})`; // 拼接
        })
        .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}
      `
      );
    });
};

13、webpack中loader和plugin的区别是什么

回答:

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打包过程中的某些节点,执行广泛的任务

14、webpack、rollup、parcel 它们的优劣?

回答:

Rollup的优势:

  • 输出的结果更加扁平,执行效率就会更高
  • 自动移除未引用的代码:Tree Shaking
  • 打包结果依然完全可读:与源码基本一致

Rollup的缺点:

  • 加载非ESM的第三方模块比较复杂:需要配置一些插件
  • 模块最终都被打包到一个函数中,无法实现HMR
  • 浏览器环境中,代码拆分功能依赖 AMD 库。
    • 代码拆分无法使用 iife 格式,只能使用 amd 格式。
    • 这样就必须通过实现 AMD 规范的库来使用。

Parcel 的优点:

  • 真正意义上实现了完全零配置,对项目没有任何的侵入。
  • 自动安装依赖的体验,使开发更加专注于编码。
  • 构建速度更快,多进程同时工作。(webpack可以借助插件实现该功能)

Parcel的出现,是由于当时webpack使用上过于繁琐,文档不是很清晰。

而现在 webpack 已经越来越完善,开发者随着使用越来越熟悉。

并且 webpack 有更好的生态,扩展也会更丰富,出现问题很容易去解决

webpack 几乎可以实现Rollup和parcel 的全部功能。

并且不存在Rollup的那些缺点,并且随着发展,Rollup中的绝大多数优势几乎已经被抹平。

但webpack过于大而全,总体来说:

  • 应用开发使用 webpack

  • 库/框架开发使用 rollup

  • 快速构建使用 parcel

其他官方答案:

webpack

为处理资源管理和分割代码而生,可以包含任何类型的文件。灵活,插件多。

rollup

用标准化的格式(es6)来写代码,通过减少死代码尽可能地缩小包体积。

parcel

超快的打包速度,多线程在多核上并发编译,不用任何配置。

对比

  1. 配置

webpack和rollup都需要配config文件,指明entry, output, plugin,transformations。二者的细微区别在于:
rollup 有对import/export所做的node polyfills,webpack没有
rollup支持相对路径,而webpack没有,所以得使用path.resolve/path.join。

parcel则是完全开箱可用的,不用配置。

  1. 入口文件

webpack只支持js文件作为入口文件,如果要以其他格式的文件作为入口,比如html文件为入口,如要加第三方Plugin。

rollup可以用html作为入口文件,但也需要plugin,比如rollup-plugin-html-entry。

parcel可以用index.html作为入口文件,而且它会通过看index.html的script tag里包含的什么自己找到要打包生成哪些js文件。

  1. transformations

transformations指的是把其他文件转化成js文件的过程,需要经过transformation才能够被打包。

webpack使用Loaders来处理。

rollup使用plugins来处理。

parcel会自动去转换,当找到配置文件比如.babelrc, .postcssrc后就会自动转。

  1. 摇树优化

摇树优化是webpack的一大特性。需要1,用import/export语法,2,在package.json中加副作用的入口,3,加上支持去除死代码的缩小器(uglifyjsplugin)。

rollup会统计引入的代码并排除掉那些没有被用到的。这使您可以在现有工具和模块的基础上构建,而无需添加额外的依赖项或膨胀项目的大小。

parcel不支持摇树优化。

  1. dev server

webpack用webpack-dev-server。

rollup用rollup-plugin-serve和rollup-plugin-livereload共同作用。

parcel内置的有dev server。

  1. 热更新

webpack的 wepack-dev-server支持hot模式。

rollup不支持hmr。

parcel有内置的hmr。

  1. 代码分割

webpack通过在entry中手动设置,使用CommonsChunkPlugin,和模块内的内联函数动态引入来做代码分割。

rollup有实验性的代码分割特性。它是用es模块在浏览器中的模块加载机制本身来分割代码的。需要把experimentalCodeSplitting 和 experimentalDynamicImport 设为true。

parcel支持0配置的代码分割。主要是通过动态improt。

15、babel.config.js 和 .babelrc 有什么区别?

回答:

Babel 有两种并行的配置文件格式,可以一起使用,也可以分开使用。

  • babel.config.js - 项目范围的配置

    • 针对了第三方的组件和自己的代码内容
  • .babelrc - 相对文件的配置

    • 加载规则是按目录加载的,是只针对自己的代码

一般有了babel.config.js 就不会在去执行.babelrc的设置。

16、webpack 中 tree shaking 的用途和原理是什么?

回答:

Tree shaking 是一个术语,通常用于描述移除Javascript上下文中的未引用代码(dead-code)的行为。

它依赖于ES6中的import和export语句,用来检测代码模块是否被导出、导入,且被Javascript文件使用。

在webpack中就是将多个JS文件打包为单个文件时,自动删除未引用的代码。以使最终文件具有简洁的结构和最小化大小。

webpack 通过usedExports: true 表示在输出结果中模块只导出外部使用了的成员。

minimize: true开启代码压缩优化,删除注释、删除没有用到的代码、删除空白、替换变量名为简短的名称等。

官方答案:

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(声明)级别。

17、阐述一下 eventbus 的原理,讲述eventbus在vue中的实践

回答:

eventbus 原理是创建一个事件中心,注册一些事件,并提供触发事件的方法。

Vue中,创建一个公共实例作为事件中心,在组件中引入这个实例。

通过 o n 注 册 自 定 义 事 件 , 在 需 要 触 发 地 方 使 用 on注册自定义事件,在需要触发地方使用 on使emit触发事件。

官方答案:

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))
    }
  }
}

使用

const EventBus = new EventBusClass()
EventBus.on('fn1', function(msg) {
    alert(`订阅的消息是:${msg}`);
});
EventBus.emit('fn1', '你好,世界!');

18、vue-loader 的实现原理是什么

回答:

vue-loader加载器可以解析处理vue单文件组件(SFC)。

它可以将vue组件解析的每个部分使用对应的loader,例如sytle标签使用sass,template标签使用pub。

具体过程:

  1. 通过编译器,将vue中template、script、style标签中的内容提取,转化为导入导出的方式,用type参数区分使用的loader
  2. VueLoaderPlugin插件会克隆webpack中的rules,并根据type去执行对应的rules。

官方答案:

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-style-loader或者style-loader 将 css 放在 <style> 里面,最后注入到 HTML 中。

19、在 EcmaScript 新特性中,暂时性死区有什么作用

回答:

使用let 和 const 声明的变量,会绑定当前代码块的块级作用域,不受外部的影响,并且在执行声明语句之前不允许以任何形式访问变量(仍然进行了变量提升,只不过增加了限制)。

目的是明确变量的使用区域(绑定的块级作用域)和使用顺序(声明后使用)。

官方答案:

暂时性死区是ECMAScript与作用域相关的一个新语义模块, 在ES2015(又叫ES6)中引入。

https://sinaad.github.io/xfe/2016/02/26/temporal-dead-zone-tdz-demystified/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值