深入学习和理解Babel

Hi,大家好,我是小庄。

在我们平时写代码的过程中可能没有感觉Babel的存在,但其实只要我们写JS代码,Babel已经无处不在、无时不刻的在影响着我们的代码。

因此这篇文章,我们就对Babel的配置以及使用做一个深入的学习和总结。

一、Babel是什么

Babel官网对Babel的定义就是:

Babel 是一个 JavaScript 编译器。

用通俗的话解释就是它主要用于将高版本的JavaScript代码转为向后兼容的JS代码,从而能让我们的代码运行在更低版本的浏览器或者其他的环境中。

比如我们在代码中使用了ES6箭头函数:

var fn = (num) => num + 2;

但我们如果用IE11浏览器(鬼知道用户会用什么浏览器来看)运行的话会出现报错;但是经过Babel编译之后的代码就可以运行在IE11以及更低版本的浏览器中了:

var fn = function fn(num) {
  return num + 2;
}

Babel就是做了这样的编译转换工作,来让我们不用考虑浏览器的兼容性问题,只要专心于代码的编写工作。

二、Babel的历史

Babel的前身是从6to5这个库发展而来,6to5的作者是Facebook的澳大利亚工程师Sebastian McKenzie在2014年发布的;从它的名字我们也能看出来,主要的功能就是将ES6转成ES5,我们如今也还能在npm官网看到这个包,不过作者提示已经迁移到Babel了:
在这里插入图片描述

在2015年1月份,6to5和Esnext的团队决定一起开发6to5,并且改名为Babel,解析引擎改名为Babylon。

三、Babel和Babylon的含义

  • Babylon的翻译是巴比伦,意指巴比伦文明。

  • Babel的翻译是巴别塔,又名通天塔;当时地上的人们都说同一种语言,当人们离开东方之后,他们来到了示拿之地。在那里,人们想方设法烧砖好让他们能够造出一座城和一座高耸入云的塔来传播自己的名声,以免他们分散到世界各地。上帝来到人间后看到了这座城和这座塔,说一群只说一种语言的人以后便没有他们做不成的事了;于是上帝将他们的语言打乱,这样他们就不能听懂对方说什么了,还把他们分散到了世界各地,这座塔也停止了修建,这座塔就被称为“巴别塔”。

四、Babel版本及区别

  • 2015-02-15,6to5重命名为babel;

  • 2015-03-31,babel 5.0发布;

  • 2015-10-30,babel 6.0发布;

  • 2018-08-27,babel 7.0发布;

babel5及之前是一个包含CLI工具+编译器+转换器的集合工具包;babel6之后进行了拆分,集合包被分成多个包:

  • babel-cli,其中包含babel命令行界面;

  • babel-core,包括了Node有关的API和require钩子;

  • babel-polyfill,可以建立一个完整的ES2015环境;

babel6默认情况下不携带任何转换器,需要自行安装所需的插件和转换器,通过babel-xxx来安装对应的工具包。

而Babel7用了npm的private scope,把所有的包都挂载@babel下,通过@babel/xxx来安装,不用在node_modules下看到一堆的babel-xxx包。

本文主要以Babel7作为开发工具,并进行详解。

五、Babel7详解

@babel/core

@babel/core我们在很多地方都看到,它是Babel进行转码的核心依赖包。

我们常用的@babel/cli和@babel/node都依赖于它,即当用户要安装和使用@babel/cli、@babel/node时,就必须先安装@babel/core。

@babel/core中包含多个模块,而实现转码功能的主要模块为以下三个:

@babel/parser 、 @babel/traverse 、 @babel/generator

如下,我们查看下@babel/core源码中的package.json:

在这里插入图片描述

而那三个模块的功能分别如下:

  • @babel/parser: @babel/core中parse阶段的主要功能模块。用于接受源码,进⾏词法分析、语法分析,⽣成AST;

补充:@babel/parser已经内置支持很多语法. 例如JSX、Typescript、Flow、以及最新的ECMAScript规范。目前为了执行效率,@babel/parser是不支持扩展的,由官方进行维护。如果你要支持自定义语法,可以 fork 它,不过这种场景非常少。

  • @babel/traverse:@babel/core中transform阶段的主要功能模块。用于接受⼀个AST,并对其遍历,根据babel配置中的preset、plugin进⾏逻辑处理,进⾏替换、删除、添加节点,生成最终的AST;

  • @babel/generator:@babel/core中gernerate阶段的主要功能模块。用于接受最终⽣成的AST,并将其转换为代码字符串,同时此过程也可以创建source map;

三个模块相互配合,其转码流程为:

input string -> @babel/parser parser -> AST -> transformer[s] -> AST -> @babel/generator -> output string

在这里插入图片描述
在这里插入图片描述

下面,我们通过实践简单例子,来看一下@babel/core是如何来进行解析:

var babelCore = require("@babel/core");
var sourceCode = `let fn = (num) => num + 2`;var options = {
  //是否生成解析的代码
  code: true,
  //是否生成抽象语法树
  ast: true,
  //是否生成sourceMap
  sourceMaps: true,
  plugins: [],
  presets: [],
};
​
babelCore.transform(sourceCode, options, function (err, result) {
  console.log(sourceCode);
  console.log(result.code);
  console.log(result.map);
  console.log(result.ast);
});

在这里插入图片描述

可以发现,原来的es6箭头函数在结果中几乎原封不动的返回出来了。

为什么会这样呢?

正如上面所说,@babel/core中的第二个模块@babel/traverse,它需要根据babel配置中的preset、plugin进⾏逻辑处理,然后进⾏替换、删除、添加节点生成最终的AST。因此当我们不添加任何插件的时候,输入输出代码是相同的。

同时,我们可以看到,在@babel/core转换时还有几个副产物:code、ast和map,我们可以通过options配置,根据需要对这几个副产物进行选择性的输出。

Options配置文档:https://www.babeljs.cn/docs/options

除了transform这个转换方法,还有transformSync、transformAsync和transformFileSync等同步异步API,都可以在babel官方文档找到。

babel官方文档:https://www.babeljs.cn/docs/

@babel/cli

@babel/cli是Babel自带了一个内置的CLI命令行工具,我们就可以通过命令行来编译文件;它有两种调用方式,可以通过全局安装或者本地安装调用,选用一种即可,推荐在项目本地安装。

//全局安装调用
npm install --g @babel/cli
babel index.js -o output.js
​
//本地安装调用
npm install --save-dev @babel/cli
npx babel index.js -o output.js

@babel/cli还可以使用以下命令参数:
在这里插入图片描述

配置文件

我们虽然可以在命令行中配置各种插件(plugins)或者预设(presets,也就是一组插件),但是这样并不利于后期的查看或者维护,而且大多时候babel都是结合webpack或者gulp等打包工具开发,不会直接通过命令行的方式;因此Babel推荐通过配置文件的方式来进行管理。

Babel的配置文件主要有.babelrc、.babelrc.js、babel.config.js和package.json,他们的配置选项都是相同的,作用也是一样,主要区别在于格式语法的不同,因此我们在项目中只需要选择其中一种即可。

对于.babelrc,它的配置主要是JSON格式的,像这样:

{
  "presets": [...],
  "plugins": [...]
}

而.babelrc.js和babel.config.js同样都是JS语法,通过module.exports输出配置:

module.exports = function (api) {
  api.cache(true);
  const presets = [ ... ];
  const plugins = [ ... ];
  if (process.env["ENV"] === "prod") {
    plugins.push(...);
  }
  return {
    presets,
    plugins
  };
}

我们还可以根据环境来进行动态的配置。而在package.json中,需要增加babel的属性:

{
  "name": "demo",
  "version": "1.0.0",
  "babel": {
    "presets": [ ... ],
    "plugins": [ ... ],
  }
}

我们可以在配置文件中加入一些插件或者预设,来扩展@babel/core的转换功能;只需要将对应的插件或预设名字加入数组即可;比如我们常用的ES6箭头函数,就是通过@babel/plugin-transform-arrow-functions这个插件来转换:

//.babelrc
{
  "plugins": ["@babel/plugin-transform-arrow-functions"]
}

但有时候我们需要对插件和预设设置参数,就不能直接使用字符串的形式了;而应再包裹一层数组,数组的第一项是名称,第二项是设置的参数对象:

//.babelrc
{
  "plugins": [
    [
      "@babel/plugin-transform-arrow-functions", 
      { "spec": true }
    ]
  ]
}

这样我们的箭头函数就能正常转换了。

Babel插件和预设

Babel的插件大致可以分为语法插件和转换插件:

  • 语法插件:作用于@babel/core中的parse阶段,使得babel能够解析更多的语法,官方的语法插件以@babel/plugin-syntax开头;

  • 转换插件:作用于@babel/core中的transform阶段,负责转换 AST 的形态,官方的转换插件以@babel/plugin-transform(正式)或者 @babel/plugin-proposal(提案)开头。

补充1:转换插件将启用相应的语法插件,因此不必同时指定这两种插件。


补充2:语法插件虽名为插件,但其本身并不具有功能性。正如上面说了 @babel/parser 已经支持了很多 JavaScript语法特性,同时@babel/parser也不支持扩展。因此@babel/plugin-syntax-*实际上只是用于开启或者配置@babel/parser的某个功能特性。但一般用户不需要关心这个,用户也可以通过parserOpts配置项来直接配置@babel/parser。

Babel官网提供了近一百个插件,但是如果我们的代码中一个一个的配置插件就需要对每一个插件有所了解,这样必然会耗费大量的时间精力;为此,Babel提供了预设(presets)的概念,意思就是预先设置好的一系列插件包;这就相当于肯德基中的套餐,将众多产品进行搭配组合,适合不同的人群需要;总有一款适合我们的套餐。

比如@babel/preset-es2015就是用来将部分ES6语法转换成ES5语法,@babel/preset-stage-x可以将处于某一阶段的js语法编译为正式版本的js代码,而@babel/preset-stage-x也已经被Babel废弃了,有兴趣的童鞋可以看这篇官方的文章

我们实际会用到的预设有以下:

  • @babel/preset-env

  • @babel/preset-flow

  • @babel/preset-react

  • @babel/preset-typescript

根据名字我们可以大致猜出每个预设的使用场景,我们重点了解一下@babel/preset-env,它的作用是根据环境来转换代码。

执行顺序

插件和预设都是通过数组的形式在配置文件中配置,如果插件和预设都要处理同一个代码片段,那么会根据一下执行规则来判定:

  • 插件比预设先执行;

  • 插件执行顺序是插件数组从前向后执行;

  • 预设执行顺序是预设数组从后向前执行;

@babel/preset-env

我们来看一下官网对它的描述:

@babel/preset-env是一个智能预设,可让您使用最新的JavaScript,而无需微观管理目标环境所需的语法转换(以及可选的浏览器polyfill)。这都使您的生活更轻松,JavaScript包更小!

我们在项目中不会关心Babel用了哪些插件,支持哪些ES6语法;我们更多关心的是支持哪些浏览器版本这个层面,比如我们在项目中使用了箭头函数、Class、Const和模板字符串:

let fun = () => console.log("hello babel.js");
class Person {
  constructor(name) {
    this.name = name;
  }
  say() {
    console.log(`my name is:${this.name}`);
  }
}
const tom = new Person("tom");
tom.say();

但是假如我们的项目需要支持IE10,因此我们需要修改.babelrc:

{
  "presets": ["@babel/preset-env"]
}

或者对它进行缩写:

{
  "presets": ["@babel/env"]
}

通过Babel编译后输出:

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }var fun = function fun() {
  return console.log('hello babel.js');
};var Person = /*#__PURE__*/function () {
  function Person(name) {
    _classCallCheck(this, Person);this.name = name;
  }_createClass(Person, [{
    key: "say",
    value: function say() {
      console.log("my name is\uFF1A".concat(this.name));
    }
  }]);return Person;
}();var tom = new Person('tom');
tom.say();

可以发现虽然我们没有配置任何转换插件,但是上面写的的箭头函数、Class、Const和模板字符串语法都已经被转换了;默认情况下,@babel/env等于@babel/preset-es2015、@babel/preset-es2016和@babel/preset-es2017三个套餐的叠加。

那如果我们只需要支持最新的Chrome了,可以继续修改.babelrc:

{
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 2 Chrome versions"
      }
    ]
  ]
}

targets中的含义是最新的两个Chrome版本,Babel再次编译输出:

"use strict";let fun = () => console.log("hello babel.js");class Person {
  constructor(name) {
    this.name = name;
  }say() {
    console.log(`my name is:${this.name}`);
  }}const tom = new Person("tom");
tom.say();

而最新版本的Chrome已经支持箭头函数、Class、Const和模板字符串,所以在编译时不会在进行转换。

上面的target字段不少同学肯定看着很眼熟,这个工具能够根据项目中指定的目标浏览器自动来进行配置,这里我们就不展开深入讨论了;它也可以单独在项目中配置一个.browserslistrc文件:

last 2 Chrome versions

这样和targets字段的使用效果是一样的;正常情况下,推荐使用browserslist的配置而很少单独配置@babel/preset-env的targets;@babel/preset-env有一些常用的配置项让我们来看一下:

targets

虽然targets不推荐使用,但是我们还是来了解一下它的用法,它是用来描述我们在项目中想要支持的目标浏览器环境,它可以是Browserslist格式的查询:

{
  "targets": "> 0.25%, not dead"
}

或者可以是一个对象,用来描述支持的最低版本的浏览器:

{
  "targets": {
    "chrome": "58",
    "ie": "11"
  }
}

其他的浏览器版本还可以是:opera、edge、firefox、safari、ios、android、node、electron等。

spec

这个属性主要是给其他插件传递参数(比如@babel/plugin-transform-arrow-functions),默认是false,设为true后,我们的箭头函数会有以下改变:

将箭头函数生成的函数用.bind(this)包裹一下,以便在函数内部继续使用this,而不是重命名this。

加一个检查防止函数被实例化

给箭头函数加一个名字

loose

这个属性也主要是给其他插件传递参数(比如@babel/plugin-transform-classes),默认是false,类的方法直接定义在构造函数上;而设置为true后,类的方法被定义到了原型上面,这样在类的继承时可能会引起问题。

include

转换时总是会启用插件的数组,格式是Array<string|RegExp>,它可以是一下两种值:

  • Babel插件;

  • 内置的core-js,比如es.map,es.set等;

比如我们在last 2 Chrome versions目标浏览器环境下,不会转换箭头函数和Class,但是我们可以将转换箭头函数的插件配置到include中,这样不管我们的目标浏览器怎么更换,箭头函数语法总是会转换:

{
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 2 Chrome versions",
        "include": ["@babel/plugin-transform-arrow-functions"]
      }
    ]
  ]
}

useBuiltIns和corejs

useBuiltIns这个属性决定是否引入polyfill,可以配置三个值:false(不引入)、usage(按需引入)和entry(项目入口处引入);corejs表示引入哪个版本的core-js,可以选择2(默认)或者3,只有当useBuiltIns不为false时才会生效。

@babel/polyfill

虽然@babel/preset-env可以转换大多高版本的JS语法,但是一些ES6原型链上的函数(比如数组实例上的的filter、fill、find等函数)以及新增的内置对象(比如Promise、Proxy等对象),是低版本浏览器本身内核就不支持,因此@babel/preset-env面对他们时也无能为力。

比如我们常用的filter函数,在IE浏览器上就会出现兼容性问题,因此我们通过polyfill(垫片)的方式来解决,下面是filter函数简单的兼容代码:

if (!Array.prototype.filter) {
  Array.prototype.filter = function (fun /*, thisp*/ ) {
    var len = this.length;
    if (typeof fun != "function") {
      throw new TypeError();
    }
    var res = new Array();
    var thisp = arguments[1];
    for (var i = 0; i < len; i++) {
      if (i in this) {
        var val = this[i];
        if (fun.call(thisp, val, i, this)) {
          res.push(val);
        }
      }
    }
    return res;
  };
}


但是ES有那么多函数和内置对象,我们不可能一个一个都手写来解决,这就到了@babel/polyfill用武之处了;首先我们需要在项目中安装它:

npm install --save @babel/polyfill

安装完成后在需要转换的文件入口加入引用代码:

import '@babel/polyfill'

或者我们也可以在Webpack入口处进行引入:

module.exports = {
  entry: ["@babel/polyfill", "./src/index.js"],
};

然后通过webpack来打包,这样就能看到在我们的代码中加入了很多的兼容代码。
在这里插入图片描述

发现我们数组的fill、filter和findIndex等方法都打包进去了,但是看到这么多密密麻麻的兼容代码,眼尖的童鞋肯定会发现以下两个问题:

打包出来生成的文件非常的大;有一些语法特性可能是我们没用到的,但是Webpack不管三七二十一全都引用进去了,导致打包出来的文件非常庞大。

污染全局变量;polyfill给很多类的原型链上添加函数,如果我们开发的是一个类库给其他开发者使用,这种情况会非常不可控。

因此从Babel7.4开始@babel/polyfill就不推荐使用了,而是直接引入core-js与regenerator-runtime两个包;而@babel/polyfill本身也是这两个包的集合;在上面webpack打包出来的dist文件我们也可以看到,引用的也是这两个包。那core-js到底是什么呢?

  • 它是JavaScript标准库的polyfill;

  • 它尽可能的进行模块化,让你能选择你需要的功能;

  • 它和babel高度集成,可以对core-js的引入进行最大程度的优化;

目前我们使用的默认都是core-js@2,但它已经封锁了分支,在此之后的特性都只会添加到core-js@3,因此也是推荐使用最新的core-js@3。

@babel/preset-env与core-js

在上面@babel/preset-env配置中有useBuiltIns和corejs两个属性,是用来控制所需的core-js版本;我们以Object.assign、filter和Promise为例:

Object.assign({}, {});[(1, 5, 10, 15)].filter(function (value) {
  return value > 9;
});let promise = new Promise((resolve, reject) => {
  resolve(1);
});

然后修改配置文件,如果我们将useBuiltIns配置为非false而没有指定corejs的版本,Babel会提示我们需要配置corejs的版本:

在这里插入图片描述

秉承着用新不用旧的原则,毅然选择core-js@3:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "corejs": 3
      }
    ]
  ]
}

可以看到我们的打包的文件自动引入了core-js中的模块:

"use strict";require("core-js/modules/es.array.filter");require("core-js/modules/es.object.assign");require("core-js/modules/es.object.to-string");require("core-js/modules/es.promise");
​
Object.assign({}, {});
[(1, 5, 10, 15)].filter(function (value) {
  return value > 9;
});
var promise = new Promise(function (resolve, reject) {
  resolve(1);
});

而且我们发现它只引入了部分模块;这就比较厉害了,它不仅会考虑到代码中用到的新特性,还会参考目标浏览器的环境来进行按需引入;而useBuiltIns设置为entry的情况则会将core-js中的模块在入口处全部引入,这里就不再演示。

@babel/runtime

我们在上面通过@babel/preset-env转换Class类时发现输出文件的头部多了_classCallCheck、_defineProperties和_createClass三个函数声明,这就是注入的函数,称为辅助函数;@babel/preset-env在转换时注入了函数声明,以便语法转换后使用。

但是我们开发项目时,文件少则几十个,多个上百个,如果每个文件都注入了函数声明,再通过打包工具打包后输出文件又会非常庞大,影响性能。

因此,Babel提供的解决思路是把这些辅助函数都放到一个npm包里面,在每次需要使用的时候就从这个包里把函数require出来;这样即使有几千个文件,也都是对函数进行引用,而不是复制代码;最后通过webpack等工具打包时,只会将npm包中引用到的函数打包一次,这样就复用了代码,减少打包文件的大小。

@babel/runtime就是这些辅助函数的集合包,我们查看@babel/runtime下面的helpers,可以发现导出了很多函数,以及我们上面提及到的_classCallCheck函数:
在这里插入图片描述

首先当然是需要安装@babel/runtime这个包,除此之外还需要安装@babel/plugin-transform-runtime,这个插件的作用是移除@babel/preset-env注入的辅助函数,将其替换为@babel/runtime/helpers中函数的引用。

npm install --save @babel/runtime
npm install --save-dev @babel/plugin-transform-runtime

然后修改我们的配置文件:

{
  "presets": ["@babel/env"],
  "plugins": ["@babel/transform-runtime"]
}

再次打包发现我们的辅助函数已经变成下面的引用方式了:

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

@babel/plugin-transform-runtime

上面我们说到@babel/polyfill会建立一个完整的ES2015环境,因此造成了全局变量的污染;虽然使用core-js不会引入全部模块,但是也会污染部分全局变量。

而@babel/plugin-transform-runtime除了能够转换上面的辅助函数,还能对代码中的新特性API进行一个转换,还是以我们的filter函数和Promise对象为例:

[1, 5, 10, 15].filter((value) => {
  return value > 9;
});
let promise = new Promise((resolve, reject) => {
  resolve(1);
});

然后修改我们的配置文件.babelrc:

{
  "presets": ["@babel/env"],
  "plugins": [
    [
      "@babel/transform-runtime",
      {
        "corejs": 3
      }
    ]
  ]
}

再次查看打包出来的文件发现filter和Promise已经转换成了引用的方式:

"use strict";var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));var _filter = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/filter"));var _context;

(0, _filter["default"])(_context = [1, 5, 10, 15]).call(_context, function (value) {
  return value > 9;
});
var promise = new _promise["default"](function (resolve, reject) {
  resolve(1);
});

我们发现打包出来的模块是从@babel/runtime-corejs3这个包里面引用的,同时我们可以发现:

@babel/runtime-corejs2 ≈ @babel/runtime+core-js+regenerator ≈
@babel/runtime+@babel/polyfill

@babel/polyfill和@babel/runtime-corejs的区别

经过下面这么多例子,总结一下@babel/polyfill和@babel/runtime-corejs的区别:前者改造目标浏览器,让你的浏览器拥有本来不支持的特性;后者改造你的代码,让你的代码能在所有目标浏览器上运行,但不改造浏览器。

一个显而易见的区别就是打开IE11浏览器,如果引入了@babel/polyfill,在控制台我们可以执行Object.assign({}, {});而如果引入了@babel/runtime-corejs,会提示你报错,因为Object上没有assign函数。

​六、参考文档

https://www.babeljs.cn/docs/

https://zhuanlan.zhihu.com/p/326824078

七、补充部分

关注公众号:【深漂程序员小庄】:
内含丰富的学习资源和面试经验(不限前端、java、算法),还有学习交流群可加,并且还有各大厂大佬可一起交流学习,一起进步~添加小庄微信,回复【加群】,可加入互联网技术交流群:

在这里插入图片描述

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深入理解现代JavaScript是指对JavaScript语言的深入学习理解,包括其核心概念、特性和高级用法。以下是一些可以帮助你深入理解现代JavaScript的主题: 1. 闭包:了解闭包的概念和作用,以及如何使用闭包来创建私有变量和函数。 2. 原型和原型链:理解JavaScript中的原型继承机制,包括原型对象、原型链和原型继承的实现方式。 3. 异步编程:掌握JavaScript中的异步编程模式,包括回调函数、Promise、async/await等,以及如何处理异步操作和避免回调地狱。 4. ES6+特性:熟悉ES6及其之后版本引入的新特性,如箭头函数、解构赋值、模板字符串、类和模块等。 5. 函数式编程:了解函数式编程的概念和优势,学习如何使用高阶函数、纯函数和不可变数据等函数式编程的概念和技巧。 6. 模块化开发:掌握模块化开发的概念和实践,包括CommonJS、ES6模块和AMD等模块化规范。 7. 工具和框架:熟悉常用的JavaScript工具和框架,如Webpack、Babel、React和Vue等,了解它们的使用和原理。 8. 性能优化:学习如何优化JavaScript代码的性能,包括减少重绘和回流、使用缓存、懒加载和代码分割等技巧。 9. 错误处理和调试:掌握JavaScript中的错误处理和调试技巧,包括使用try-catch语句、调试工具和日志记录等。 10. 安全性考虑:了解JavaScript中的安全性问题,包括跨站脚本攻击(XSS)、跨站请求伪造(CSRF)和点击劫持等,学习如何防范这些安全威胁。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值