master 02dd0ecfbb7c cached
28 files
1.7 MB
453.6k tokens
1 requests
Download .txt
Showing preview only (2,031K chars total). Download the full file or copy to clipboard to get everything.
Repository: ten-ltw/JavaScript-The-Definitive-Guide-7th-zh
Branch: master
Commit: 02dd0ecfbb7c
Files: 28
Total size: 1.7 MB

Directory structure:
gitextract_0ubatihb/

├── .github/
│   └── workflows/
│       └── gh-pages.yml
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── archetypes/
│   └── default.md
├── config.toml
├── content/
│   └── posts/
│       ├── ch1.md
│       ├── ch10.md
│       ├── ch11.md
│       ├── ch12.md
│       ├── ch13.md
│       ├── ch14.md
│       ├── ch15.md
│       ├── ch16.md
│       ├── ch17.md
│       ├── ch2.md
│       ├── ch3.md
│       ├── ch4.md
│       ├── ch5.md
│       ├── ch6.md
│       ├── ch7.md
│       ├── ch8.md
│       └── ch9.md
├── layouts/
│   └── partials/
│       └── head/
│           └── seo.html
└── static/
    ├── CNAME
    ├── browserconfig.xml
    └── site.webmanifest

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/gh-pages.yml
================================================
name: Js

on:
  push:
    branches:
      - master  # Set a branch to deploy
  pull_request:

jobs:
  deploy:
    runs-on: ubuntu-20.04
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: true  # Fetch Hugo themes (true OR recursive)
          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: '0.91.2'
          extended: true

      - name: Build
        run: hugo -D

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          external_repository: ten-ltw/JavaScript-The-Definitive-Guide-7th-zh
          personal_token: ${{ secrets.JS_GITHUB_TOKEN }}
          publish_dir: ./public
          publish_branch: page

      - name: Algolia Docsearch Uploader
        uses: guzhongren/algolia-docsearch-upload-action@v1.0.0
        env:
          FILE_PATH: "./public/index.json"
          APPLICATION_ID: ${{secrets.ALGOLIA_APPLICATION_ID}}
          ADMIN_API_KEY: ${{secrets.ALGOLIA_API_KEY}}
          INDEX_NAME: "JS"

================================================
FILE: .gitignore
================================================
# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig

# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,hugo
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,macos,hugo

### Hugo ###
# Generated files by hugo
/public/
/resources/_gen/
/assets/jsconfig.json
hugo_stats.json

# Executable may be added to repository
hugo.exe
hugo.darwin
hugo.linux

# Temporary lock file while building
/.hugo_build.lock

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### macOS Patch ###
# iCloud generated files
*.icloud

### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets

# Local History for Visual Studio Code
.history/

# Built Visual Studio Code Extensions
*.vsix

### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide

# Support for Project snippet scope
.vscode/*.code-snippets

# Ignore code-workspaces
*.code-workspace

# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,hugo

# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)



================================================
FILE: .gitmodules
================================================
[submodule "themes/LoveIt"]
	path = themes/LoveIt
	url = https://github.com/dillonzq/LoveIt.git


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Mr.Ten

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# JavaScript-The-Definitive-Guide-7th-zh

《JavaScript 权威指南第七版》中英对照

在线阅读:[https://js.okten.cn/](https://js.okten.cn/)

<img src="./content/posts/cover.jpg" width=24% />

## 目录

- [第 1 章 JavaScript 概述](content/posts/ch1.md)
- [第 2 章 词法结构](content/posts/ch2.md)
- [第 3 章 类型、值和变量](content/posts/ch3.md)
- [第 4 章 表达式和运算符](content/posts/ch4.md)
- [第 5 章 语句](content/posts/ch5.md)
- [第 6 章 对象](content/posts/ch6.md)
- [第 7 章 数组](content/posts/ch7.md)
- [第 8 章 函数](content/posts/ch8.md)
- [第 9 章 类](content/posts/ch9.md)
- [第 10 章 模块](content/posts/ch10.md)
- [第 11 章 JavaScript 标准库](content/posts/ch11.md)
- [第 12 章 迭代器和生成器](content/posts/ch12.md)
- [第 13 章 异步 JavaScript](content/posts/ch13.md)
- [第 14 章 元编程](content/posts/ch14.md)
- [第 15 章 Web 浏览器中的 JavaScript](content/posts/ch15.md)
- [第 16 章 服务器端 JavaScript](content/posts/ch16.md)
- [第 17 章 JavaScript 工具和扩展](content/posts/ch17.md)

模块、标准库选择性阅读
Node 相关先放一放

## License

[MIT](./LICENSE)


================================================
FILE: archetypes/default.md
================================================
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---



================================================
FILE: config.toml
================================================
# for build
baseURL = 'https://blog.okten.cn/'
theme = "LoveIt"
minify = true
html = "keep"
Whitespace = false

title = "《JavaScript 权威指南第七版》中英对照"
# 网站语言, 仅在这里 CN 大写 ["en", "zh-CN", "fr", "pl", ...]
languageCode = "zh-CN"
# 语言名称 ["English", "简体中文", "Français", "Polski", ...]
languageName = "简体中文"
# 是否包括中日韩文字
hasCJKLanguage = true

# 作者配置
[author]
  name = "Ten Li"
  email = "li.tw.ten@gmail.com"
  link = "https://blog.okten.cn/"

# 菜单配置
[menu]
  [[menu.main]]
    weight = 1
    identifier = "posts"
    # 你可以在名称 (允许 HTML 格式) 之前添加其他信息, 例如图标
    pre = "<i class='fas fa-thin fa-archive'></i>"
    # 你可以在名称 (允许 HTML 格式) 之后添加其他信息, 例如图标
    post = ""
    name = "所有章节"
    url = "/posts/"
    # 当你将鼠标悬停在此菜单链接上时, 将显示的标题
    title = ""

[markup]
  [markup.highlight]
    noClasses = false
[module]
  [module.hugoVersion]
    extended = true
    min = "0.62.0"
[params]
  description = "This is my blog"
  # date format
  dateFormat = "2006-01-02"
  # website description for RSS, SEO, Open Graph and Twitter Cards
  # 网站描述, 用于 RSS, SEO, Open Graph 和 Twitter Cards
  # website images for Open Graph and Twitter Cards
  # 网站图片, 用于 Open Graph 和 Twitter Cards
  images = []
  [params.header]
    desktopMode = "fixed"
    mobileMode = "auto"
    [params.header.title]
      # URL of the LOGO
      # LOGO 的 URL
      logo = ""
      name = "《JavaScript 权威指南第七版》中英对照"
      pre = ""
      post = ""

  # Footer config
  # 页面底部信息配置
  [params.footer]
    enable = true
    custom = ""
    hugo = false
    copyright = true
    author = true
    since = 2020
    # ICP info only in China (HTML format is supported)
    # ICP 备案信息,仅在中国使用 (支持 HTML 格式)
    icp = ""
    # license info (HTML format is supported)
    # 许可协议信息 (支持 HTML 格式)
    license= "<a rel=‘license external nofollow noopener noreffer’ href=‘https://creativecommons.org/licenses/by-nc/4.0/’ target=‘_blank’>CC BY-NC 4.0</a>"
  # Section (all posts) page config
  [params.section]
    paginate = 20
    dateFormat = "01-02"
    # amount of RSS pages
    # RSS 文章数目
    rss = 10

  # List (category or tag) page config
  # List (目录或标签) 页面配置
  [params.list]
    # special amount of posts in each list page
    # list 页面每页显示文章数量
    paginate = 20
    # date format (month and day)
    # 日期格式 (月和日)
    dateFormat = "01-02"
    # amount of RSS pages
    # RSS 文章数目
    rss = 10

  # App icon config
  # 应用图标配置
  [params.app]
    # optional site title override for the app when added to an iOS home screen or Android launcher
    # 当添加到 iOS 主屏幕或者 Android 启动器时的标题, 覆盖默认标题
    title = "《JavaScript 权威指南第七版》中英对照"
    # whether to omit favicon resource links
    # 是否隐藏网站图标资源链接
    noFavicon = false
    # modern SVG favicon to use in place of older style .png and .ico files
    # 更现代的 SVG 网站图标, 可替代旧的 .png 和 .ico 文件
    svgFavicon = ""
    # Android browser theme color
    # Android 浏览器主题色
    themeColor = "#ffffff"
    # Safari mask icon color
    # Safari 图标颜色
    iconColor = "#5bbad5"
    # Windows v8-11 tile color
    # Windows v8-11 磁贴颜色
    tileColor = "#da532c"

  # Search config
  # 搜索配置
  [params.search]
    enable = true
    # type of search engine ["lunr", "algolia"]
    # 搜索引擎的类型 ["lunr", "algolia"]
    type = "algolia"
    # max index length of the chunked content
    # 文章内容最长索引长度
    contentLength = 4000
    placeholder = "搜索文章标题或内容..."
    # max number of results length
    # 最大结果数目
    maxResultLength = 10
    # snippet length of the result
    # 结果内容片段长度
    snippetLength = 30
    # HTML tag name of the highlight part in results
    # 搜索结果中高亮部分的 HTML 标签
    highlightTag = "em"
    # whether to use the absolute URL based on the baseURL in search index
    # 是否在搜索索引中使用基于 baseURL 的绝对路径
    absoluteURL = false
    [params.search.algolia]
      index = "JS"
      appID = "I74G1T3YFH"
      searchKey = "9f39718c1951c948495770664edec6b8"

  # Home page config
  # 主页信息设置
  [params.home]
    # amount of RSS pages
    # RSS 文章数目
    rss = 10
    # Home page profile
    # 主页个人信息
    [params.home.profile]
      enable = true
      # Gravatar Email for preferred avatar in home page
      # Gravatar 邮箱,用于优先在主页显示的头像
      gravatarEmail = ""
      # URL of avatar shown in home page
      # 主页显示头像的 URL
      avatarURL = "./posts/cover.jpg"
      # title shown in home page (HTML format is supported)
      # 主页显示的网站标题 (支持 HTML 格式)
      # title = "<div style='background-color: #b59595;top: 0;left: 0;height: 100%;width: 100%;z-index: -1;position: absolute;'></div>"
      # subtitle shown in home page (HTML format is supported)
      # 主页显示的网站副标题 (允许 HTML 格式)
      subtitle = "《JavaScript 权威指南第七版》中英对照"
      # whether to use typeit animation for subtitle
      # 是否为副标题显示打字机动画
      typeit = true
      # whether to show social links
      # 是否显示社交账号
      social = true
      # disclaimer (HTML format is supported)
      # 免责声明 (支持 HTML 格式)
      disclaimer = ""
    # Home page posts
    # 主页文章列表
    [params.home.posts]
      enable = true
      # special amount of posts in each home posts page
      # 主页每页显示文章数量
      paginate = 6
  # Social config in home page
  # 主页的社交信息设置
  [params.social]
    GitHub = "ten-ltw"
    Linkedin = ""
    Twitter = ""
    Instagram = "ten_is_studing"
    Facebook = ""
    Telegram = ""
    Medium = ""
    Gitlab = ""
    Youtubelegacy = ""
    Youtubecustom = ""
    Youtubechannel = ""
    Tumblr = ""
    Quora = ""
    Keybase = ""
    Pinterest = ""
    Reddit = ""
    Codepen = ""
    FreeCodeCamp = ""
    Bitbucket = ""
    Stackoverflow = ""
    Weibo = ""
    Odnoklassniki = ""
    VK = ""
    Flickr = ""
    Xing = ""
    Snapchat = ""
    Soundcloud = ""
    Spotify = ""
    Bandcamp = ""
    Paypal = ""
    Fivehundredpx = ""
    Mix = ""
    Goodreads = ""
    Lastfm = ""
    Foursquare = ""
    Hackernews = ""
    Kickstarter = ""
    Patreon = ""
    Steam = ""
    Twitch = ""
    Strava = ""
    Skype = ""
    Whatsapp = ""
    Zhihu = "dan-ke-58-70"
    Douban = ""
    Angellist = ""
    Slidershare = ""
    Jsfiddle = ""
    Deviantart = ""
    Behance = ""
    Dribbble = ""
    Wordpress = ""
    Vine = ""
    Googlescholar = ""
    Researchgate = ""
    Mastodon = ""
    Thingiverse = ""
    Devto = ""
    Gitea = ""
    XMPP = ""
    Matrix = ""
    Bilibili = ""
    Discord = ""
    DiscordInvite = ""
    Lichess = ""
    ORCID = ""
    Pleroma = ""
    Kaggle = ""
    Email = "li.tw.ten@gmail.com"
    Phone = ""
    RSS = ""

  # Page global config
  # 文章页面全局配置
  [params.page]
    # whether to hide a page from home page
    # 是否在主页隐藏一篇文章
    hiddenFromHomePage = false
    # whether to hide a page from search results
    # 是否在搜索结果中隐藏一篇文章
    hiddenFromSearch = false
    # whether to enable twemoji
    # 是否使用 twemoji
    twemoji = false
    # whether to enable lightgallery
    # 是否使用 lightgallery
    lightgallery = false
    # whether to enable the ruby extended syntax
    # 是否使用 ruby 扩展语法
    ruby = true
    # whether to enable the fraction extended syntax
    # 是否使用 fraction 扩展语法
    fraction = true
    # whether to enable the fontawesome extended syntax
    # 是否使用 fontawesome 扩展语法
    fontawesome = true
    # whether to show link to Raw Markdown content of the content
    # 是否显示原始 Markdown 文档内容的链接
    linkToMarkdown = true
    # whether to show the full text content in RSS
    # 是否在 RSS 中显示全文内容
    rssFullText = false
    # Table of the contents config
    # 目录配置
    [params.page.toc]
      # whether to enable the table of the contents
      # 是否使用目录
      enable = true
      # whether to keep the static table of the contents in front of the post
      # 是否保持使用文章前面的静态目录
      keepStatic = false
      # whether to make the table of the contents in the sidebar automatically collapsed
      # 是否使侧边目录自动折叠展开
      auto = true
    # Code config
    # 代码配置
    [params.page.code]
      # whether to show the copy button of the code block
      # 是否显示代码块的复制按钮
      copy = true
      # the maximum number of lines of displayed code by default
      # 默认展开显示的代码行数
      maxShownLines = 50
    # KaTeX mathematical formulas config (KaTeX https://katex.org/)
    # KaTeX 数学公式配置 (KaTeX https://katex.org/)
    [params.page.math]
      enable = false
      # default inline delimiter is $ ... $ and \( ... \)
      # 默认行内定界符是 $ ... $ 和 \( ... \)
      inlineLeftDelimiter = ""
      inlineRightDelimiter = ""
      # default block delimiter is $$ ... $$, \[ ... \], \begin{equation} ... \end{equation} and some other functions
      # 默认块定界符是 $$ ... $$, \[ ... \],  \begin{equation} ... \end{equation} 和一些其它的函数
      blockLeftDelimiter = ""
      blockRightDelimiter = ""
      # KaTeX extension copy_tex
      # KaTeX 插件 copy_tex
      copyTex = true
      # KaTeX extension mhchem
      # KaTeX 插件 mhchem
      mhchem = true
    # Mapbox GL JS config (Mapbox GL JS https://docs.mapbox.com/mapbox-gl-js)
    # Mapbox GL JS 配置 (Mapbox GL JS https://docs.mapbox.com/mapbox-gl-js)
    [params.page.mapbox]
      # access token of Mapbox GL JS
      # Mapbox GL JS 的 access token
      accessToken = ""
      # style for the light theme
      # 浅色主题的地图样式
      lightStyle = "mapbox://styles/mapbox/light-v10?optimize=true"
      # style for the dark theme
      # 深色主题的地图样式
      darkStyle = "mapbox://styles/mapbox/dark-v10?optimize=true"
      # whether to add NavigationControl (https://docs.mapbox.com/mapbox-gl-js/api/#navigationcontrol)
      # 是否添加 NavigationControl (https://docs.mapbox.com/mapbox-gl-js/api/#navigationcontrol)
      navigation = true
      # whether to add GeolocateControl (https://docs.mapbox.com/mapbox-gl-js/api/#geolocatecontrol)
      # 是否添加 GeolocateControl (https://docs.mapbox.com/mapbox-gl-js/api/#geolocatecontrol)
      geolocate = true
      # whether to add ScaleControl (https://docs.mapbox.com/mapbox-gl-js/api/#scalecontrol)
      # 是否添加 ScaleControl (https://docs.mapbox.com/mapbox-gl-js/api/#scalecontrol)
      scale = true
      # whether to add FullscreenControl (https://docs.mapbox.com/mapbox-gl-js/api/#fullscreencontrol)
      # 是否添加 FullscreenControl (https://docs.mapbox.com/mapbox-gl-js/api/#fullscreencontrol)
      fullscreen = true
    # Social share links in post page
    # 文章页面的分享信息设置
    [params.page.share]
      enable = true
      Twitter = true
      Facebook = true
      Linkedin = false
      Whatsapp = false
      Pinterest = false
      Tumblr = false
      HackerNews = true
      Reddit = false
      VK = false
      Buffer = false
      Xing = false
      Line = true
      Instapaper = false
      Pocket = false
      Flipboard = false
      Weibo = true
      Blogger = false
      Baidu = false
      Odnoklassniki = false
      Evernote = false
      Skype = false
      Trello = false
      Mix = false
    # Comment config
    # 评论系统设置
    [params.page.comment]
      enable = false
      # Disqus comment config (https://disqus.com/)
      # Disqus 评论系统设置 (https://disqus.com/)
      [params.page.comment.disqus]
        enable = false
        # Disqus shortname to use Disqus in posts
        # Disqus 的 shortname,用来在文章中启用 Disqus 评论系统
        shortname = ""
      # Gitalk comment config (https://github.com/gitalk/gitalk)
      # Gitalk 评论系统设置 (https://github.com/gitalk/gitalk)
      [params.page.comment.gitalk]
        enable = false
        owner = ""
        repo = ""
        clientId = ""
        clientSecret = ""
      # Valine comment config (https://github.com/xCss/Valine)
      # Valine 评论系统设置 (https://github.com/xCss/Valine)
      [params.page.comment.valine]
        enable = false
        appId = ""
        appKey = ""
        placeholder = ""
        avatar = "mp"
        meta= ""
        pageSize = 10
        # automatically adapt the current theme i18n configuration when empty
        # 为空时自动适配当前主题 i18n 配置
        lang = ""
        visitor = true
        recordIP = true
        highlight = true
        enableQQ = false
        serverURLs = ""
        # emoji data file name, default is "google.yml"
        # ["apple.yml", "google.yml", "facebook.yml", "twitter.yml"]
        # located in "themes/LoveIt/assets/lib/valine/emoji/" directory
        # you can store your own data files in the same path under your project:
        # "assets/lib/valine/emoji/"
        # emoji 数据文件名称, 默认是 "google.yml"
        # ["apple.yml", "google.yml", "facebook.yml", "twitter.yml"]
        # 位于 "themes/LoveIt/assets/lib/valine/emoji/" 目录
        # 可以在你的项目下相同路径存放你自己的数据文件:
        # "assets/lib/valine/emoji/"
        emoji = ""
      # Facebook comment config (https://developers.facebook.com/docs/plugins/comments)
      # Facebook 评论系统设置 (https://developers.facebook.com/docs/plugins/comments)
      [params.page.comment.facebook]
        enable = false
        width = "100%"
        numPosts = 10
        appId = ""
        # automatically adapt the current theme i18n configuration when empty
        # 为空时自动适配当前主题 i18n 配置
        languageCode = ""
      # Telegram comments config (https://comments.app/)
      # Telegram comments 评论系统设置 (https://comments.app/)
      [params.page.comment.telegram]
        enable = false
        siteID = ""
        limit = 5
        height = ""
        color = ""
        colorful = true
        dislikes = false
        outlined = false
      # Commento comment config (https://commento.io/)
      # Commento comment 评论系统设置 (https://commento.io/)
      [params.page.comment.commento]
        enable = false
      # utterances comment config (https://utteranc.es/)
      # utterances comment 评论系统设置 (https://utteranc.es/)
      [params.page.comment.utterances]
        enable = false
        # owner/repo
        repo = ""
        issueTerm = "pathname"
        label = ""
        lightTheme = "github-light"
        darkTheme = "github-dark"
      # giscus comment config (https://giscus.app/)
      # giscus comment 评论系统设置 (https://giscus.app/zh-CN)
      [params.page.comment.giscus]
        # You can refer to the official documentation of giscus to use the following configuration.
        # 你可以参考官方文档来使用下列配置
        enable = false
        repo = ""
        repoId = ""
        category = "Announcements"
        categoryId = ""
        # automatically adapt the current theme i18n configuration when empty
        # 为空时自动适配当前主题 i18n 配置
        lang = ""
        mapping = "pathname"
        reactionsEnabled = "1"
        emitMetadata = "0"
        inputPosition = "bottom"
        lazyLoading = false
        lightTheme = "light"
        darkTheme = "dark"
    # Third-party library config
    # 第三方库配置
    [params.page.library]
      [params.page.library.css]
        # someCSS = "some.css"
        # located in "assets/" 位于 "assets/"
        # Or 或者
        # someCSS = "https://cdn.example.com/some.css"
      [params.page.library.js]
        # someJavascript = "some.js"
        # located in "assets/" 位于 "assets/"
        # Or 或者
        # someJavascript = "https://cdn.example.com/some.js"
    # Page SEO config
    # 页面 SEO 配置
    [params.page.seo]
      # image URL
      # 图片 URL
      images = []
      # Publisher info
      # 出版者信息
      [params.page.seo.publisher]
        name = ""
        logoUrl = ""

  # TypeIt config
  # TypeIt 配置
  [params.typeit]
    # typing speed between each step (measured in milliseconds)
    # 每一步的打字速度 (单位是毫秒)
    speed = 100
    # blinking speed of the cursor (measured in milliseconds)
    # 光标的闪烁速度 (单位是毫秒)
    cursorSpeed = 1000
    # character used for the cursor (HTML format is supported)
    # 光标的字符 (支持 HTML 格式)
    cursorChar = "|"
    # cursor duration after typing finishing (measured in milliseconds, "-1" means unlimited)
    # 打字结束之后光标的持续时间 (单位是毫秒, "-1" 代表无限大)
    duration = -1

  # Site verification code for Google/Bing/Yandex/Pinterest/Baidu
  # 网站验证代码,用于 Google/Bing/Yandex/Pinterest/Baidu
  [params.verification]
    google = ""
    bing = ""
    yandex = ""
    pinterest = ""
    baidu = ""

  # Site SEO config
  # 网站 SEO 配置
  [params.seo]
    # image URL
    # 图片 URL
    image = ""
    # thumbnail URL
    # 缩略图 URL
    thumbnailUrl = ""

  # Analytics config
  # 网站分析配置
  [params.analytics]
    enable = true
    # Google Analytics
    [params.analytics.google]
      id = "G-BK52F3N7KL"
      # whether to anonymize IP
      # 是否匿名化用户 IP
      anonymizeIP = true
    # Fathom Analytics
    [params.analytics.fathom]
      id = ""
      # server url for your tracker if you're self hosting
      # 自行托管追踪器时的主机路径
      server = ""
    # Plausible Analytics
    [params.analytics.plausible]
      dataDomain = ""
    # Yandex Metrica
    [params.analytics.yandexMetrica]
      id = ""

  # Cookie consent config
  # Cookie 许可配置
  [params.cookieconsent]
    enable = false
    # text strings used for Cookie consent banner
    # 用于 Cookie 许可横幅的文本字符串
    [params.cookieconsent.content]
      message = ""
      dismiss = ""
      link = ""

  # CDN config for third-party library files
  # 第三方库文件的 CDN 设置
  [params.cdn]
    # CDN data file name, disabled by default
    # ["jsdelivr.yml"]
    # located in "themes/LoveIt/assets/data/cdn/" directory
    # you can store your own data files in the same path under your project:
    # "assets/data/cdn/"
    # CDN 数据文件名称, 默认不启用
    # ["jsdelivr.yml"]
    # 位于 "themes/LoveIt/assets/data/cdn/" 目录
    # 可以在你的项目下相同路径存放你自己的数据文件:
    # "assets/data/cdn/"
    data = "jsdelivr.yml"

  # Compatibility config
  # 兼容性设置
  [params.compatibility]
    # whether to use Polyfill.io to be compatible with older browsers
    # 是否使用 Polyfill.io 来兼容旧式浏览器
    polyfill = false
    # whether to use object-fit-images to be compatible with older browsers
    # 是否使用 object-fit-images 来兼容旧式浏览器
    objectFit = false

[outputs]
  home = ["HTML", "RSS", "JSON"]


================================================
FILE: content/posts/ch1.md
================================================
---
title: "第 1 章 JavaScript 概述"
date: 2020-11-02T22:18:42+08:00
---

JavaScript is the programming language of the web. The overwhelming majority of websites use JavaScript, and all modern web browsers—on desktops, tablets, and phones—include JavaScript interpreters, making JavaScript the most-deployed programming language in history. Over the last decade, Node.js has enabled JavaScript programming outside of web browsers, and the dramatic success of Node means that JavaScript is now also the most-used programming language among software developers. Whether you’re starting from scratch or are already using JavaScript professionally, this book will help you master the language.

> JavaScript 是 web 的编程语言。绝大多数的网站都使用 JavaScript,所有的现代网络浏览器——台式机、平板电脑和手机——都包括 JavaScript 解释器,这使得 JavaScript 成为历史上部署最多的编程语言。在过去的十年中,Node.js 使得 JavaScript 编程可以在 web 浏览器之外进行,Node 的巨大成功意味着 JavaScript 现在也是软件开发人员最常用的编程语言。无论你是从零开始还是已经开始专业地使用 JavaScript,这本书都会帮助你掌握这门语言。

If you are already familiar with other programming languages, it may help you to know that JavaScript is a high-level, dynamic, interpreted programming language that is well-suited to object-oriented and functional programming styles. JavaScript’s variables are untyped. Its syntax is loosely based on Java, but the languages are otherwise unrelated. JavaScript derives its first-class functions from Scheme and its prototype-based inheritance from the little-known language Self. But you do not need to know any of those languages, or be familiar with those terms, to use this book and learn JavaScript.

> 如果您已经熟悉了其他编程语言,那么了解 JavaScript 是一种高级的、动态的、解释的编程语言可能会对您有所帮助,它非常适合面向对象和函数式编程风格。JavaScript 的变量是无类型的。它的语法不严格地讲基于 Java,但是这两种语言在其他方面是无关的。JavaScript 的 first-class 类型函数衍生于 Scheme,从鲜为人知的 Self 继承基于原型的函数。但您不需要了解任何这些语言,或熟悉这些术语,以使用这本书和学习 JavaScript。

The name “JavaScript” is quite misleading. Except for a superficial syntactic resemblance, JavaScript is completely different from the Java programming language. And JavaScript has long since outgrown its scripting-language roots to become a robust and efficient general-purpose language suitable for serious software engineering and projects with huge codebases.

> “JavaScript”这个名称很容易引起误解。除了表面上的语法相似之外,JavaScript 与 Java 编程语言完全不同。而且 JavaScript 早已超越了它的脚本语言根基,成为一种健壮、高效的通用语言,适合于具有巨大代码库的严肃软件工程和项目。

#### JAVASCRIPT: NAMES, VERSIONS, AND MODES

JavaScript was created at Netscape in the early days of the web, and technically, “JavaScript” is a trademark licensed from Sun Microsystems (now Oracle) used to describe Netscape’s (now Mozilla’s) implementation of the language. Netscape submitted the language for standardization to ECMA—the European Computer Manufacturer’s Association—and because of trademark issues, the standardized version of the language was stuck with the awkward name “ECMAScript.” In practice, everyone just calls the language JavaScript. This book uses the name “ECMAScript” and the abbreviation “ES” to refer to the language standard and to versions of that standard.

> JavaScript 是在网络早期由网景公司创建的,从技术上讲,“JavaScript”是 Sun Microsystems(现在的 Oracle)授权的商标,用来描述 Netscape 公司(现在的Mozilla 公司)对该语言的实现。Netscape 公司将这种语言提交给欧洲计算机制造商协会(ecma)进行标准化,但由于商标问题,这种语言的标准化版本只能使用一个尴尬的名字“ECMAScript”。实际上,每个人都称这种语言为 JavaScript。本书使用名称“ECMAScript”和缩写“ES”来表示该语言标准和该标准的版本。

For most of the 2010s, version 5 of the ECMAScript standard has been supported by all web browsers. This book treats ES5 as the compatibility baseline and no longer discusses earlier versions of the language. ES6 was released in 2015 and added major new features—including class and module syntax—that changed JavaScript from a scripting language into a serious, general-purpose language suitable for large-scale software engineering. Since ES6, the ECMAScript specification has moved to a yearly release cadence, and versions of the language—ES2016, ES2017, ES2018, ES2019, and ES2020—are now identified by year of release.

> 对于2010年代的大多数版本,所有 web 浏览器都支持 ECMAScript 标准的第5版。本书将 ES5 作为兼容性基线,不再讨论该语言的早期版本。ES6 于2015年发布,增加了主要的新特性(包括类和模块语法),使 JavaScript 从一种脚本语言变成了一种严肃的、适用于大规模软件工程的通用语言。自 ES6 以来,ECMAScript 规范已经以每年发布一次为基调,该语言的版本——es2016、ES2017、ES2018、ES2019和es2020——现在以发布年份来确定。

As JavaScript evolved, the language designers attempted to correct flaws in the early (pre-ES5) versions. In order to maintain backward compatibility, it is not possible to remove legacy features, no matter how flawed. But in ES5 and later, programs can opt in to JavaScript’s strict mode in which a number of early language mistakes have been corrected. The mechanism for opting in is the “use strict” directive described in §5.6.3. That section also summarizes the differences between legacy JavaScript and strict JavaScript. In ES6 and later, the use of new language features often implicitly invokes strict mode. For example, if you use the ES6 class keyword or create an ES6 module, then all the code within the class or module is automatically strict, and the old, flawed features are not available in those contexts. This book will cover the legacy features of JavaScript but is careful to point out that they are not available in strict mode.

> 随着 JavaScript 的发展,语言设计者试图纠正早期版本(es5 之前)的缺陷。为了保持向后兼容性,不可能删除遗留特性,无论其缺陷有多大。但在 ES5 及以后版本中,程序可以选择使用 JavaScript 的严格模式,在这种模式中,早期的一些语言错误已经得到了纠正。选择加入的机制是 §5.6.3 中描述的“严格使用”指令。这一节还总结了传统 JavaScript 和严格 JavaScript 之间的区别。在 ES6 及以后版本中,使用新的语言特性通常会隐式地调用 strict 模式。例如,如果您使用 ES6 类关键字或创建 ES6 模块,那么类或模块中的所有代码将自动严格,并且在这些上下文中不能使用旧的、有缺陷的特性。这本书将涵盖 JavaScript 的遗留特性,但小心地指出,它们在严格模式下是不可用的。

To be useful, every language must have a platform, or standard library, for performing things like basic input and output. The core JavaScript language defines a minimal API for working with numbers, text, arrays, sets, maps, and so on, but does not include any input or output functionality. Input and output (as well as more sophisticated features, such as networking, storage, and graphics) are the responsibility of the “host environment” within which JavaScript is embedded.

> 每种语言都必须有一个平台或标准库来执行基本输入和输出之类的操作。核心 JavaScript 语言定义了一个极小的 API 处理数字、文本、数组、集合、映射等,但不包括任何输入或输出功能。输入和输出(以及更复杂的特性,如网络、存储和图形)由嵌入 JavaScript 的“宿主”负责。

The original host environment for JavaScript was a web browser, and this is still the most common execution environment for JavaScript code. The web browser environment allows JavaScript code to obtain input from the user’s mouse and keyboard and by making HTTP requests. And it allows JavaScript code to display output to the user with HTML and CSS.

> JavaScript 的原始宿主环境是一个 web 浏览器,这仍然是 JavaScript 代码最常见的执行环境。web 浏览器环境允许 JavaScript 代码通过发送 HTTP 请求从用户的鼠标和键盘获取输入。它允许 JavaScript 代码用 HTML 和 CSS 向用户显示输出。

Since 2010, another host environment has been available for JavaScript code. Instead of constraining JavaScript to work with the APIs provided by a web browser, Node gives JavaScript access to the entire operating system, allowing JavaScript programs to read and write files, send and receive data over the network, and make and serve HTTP requests. Node is a popular choice for implementing web servers and also a convenient tool for writing simple utility scripts as an alternative to shell scripts.

> 从2010年开始,另一个宿主已经可以用于 JavaScript 代码。Node 没有限制 JavaScript 使用 web 浏览器提供的 api,而是允许 JavaScript 访问整个操作系统,允许 JavaScript 程序读和写文件,通过网络发送和接收数据,以及发出和服务 HTTP 请求。Node 是实现 web 服务器的常用选择,也是编写简单实用程序脚本的方便工具,可以作为 shell 脚本的替代。

Most of this book is focused on the JavaScript language itself. Chapter 11 documents the JavaScript standard library, Chapter 15 introduces the web browser host environment, and Chapter 16 introduces the Node host environment.

> 这本书的大部分内容都集中在 JavaScript 语言本身。第 11 章介绍了 JavaScript 标准库,第 15 章介绍了 web 浏览器宿主,第 16 章介绍了 Node 宿主。

This book covers low-level fundamentals first, and then builds on those to more advanced and higher-level abstractions. The chapters are intended to be read more or less in order. But learning a new programming language is never a linear process, and describing a language is not linear either: each language feature is related to other features, and this book is full of cross-references—sometimes backward and sometimes forward—to related material. This introductory chapter makes a quick first pass through the language, introducing key features that will make it easier to understand the in-depth treatment in the chapters that follow. If you are already a practicing JavaScript programmer, you can probably skip this chapter. (Although you might enjoy reading Example 1-1 at the end of the chapter before you move on.)

> 这本书首先介绍了底层基础知识,然后在这些基础上构建更高级和更高级别的抽象。这些章节的目的是让读者或多或少地按顺序阅读。但是学习一种新的编程语言从来都不是一个线性的过程,描述一种语言也不是线性的:每一种语言的特性都与其他特性相关,这本书充满了交叉引用——有时向后,有时向前的相关材料。这一介绍性章节对该语言进行了一次快速的介绍,并介绍了一些关键特性,这些特性将使后续章节的深入处理更容易理解。如果你已经是一个实践 JavaScript 程序员,你可以跳过这一章。(尽管你可能会喜欢阅读本章末尾的示例 1-1,然后再继续阅读。)

## 1.1 Exploring JavaScript

When learning a new programming language, it’s important to try the examples in the book, then modify them and try them again to test your understanding of the language. To do that, you need a JavaScript interpreter.

> 在学习一门新的编程语言时,试一试书中的示例是很重要的,然后修改它们,再试一次来测试你对这门语言的理解。为此,需要一个 JavaScript 解释器。

The easiest way to try out a few lines of JavaScript is to open up the web developer tools in your web browser (with F12, Ctrl-Shift-I, or Command-Option-I) and select the Console tab. You can then type code at the prompt and see the results as you type. Browser developer tools often appear as panes at the bottom or right of the browser window, but you can usually detach them as separate windows (as pictured in Figure 1-1), which is often quite convenient.

> 尝试几行 JavaScript 的最简单方法是在web浏览器中打开 web developer 工具(使用 F12、Ctrl-Shift-I 或 Command-Option-I)并选择 Console 选项卡。然后可以在提示符处键入代码,并在键入时查看结果。浏览器开发人员工具通常以窗格的形式出现在浏览器窗口的底部或右侧,但是您通常可以将它们分离为单独的窗口(如图1-1所示),这通常非常方便。

<Figures figure="1-1">The JavaScript console in Firefox’s Developer Tools</Figures>

Another way to try out JavaScript code is to download and install Node from https://nodejs.org. Once Node is installed on your system, you can simply open a Terminal window and type node to begin an interactive JavaScript session like this one:

> 另一种尝试 JavaScript 代码的方法是从[https://nodejs.org](https://nodejs.org)下载并安装 Node。一旦在你的系统上安装了 Node,你可以简单地打开一个终端窗口,输入 Node 来开始一个交互式的 JavaScript 会话,就像这样:

```sh
$ node
Welcome to Node.js v12.13.0.
Type ".help" for more information.
> .help
.break    Sometimes you get stuck, this gets you out
.clear    Alias for .break
.editor   Enter editor mode
.exit     Exit the repl
.help     Print this help message
.load     Load JS from a file into the REPL session
.save     Save all evaluated commands in this REPL session to a file

Press ^C to abort current expression, ^D to exit the repl
> let x = 2, y = 3;
undefined
> x + y
5
> (x === 2) && (y === 3)
true
> (x > 3) || (y < 3)
false
```
## 1.2 Hello World

When you are ready to start experimenting with longer chunks of code, these line-by-line interactive environments may no longer be suitable, and you will probably prefer to write your code in a text editor. From there, you can copy and paste to the JavaScript console or into a Node session. Or you can save your code to a file (the traditional filename extension for JavaScript code is .js) and then run that file of JavaScript code with Node:

> 当您准备开始尝试更长的代码块时,这些逐行交互环境可能不再适合,您可能更喜欢在文本编辑器中编写代码。从那里,您可以复制并粘贴到JavaScript控制台或节点会话中。或者你可以将你的代码保存到一个文件中(传统的 JavaScript 代码的文件名扩展名是 .js),然后用 Node 运行该 JavaScript 代码文件:

```sh
$ node snippet.js
```

If you use Node in a noninteractive manner like this, it won’t automatically print out the value of all the code you run, so you’ll have to do that yourself. You can use the function console.log() to display text and other JavaScript values in your terminal window or in a browser’s developer tools console. So, for example, if you create a hello.js file containing this line of code:

> 如果像这样以非交互的方式使用 Node,它不会自动打印出您运行的所有代码的值,因此您必须自己执行。可以使用 console.log() 函数在终端窗口或浏览器的 developer tools 控制台中显示文本和其他 JavaScript 值。因此,例如,如果你创建一个 hello.js 文件,其中包含这行代码:

```js
console.log("Hello World!");
```

and execute the file with node hello.js, you’ll see the message “Hello World!” printed out.

> 并执行 `node hello.js` ,你会看到消息“Hello World!”打印出来。

If you want to see that same message printed out in the JavaScript console of a web browser, create a new file named hello.html, and put this text in it:

> 如果您想在 web 浏览器的 JavaScript 控制台中看到同样的消息打印出来,那么创建一个名为 hello.html 的文件,并把这段文字放进去:

```js
<script src="hello.js"></script>
```

Then load hello.html into your web browser using a `file://` URL like this one:

> 然后使用 `file://` URL将 hello.html 加载到浏览器中,如下图所示:

```
file:///Users/username/javascript/hello.html
```

Open the developer tools window to see the greeting in the console.

> 打开 developer tools 窗口,在控制台中查看问候语。

## 1.3 A Tour of JavaScript

This section presents a quick introduction, through code examples, to the JavaScript language. After this introductory chapter, we dive into JavaScript at the lowest level: Chapter 2 explains things like JavaScript comments, semicolons, and the Unicode character set. Chapter 3 starts to get more interesting: it explains JavaScript variables and the values you can assign to those variables.

> 本节通过代码示例快速介绍 JavaScript 语言。在这一介绍性章节之后,我们将深入到 JavaScript 的底层:第 2 章说明了 JavaScript 注释、分号和 Unicode 字符集等内容。第 3 章开始变得更有趣:它说明了 JavaScript 变量和你可以为这些变量赋值的值。

Here’s some sample code to illustrate the highlights of those two chapters:

> 下面是一些示例代码,演示这两章的重点内容:

```js
// Anything following double slashes is an English-language comment.
// Read the comments carefully: they explain the JavaScript code.

// A variable is a symbolic name for a value.
// Variables are declared with the let keyword:
let x;                     // Declare a variable named x.

// Values can be assigned to variables with an = sign
x = 0;                     // Now the variable x has the value 0
x                          // => 0: A variable evaluates to its value.

// JavaScript supports several types of values
x = 1;                     // Numbers.
x = 0.01;                  // Numbers can be integers or reals.
x = "hello world";         // Strings of text in quotation marks.
x = 'JavaScript';          // Single quote marks also delimit strings.
x = true;                  // A Boolean value.
x = false;                 // The other Boolean value.
x = null;                  // Null is a special value that means "no value."
x = undefined;             // Undefined is another special value like null.
```

Two other very important types that JavaScript programs can manipulate are objects and arrays. These are the subjects of Chapters 6 and 7, but they are so important that you’ll see them many times before you reach those chapters:

> JavaScript 程序可以操作的另外两种非常重要的类型是对象和数组。这些是第 6 章和第 7 章的主题,但它们非常重要,在你读到这些章节之前,你会多次看到它们:

```js
// JavaScript's most important datatype is the object.
// An object is a collection of name/value pairs, or a string to value map.
let book = {               // Objects are enclosed in curly braces.
    topic: "JavaScript",   // The property "topic" has value "JavaScript."
    edition: 7             // The property "edition" has value 7
};                         // The curly brace marks the end of the object.

// Access the properties of an object with . or []:
book.topic                 // => "JavaScript"
book["edition"]            // => 7: another way to access property values.
book.author = "Flanagan";  // Create new properties by assignment.
book.contents = {};        // {} is an empty object with no properties.

// Conditionally access properties with ?. (ES2020):
book.contents?.ch01?.sect1 // => undefined: book.contents has no ch01 property.

// JavaScript also supports arrays (numerically indexed lists) of values:
let primes = [2, 3, 5, 7]; // An array of 4 values, delimited with [ and ].
primes[0]                  // => 2: the first element (index 0) of the array.
primes.length              // => 4: how many elements in the array.
primes[primes.length-1]    // => 7: the last element of the array.
primes[4] = 9;             // Add a new element by assignment.
primes[4] = 11;            // Or alter an existing element by assignment.
let empty = [];            // [] is an empty array with no elements.
empty.length               // => 0

// Arrays and objects can hold other arrays and objects:
let points = [             // An array with 2 elements.
    {x: 0, y: 0},          // Each element is an object.
    {x: 1, y: 1}
];
let data = {                 // An object with 2 properties
    trial1: [[1,2], [3,4]],  // The value of each property is an array.
    trial2: [[2,3], [4,5]]   // The elements of the arrays are arrays.
};
```

#### COMMENT SYNTAX IN CODE EXAMPLES

You may have noticed in the preceding code that some of the comments begin with an arrow (=>). These show the value produced by the code before the comment and are my attempt to emulate an interactive JavaScript environment like a web browser console in a printed book.

> 您可能已经注意到,在前面的代码中,一些注释以箭头开头(=>)。这些代码显示了注释之前代码产生的值,我试图模拟交互式 JavaScript 环境,就像印刷书籍中的 web 浏览器控制台一样。

Those // => comments also serve as an assertion, and I’ve written a tool that tests the code and verifies that it produces the value specified in the comment. This should help, I hope, to reduce errors in the book.

> 那些 // => 注释也用作断言,我编写了一个工具来测试代码并验证它是否生成注释中指定的值。我希望这有助于减少书中的错误。

There are two related styles of comment/assertion. If you see a comment of the form // a == 42, it means that after the code before the comment runs, the variable a will have the value 42. If you see a comment of the form // !, it means that the code on the line before the comment throws an exception (and the rest of the comment after the exclamation mark usually explains what kind of exception is thrown).

> 注释和断言有两种相关的风格。如果您看到 `// a == 42` 形式的注释,这意味着在注释运行之前的代码之后,变量a的值将是42。如果您看到 `// !` 形式的注释,这意味着注释前一行的代码抛出一个异常(感叹号之后的注释的其余部分通常解释抛出的是哪种异常)。

You’ll see these comments used throughout the book.

> 您将在整本书中看到这些注释。

The syntax illustrated here for listing array elements within square braces or mapping object property names to property values inside curly braces is known as an initializer expression, and it is just one of the topics of Chapter 4. An expression is a phrase of JavaScript that can be evaluated to produce a value. For example, the use of . and [] to refer to the value of an object property or array element is an expression.

> 这里演示的在方括号内列出数组元素或在花括号内将对象属性名映射到属性值的语法称为初始化表达式,它只是第 4 章的主题之一。表达式是 JavaScript 的一个短语,可以计算它来产生一个值。例如,的使用。和 [] 引用对象属性或数组元素的值是一个表达式。

One of the most common ways to form expressions in JavaScript is to use operators:

> 在 JavaScript 中形成表达式最常见的方法之一是使用运算符:

```js
// Operators act on values (the operands) to produce a new value.
// Arithmetic operators are some of the simplest:
3 + 2                      // => 5: addition
3 - 2                      // => 1: subtraction
3 * 2                      // => 6: multiplication
3 / 2                      // => 1.5: division
points[1].x - points[0].x  // => 1: more complicated operands also work
"3" + "2"                  // => "32": + adds numbers, concatenates strings

// JavaScript defines some shorthand arithmetic operators
let count = 0;             // Define a variable
count++;                   // Increment the variable
count--;                   // Decrement the variable
count += 2;                // Add 2: same as count = count + 2;
count *= 3;                // Multiply by 3: same as count = count * 3;
count                      // => 6: variable names are expressions, too.

// Equality and relational operators test whether two values are equal,
// unequal, less than, greater than, and so on. They evaluate to true or false.
let x = 2, y = 3;          // These = signs are assignment, not equality tests
x === y                    // => false: equality
x !== y                    // => true: inequality
x < y                      // => true: less-than
x <= y                     // => true: less-than or equal
x > y                      // => false: greater-than
x >= y                     // => false: greater-than or equal
"two" === "three"          // => false: the two strings are different
"two" > "three"            // => true: "tw" is alphabetically greater than "th"
false === (x > y)          // => true: false is equal to false

// Logical operators combine or invert boolean values
(x === 2) && (y === 3)     // => true: both comparisons are true. && is AND
(x > 3) || (y < 3)         // => false: neither comparison is true. || is OR
!(x === y)                 // => true: ! inverts a boolean value
```

If JavaScript expressions are like phrases, then JavaScript statements are like full sentences. Statements are the topic of Chapter 5. Roughly, an expression is something that computes a value but doesn’t do anything: it doesn’t alter the program state in any way. Statements, on the other hand, don’t have a value, but they do alter the state. You’ve seen variable declarations and assignment statements above. The other broad category of statement is control structures, such as conditionals and loops. You’ll see examples below, after we cover functions.

> 如果 JavaScript 表达式像短语,那么 JavaScript 语句就像完整的句子。这部分是第 5 章的主题。粗略地说,表达式不做任何事情只计算一个值:它不以任何方式改变程序状态。另一方面,语句没有值,但是它们可以改变状态。您已经在上面看到了变量声明和赋值语句。语句的另一大类是控制结构,比如条件语句和循环。在介绍函数之后,您将看到下面的示例。

A function is a named and parameterized block of JavaScript code that you define once, and can then invoke over and over again. Functions aren’t covered formally until Chapter 8, but like objects and arrays, you’ll see them many times before you get to that chapter. Here are some simple examples:

> 函数是一个已命名和参数化的 JavaScript 代码块,您只定义一次,然后可以反复调用它。函数直到第 8 章才正式介绍,但就像对象和数组一样,在进入这一章之前,您将多次看到它们。下面是一些简单的例子:

```js
// Functions are parameterized blocks of JavaScript code that we can invoke.
function plus1(x) {        // Define a function named "plus1" with parameter "x"
    return x + 1;          // Return a value one larger than the value passed in
}                          // Functions are enclosed in curly braces

plus1(y)                   // => 4: y is 3, so this invocation returns 3+1

let square = function(x) { // Functions are values and can be assigned to vars
    return x * x;          // Compute the function's value
};                         // Semicolon marks the end of the assignment.

square(plus1(y))           // => 16: invoke two functions in one expression
```

In ES6 and later, there is a shorthand syntax for defining functions. This concise syntax uses => to separate the argument list from the function body, so functions defined this way are known as arrow functions. Arrow functions are most commonly used when you want to pass an unnamed function as an argument to another function. The preceding code looks like this when rewritten to use arrow functions:

> 在 ES6 及以后版本中,有一种用于定义函数的快捷语法。这种简洁的语法使用 => 将参数列表与函数体分开,因此以这种方式定义的函数称为箭头函数。当您希望将一个未命名的函数作为参数传递给另一个函数时,最常用的是箭头函数。前面的代码看起来像这样,当重写使用箭头函数:

```js
const plus1 = x => x + 1;   // The input x maps to the output x + 1
const square = x => x * x;  // The input x maps to the output x * x
plus1(y)                    // => 4: function invocation is the same
square(plus1(y))            // => 16
```

When we use functions with objects, we get methods:

> 当我们使用函数和对象时,我们得到方法:

```js
// When functions are assigned to the properties of an object, we call
// them "methods."  All JavaScript objects (including arrays) have methods:
let a = [];                // Create an empty array
a.push(1,2,3);             // The push() method adds elements to an array
a.reverse();               // Another method: reverse the order of elements

// We can define our own methods, too. The "this" keyword refers to the object
// on which the method is defined: in this case, the points array from earlier.
points.dist = function() { // Define a method to compute distance between points
    let p1 = this[0];      // First element of array we're invoked on
    let p2 = this[1];      // Second element of the "this" object
    let a = p2.x-p1.x;     // Difference in x coordinates
    let b = p2.y-p1.y;     // Difference in y coordinates
    return Math.sqrt(a*a + // The Pythagorean theorem
                     b*b); // Math.sqrt() computes the square root
};
points.dist()              // => Math.sqrt(2): distance between our 2 points
```

Now, as promised, here are some functions whose bodies demonstrate common JavaScript control structure statements:

> 现在,如前所述,下面是一些函数,它们的主体演示了常见的 JavaScript 控制结构语句:

```js
// JavaScript statements include conditionals and loops using the syntax
// of C, C++, Java, and other languages.
function abs(x) {          // A function to compute the absolute value.
    if (x >= 0) {          // The if statement...
        return x;          // executes this code if the comparison is true.
    }                      // This is the end of the if clause.
    else {                 // The optional else clause executes its code if
        return -x;         // the comparison is false.
    }                      // Curly braces optional when 1 statement per clause.
}                          // Note return statements nested inside if/else.
abs(-10) === abs(10)       // => true

function sum(array) {      // Compute the sum of the elements of an array
    let sum = 0;           // Start with an initial sum of 0.
    for(let x of array) {  // Loop over array, assigning each element to x.
        sum += x;          // Add the element value to the sum.
    }                      // This is the end of the loop.
    return sum;            // Return the sum.
}
sum(primes)                // => 28: sum of the first 5 primes 2+3+5+7+11

function factorial(n) {    // A function to compute factorials
    let product = 1;       // Start with a product of 1
    while(n > 1) {         // Repeat statements in {} while expr in () is true
        product *= n;      // Shortcut for product = product * n;
        n--;               // Shortcut for n = n - 1
    }                      // End of loop
    return product;        // Return the product
}
factorial(4)               // => 24: 1*4*3*2

function factorial2(n) {   // Another version using a different loop
    let i, product = 1;    // Start with 1
    for(i=2; i <= n; i++)  // Automatically increment i from 2 up to n
        product *= i;      // Do this each time. {} not needed for 1-line loops
    return product;        // Return the factorial
}
factorial2(5)              // => 120: 1*2*3*4*5
```

JavaScript supports an object-oriented programming style, but it is significantly different than “classical” object-oriented programming languages. Chapter 9 covers object-oriented programming in JavaScript in detail, with lots of examples. Here is a very simple example that demonstrates how to define a JavaScript class to represent 2D geometric points. Objects that are instances of this class have a single method, named distance(), that computes the distance of the point from the origin:

> JavaScript 支持面向对象的编程风格,但它与“经典的”面向对象编程语言有显著的不同。第 9 章详细介绍了 JavaScript 的面向对象编程,并提供了很多示例。下面是一个非常简单的示例,演示了如何定义一个 JavaScript 类来表示 2D 几何点。作为这个类的实例的对象有一个名为 distance() 的方法,它计算点到原点的距离:

```js
class Point {              // By convention, class names are capitalized.
    constructor(x, y) {    // Constructor function to initialize new instances.
        this.x = x;        // This keyword is the new object being initialized.
        this.y = y;        // Store function arguments as object properties.
    }                      // No return is necessary in constructor functions.

    distance() {           // Method to compute distance from origin to point.
        return Math.sqrt(  // Return the square root of x² + y².
            this.x * this.x +  // this refers to the Point object on which
            this.y * this.y    // the distance method is invoked.
        );
    }
}

// Use the Point() constructor function with "new" to create Point objects
let p = new Point(1, 1);   // The geometric point (1,1).

// Now use a method of the Point object p
p.distance()               // => Math.SQRT2
```

This introductory tour of JavaScript’s fundamental syntax and capabilities ends here, but the book continues with self-contained chapters that cover additional features of the language:

> 关于 JavaScript 基本语法和能力的介绍到此结束,但这本书继续以自成体系的章节介绍该语言的其他特性:

Chapter 10, Modules

Shows how JavaScript code in one file or script can use JavaScript functions and classes defined in other files or scripts.

> 演示一个文件或脚本中的 JavaScript 代码如何使用在其他文件或脚本中定义的 JavaScript 函数和类。

Chapter 11, The JavaScript Standard Library

Covers the built-in functions and classes that are available to all JavaScript programs. This includes important data stuctures like maps and sets, a regular expression class for textual pattern matching, functions for serializing JavaScript data structures, and much more.

> 介绍所有 JavaScript 程序可用的内置函数和类。这包括重要的数据结构,如映射和集合、用于文本模式匹配的正则表达式类、用于序列化 JavaScript 数据结构的函数等等。

Chapter 12, Iterators and Generators

Explains how the for/of loop works and how you can make your own classes iterable with for/of. It also covers generator functions and the yield statement.

> 解释 for/of 循环是如何工作的,以及如何使用 for/of 使您自己的类可迭代。它还包括生成器函数和 yield 语句。

Chapter 13, Asynchronous JavaScript

This chapter is an in-depth exploration of asynchronous programming in JavaScript, covering callbacks and events, Promise-based APIs, and the async and await keywords. Although the core JavaScript language is not asynchronous, asynchronous APIs are the default in both web browsers and Node, and this chapter explains the techniques for working with those APIs.

> 本章对 JavaScript 中的异步编程进行了深入的探索,涵盖了回调和事件、基于承诺的 api 以及异步和等待关键字。尽管核心 JavaScript 语言不是异步的,异步 api 在 web 浏览器和节点中都是默认的,本章解释了使用这些 api 的技术。

Chapter 14, Metaprogramming

Introduces a number of advanced features of JavaScript that may be of interest to programmers writing libraries of code for other JavaScript programmers to use.

> 介绍了 JavaScript 的许多高级特性,编写代码库供其他 JavaScript 程序员使用的程序员可能会对这些特性感兴趣。

Chapter 15, JavaScript in Web Browsers

Introduces the web browser host environment, explains how web browsers execute JavaScript code, and covers the most important of the many APIs defined by web browsers. This is by far the longest chapter in the book.

> 介绍 web 浏览器宿主,解释 web 浏览器如何执行 JavaScript 代码,并涵盖 web 浏览器定义的许多 api 中最重要的一个。这是书中最长的一章。

Chapter 16, Server-Side JavaScript with Node

Introduces the Node host environment, covering the fundamental programming model and the data structures and APIs that are most important to understand.

> 介绍 Node 宿主,涵盖基本编程模型以及需要理解的最重要的数据结构和 api。

Chapter 17, JavaScript Tools and Extensions

Covers tools and language extensions that are worth knowing about because they are widely used and may make you a more productive programmer.

> 涵盖了值得了解的工具和语言扩展,因为它们被广泛使用,可能使您成为更有生产力的程序员。

## 1.4 Example: Character Frequency Histograms

This chapter concludes with a short but nontrivial JavaScript program. Example 1-1 is a Node program that reads text from standard input, computes a character frequency histogram from that text, and then prints out the histogram. You could invoke the program like this to analyze the character frequency of its own source code:

> 本章以一个简短但不平凡的 JavaScript 程序结束。示例 1-1 是一个节点程序,它从标准输入中读取文本,根据文本计算字符频率直方图,然后打印该直方图。你可以调用这样的程序来分析字符频率自己的源代码:

```sh
$ node charfreq.js < charfreq.js
T: ########### 11.22%
E: ########## 10.15%
R: ####### 6.68%
S: ###### 6.44%
A: ###### 6.16%
N: ###### 5.81%
O: ##### 5.45%
I: ##### 4.54%
H: #### 4.07%
C: ### 3.36%
L: ### 3.20%
U: ### 3.08%
/: ### 2.88%
```

This example uses a number of advanced JavaScript features and is intended to demonstrate what real-world JavaScript programs can look like. You should not expect to understand all of the code yet, but be assured that all of it will be explained in the chapters that follow.

> 这个示例使用了许多高级 JavaScript 特性,旨在演示真实的 JavaScript 程序是什么样的。你不应该期望理解所有的代码,但是要确保所有的代码将在下面的章节中被解释。

Example 1-1. Computing character frequency histograms with JavaScript

```js
/**
 * This Node program reads text from standard input, computes the frequency
 * of each letter in that text, and displays a histogram of the most
 * frequently used characters. It requires Node 12 or higher to run.
 *
 * In a Unix-type environment you can invoke the program like this:
 *    node charfreq.js < corpus.txt
 */

// This class extends Map so that the get() method returns the specified
// value instead of null when the key is not in the map
class DefaultMap extends Map {
    constructor(defaultValue) {
        super();                          // Invoke superclass constructor
        this.defaultValue = defaultValue; // Remember the default value
    }

    get(key) {
        if (this.has(key)) {              // If the key is already in the map
            return super.get(key);        // return its value from superclass.
        }
        else {
            return this.defaultValue;     // Otherwise return the default value
        }
    }
}

// This class computes and displays letter frequency histograms
class Histogram {
    constructor() {
        this.letterCounts = new DefaultMap(0);  // Map from letters to counts
        this.totalLetters = 0;                  // How many letters in all
    }

    // This function updates the histogram with the letters of text.
    add(text) {
        // Remove whitespace from the text, and convert to upper case
        text = text.replace(/\s/g, "").toUpperCase();

        // Now loop through the characters of the text
        for(let character of text) {
            let count = this.letterCounts.get(character); // Get old count
            this.letterCounts.set(character, count+1);    // Increment it
            this.totalLetters++;
        }
    }

    // Convert the histogram to a string that displays an ASCII graphic
    toString() {
        // Convert the Map to an array of [key,value] arrays
        let entries = [...this.letterCounts];

        // Sort the array by count, then alphabetically
        entries.sort((a,b) => {              // A function to define sort order.
            if (a[1] === b[1]) {             // If the counts are the same
                return a[0] < b[0] ? -1 : 1; // sort alphabetically.
            } else {                         // If the counts differ
                return b[1] - a[1];          // sort by largest count.
            }
        });

        // Convert the counts to percentages
        for(let entry of entries) {
            entry[1] = entry[1] / this.totalLetters*100;
        }

        // Drop any entries less than 1%
        entries = entries.filter(entry => entry[1] >= 1);

        // Now convert each entry to a line of text
        let lines = entries.map(
            ([l,n]) => `${l}: ${"#".repeat(Math.round(n))} ${n.toFixed(2)}%`
        );

        // And return the concatenated lines, separated by newline characters.
        return lines.join("\n");
    }
}

// This async (Promise-returning) function creates a Histogram object,
// asynchronously reads chunks of text from standard input, and adds those chunks to
// the histogram. When it reaches the end of the stream, it returns this histogram
async function histogramFromStdin() {
    process.stdin.setEncoding("utf-8"); // Read Unicode strings, not bytes
    let histogram = new Histogram();
    for await (let chunk of process.stdin) {
        histogram.add(chunk);
    }
    return histogram;
}

// This one final line of code is the main body of the program.
// It makes a Histogram object from standard input, then prints the histogram.
histogramFromStdin().then(histogram => { console.log(histogram.toString()); });
```

## 1.5 Summary

This book explains JavaScript from the bottom up. This means that we start with low-level details like comments, identifiers, variables, and types; then build to expressions, statements, objects, and functions; and then cover high-level language abstractions like classes and modules. I take the word definitive in the title of this book seriously, and the coming chapters explain the language at a level of detail that may feel off-putting at first. True mastery of JavaScript requires an understanding of the details, however, and I hope that you will make time to read this book cover to cover. But please don’t feel that you need to do that on your first reading. If you find yourself feeling bogged down in a section, simply skip to the next. You can come back and master the details once you have a working knowledge of the language as a whole.

> 这本书从下至上地说明了 JavaScript。这意味着我们从底层细节开始,比如注释、标识符、变量和类型;然后构建表达式、语句、对象和函数;然后介绍高级语言抽象,比如类和模块。我在这本书的标题中认真地使用了“权威”这个词,接下来的章节会详细地解释这种语言,一开始可能会让人感到不快。然而,真正掌握 JavaScript 需要了解细节,我希望您能抽出时间从头到尾阅读这本书。但请不要觉得在第一次阅读时就需要这样做。如果你发现自己在某个章节陷入困境,直接跳到下一个章节。一旦你对语言有了整体的应用知识,你可以回过头来掌握细节。

================================================
FILE: content/posts/ch10.md
================================================
---
title: "第 10 章 模块"
date: 2020-11-02T22:18:33+08:00
---

The goal of modular programming is to allow large programs to be assembled using modules of code from disparate authors and sources and for all of that code to run correctly even in the presence of code that the various module authors did not anticipate. As a practical matter, modularity is mostly about encapsulating or hiding private implementation details and keeping the global namespace tidy so that modules cannot accidentally modify the variables, functions, and classes defined by other modules.

> 模块化编程的目标是允许使用来自不同作者和源的代码模块来组装大型程序,并且即使出现了不同模块作者没有预料到的代码,所有这些代码也能正确运行。作为一个实际问题,模块化主要是关于封装或隐藏私有实现细节和保持全局名称空间整洁,以便模块不会意外地修改其他模块定义的变量、函数和类。

Until recently, JavaScript had no built-in support for modules, and programmers working on large code bases did their best to use the weak modularity available through classes, objects, and closures. Closure-based modularity, with support from code-bundling tools, led to a practical form of modularity based on a require() function, which was adopted by Node. require()-based modules are a fundamental part of the Node programming environment but were never adopted as an official part of the JavaScript language. Instead, ES6 defines modules using import and export keywords. Although import and export have been part of the language for years, they were only implemented by web browsers and Node relatively recently. And, as a practical matter, JavaScript modularity still depends on code-bundling tools.

> 直到最近,JavaScript 还没有对模块的内置支持,在大型代码库上工作的程序员尽力使用类、对象和闭包的弱模块性。基于闭包的模块化在代码捆绑工具的支持下,实际使用中形成了一种基于 require() 函数的模块化形式,Node 采用了这种形式。基于 require() 的模块是 Node 编程环境的基本部分,但从未被作为 JavaScript 语言的正式部分采用。相反,ES6 使用 import 和 import 关键字定义模块。虽然 import 和 export 已经成为语言的一部分很多年了,但是它们只是最近才被 web 浏览器和 Node 实现。而且,作为一个实际问题,JavaScript 模块化仍然依赖于代码捆绑工具。

The sections that follow cover:

> 以下各节包括:

- Do-it-yourself modules with classes, objects, and closures
- Node modules using require()
- ES6 modules using export, import, and import()

---

> - 使用类、对象和闭包自己做模块
> - 使用 require() 的 Node 模块
> - 使用 export、import 和 import() 的ES6模块

## 10.1 Modules with Classes, Objects, and Closures

Though it may be obvious, it is worth pointing out that one of the important features of classes is that they act as modules for their methods. Think back to Example 9-8. That example defined a number of different classes, all of which had a method named has(). But you would have no problem writing a program that used multiple set classes from that example: there is no danger that the implementation of has() from SingletonSet will overwrite the has() method of BitSet, for example.

> 尽管这可能很明显,但值得指出的是,类的重要特性之一是它们充当其方法的模块。回想一下示例 9-8。该示例定义了许多不同的类,所有这些类都有一个名为 has() 的方法。但是,在编写使用该示例中的多个 set 类的程序时没有问题:例如,SingletonSet 的 has() 实现不会覆盖 BitSet 的 has() 方法。

The reason that the methods of one class are independent of the methods of other, unrelated classes is that the methods of each class are defined as properties of independent prototype objects. The reason that classes are modular is that objects are modular: defining a property in a JavaScript object is a lot like declaring a variable, but adding properties to objects does not affect the global namespace of a program, nor does it affect the properties of other objects. JavaScript defines quite a few mathematical functions and constants, but instead of defining them all globally, they are grouped as properties of a single global Math object. This same technique could have been used in Example 9-8. Instead of defining global classes with names like SingletonSet and BitSet, that example could have been written to define only a single global Sets object, with properties referencing the various classes. Users of this Sets library could then refer to the classes with names like Sets.Singleton and Sets.Bit.

> 一个类的方法独立于其他不相关类的方法的原因是,每个类的方法都被定义为独立原型对象的属性。类是模块化的原因是对象是模块化的:在 JavaScript 对象中定义属性非常类似于声明变量,但是向对象添加属性不会影响程序的全局命名空间,也不会影响其他对象的属性。JavaScript 定义了很多数学函数和常量,但它们不是全局定义的,而是分组为 Math 全局对象的单个属性。同样的技术也可以用在示例 9-8 中。不使用 SingletonSet 和 BitSet 这样的名称定义全局类,这个示例可以编写为只有一个 Sets 全局对象,Sets 的属性引用各种类。然后,用户可以使用这个 Sets 库通过 Sets.Singleton 和 Sets.Bit 的名称来获取类的引用。

Using classes and objects for modularity is a common and useful technique in JavaScript programming, but it doesn’t go far enough. In particular, it doesn’t offer us any way to hide internal implementation details inside the module. Consider Example 9-8 again. If we were writing that example as a module, maybe we would have wanted to keep the various abstract classes internal to the module, only making the concrete subclasses available to users of the module. Similarly, in the BitSet class, the _valid() and _has() methods are internal utilities that should not really be exposed to users of the class. And BitSet.bits and BitSet.masks are implementation details that would be better off hidden.

> 使用类和对象实现模块化是 JavaScript 编程中常见而有用的技术,但这还不够。特别是,它没有提供任何方法来隐藏模块内部的实现细节。再次考虑示例 9-8。如果我们将该示例作为一个模块来编写,也许我们会希望将各种抽象类保留在模块内部,只让具体的子类对模块的用户可用。同样,在 BitSet 类中,_valid() 和 _has() 方法是内部实用程序,不应该向类的用户公开它们。BitSet.bits 和 BitSet.masks 是实现细节,最好隐藏起来。

As we saw in §8.6, local variables and nested functions declared within a function are private to that function. This means that we can use immediately invoked function expressions to achieve a kind of modularity by leaving the implementation details and utility functions hidden within the enclosing function but making the public API of the module the return value of the function. In the case of the BitSet class, we might structure the module like this:

> 正如我们在 §8.6 中看到的,在函数中声明的局部变量和嵌套函数是该函数私有的。这意味着我们可以使用立即调用函数表达式来实现一种模块化,方法是将实现细节和实用函数隐藏在封装的函数中,而将模块的公共 API 作为函数的返回值。在 BitSet 类的情况下,我们可以像这样构造模块:

```js
const BitSet = (function() { // Set BitSet to the return value of this function
    // Private implementation details here
    function isValid(set, n) { ... }
    function has(set, byte, bit) { ... }
    const BITS = new Uint8Array([1, 2, 4, 8, 16, 32, 64, 128]);
    const MASKS = new Uint8Array([~1, ~2, ~4, ~8, ~16, ~32, ~64, ~128]);

    // The public API of the module is just the BitSet class, which we define
    // and return here. The class can use the private functions and constants
    // defined above, but they will be hidden from users of the class
    return class BitSet extends AbstractWritableSet {
        // ... implementation omitted ...
    };
}());
```

This approach to modularity becomes a little more interesting when the module has more than one item in it. The following code, for example, defines a mini statistics module that exports mean() and stddev() functions while leaving the implementation details hidden:

> 当模块中有多个条目时,这种模块化方法就变得更有趣了。例如,下面的代码定义了一个迷你统计模块,它导出 mean() 和 stddev() 函数,同时隐藏实现细节:

```js
// This is how we could define a stats module
const stats = (function() {
    // Utility functions private to the module
    const sum = (x, y) => x + y;
    const square = x => x * x;

    // A public function that will be exported
    function mean(data) {
        return data.reduce(sum)/data.length;
    }

    // A public function that we will export
    function stddev(data) {
        let m = mean(data);
        return Math.sqrt(
            data.map(x => x - m).map(square).reduce(sum)/(data.length-1)
        );
    }

    // We export the public function as properties of an object
    return { mean, stddev };
}());

// And here is how we might use the module
stats.mean([1, 3, 5, 7, 9])   // => 5
stats.stddev([1, 3, 5, 7, 9]) // => Math.sqrt(10)
```
### 10.1.1 Automating Closure-Based Modularity
Note that it is a fairly mechanical process to transform a file of JavaScript code into this kind of module by inserting some text at the beginning and end of the file. All that is needed is some convention for the file of JavaScript code to indicate which values are to be exported and which are not.

> 请注意,通过在文件的开头和结尾插入一些文本来将 JavaScript 代码文件转换为这种模块是相当机械的过程。所需要做的只是为 JavaScript 代码文件提供一些约定,以指示要导出的值和不导出的值。

Imagine a tool that takes a set of files, wraps the content of each of those files within an immediately invoked function expression, keeps track of the return value of each function, and concatenates everything into one big file. The result might look something like this:

> 想象一下一个工具,它需要一组文件,将每个文件的内容包装在立即调用的函数表达式中,跟踪每个函数的返回值,并将所有内容连接到一个大文件中。结果可能看起来像这样:

```js
const modules = {};
function require(moduleName) { return modules[moduleName]; }

modules["sets.js"] = (function() {
    const exports = {};

    // The contents of the sets.js file go here:
    exports.BitSet = class BitSet { ... };

    return exports;
}());

modules["stats.js"] = (function() {
    const exports = {};

    // The contents of the stats.js file go here:
    const sum = (x, y) => x + y;
    const square = x = > x * x;
    exports.mean = function(data) { ... };
    exports.stddev = function(data) { ... };

    return exports;
}());
```
With modules bundled up into a single file like the one shown in the preceding example, you can imagine writing code like the following to make use of those modules:

> 如上一个示例所示,将模块捆绑到单个文件中,可以想象编写如下代码来利用这些模块:

```js
// Get references to the modules (or the module content) that we need
const stats = require("stats.js");
const BitSet = require("sets.js").BitSet;

// Now write code using those modules
let s = new BitSet(100);
s.insert(10);
s.insert(20);
s.insert(30);
let average = stats.mean([...s]); // average is 20
```
This code is a rough sketch of how code-bundling tools (such as webpack and Parcel) for web browsers work, and it’s also a simple introduction to the require() function like the one used in Node programs.

> 这段代码概述了用于网络浏览器的代码捆绑工具(例如 webpack 和 Parcel)的工作方式,并且是对 require() 函数(如 Node 程序中使用的函数)的简单介绍。

## 10.2 Modules in Node
In Node programming, it is normal to split programs into as many files as seems natural. These files of JavaScript code are assumed to all live on a fast filesystem. Unlike web browsers, which have to read files of JavaScript over a relatively slow network connection, there is no need or benefit to bundling a Node program into a single JavaScript file.

In Node, each file is an independent module with a private namespace. Constants, variables, functions, and classes defined in one file are private to that file unless the file exports them. And values exported by one module are only visible in another module if that module explicitly imports them.

Node modules import other modules with the require() function and export their public API by setting properties of the Exports object or by replacing the module.exportsobject entirely.

### 10.2.1 Node Exports
Node defines a global exports object that is always defined. If you are writing a Node module that exports multiple values, you can simply assign them to the properties of this object:
```js
const sum = (x, y) => x + y;
const square = x => x * x;

exports.mean = data => data.reduce(sum)/data.length;
exports.stddev = function(d) {
    let m = exports.mean(d);
    return Math.sqrt(d.map(x => x - m).map(square).reduce(sum)/(d.length-1));
};
```
Often, however, you want to define a module that exports only a single function or class rather than an object full of functions or classes. To do this, you simply assign the single value you want to export to module.exports:
```js
module.exports = class BitSet extends AbstractWritableSet {
    // implementation omitted
};
```
The default value of module.exports is the same object that exports refers to. In the previous stats module, we could have assigned the mean function to module.exports.mean instead of exports.mean. Another approach with modules like the stats module is to export a single object at the end of the module rather than exporting functions one by one as you go:
```js
// Define all the functions, public and private
const sum = (x, y) => x + y;
const square = x => x * x;
const mean = data => data.reduce(sum)/data.length;
const stddev = d => {
    let m = mean(d);
    return Math.sqrt(d.map(x => x - m).map(square).reduce(sum)/(d.length-1));
};

// Now export only the public ones
module.exports = { mean, stddev };
```
### 10.2.2 Node Imports
A Node module imports another module by calling the require() function. The argument to this function is the name of the module to be imported, and the return value is whatever value (typically a function, class, or object) that module exports.

If you want to import a system module built in to Node or a module that you have installed on your system via a package manager, then you simply use the unqualified name of the module, without any “/” characters that would turn it into a filesystem path:
```js
// These modules are built in to Node
const fs = require("fs");           // The built-in filesystem module
const http = require("http");       // The built-in HTTP module

// The Express HTTP server framework is a third-party module.
// It is not part of Node but has been installed locally
const express = require("express");
```
When you want to import a module of your own code, the module name should be the path to the file that contains that code, relative to the current module’s file. It is legal to use absolute paths that begin with a / character, but typically, when importing modules that are part of your own program, the module names will begin with ./ or sometimes ../ to indicate that they are relative to the current directory or the parent directory. For example:
```js
const stats = require('./stats.js');
const BitSet = require('./utils/bitset.js');
```
(You can also omit the .js suffix on the files you’re importing and Node will still find the files, but it is common to see these file extensions explicitly included.)

When a module exports just a single function or class, all you have to do is require it. When a module exports an object with multiple properties, you have a choice: you can import the entire object, or just import the specific properties (using destructuring assignment) of the object that you plan to use. Compare these two approaches:
```js
// Import the entire stats object, with all of its functions
const stats = require('./stats.js');

// We've got more functions than we need, but they're neatly
// organized into a convenient "stats" namespace.
let average = stats.mean(data);

// Alternatively, we can use idiomatic destructuring assignment to import
// exactly the functions we want directly into the local namespace:
const { stddev } = require('./stats.js');

// This is nice and succinct, though we lose a bit of context
// without the 'stats' prefix as a namspace for the stddev() function.
let sd = stddev(data);
```
### 10.2.3 Node-Style Modules on the Web
Modules with an Exports object and a require() function are built in to Node. But if you’re willing to process your code with a bundling tool like webpack, then it is also possible to use this style of modules for code that is intended to run in web browsers. Until recently, this was a very common thing to do, and you may see lots of web-based code that still does it.

> Node 中内置了带有 Exports 对象和 require() 函数的模块。 但是,如果要使用诸如 webpack 之类的捆绑工具来处理代码,则也可以将这种样式的模块用于要在网络浏览器中运行的代码。直到最近,这还是一种非常常用的做法,可能会看到很多基于 Web 的代码仍然这样做。

Now that JavaScript has its own standard module syntax, however, developers who use bundlers are more likely to use the official JavaScript modules with import and export statements.

> 现在,JavaScript 具有自己的标准模块语法,但是使用捆绑程序的开发人员更喜欢将正式的 JavaScript 模块与 import 和 export 语句一起使用。

## 10.3 Modules in ES6
ES6 adds import and export keywords to JavaScript and finally supports real modularity as a core language feature. ES6 modularity is conceptually the same as Node modularity: each file is its own module, and constants, variables, functions, and classes defined within a file are private to that module unless they are explicitly exported. Values that are exported from one module are available for use in modules that explicitly import them. ES6 modules differ from Node modules in the syntax used for exporting and importing and also in the way that modules are defined in web browsers. The sections that follow explain these things in detail.

> ES6 给 JavaScript 添加了 import 和 export 关键字,并且最终支持真正的模块化,将其作为核心语言特性。ES6 模块化概念上和 Node 的模块化相同:每个文件是它们自己的模块,定义在文件中的常量、变量、函数和类是模块私有成员,除非它们是被显示导出。

First, though, note that ES6 modules are also different from regular JavaScript “scripts” in some important ways. The most obvious difference is the modularity itself: in regular scripts, top-level declarations of variables, functions, and classes go into a single global context shared by all scripts. With modules, each file has its own private context and can use the import and export statements, which is the whole point, after all. But there are other differences between modules and scripts as well. Code inside an ES6 module (like code inside any ES6 class definition) is automatically in strict mode (see §5.6.3). This means that, when you start using ES6 modules, you’ll never have to write "use strict" again. And it means that code in modules cannot use the with statement or the arguments object or undeclared variables. ES6 modules are even slightly stricter than strict mode: in strict mode, in functions invoked as functions, this is undefined. In modules, this is undefined even in top-level code. (By contrast, scripts in web browsers and Node set this to the global object.)

> 首先,请注意,ES6 模块在某些重要方面也与常规 JavaScript “脚本”不同。最明显的区别是模块化本身:在常规脚本中,变量,函数和类的顶级声明在所有脚本共享的一个全局上下文中。模块每个文件都有其自己的专用上下文,并且可以使用 import 和 export 语句。但是模块和脚本之间也存在其他差异。ES6 模块内的代码(类似 ES6 类内定义的代码)将自动进入严格模式(请参见 §5.6.3)。这意味着,当使用 ES6 模块时,无需再写“use strict”。这意味着模块中的代码不能使用 with 语句或 arguments 对象或未声明的变量。ES6 模块甚至比严格模式稍微严格:在严格模式下,在作为函数调用的函数中,this 是 undefined。在模块中,this 在顶层代码中也是 undefined。(相比之下,Web 浏览器和 Node 中的脚本将 this 设置为全局对象。)

#### ES6 MODULES ON THE WEB AND IN NODE
ES6 modules have been in use on the web for years with the help of code bundlers like webpack, which combine independent modules of JavaScript code into large, non-modular bundles suitable for inclusion into web pages. At the time of this writing, however, ES6 modules are finally supported natively by all web browsers other than Internet Explorer. When used natively, ES6 modules are added into HTML pages with a special `<script type="module">` tag, described later in this chapter.

> ES6模块在诸如 webpack 之类的代码打包器的帮助下已在 Web 上使用多年,该打包器将 JavaScript 代码的独立模块组合成适合于包含在网页中的大型非模块化捆绑包。但是,在撰写本文时,除 Internet Explorer 之外,所有 Web 浏览器都支持 ES6 模块。本地使用时,ES6 模块通过特殊的 `<script type="module">` 标签添加到HTML页面中,本章稍后将进行介绍。

And meanwhile, having pioneered JavaScript modularity, Node finds itself in the awkward position of having to support two not entirely compatible module systems. Node 13 supports ES6 modules, but for now, the vast majority of Node programs still use Node modules.

> 同时,由于拥有 JavaScript 模块化的先驱,Node 处于必须支持两个不完全兼容的模块系统的尴尬境地。Node 13 支持 ES6 模块,但是到目前为止,绝大多数 Node 程序仍在使用 Node 模块。

### 10.3.1 ES6 Exports
To export a constant, variable, function, or class from an ES6 module, simply add the keyword export before the declaration:

> 要从 ES6 模块导出常量,变量,函数或类,只需在声明之前添加关键字 export:

```js
export const PI = Math.PI;

export function degreesToRadians(d) { return d * PI / 180; }

export class Circle {
    constructor(r) { this.r = r; }
    area() { return PI * this.r * this.r; }
}
```
As an alternative to scattering export keywords throughout your module, you can define your constants, variables, functions, and classes as you normally would, with no export statement, and then (typically at the end of your module) write a single export statement that declares exactly what is exported in a single place. So instead of writing three individual exports in the preceding code, we could have equivalently written a single line at the end:

> 作为在整个模块中散播 export 关键字的替代方法,可以像平常一样定义常量、变量、函数和类,无需导出语句,然后(通常在模块末尾)编写一个导出语句,在一个位置精确的声明要导出的所有内容。因此,我们可以在末尾写入一行代码,而不是在上述代码中编写三个单独的导出:

```js
export { Circle, degreesToRadians, PI };
```
This syntax looks like the export keyword followed by an object literal (using shorthand notation). But in this case, the curly braces do not actually define an object literal. This export syntax simply requires a comma-separated list of identifiers within curly braces.

> 这种语法看起来像导出关键字,后跟对象字面量(使用速记符号)。但在这种情况下,花括号实际上并不是定义对象字面量。此导出语法只需要在大括号中的标识符列表用逗号分隔。

It is common to write modules that export only one value (typically a function or class), and in this case, we usually use export default instead of export:

> 编写只导出一个值(通常是一个函数或类)的模块是很常见的,在这种情况下,我们通常使用 export default 而不是 export:

```js
export default class BitSet {
    // implementation omitted
}
```
Default exports are slightly easier to import than non-default exports, so when there is only one exported value, using export default makes things easier for the modules that use your exported value.

> 默认导出比非默认导出更容易导入,因此,当只有一个导出值时,使用默认导出可以使模块的使用更容易。

Regular exports with export can only be done on declarations that have a name. Default exports with export default can export any expression including anonymous function expressions and anonymous class expressions. This means that if you use export default, you can export object literals. So unlike the export syntax, if you see curly braces after export default, it really is an object literal that is being exported.

> 带 export 的常规导出只能在具有名称的声明上进行。export default 默认导出可以导出任何表达式,包括匿名函数表达式和匿名类表达式。这意味着,如果使用默认导出,则可以导出对象字面量。因此,与导出语法不同,如果在默认导出后看到花括号,则它实际上是要导出的对象字面量。

It is legal, but somewhat uncommon, for modules to have a set of regular exports and also a default export. If a module has a default export, it can only have one.

> 同时有 export 和 export default 的模块是合法的,但是不常用。如果模块的默认导出最多仅有一个。

Finally, note that the export keyword can only appear at the top level of your JavaScript code. You may not export a value from within a class, function, loop, or conditional. (This is an important feature of the ES6 module system and enables static analysis: a modules export will be the same on every run, and the symbols exported can be determined before the module is actually run.)

> 最后,请注意 export 关键字只能出现在 JavaScript 代码的顶层。 不能从类,函数,循环或条件内导出值。(这是 ES6 模块系统的重要特性,并且可以进行静态分析:每次运行时模块导出都是相同的,并且可以在模块实际运行之前确定导出的符号。)

### 10.3.2 ES6 Imports
You import values that have been exported by other modules with the import keyword. The simplest form of import is used for modules that define a default export:

> 使用 import 关键字导入其他模块导出的值。最简单的导入形式是导入默认导出定义的模块:

```js
import BitSet from './bitset.js';
```
This is the import keyword, followed by an identifier, followed by the from keyword, followed by a string literal that names the module whose default export we are importing. The default export value of the specified module becomes the value of the specified identifier in the current module.

> import 关键字后面跟一个标识符,然后是 from 关键字,后接要导入的默认导出字符串字面量模块名称。模块指定的默认导出值将编程当前模块内指定的标识符的值。

The identifier to which the imported value is assigned is a constant, as if it had been declared with the const keyword. Like exports, imports can only appear at the top level of a module and are not allowed within classes, functions, loops, or conditionals. By near-universal convention, the imports needed by a module are placed at the start of the module. Interestingly, however, this is not required: like function declarations, imports are “hoisted” to the top, and all imported values are available for any of the module’s code runs.

> 赋导入值的标识符是一个常量,就像它使用了 const 关键字声明一样。和导出一样,只能在模块的顶层导入,不能在类、函数、循环或条件中导入。根据近似通用规约,需要在模块的开头进行模块导入。但有趣的是,这不是必须的:就像函数声明,导入“提前”到顶部,所有的导入值在整个模块代码中运行时可以使用。

The module from which a value is imported is specified as a constant string literal in single quotes or double quotes. (You may not use a variable or other expression whose value is a string, and you may not use a string within backticks because template literals can interpolate variables and do not always have constant values.) In web browsers, this string is interpreted as a URL relative to the location of the module that is doing the importing. (In Node, or when using a bundling tool, the string is interpreted as a filename relative to the current module, but this makes little difference in practice.) A module specifier string must be an absolute path starting with “/”, or a relative path starting with “./” or “../”, or a complete URL a with protocol and hostname. The ES6 specification does not allow unqualified module specifier strings like “util.js” because it is ambiguous whether this is intended to name a module in the same directory as the current one or some kind of system module that is installed in some special location. (This restriction against “bare module specifiers” is not honored by code-bundling tools like webpack, which can easily be configured to find bare modules in a library directory that you specify.) A future version of the language may allow “bare module specifiers,” but for now, they are not allowed. If you want to import a module from the same directory as the current one, simply place “./” before the module name and import from “./util.js” instead of “util.js”.

> 被导入的模块用单引号或双引号括上的字符串字面量常量指定。(不可以使用变量或其他值为字符串的表达式,也不可以使用带反引号的字符串,因为模板字面量可以插入变量,它不总是一个常量值。)在 Web 浏览器中,字符串像 URL 一样描述导入模块的相对位置。(在 Node中,或者使用代码捆绑工具时,字符串描述一个对于当前模块的相对文件名,这在事件中略有不同。)以“/”开始的模块指定字符串时绝对路径,“./”和“../”开头的是相对路径,或者是带有协议和主机名的完整 URL。ES6 明确规定不允许使用诸如“util.js”这样的不合规范的字符串,因为它是否是相对路径还是绝对路径上有歧义。(诸如 webpack 之类的代码捆绑工具不能满足对“裸模块说明符”的这种限制,可以将其轻松配置为在指定的库中查找裸模块。)未来版本 JavaScript 可能允许使用“裸模块说明符”,但目前不允许这样做。 如果要从与当前目录相同的目录中导入模块,只需在模块名称前放置“./”,然后从“./util.js”而不是“ util.js”导入。

So far, we’ve only considered the case of importing a single value from a module that uses export default. To import values from a module that exports multiple values, we use a slightly different syntax:

> 到目前为止,我们仅考虑了从使用默认导出的模块中导入单个值的情况。要从导出多个值的模块导入值,我们使用略有不同的语法:

```js
import { mean, stddev } from "./stats.js";
```
Recall that default exports do not need to have a name in the module that defines them. Instead, we provide a local name when we import those values. But non-default exports of a module do have names in the exporting module, and when we import those values, we refer to them by those names. The exporting module can export any number of named value. An import statement that references that module can import any subset of those values simply by listing their names within curly braces. The curly braces make this kind of import statement look something like a destructuring assignment, and destructuring assignment is actually a good analogy for what this style of import is doing. The identifiers within curly braces are all hoisted to the top of the importing module and behave like constants.

> 回想一下,默认导出不需要在定义它们的模块中具有名称。而是在导入这些值时提供一个本地名称。但是,模块的非默认导出在导出模块中具有名称,并且当我们导入这些值时,我们通过这些名称来引用它们。导出模块可以导出任意数量的命名值。导入语句引用模块,可以简单地通过在花括号中列出它们的名称来导入这些名称对应的值的任何子集。花括号使这种导入语句看起来像是一个解构赋值,而解构赋值实际上是很好的案例来解释导入的这种风格。花括号中的标识符都被提前到导入模块的顶部,并且行为如同常量。

Style guides sometimes recommend that you explicitly import every symbol that your module will use. When importing from a module that defines many exports, however, you can easily import everything with an import statement like this:

> 有时,风格指南建议显示导入模块将使用的每个符号。但是,从定义许多导出的模块进行导入时,可以使用如下的 import 语句轻松导入所有内容:

```js
import * as stats from "./stats.js";
```
An import statement like this creates an object and assigns it to a constant named stats. Each of the non-default exports of the module being imported becomes a property of this stats object. Non-default exports always have names, and those are used as property names within the object. Those properties are effectively constants: they cannot be overwritten or deleted. With the wildcard import shown in the previous example, the importing module would use the imported mean() and stddev() functions through the stats object, invoking them as stats.mean() and stats.stddev().

> 像这样的导入语句创建一个对象并将其赋值给一个名为 stats 的常量。模块中每一个非默认导出被导入成 stats 对象的一个属性。非默认导出始终有名称,在对象中它们被用作属性名。那些属性实际上是常量:它们不能被重写或删除。通过前面例子中展示的通配符导入,导入的模块通过 stats 对象使用 mean() 和 stddev() 函数,用 stats.mean() 和 stats.stddev() 来调用它们。

Modules typically define either one default export or multiple named exports. It is legal, but somewhat uncommon, for a module to use both export and export default. But when a module does that, you can import both the default value and the named values with an import statement like this:

> 模块通常定义一个默认导出或者多个命名导出。模块内同时使用导出和默认导出是合法的,但是不常见。但是当一个模块这样做了,可以通过下面这种方式将默认值和命名值通过一个导入语句导入:

```js
import Histogram, { mean, stddev } from "./histogram-stats.js";
```
So far, we’ve seen how to import from modules with a default export and from modules with non-default or named exports. But there is one other form of the import statement that is used with modules that have no exports at all. To include a no-exports module into your program, simply use the import keyword with the module specifier:

> 到目前为止,我们已经了解了如何从具有默认导出的模块以及具有非默认或命名导出的模块导入。但是 import 语句还有另一种形式,可以用于没有导出的模块。要将无导出模块包含到程序中,只需使用 import 关键字和模块说明符:

```js
import "./analytics.js";
```
A module like this runs the first time it is imported. (And subsequent imports do nothing.) A module that just defines functions is only useful if it exports at least one of those functions. But if a module runs some code, then it can be useful to import even without symbols. An analytics module for a web application might run code to register various event handlers and then use those event handlers to send telemetry data back to the server at appropriate times. The module is self-contained and does not need to export anything, but we still need to import it so that it does actually run as part of our program.

> 这样的模块在首次运行时导入。(并且随后的导入不执行任何操作。)仅定义函数的模块只有在导出至少一个函数时才有用。但是,如果模块运行一些代码,那么即使没有符号也可以导入。Web 应用程序的分析模块可能会运行代码来注册各种事件处理器,然后使用这些事件处理程序在适当的时间将遥测数据发送回服务器。该模块是独立的,不需要导出任何东西,但是我们仍然需要导入它,以便它实际上可以作为程序的一部分运行。

Note that you can use this import-nothing import syntax even with modules that do have exports. If a module defines useful behavior independent of the values it exports, and if your program does not need any of those exported values, you can still import the module . just for that default behavior.

> 请注意,即使对于具有导出的模块,也可以使用不导入任何内容的导入语法。如果模块定义的有用行为与导出的值无关,并且程序不需要任何导出的值,则仍可以用 . 导入模块,这仅用于默认导出。

### 10.3.3 Imports and Exports with Renaming
If two modules export two different values using the same name and you want to import both of those values, you will have to rename one or both of the values when you import it. Similarly, if you want to import a value whose name is already in use in your module, you will need to rename the imported value. You can use the as keyword with named imports to rename them as you import them:

> 如果两个模块使用相同的名称导出两个不同的值,并且要导入这两个值,则在导入时必须重命名一个或两个值。同样,如果模块要导入值的名称已在模块中使用,则需要重命名导入的值。可以在命名导入中使用 as 关键字,以在导入它们时重命名它们:

```js
import { render as renderImage } from "./imageutils.js";
import { render as renderUI } from "./ui.js";
```
These lines import two functions into the current module. The functions are both named render() in the modules that define them but are imported with the more descriptive and disambiguating names renderImage() and renderUI().

> 这些行将两个功能导入当前模块。这些函数在定义它们的模块中都被命名为 render(),但使用更具描述性和消除歧义性的 renderImage() 和 renderUI() 名称导入。

Recall that default exports do not have a name. The importing module always chooses the name when importing a default export. So there is no need for a special syntax for renaming in that case.

> 回想一下,默认导出没有名称。导入默认导出时,导入模块始终选择名称。因此,在这种情况下,不需要特殊的语法来重命名。

Having said that, however, the possibility of renaming on import provides another way of importing from modules that define both a default export and named exports. Recall the “./histogram-stats.js” module from the previous section. Here is another way to import both the default and named exports of that module:

> 话虽如此,导入重命名的可能性提供另外一种导入,从模块导入同时定义默认导出和命名导出。回顾上一节中的“ ./histogram-stats.js”模块。这是同时导入该模块的默认导出和命名导出的另一种方法:

```js
import { default as Histogram, mean, stddev } from "./histogram-stats.js";
```
In this case, the JavaScript keyword default serves as a placeholder and allows us to indicate that we want to import and provide a name for the default export of the module.

> 在这种情况下,JavaScript 关键字 default 用作占位符,并允许我们指示我们要导入并为模块的默认导出提供名称。

It is also possible to rename values as you export them, but only when using the curly brace variant of the export statement. It is not common to need to do this, but if you chose short, succinct names for use inside your module, you might prefer to export your values with more descriptive names that are less likely to conflict with other modules. As with imports, you use the as keyword to do this:

> 也可以在导出时重命名值,但仅在使用 export 语句的花括号时才可以。这样做通常并不常见,但是如果选择在模块内部使用简短的简短名称,则可能更喜欢使用描述性较强的名称导出值,这些名称不太可能与其他模块发生冲突。与导入一样,可以使用 as 关键字执行此操作:

```js
export {
    layout as calculateLayout,
    render as renderLayout
};
```
Keep in mind that, although the curly braces look something like object literals, they are not, and the export keyword expects a single identifier before the as, not an expression. This means, unfortunately, that you cannot use export renaming like this:

> 请记住,尽管花括号看起来像对象字面量,但实际上并非如此,并且 export 关键字期望在 as 之前有一个标识符,而不是表达式。不幸的是,这意味着不能像这样使用导出重命名:

```js
export { Math.sin as sin, Math.cos as cos }; // SyntaxError
```
### 10.3.4 Re-Exports
Throughout this chapter, we’ve discussed a hypothetical “./stats.js” module that exports mean() and stddev() functions. If we were writing such a module and we thought that many users of the module would want only one function or the other, then we might want to define mean() in a “./stats/mean.js” module and define stddev() in “./stats/stddev.js”. That way, programs only need to import exactly the functions they need and are not bloated by importing code they do not need.

> 在本章中,我们讨论了一个假定的“./stats.js”模块,该模块导出了 mean() 和 stddev() 函数。如果我们正在编写这样一个模块,并且该模块的用户只想要一个函数或另一个函数,那么我们可能想在“./stats/mean.js”模块中定义 mean(),并定义在“./stats/stddev.js”模块中的 stddev()。这样,程序仅需要完全导入所需的功能,而不会因导入不需要的代码而肿。

Even if we had defined these statistical functions in individual modules, however, we might expect that there would be plenty of programs that want both functions and would appreciate a convenient “./stats.js” module from which they could import both on one line.

> 但是,即使我们在单个模块中定义了这些统计函数,我们也可能有很多程序需要这两个功能,并且希望使用一行代码方便的导入“./stats.js”模块。

Given that the implementations are now in separate files, defining this “./stat.js” module is simple:

> 鉴于实现现在位于单独的文件中,因此定义这样的“./stat.js”模块会非常简单:

```js
import { mean } from "./stats/mean.js";
import { stddev } from "./stats/stddev.js";
export { mean, stdev };
```
ES6 modules anticipate this use case and provide a special syntax for it. Instead of importing a symbol simply to export it again, you can combine the import and the export steps into a single “re-export” statement that uses the export keyword and the from keyword:

> ES6 模块预见了这种使用场景,并为此提供了一种特殊的语法。可以使用 export 和 from 关键字合并导入和导出到一个单独的“再导出”语句中,而不是简单地再次导入即可导入符号:

```js
export { mean } from "./stats/mean.js";
export { stddev } from "./stats/stddev.js";
```
Note that the names mean and stddev are not actually used in this code. If we are not being selective with a re-export and simply want to export all of the named values from another module, we can use a wildcard:

> 请注意,此代码中并未实际使用名称 mean 和 stddev。如果我们不选择再导出,而只是想从另一个模块中导出所有命名值,则可以使用通配符:

```js
export * from "./stats/mean.js";
export * from "./stats/stddev.js";
```
Re-export syntax allows renaming with as just as regular import and export statements do. Suppose we wanted to re-export the mean() function but also define average() as another name for the function. We could do that like this:

> 再导出语法允许重命名,就像常规的导入和导出语句一样。 假设我们要再导出 mean() 函数,但还要将该函数定义为 average()。 我们可以这样做:

```js
export { mean, mean as average } from "./stats/mean.js";
export { stddev } from "./stats/stddev.js";
```
All of the re-exports in this example assume that the “./stats/mean.js” and “./stats/stddev.js” modules export their functions using export instead of export default. In fact, however, since these are modules with only a single export, it would have made sense to define them with export default. If we had done so, then the re-export syntax is a little more complicated because it needs to define a name for the unnamed default exports. We can do that like this:

> 所有的再导出示例中,“./stats/mean.js”和“./stats/stddev.js”模块都使用导出而不是默认导出来导出它们的函数。但是,实际上,由于这些模块仅具有单个导出,因此使用导出默认定义它们是更明智的。如果我们这样做,那么再导出语法会稍微复杂一点,因为它需要为未命名的默认导出定义一个名称。 我们可以这样做:

```js
export { default as mean } from "./stats/mean.js";
export { default as stddev } from "./stats/stddev.js";
```
If you want to re-export a named symbol from another module as the default export of your module, you could do an import followed by an export default, or you could combine the two statements like this:

> 如果要从另一个模块中再导出命名符号作为模块的默认导出,则可以先进行导入,然后再默认导出,或者可以将以下两个语句组合在一起:

```js
// Import the mean() function from ./stats.js and make it the
// default export of this module
export { mean as default } from "./stats.js"
```
And finally, to re-export the default export of another module as the default export of your module (though it is unclear why you would want to do this, since users could simply import the other module directly), you can write:

> 最后,要将另一个模块的默认导出再导出为模块的默认导出(尽管不清楚为什么要这样做,因为用户可以直接导入另一个模块),可以这样编写:

```js
// The average.js module simply re-exports the stats/mean.js default export
export { default } from "./stats/mean.js"
```
### 10.3.5 JavaScript Modules on the Web
The preceding sections have described ES6 modules and their import and export declarations in a somewhat abstract manner. In this section and the next, we’ll be discussing how they actually work in web browsers, and if you are not already an experienced web developer, you may find the rest of this chapter easier to understand after you have read Chapter 15.

> 前面的章节以某种抽象的方式描述了 ES6 模块及其导入和导出声明。在本节和下一部分中,我们将讨论它们在 Web 浏览器中的实际工作方式,如果您还不是经验丰富的 Web 开发人员,则在阅读第 15 章之后,可能会发现本章的其余部分更容易理解。

As of early 2020, production code using ES6 modules is still generally bundled with a tool like webpack. There are trade-offs to doing this, [^1] but on the whole, code bundling tends to give better performance. That may well change in the future as network speeds grow and browser vendors continue to optimize their ES6 module implementations.

> 截至 2020 年初,使用 ES6 模块的生产代码通常仍与 webpack 之类的工具捆绑在一起。 这样做是有折衷的 [^1],但是总的来说,代码捆绑往往会提供更好的性能。随着网络速度的增长以及浏览器供应商继续优化其 ES6 模块的实现,将来这种情况可能会发生很大的变化。

Even though bundling tools may still be desirable in production, they are no longer required in development since all current browsers provide native support for JavaScript modules. Recall that modules use strict mode by default, this does not refer to a global object, and top-level declarations are not shared globally by default. Since modules must be executed differently than legacy non-module code, their introduction requires changes to HTML as well as JavaScript. If you want to natively use import directives in a web browser, you must tell the web browser that your code is a module by using a `<script type="module">` tag.

> 即使在生产中仍可能需要捆绑工具,但由于当前所有的浏览器都为 JavaScript 模块提供了本机支持,因此在开发中不再需要捆绑工具。回想一下,默认情况下模块使用严格模式,this 不引用全局对象,并且顶级声明默认不全局共享。由于模块的执行方式必须不同于传统的非模块代码,因此其引入 requires 对 HTML 和 JavaScript 进行更改。如果要在 Web 浏览器本地使用 import 指令,则必须通过使用 `<script type =“ module”>` 标记来告知 Web 浏览器您的代码是模块。

One of the nice features of ES6 modules is that each module has a static set of imports. So given a single starting module, a web browser can load all of its imported modules and then load all of the modules imported by that first batch of modules, and so on, until a complete program has been loaded. We’ve seen that the module specifier in an import statement can be treated as a relative URL. A `<script type="module">` tag marks the starting point of a modular program. None of the modules it imports are expected to be in `<script>` tags, however: instead, they are loaded on demand as regular JavaScript files and are executed in strict mode as regular ES6 modules. Using a `<script type="module">` tag to define the main entry point for a modular JavaScript program can be as simple as this:
```js
<script type="module">import "./main.js";</script>
```
Code inside an inline `<script type="module">` tag is an ES6 module, and as such can use the export statement. There is not any point in doing so, however, because the HTML `<script>` tag syntax does not provide any way to define a name for inline modules, so even if such a module does export a value, there is no way for another module to import it.

Scripts with the type="module" attribute are loaded and executed like scripts with the defer attribute. Loading of the code begins as soon as the HTML parser encounters the `<script>` tag (in the case of modules, this code-loading step may be a recursive process that loads multiple JavaScript files). But code execution does not begin until HTML parsing is complete. And once HTML parsing is complete, scripts (both modular and non) are executed in the order in which they appear in the HTML document.

You can modify the execution time of modules with the async attribute, which works the same way for modules that it does for regular scripts. An async module will execute as soon as the code is loaded, even if HTML parsing is not complete and even if this changes the relative ordering of the scripts.

Web browsers that support `<script type="module">` must also support `<script nomodule>`. Browsers that are module-aware ignore any script with the nomodule attribute and will not execute it. Browsers that do not support modules will not recognize the nomodule attribute, so they will ignore it and run the script. This provides a powerful technique for dealing with browser compatibility issues. Browsers that support ES6 modules also support other modern JavaScript features like classes, arrow functions, and the for/of loop. If you write modern JavaScript and load it with `<script type="module">`, you know that it will only be loaded by browsers that can support it. And as a fallback for IE11 (which, in 2020, is effectively the only remaining browser that does not support ES6), you can use tools like Babel and webpack to transform your code into non-modular ES5 code, then load that less-efficient transformed code via `<script nomodule>`.

Another important difference between regular scripts and module scripts has to do with cross-origin loading. A regular `<script>` tag will load a file of JavaScript code from any server on the internet, and the internet’s infrastructure of advertising, analytics, and tracking code depends on that fact. But `<script type="module">` provides an opportunity to tighten this up, and modules can only be loaded from the same origin as the containing HTML document or when proper CORS headers are in place to securely allow cross-origin loads. An unfortunate side effect of this new security restriction is that it makes it difficult to test ES6 modules in development mode using file: URLs. When using ES6 modules, you will likely need to set up a static web server for testing.

Some programmers like to use the filename extension .mjs to distinguish their modular JavaScript files from their regular, non-modular JavaScript files with the traditional .js extension. For the purposes of web browsers and `<script>` tags, the file extension is actually irrelevant. (The MIME type is relevant, however, so if you use .mjs files, you may need to configure your web server to serve them with the same MIME type as .js files.) Node’s support for ES6 does use the filename extension as a hint to distinguish which module system is used by each file it loads. So if you are writing ES6 modules and want them to be usable with Node, then it may be helpful to adopt the .mjs naming convention.

### 10.3.6 Dynamic Imports with import()
We’ve seen that the ES6 import and export directives are completely static and enable JavaScript interpreters and other JavaScript tools to determine the relationships between modules with simple text analysis while the modules are being loaded without having to actually execute any of the code in the modules. With statically imported modules, you are guaranteed that the values you import into a module will be ready for use before any of the code in your module begins to run.

On the web, code has to be transferred over a network instead of being read from the filesystem. And once transfered, that code is often executed on mobile devices with relatively slow CPUs. This is not the kind of environment where static module imports—which require an entire program to be loaded before any of it runs—make a lot of sense.

It is common for web applications to initially load only enough of their code to render the first page displayed to the user. Then, once the user has some preliminary content to interact with, they can begin to load the often much larger amount of code needed for the rest of the web app. Web browsers make it easy to dynamically load code by using the DOM API to inject a new `<script>` tag into the current HTML document, and web apps have been doing this for many years.

Although dynamic loading has been possible for a long time, it has not been part of the language itself. That changes with the introduction of import() in ES2020 (as of early 2020, dynamic import is supported by all browsers that support ES6 modules). You pass a module specifier to import() and it returns a Promise object that represents the asynchronous process of loading and running the specified module. When the dynamic import is complete, the Promise is “fulfilled” (see Chapter 13 for complete details on asynchronous programming and Promises) and produces an object like the one you would get with the import * as form of the static import statement.

So instead of importing the “./stats.js” module statically, like this:
```js
import * as stats from "./stats.js";
we might import it and use it dynamically, like this:

import("./stats.js").then(stats => {
    let average = stats.mean(data);
})
```
Or, in an async function (again, you may need to read Chapter 13 before you’ll understand this code), we can simplify the code with await:
```js
async analyzeData(data) {
    let stats = await import("./stats.js");
    return {
        average: stats.mean(data),
        stddev: stats.stddev(data)
    };
}
```
The argument to import() should be a module specifier, exactly like one you’d use with a static import directive. But with import(), you are not constrained to use a constant string literal: any expression that evaluates to a string in the proper form will do.

Dynamic import() looks like a function invocation, but it actually is not. Instead, import() is an operator and the parentheses are a required part of the operator syntax. The reason for this unusual bit of syntax is that import() needs to be able to resolve module specifiers as URLs relative to the currently running module, and this requires a bit of implementation magic that would not be legal to put in a JavaScript function. The function versus operator distinction rarely makes a difference in practice, but you’ll notice it if you try writing code like console.log(import); or let require = import;.

Finally, note that dynamic import() is not just for web browsers. Code-packaging tools like webpack can also make good use of it. The most straightforward way to use a code bundler is to tell it the main entry point for your program and let it find all the static import directives and assemble everything into one large file. By strategically using dynamic import() calls, however, you can break that one monolithic bundle up into a set of smaller bundles that can be loaded on demand.

### 10.3.7 import.meta.url
There is one final feature of the ES6 module system to discuss. Within an ES6 module (but not within a regular `<script> `or a Node module loaded with require()), the special syntax import.meta refers to an object that contains metadata about the currently executing module. The url property of this object is the URL from which the module was loaded. (In Node, this will be a file:// URL.)

The primary use case of import.meta.url is to be able to refer to images, data files, or other resources that are stored in the same directory as (or relative to) the module. The URL() constructor makes it easy to resolve a relative URL against an absolute URL like import.meta.url. Suppose, for example, that you have written a module that includes strings that need to be localized and that the localization files are stored in an l10n/ directory, which is in the same directory as the module itself. Your module could load its strings using a URL created with a function, like this:
```js
function localStringsURL(locale) {
    return new URL(`l10n/${locale}.json`, import.meta.url);
}
```
## 10.4 Summary
The goal of modularity is to allow programmers to hide the implementation details of their code so that chunks of code from various sources can be assembled into large programs without worrying that one chunk will overwrite functions or variables of another. This chapter has explained three different JavaScript module systems:

- In the early days of JavaScript, modularity could only be achieved through the clever use of immediately invoked function expressions.
- Node added its own module system on top of the JavaScript language. Node modules are imported with require() and define their exports by setting properties of the Exports object, or by setting the module.exports property.
- In ES6, JavaScript finally got its own module system with import and export keywords, and ES2020 is adding support for dynamic imports with import().

---

1. For example: web apps that have frequent incremental updates and users who make frequent return visits may find that using small modules instead of large bundles can result in better average load times because of better utilization of the user’s browser cache.

================================================
FILE: content/posts/ch11.md
================================================
---
title: "第 11 章 JavaScript 标准库"
date: 2020-11-02T22:18:32+08:00
---

Some datatypes, such as numbers and strings (Chapter 3), objects (Chapter 6), and arrays (Chapter 7) are so fundamental to JavaScript that we can consider them to be part of the language itself. This chapter covers other important but less fundamental APIs that can be thought of as defining the “standard library” for JavaScript: these are useful classes and functions that are built in to JavaScript and available to all JavaScript programs in both web browsers and in Node.[^1]

> 一些数据类型,如数字和字符串(第 3 章)、对象(第 6 章)和数组(第 7 章)是 JavaScript 的基础,我们可以将它们视为语言本身的一部分。这一章涵盖了其他重要但不那么基础的 api,这些 api 可以被认为是定义 JavaScript 的“标准库”:这些是 JavaScript 内建的有用的类和函数,在 web 浏览器和 Node 中的所有 JavaScript 程序中都可以使用。 [^1]

The sections of this chapter are independent of one another, and you can read them in any order. They cover:

> 本章的各个章节是相互独立的,你可以以任何顺序阅读它们。他们包括:

- The Set and Map classes for representing sets of values and mappings from one set of values to another set of values.
- Array-like objects known as TypedArrays that represent arrays of binary data, along with a related class for extracting values from non-array binary data.
- Regular expressions and the RegExp class, which define textual patterns and are useful for text processing. This section also covers regular expression syntax in detail.
- The Date class for representing and manipulating dates and times.
- The Error class and its various subclasses, instances of which are thrown when errors occur in JavaScript programs.
- The JSON object, whose methods support serialization and deserialization of JavaScript data structures composed of objects, arrays, strings, numbers, and booleans.
- The Intl object and the classes it defines that can help you localize your JavaScript programs.
- The Console object, whose methods output strings in ways that are particularly useful for debugging programs and logging the behavior of those programs.
- The URL class, which simplifies the task of parsing and manipulating URLs. This section also covers global functions for encoding and decoding URLs and their component parts.
- setTimeout() and related functions for specifying code to be executed after a specified interval of time has elapsed.

---

> - Set 和 Map 类,用于表示一组值以及从一组值到另一组值的映射。
> - 类数组对象,称为 TypedArrays,表示二进制数据的数组,以及从非数组二进制数据中提取值的相关类。
> - 正则表达式和 RegExp 类,它们定义文本模型,对文本处理很有用。本节还详细介绍了正则表达式语法。
> - 用于表示和操作日期和时间的 Date 类。
> - Error 类及其各种子类,在 JavaScript 程序中发生错误时抛出它们的实例。
> - JSON 对象,其方法支持由对象、数组、字符串、数字和布尔值组成的 JavaScript 数据结构的序列化和反序列化。
> - Intl 对象和它定义的类可以帮助你本地化你的 JavaScript 程序。
> - Console 对象,其方法输出字符串的方式对调试程序和记录这些程序的行为特别有用。
> - URL 类,它简化了解析和操纵 URL 的任务。本节还介绍了用于编码和解码 url 及其组件的全局函数。
> - setTimeout() 和相关函数,用于指定在指定的时间间隔后执行的代码。

Some of the sections in this chapter—notably, the sections on typed arrays and regular expressions—are quite long because there is significant background information you need to understand before you can use those types effectively. Many of the other sections, however, are short: they simply introduce a new API and show some examples of its use.

> 本章中的一些部分(特别是关于类型化数组和正则表达式的部分)相当长,因为在有效使用这些类型之前,需要了解它们重要的背景信息。但是,其他部分都很简短:它们只是介绍了一个新的 API 并展示了一些使用它的示例。

## 11.1 Sets and Maps

JavaScript’s Object type is a versatile data structure that can be used to map strings (the object’s property names) to arbitrary values. And when the value being mapped to is something fixed like true, then the object is effectively a set of strings.

> JavaScript 的对象类型是一种通用的数据结构,可用于将字符串(对象的属性名称)映射到任意值。当被映射到的值是固定的,比如 true,那么对象实际上是一组字符串。

Objects are actually used as maps and sets fairly routinely in JavaScript programming, but this is limited by the restriction to strings and complicated by the fact that objects normally inherit properties with names like “toString”, which are not typically intended to be part of the map or set.

> 在 JavaScript 编程中,对象通常被用作映射和集合,但这受到字符串的限制,而且由于对象通常继承诸如“toString”之类的属性而变得复杂,这些属性通常不准备成为映射或集合的一部分。

For this reason, ES6 introduces true Set and Map classes, which we’ll cover in the sub-sections that follow.

> 因此,ES6引入了 true Set 和 Map 类,我们将在后面的子节中介绍它们。

### 11.1.1 The Set Class

A set is a collection of values, like an array is. Unlike arrays, however, sets are not ordered or indexed, and they do not allow duplicates: a value is either a member of a set or it is not a member; it is not possible to ask how many times a value appears in a set.

> set 是值的集合,就像数组。但不同于数组,set 没有被排序或被索引,并且它们不允许重复:值要么是集合的成员要么不是集合的成员;不能知道一个值在集合中出现多少次。

Create a Set object with the Set() constructor:

> 使用 Set() 创建 Set 对象:

```js
let s = new Set();       // A new, empty set
let t = new Set([1, s]); // A new set with two members
```
The argument to the Set() constructor need not be an array: any iterable object (including other Set objects) is allowed:

> Set() 构造函数的实参不必是数组:允许任何可迭代的对象(包括其他 Set 对象):

```js
let t = new Set(s);                  // A new set that copies the elements of s.
let unique = new Set("Mississippi"); // 4 elements: "M", "i", "s", and "p"
```
The size property of a set is like the length property of an array: it tells you how many values the set contains:

> set 的 size 属性类似于数组的 length 属性:它告诉你该集合包含多少个值:

```js
unique.size        // => 4
```
Sets don’t need to be initialized when you create them. You can add and remove elements at any time with add(), delete(), and clear(). Remember that sets cannot contain duplicates, so adding a value to a set when it already contains that value has no effect:

> 创建 set 时无需初始化。可以随时使用add()、delete() 和 clear() 添加和删除元素。请记住,集合不能包含重复项,因此添加已经包含在该 set 中的值是一个无效操作:

```js
let s = new Set();  // Start empty
s.size              // => 0
s.add(1);           // Add a number
s.size              // => 1; now the set has one member
s.add(1);           // Add the same number again
s.size              // => 1; the size does not change
s.add(true);        // Add another value; note that it is fine to mix types
s.size              // => 2
s.add([1,2,3]);     // Add an array value
s.size              // => 3; the array was added, not its elements
s.delete(1)         // => true: successfully deleted element 1
s.size              // => 2: the size is back down to 2
s.delete("test")    // => false: "test" was not a member, deletion failed
s.delete(true)      // => true: delete succeeded
s.delete([1,2,3])   // => false: the array in the set is different
s.size              // => 1: there is still that one array in the set
s.clear();          // Remove everything from the set
s.size              // => 0
```
There are a few important points to note about this code:

> 关于此代码,有几点要注意:

The add() method takes a single argument; if you pass an array, it adds the array itself to the set, not the individual array elements. add() always returns the set it is invoked on, however, so if you want to add multiple values to a set, you can used chained method calls like s.add('a').add('b').add('c');.

> add() 方法有一个实参。如果传递数组,它将数组本身而不是单个数组元素添加到 set 中。 add() 始终返回调用它的 set,因此,如果要向 set 添加多个值,则可以使用方法链调用,如s.add('a').add('b').add('C');。

The delete() method also only deletes a single set element at a time. Unlike add(), however, delete() returns a boolean value. If the value you specify was actually a member of the set, then delete() removes it and returns true. Otherwise, it does nothing and returns false.

> delete() 方法一次也只删除一个 set 元素。与 add() 不同,delete() 返回一个布尔值。如果指定的值是 set 的成员,则 delete() 会将其删除并返回 true。否则,它不执行任何操作并返回 false。

Finally, it is very important to understand that set membership is based on strict equality checks, like the === operator performs. A set can contain both the number 1 and the string "1", because it considers them to be distinct values. When the values are objects (or arrays or functions), they are also compared as if with ===. This is why we were unable to delete the array element from the set in this code. We added an array to the set and then tried to remove that array by passing a different array (albeit with the same elements) to the delete() method. In order for this to work, we would have had to pass a reference to exactly the same array.

> 最后,set 成员关系是基于严格的相等性检查(如 === 运算符执行),这是非常重要的。一个 set 可以同时包含数字 1 和字符串 "1",因为它认为它们是不同的值。当值是对象(或数组或函数)时,它们也将用 === 进行比较。这就是此代码集中为什么我们无法从删除数组元素的原因。我们向 set 中添加了一个数组,然后尝试通过将另一个数组(尽管具有相同的元素)传递给 delete() 方法来删除该数组。为了使它起作用,我们必须传递完全相同的数组引用。

#### NOTE
Python programmers take note: this is a significant difference between JavaScript and Python sets. Python sets compare members for equality, not identity, but the trade-off is that Python sets only allow immutable members, like tuples, and do not allow lists and dicts to be added to sets.

> Python 程序员请注意:JavaScript 和 Python 的 set 之间有个很大的差异。 Python set 比较成员是为了相等,而不是标识,但是要权衡的是 Python set 只允许不可变成员(例如元组),并且不允许将 list 和 dict 添加到集合中。(todo)

In practice, the most important thing we do with sets is not to add and remove elements from them, but to check to see whether a specified value is a member of the set. We do this with the has() method:

> 实际上,我们对 set 进行的最重要的操作不是在元素中添加和删除元素,而是检查指定的值是否是集合的成员。我们使用 has() 方法做到这一点:

```js
let oneDigitPrimes = new Set([2,3,5,7]);
oneDigitPrimes.has(2)    // => true: 2 is a one-digit prime number
oneDigitPrimes.has(3)    // => true: so is 3
oneDigitPrimes.has(4)    // => false: 4 is not a prime
oneDigitPrimes.has("5")  // => false: "5" is not even a number
```
The most important thing to understand about sets is that they are optimized for membership testing, and no matter how many members the set has, the has() method will be very fast. The includes() method of an array also performs membership testing, but the time it takes is proportional to the size of the array, and using an array as a set can be much, much slower than using a real Set object.

> 关于 set 最重要的要了解的是,它们已针对成员资格测试进行了优化,并且无论 set 有多少个成员,has() 方法都将非常快。 数组的 includes() 方法也执行成员资格测试,但是花费的时间与数组的大小成正比,并且将数组作为 set 使用比使用真正的 Set 对象要慢得多。

The Set class is iterable, which means that you can use a for/of loop to enumerate all of the elements of a set:

> Set 类是可迭代的,这意味着您可以使用 for/of 循环枚举集合中的所有元素:

```js
let sum = 0;
for(let p of oneDigitPrimes) { // Loop through the one-digit primes
    sum += p;                  // and add them up
}
sum                            // => 17: 2 + 3 + 5 + 7
```
Because Set objects are iterable, you can convert them to arrays and argument lists with the ... spread operator:

> 因为 Set 对象是可迭代的,所以可以使用 ... 展开运算符将它们转换为数组和实参列表:

```js
[...oneDigitPrimes]         // => [2,3,5,7]: the set converted to an Array
Math.max(...oneDigitPrimes) // => 7: set elements passed as function arguments
```
Sets are often described as “unordered collections.” This isn’t exactly true for the JavaScript Set class, however. A JavaScript set is unindexed: you can’t ask for the first or third element of a set the way you can with an array. But the JavaScript Set class always remembers the order that elements were inserted in, and it always uses this order when you iterate a set: the first element inserted will be the first one iterated (assuming you haven’t deleted it first), and the most recently inserted element will be the last one iterated. [^2]

> Set 通常被称为“无序集合”。 但是,对于 JavaScript 的 Set 类而言,并非完全如此。JavaScript Set 未索引:无法像使用数组那样获取 Set 的第一个或第三个元素。 但是 JavaScript Set 类始终记住插入元素的顺序,并且在遍历集合时始终使用此顺序:插入的第一个元素将是第一个遍历的元素(假定没有先删除它),并且,最后插入的元素将是最后一个遍历的元素。

In addition to being iterable, the Set class also implements a forEach() method that is similar to the array method of the same name:

> 除了可迭代之外,Set 类还实现了一个 forEach() 方法,该方法类似于同名的 array 方法:

```js
let product = 1;
oneDigitPrimes.forEach(n => { product *= n; });
product     // => 210: 2 * 3 * 5 * 7
```
The forEach() of an array passes array indexes as the second argument to the function you specify. Sets don’t have indexes, so the Set class’s version of this method simply passes the element value as both the first and second argument.

> 数组的 forEach() 将数组索引作为指定函数的第二个实参传递。集合没有索引,因此此方法的 Set 类版本仅将元素值作为第一个和第二个实参传递。

### 11.1.2 The Map Class
A Map object represents a set of values known as keys, where each key has another value associated with (or “mapped to”) it. In a sense, a map is like an array, but instead of using a set of sequential integers as the keys, maps allow us to use arbitrary values as “indexes.” Like arrays, maps are fast: looking up the value associated with a key will be fast (though not as fast as indexing an array) no matter how large the map is.

> Map 对象代表一组称为键的值,其中每个键具有与其关联(或“映射到”)的另一个值。从某种意义上讲,映射就像一个数组,但是映射不是使用一组连续的整数作为键,而是允许我们使用任意值作为“索引”。像数组一样,映射是快速的:无论映射有多大查找与键关联的值都很快(尽管不如索引数组快)。

Create a new map with the Map() constructor:

> 用 Map() 构造函数闯将一个 map:

```js
let m = new Map();  // Create a new, empty map
let n = new Map([   // A new map initialized with string keys mapped to numbers
    ["one", 1],
    ["two", 2]
]);
```
The optional argument to the Map() constructor should be an iterable object that yields two element [key, value] arrays. In practice, this means that if you want to initialize a map when you create it, you’ll typically write out the desired keys and associated values as an array of arrays. But you can also use the Map() constructor to copy other maps or to copy the property names and values from an existing object:

> Map() 构造函数的可选实参应该是一个可迭代的对象,该对象可产生两个元素 [key,value] 数组。 实际上,这意味着如果要在创建 map 时初始化,通常会将所需的键和映射的值写为数组的数组。 但是,也可以使用 Map() 构造函数复制其他 map,或者从现有对象复制属性名称和值:

```js
let copy = new Map(n); // A new map with the same keys and values as map n
let o = { x: 1, y: 2}; // An object with two properties
let p = new Map(Object.entries(o)); // Same as new map([["x", 1], ["y", 2]])
```
Once you have created a Map object, you can query the value associated with a given key with get() and can add a new key/value pair with set(). Remember, though, that a map is a set of keys, each of which has an associated value. This is not quite the same as a set of key/value pairs. If you call set() with a key that already exists in the map, you will change the value associated with that key, not add a new key/value mapping. In addition to get() and set(), the Map class also defines methods that are like Set methods: use has() to check whether a map includes the specified key; use delete() to remove a key (and its associated value) from the map; use clear() to remove all key/value pairs from the map; and use the size property to find out how many keys a map contains.

> 创建 Map 对象后,可以用 get() 查询给定键的值,并可以用 set() 添加新的键值对。但是请记住,map 是一组键,每个键映射一个值。这与一组键值对不太相同。如果调用 set() 添加一个 map 中已存在的键的键值对,会改变这个键映射的值,而不是添加一个新的键值对映射。除了 get() 和 set(),Map 类也定义了类似于 Set 的方法:使用 has() 判断 map 是否包含指定键;使用 delete() 删除 map 中的一个键(和它映射的值);使用 clear() 来移除 map 中所有的键值对;使用 size 属性知道 map 中包含多少个键。

```js
let m = new Map();   // Start with an empty map
m.size               // => 0: empty maps have no keys
m.set("one", 1);     // Map the key "one" to the value 1
m.set("two", 2);     // And the key "two" to the value 2.
m.size               // => 2: the map now has two keys
m.get("two")         // => 2: return the value associated with key "two"
m.get("three")       // => undefined: this key is not in the set
m.set("one", true);  // Change the value associated with an existing key
m.size               // => 2: the size doesn't change
m.has("one")         // => true: the map has a key "one"
m.has(true)          // => false: the map does not have a key true
m.delete("one")      // => true: the key existed and deletion succeeded
m.size               // => 1
m.delete("three")    // => false: failed to delete a nonexistent key
m.clear();           // Remove all keys and values from the map
```
Like the add() method of Set, the set() method of Map can be chained, which allows maps to be initialized without using arrays of arrays:

> 像 Set 的 add() 方法一样,Map 的 set() 方法也可以链式使用,这允许在不使用数组的情况下初始化 map:

```js
let m = new Map().set("one", 1).set("two", 2).set("three", 3);
m.size        // => 3
m.get("two")  // => 2
```
As with Set, any JavaScript value can be used as a key or a value in a Map. This includes null, undefined, and NaN, as well as reference types like objects and arrays. And as with the Set class, Map compares keys by identity, not by equality, so if you use an object or array as a key, it will be considered different from every other object and array, even those with exactly the same properties or elements:

> 与 Set 一样,任何 JavaScript 的值都可以被用作 Map 的键或值。包括 null、undefined 和 NaN,也包括像对象和数组这种引用类型。并且和 Set 类一样,Map 通过标识来比较,不是通过相等,所以如果想要使用对象或数组作为键,它们会被认为不同于任何其他的对象和数组,即使那些属性或元素完全相同的引用:

```js
let m = new Map();   // Start with an empty map.
m.set({}, 1);        // Map one empty object to the number 1.
m.set({}, 2);        // Map a different empty object to the number 2.
m.size               // => 2: there are two keys in this map
m.get({})            // => undefined: but this empty object is not a key
m.set(m, undefined); // Map the map itself to the value undefined.
m.has(m)             // => true: m is a key in itself
m.get(m)             // => undefined: same value we'd get if m wasn't a key
```
Map objects are iterable, and each iterated value is a two-element array where the first element is a key and the second element is the value associated with that key. If you use the spread operator with a Map object, you’ll get an array of arrays like the ones that we passed to the Map() constructor. And when iterating a map with a for/of loop, it is idiomatic to use destructuring assignment to assign the key and value to separate variables:

> Map 是可迭代对象,每一个迭代值是两个元素数组,第一个元素是键,第二个元素是键映射的值。如果对 Map 对象使用展开运算符,会得到一个数组的数组,就像传递给 Map() 构造函数的实参。当使用 for/of 循环遍历一个 map 时,常使用解构赋值将键和值赋值给展开变量:

```js
let m = new Map([["x", 1], ["y", 2]]);
[...m]    // => [["x", 1], ["y", 2]]

for(let [key, value] of m) {
    // On the first iteration, key will be "x" and value will be 1
    // On the second iteration, key will be "y" and value will be 2
}
```
Like the Set class, the Map class iterates in insertion order. The first key/value pair iterated will be the one least recently added to the map, and the last pair iterated will be the one most recently added.

> 与 Set 类一样,Map 类按照插入顺序进行遍历。遍历的第一个键值对将是最早添加到 map 中的,而遍历的最后一个键值对将是最新添加的。

If you want to iterate just the keys or just the associated values of a map, use the keys() and values() methods: these return iterable objects that iterate keys and values, in insertion order. (The entries() method returns an iterable object that iterates key/value pairs, but this is exactly the same as iterating the map directly.)

> 如果只想遍历 map 中的键或者映射的值,使用 keys() 和 values() 方法:它们返回按照插入顺序排列的键或值可迭代对象。(entries() 方法返回一个迭代对象,该对象迭代键值对,但这与直接迭代 map 完全相同。)

```js
[...m.keys()]     // => ["x", "y"]: just the keys
[...m.values()]   // => [1, 2]: just the values
[...m.entries()]  // => [["x", 1], ["y", 2]]: same as [...m]
```
Map objects can also be iterated using the forEach() method that was first implemented by the Array class.

> Map 对象也可以使用 forEach() 方法进行遍历。

```js
m.forEach((value, key) => {  // note value, key NOT key, value
    // On the first invocation, value will be 1 and key will be "x"
    // On the second invocation, value will be 2 and key will be "y"
});
```
It may seem strange that the value parameter comes before the key parameter in the code above, since with for/of iteration, the key comes first. As noted at the start of this section, you can think of a map as a generalized array in which integer array indexes are replaced with arbitrary key values. The forEach() method of arrays passes the array element first and the array index second, so, by analogy, the forEach() method of a map passes the map value first and the map key second.

> 上面的代码中 value 参数位于 key 参数之前,这似乎很奇怪,因为对于 for/of 迭代,key 放在前面。如本节开头所述,可以将映射视为通用数组,其中整数数组索引被替换为任意键值。数组的 forEach() 方法首先传递数组元素,然后传递数组索引,因此,以此类推,map 的 forEach() 方法首先传递 map 的值,然后传递 map 键。

### 11.1.3 WeakMap and WeakSet
The WeakMap class is a variant (but not an actual subclass) of the Map class that does not prevent its key values from being garbage collected. Garbage collection is the process by which the JavaScript interpreter reclaims the memory of objects that are no longer “reachable” and cannot be used by the program. A regular map holds “strong” references to its key values, and they remain reachable through the map, even if all other references to them are gone. The WeakMap, by contrast, keeps “weak” references to its key values so that they are not reachable through the WeakMap, and their presence in the map does not prevent their memory from being reclaimed.

> WeakMap 类是 Map 类的变体(但不是真正的子类),它不会阻止其键值被垃圾回收。垃圾回收是 JavaScript 解释器回收不再“可访问”并且无法由程序使用的对象的内存的过程。常规 map 保留对其键值的“强”引用,即使对它们的所有其他引用都已消失,它们仍然可以通过映射访问。相比之下,WeakMap 保留对其键值的“弱”引用,以使它们无法通过 WeakMap 获得,并且它们在 map 中的存在也不会阻止对其内存的回收。

The WeakMap() constructor is just like the Map() constructor, but there are some significant differences between WeakMap and Map:

> WeakMap() 构造函数类似 Map() 构造函数,但是有一些重要的不同:

WeakMap keys must be objects or arrays; primitive values are not subject to garbage collection and cannot be used as keys.

> WeakMap 键必须是对象或数组;原始值不受垃圾回收的限制,不能用作键。

WeakMap implements only the get(), set(), has(), and delete() methods. In particular, WeakMap is not iterable and does not define keys(), values(), or forEach(). If WeakMap was iterable, then its keys would be reachable and it wouldn’t be weak.

> WeakMap 仅实现 get()、set()、has() 和 delete() 方法。 特别是,WeakMap 是不可迭代的,并且未定义 keys()、values() 或 forEach()。如果 WeakMap 是可迭代的,则其键将是可访问的,这让它不会“弱”。

Similarly, WeakMap does not implement the size property because the size of a WeakMap could change at any time as objects are garbage collected.

> 同样,WeakMap 也不实现 size 属性,因为随着对象被垃圾回收,WeakMap 的大小可能随时更改。

The intended use of WeakMap is to allow you to associate values with objects without causing memory leaks. Suppose, for example, that you are writing a function that takes an object argument and needs to perform some time-consuming computation on that object. For efficiency, you’d like to cache the computed value for later reuse. If you use a Map object to implement the cache, you will prevent any of the objects from ever being reclaimed, but by using a WeakMap, you avoid this problem. (You can often achieve a similar result using a private Symbol property to cache the computed value directly on the object. See §6.10.3.)

> WeakMap 的预期用途是允许将值与对象相关联而不会引起内存泄漏。例如,假设正在编写一个带有对象实参的函数,并且需要对该对象执行一些耗时的计算。为了提高效率,希望将计算出的值进行缓存以备后用。如果使用 Map 对象实现缓存,则将防止回收任何对象,但是通过使用 WeakMap,可以避免此问题。(通常可以使用私有的 Symbol 属性将计算的值直接缓存在对象上,从而获得相似的结果。请参见 §6.10.3。)

WeakSet implements a set of objects that does not prevent those objects from being garbage collected. The WeakSet() constructor works like the Set() constructor, but WeakSet objects differ from Set objects in the same ways that WeakMap objects differ from Map objects:

> WeakSet 实现了一组对象,这些对象不会阻止垃圾回收这些对象。 WeakSet() 构造函数的工作方式类似于 Set() 构造函数,但 WeakSet 对象与 Set 对象的区别与 WeakMap 对象与 Map 对象的区别相同:

WeakSet does not allow primitive values as members.

> WeakSet 不允许原始值作为成员。

WeakSet implements only the add(), has(), and delete() methods and is not iterable.

> WeakSet 仅实现 add()、has() 和 delete() 方法,并且不可迭代。

WeakSet does not have a size property.

> WeakSet 没有 size 属性。

WeakSet is not frequently used: its use cases are like those for WeakMap. If you want to mark (or “brand”) an object as having some special property or type, for example, you could add it to a WeakSet. Then, elsewhere, when you want to check for that property or type, you can test for membership in that WeakSet. Doing this with a regular set would prevent all marked objects from being garbage collected, but this is not a concern when using WeakSet.

> WeakSet 并不经常使用:其用例类似于 WeakMap 的用例。例如,如果要标记(或“烙印”)对象具有某些特殊属性或类型,则可以将其添加到 WeakSet 中。然后,在其他位置,当要检查该属性或类型时,可以测试该 WeakSet 中的成员身份。使用常规 Set 执行此操作将防止所有标记的对象被垃圾回收,但这在使用 WeakSet 时不必担心。

## 11.2 Typed Arrays and Binary Data
Regular JavaScript arrays can have elements of any type and can grow or shrink dynamically. JavaScript implementations perform lots of optimizations so that typical uses of JavaScript arrays are very fast. Nevertheless, they are still quite different from the array types of lower-level languages like C and Java. Typed arrays, which are new in ES6 [^3] are much closer to the low-level arrays of those languages. Typed arrays are not technically arrays (Array.isArray() returns false for them), but they implement all of the array methods described in §7.8 plus a few more of their own. They differ from regular arrays in some very important ways, however:

> 常规 JavaScript 数组可以具有任何类型的元素,并且可以动态增长或收缩。JavaScript 实现执行了大量优化,因此 JavaScript 数组的典型用法非常快。但是,它们仍然与诸如 C 和 Java 的低级语言的数组类型有很大不同。 ES6 [^3] 中新增的类型化数组更接近那些语言的低级数组。从类型上讲,类型化数组不是数组(Array.isArray() 为其返回false),但是它们实现了 §7.8 中描述的所有数组方法以及它们自己的其他一些方法。但是,它们在某些非常重要的方面与常规数组有所不同: 

The elements of a typed array are all numbers. Unlike regular JavaScript numbers, however, typed arrays allow you to specify the type (signed and unsigned integers and IEEE-754 floating point) and size (8 bits to 64 bits) of the numbers to be stored in the array.

> 类型化数组的元素都是数字。 但是,与常规 JavaScript 数字不同,类型化数组允许指定要存储在数组中的数字的类型(有符号和无符号整数以及 IEEE-754 浮点数)和大小(8 位至 64 位)。 

You must specify the length of a typed array when you create it, and that length can never change.

> 创建类型化数组时,必须指定其长度,并且该长度永远不会改变。 

The elements of a typed array are always initialized to 0 when the array is created.

> 创建数组时,类型化数组的元素始终会初始化为 0。

### 11.2.1 Typed Array Types
JavaScript does not define a TypedArray class. Instead, there are 11 kinds of typed arrays, each with a different element type and constructor:
> JavaScript 没有定义 TypedArray 类。相反,有11种类型化数组,每种类型具有不同的元素类型和构造函数: 

Constructor	Numeric type

Int8Array()

signed bytes

Uint8Array()

unsigned bytes

Uint8ClampedArray()

unsigned bytes without rollover

Int16Array()

signed 16-bit short integers

Uint16Array()

unsigned 16-bit short integers

Int32Array()

signed 32-bit integers

Uint32Array()

unsigned 32-bit integers

BigInt64Array()

signed 64-bit BigInt values (ES2020)

BigUint64Array()

unsigned 64-bit BigInt values (ES2020)

Float32Array()

32-bit floating-point value

Float64Array()

64-bit floating-point value: a regular JavaScript number

The types whose names begin with Int hold signed integers, of 1, 2, or 4 bytes (8, 16, or 32 bits). The types whose names begin with Uint hold unsigned integers of those same lengths. The “BigInt” and “BigUint” types hold 64-bit integers, represented in JavaScript as BigInt values (see §3.2.5). The types that begin with Float hold floating-point numbers. The elements of a Float64Array are of the same type as regular JavaScript numbers. The elements of a Float32Array have lower precision and a smaller range but require only half the memory. (This type is called float in C and Java.)

Uint8ClampedArray is a special-case variant on Uint8Array. Both of these types hold unsigned bytes and can represent numbers between 0 and 255. With Uint8Array, if you store a value larger than 255 or less than zero into an array element, it “wraps around,” and you get some other value. This is how computer memory works at a low level, so this is very fast. Uint8ClampedArray does some extra type checking so that, if you store a value greater than 255 or less than 0, it “clamps” to 255 or 0 and does not wrap around. (This clamping behavior is required by the HTML `<canvas>` element’s low-level API for manipulating pixel colors.)

Each of the typed array constructors has a BYTES_PER_ELEMENT property with the value 1, 2, 4, or 8, depending on the type.

### 11.2.2 Creating Typed Arrays
The simplest way to create a typed array is to call the appropriate constructor with one numeric argument that specifies the number of elements you want in the array:
```js
let bytes = new Uint8Array(1024);    // 1024 bytes
let matrix = new Float64Array(9);    // A 3x3 matrix
let point = new Int16Array(3);       // A point in 3D space
let rgba = new Uint8ClampedArray(4); // A 4-byte RGBA pixel value
let sudoku = new Int8Array(81);      // A 9x9 sudoku board
```
When you create typed arrays in this way, the array elements are all guaranteed to be initialized to 0, 0n, or 0.0. But if you know the values you want in your typed array, you can also specify those values when you create the array. Each of the typed array constructors has static from() and of() factory methods that work like Array.from() and Array.of():
```js
let white = Uint8ClampedArray.of(255, 255, 255, 0);  // RGBA opaque white
```
Recall that the Array.from() factory method expects an array-like or iterable object as its first argument. The same is true for the typed array variants, except that the iterable or array-like object must also have numeric elements. Strings are iterable, for example, but it would make no sense to pass them to the from() factory method of a typed array.

If you are just using the one-argument version of from(), you can drop the .from and pass your iterable or array-like object directly to the constructor function, which behaves exactly the same. Note that both the constructor and the from() factory method allow you to copy existing typed arrays, while possibly changing the type:
```js
let ints = Uint32Array.from(white);  // The same 4 numbers, but as ints
```
When you create a new typed array from an existing array, iterable, or array-like object, the values may be truncated in order to fit the type constraints of your array. There are no warnings or errors when this happens:
```js
// Floats truncated to ints, longer ints truncated to 8 bits
Uint8Array.of(1.23, 2.99, 45000) // => new Uint8Array([1, 2, 200])
```
Finally, there is one more way to create typed arrays that involves the ArrayBuffer type. An ArrayBuffer is an opaque reference to a chunk of memory. You can create one with the constructor; just pass in the number of bytes of memory you’d like to allocate:
```js
let buffer = new ArrayBuffer(1024*1024);
buffer.byteLength   // => 1024*1024; one megabyte of memory
```
The ArrayBuffer class does not allow you to read or write any of the bytes that you have allocated. But you can create typed arrays that use the buffer’s memory and that do allow you to read and write that memory. To do this, call the typed array constructor with an ArrayBuffer as the first argument, a byte offset within the array buffer as the second argument, and the array length (in elements, not in bytes) as the third argument. The second and third arguments are optional. If you omit both, then the array will use all of the memory in the array buffer. If you omit only the length argument, then your array will use all of the available memory between the start position and the end of the array. One more thing to bear in mind about this form of the typed array constructor: arrays must be memory aligned, so if you specify a byte offset, the value should be a multiple of the size of your type. The Int32Array() constructor requires a multiple of four, for example, and the Float64Array() requires a multiple of eight.

Given the ArrayBuffer created earlier, you could create typed arrays like these:
```js
let asbytes = new Uint8Array(buffer);          // Viewed as bytes
let asints = new Int32Array(buffer);           // Viewed as 32-bit signed ints
let lastK = new Uint8Array(buffer, 1023*1024); // Last kilobyte as bytes
let ints2 = new Int32Array(buffer, 1024, 256); // 2nd kilobyte as 256 integers
```
These four typed arrays offer four different views into the memory represented by the ArrayBuffer. It is important to understand that all typed arrays have an underlying ArrayBuffer, even if you do not explicitly specify one. If you call a typed array constructor without passing a buffer object, a buffer of the appropriate size will be automatically created. As described later, the buffer property of any typed array refers to its underlying ArrayBuffer object. The reason to work directly with ArrayBuffer objects is that sometimes you may want to have multiple typed array views of a single buffer.

### 11.2.3 Using Typed Arrays
Once you have created a typed array, you can read and write its elements with regular square-bracket notation, just as you would with any other array-like object:
```js
// Return the largest prime smaller than n, using the sieve of Eratosthenes
function sieve(n) {
    let a = new Uint8Array(n+1);         // a[x] will be 1 if x is composite
    let max = Math.floor(Math.sqrt(n));  // Don't do factors higher than this
    let p = 2;                           // 2 is the first prime
    while(p <= max) {                    // For primes less than max
        for(let i = 2*p; i <= n; i += p) // Mark multiples of p as composite
            a[i] = 1;
        while(a[++p]) /* empty */;       // The next unmarked index is prime
    }
    while(a[n]) n--;                     // Loop backward to find the last prime
    return n;                            // And return it
}
```
The function here computes the largest prime number smaller than the number you specify. The code is exactly the same as it would be with a regular JavaScript array, but using Uint8Array() instead of Array() makes the code run more than four times faster and use eight times less memory in my testing.

Typed arrays are not true arrays, but they re-implement most array methods, so you can use them pretty much just like you’d use regular arrays:
```js
let ints = new Int16Array(10);       // 10 short integers
ints.fill(3).map(x=>x*x).join("")    // => "9999999999"
```
Remember that typed arrays have fixed lengths, so the length property is read-only, and methods that change the length of the array (such as push(), pop(), unshift(), shift(), and splice()) are not implemented for typed arrays. Methods that alter the contents of an array without changing the length (such as sort(), reverse(), and fill()) are implemented. Methods like map() and slice() that return new arrays return a typed array of the same type as the one they are called on.

### 11.2.4 Typed Array Methods and Properties
In addition to standard array methods, typed arrays also implement a few methods of their own. The set() method sets multiple elements of a typed array at once by copying the elements of a regular or typed array into a typed array:
```js
let bytes = new Uint8Array(1024);        // A 1K buffer
let pattern = new Uint8Array([0,1,2,3]); // An array of 4 bytes
bytes.set(pattern);      // Copy them to the start of another byte array
bytes.set(pattern, 4);   // Copy them again at a different offset
bytes.set([0,1,2,3], 8); // Or just copy values direct from a regular array
bytes.slice(0, 12)       // => new Uint8Array([0,1,2,3,0,1,2,3,0,1,2,3])
```
The set() method takes an array or typed array as its first argument and an element offset as its optional second argument, which defaults to 0 if left unspecified. If you are copying values from one typed array to another, the operation will likely be extremely fast.

Typed arrays also have a subarray method that returns a portion of the array on which it is called:
```js
let ints = new Int16Array([0,1,2,3,4,5,6,7,8,9]);       // 10 short integers
let last3 = ints.subarray(ints.length-3, ints.length);  // Last 3 of them
last3[0]       // => 7: this is the same as ints[7]
```
subarray() takes the same arguments as the slice() method and seems to work the same way. But there is an important difference. slice() returns the specified elements in a new, independent typed array that does not share memory with the original array. subarray() does not copy any memory; it just returns a new view of the same underlying values:
```js
ints[9] = -1;  // Change a value in the original array and...
last3[2]       // => -1: it also changes in the subarray
```
The fact that the subarray() method returns a new view of an existing array brings us back to the topic of ArrayBuffers. Every typed array has three properties that relate to the underlying buffer:
```js
last3.buffer                 // The ArrayBuffer object for a typed array
last3.buffer === ints.buffer // => true: both are views of the same buffer
last3.byteOffset             // => 14: this view starts at byte 14 of the buffer
last3.byteLength             // => 6: this view is 6 bytes (3 16-bit ints) long
last3.buffer.byteLength      // => 20: but the underlying buffer has 20 bytes
```
The buffer property is the ArrayBuffer of the array. byteOffset is the starting position of the array’s data within the underlying buffer. And byteLength is the length of the array’s data in bytes. For any typed array, a, this invariant should always be true:
```js
a.length * a.BYTES_PER_ELEMENT === a.byteLength  // => true
```
ArrayBuffers are just opaque chunks of bytes. You can access those bytes with typed arrays, but an ArrayBuffer is not itself a typed array. Be careful, however: you can use numeric array indexing with ArrayBuffers just as you can with any JavaScript object. Doing so does not give you access to the bytes in the buffer, but it can cause confusing bugs:
```js
let bytes = new Uint8Array(8);
bytes[0] = 1;           // Set the first byte to 1
bytes.buffer[0]         // => undefined: buffer doesn't have index 0
bytes.buffer[1] = 255;  // Try incorrectly to set a byte in the buffer
bytes.buffer[1]         // => 255: this just sets a regular JS property
bytes[1]                // => 0: the line above did not set the byte
```
We saw previously that you can create an ArrayBuffer with the ArrayBuffer() constructor and then create typed arrays that use that buffer. Another approach is to create an initial typed array, then use the buffer of that array to create other views:
```js
let bytes = new Uint8Array(1024);            // 1024 bytes
let ints = new Uint32Array(bytes.buffer);    // or 256 integers
let floats = new Float64Array(bytes.buffer); // or 128 doubles
```
### 11.2.5 DataView and Endianness
Typed arrays allow you to view the same sequence of bytes in chunks of 8, 16, 32, or 64 bits. This exposes the “endianness”: the order in which bytes are arranged into longer words. For efficiency, typed arrays use the native endianness of the underlying hardware. On little-endian systems, the bytes of a number are arranged in an ArrayBuffer from least significant to most significant. On big-endian platforms, the bytes are arranged from most significant to least significant. You can determine the endianness of the underlying platform with code like this:
```js
// If the integer 0x00000001 is arranged in memory as 01 00 00 00, then
// we're on a little-endian platform. On a big-endian platform, we'd get
// bytes 00 00 00 01 instead.
let littleEndian = new Int8Array(new Int32Array([1]).buffer)[0] === 1;
```
Today, the most common CPU architectures are little-endian. Many network protocols, and some binary file formats, require big-endian byte ordering, however. If you’re using typed arrays with data that came from the network or from a file, you can’t just assume that the platform endianness matches the byte order of the data. In general, when working with external data, you can use Int8Array and Uint8Array to view the data as an array of individual bytes, but you should not use the other typed arrays with multibyte word sizes. Instead, you can use the DataView class, which defines methods for reading and writing values from an ArrayBuffer with explicitly specified byte ordering:
```js
// Assume we have a typed array of bytes of binary data to process. First,
// we create a DataView object so we can flexibly read and write
// values from those bytes
let view = new DataView(bytes.buffer,
                        bytes.byteOffset,
                        bytes.byteLength);

let int = view.getInt32(0);     // Read big-endian signed int from byte 0
int = view.getInt32(4, false);  // Next int is also big-endian
int = view.getUint32(8, true);  // Next int is little-endian and unsigned
view.setUint32(8, int, false);  // Write it back in big-endian format
```
DataView defines 10 get methods for each of the 10 typed array classes (excluding Uint8ClampedArray). They have names like getInt16(), getUint32(), getBigInt64(), and getFloat64(). The first argument is the byte offset within the ArrayBuffer at which the value begins. All of these getter methods, other than getInt8() and getUint8(), accept an optional boolean value as their second argument. If the second argument is omitted or is false, big-endian byte ordering is used. If the second argument is true, little-endian ordering is used.

DataView also defines 10 corresponding Set methods that write values into the underlying ArrayBuffer. The first argument is the offset at which the value begins. The second argument is the value to write. Each of the methods, except setInt8() and setUint8(), accepts an optional third argument. If the argument is omitted or is false, the value is written in big-endian format with the most significant byte first. If the argument is true, the value is written in little-endian format with the least significant byte first.

Typed arrays and the DataView class give you all the tools you need to process binary data and enable you to write JavaScript programs that do things like decompressing ZIP files or extracting metadata from JPEG files.

## 11.3 Pattern Matching with Regular Expressions
A regular expression is an object that describes a textual pattern. The JavaScript RegExp class represents regular expressions, and both String and RegExp define methods that use regular expressions to perform powerful pattern-matching and search-and-replace functions on text. In order to use the RegExp API effectively, however, you must also learn how to describe patterns of text using the regular expression grammar, which is essentially a mini programming language of its own. Fortunately, the JavaScript regular expression grammar is quite similar to the grammar used by many other programming languages, so you may already be familiar with it. (And if you are not, the effort you invest in learning JavaScript regular expressions will probably be useful to you in other programming contexts as well.)

> 正则表达式是描述文本模型的对象。JavaScript RegExp 类表示正则表达式,而 String 和 RegExp 都定义了使用正则表达式对文本执行强大的模式匹配和搜索替换功能的方法。但是,为了有效地使用 RegExp API,还必须学习如何使用正则表达式语法描述文本模型,该语法本质上是其自身的一种小型编程语言。幸运的是,JavaScript 正则表达式语法与许多其他编程语言所使用的语法非常相似,因此您可能已经很熟悉它。(如果不是这样,则在学习 JavaScript 正则表达式上投入的精力也可能有助于在你在其他编程环境使用。) 

The subsections that follow describe the regular expression grammar first, and then, after explaining how to write regular expressions, they explain how you can use them with methods of the String and RegExp classes.

> 接下来的小节首先描述正则表达式语法,然后在解释了如何编写正则表达式之后,它们解释了如何将其与 String 和 RegExp 类的方法一起使用。 

### 11.3.1 Defining Regular Expressions
In JavaScript, regular expressions are represented by RegExp objects. RegExp objects may be created with the RegExp() constructor, of course, but they are more often created using a special literal syntax. Just as string literals are specified as characters within quotation marks, regular expression literals are specified as characters within a pair of slash (/) characters. Thus, your JavaScript code may contain lines like this:

> 在 JavaScript 中,正则表达式以 RegExp 对象呈现。当然,RegExp 对象可能会使用 RegExp() 构造函数创建,但是经常会使用一个特殊的字面量语法来创建。和字符串文字被指定为引号内的字符一样,正则表达式文字被指定为一对斜杠 (/) 字符内的字符。因此你的 Javascript 代码可能会有这样的一行:

```JavaScript
let pattern = /s$/;
```

This line creates a new RegExp object and assigns it to the variable pattern. This particular RegExp object matches any string that ends with the letter “s.” This regular expression could have equivalently been defined with the RegExp() constructor, like this:

> 这行创建了一个新的 RegExp 对象并且将变量模型赋值给它。这个特殊的 RegExp 对象匹配任何以字母“s”结尾的字符串。这个正则表达式等效于如下用 RegExp() 构造函数定义的对象:

```JavaScript
let pattern = new RegExp("s$");
```

Regular-expression pattern specifications consist of a series of characters. Most characters, including all alphanumeric characters, simply describe characters to be matched literally. Thus, the regular expression /java/ matches any string that contains the substring “java”. Other characters in regular expressions are not matched literally but have special significance. For example, the regular expression /s$/ contains two characters. The first, “s”, matches itself literally. The second, “$”, is a special meta-character that matches the end of a string. Thus, this regular expression matches any string that contains the letter “s” as its last character.

> 正则表达式规定由一系列符号表示。大多数字符包括所有字母数字字符,都只是按字面描述要匹配的字符。但是正则表达式 `/java/` 匹配任何包含“java”的字符串。但是,正则表达式中的其他字符具有特殊意义,不是字面匹配的。例如,在正则表达式 `/s$/` 中包含两个字符。第一个“s”匹配它自身的字面量。第二个“$”,是一个特殊的元字符(meta-character)匹配的是一个字符串的结尾。因此这个正则表达式匹配的是以“s”结尾的字符串。

As we’ll see, regular expressions can also have one or more flag characters that affect how they work. Flags are specified following the second slash character in RegExp literals, or as a second string argument to the RegExp() constructor. If we wanted to match strings that end with “s” or “S”, for example, we could use the i flag with our regular expression to indicate that we want case-insensitive matching:

let pattern = /s$/i;
The following sections describe the various characters and meta-characters used in JavaScript regular expressions.

LITERAL CHARACTERS
All alphabetic characters and digits match themselves literally in regular expressions. JavaScript regular expression syntax also supports certain nonalphabetic characters through escape sequences that begin with a backslash `(\)`. For example, the sequence \n matches a literal newline character in a string. Table 11-1 lists these characters.

Table 11-1. Regular-expression literal characters
Character	Matches
Alphanumeric character

Itself

\0

The NUL character (\u0000)

\t

Tab (\u0009)

\n

Newline (\u000A)

\v

Vertical tab (\u000B)

\f

Form feed (\u000C)

\r

Carriage return (\u000D)

\xnn

The Latin character specified by the hexadecimal number nn; for example, \x0A is the same as \n.

\uxxxx

The Unicode character specified by the hexadecimal number xxxx; for example, \u0009 is the same as \t.

\u{n}

The Unicode character specified by the codepoint n, where n is one to six hexadecimal digits between 0 and 10FFFF. Note that this syntax is only supported in regular expressions that use the u flag.

\cX

The control character ^X; for example, \cJ is equivalent to the newline character \n.

A number of punctuation characters have special meanings in regular expressions. They are:
```
^ $ . * + ? = ! : | \ / ( ) [ ] { }
```
The meanings of these characters are discussed in the sections that follow. Some of these characters have special meaning only within certain contexts of a regular expression and are treated literally in other contexts. As a general rule, however, if you want to include any of these punctuation characters literally in a regular expression, you must precede them with a `\`. Other punctuation characters, such as quotation marks and @, do not have special meaning and simply match themselves literally in a regular expression.

If you can’t remember exactly which punctuation characters need to be escaped with a backslash, you may safely place a backslash before any punctuation character. On the other hand, note that many letters and numbers have special meaning when preceded by a backslash, so any letters or numbers that you want to match literally should not be escaped with a backslash. To include a backslash character literally in a regular expression, you must escape it with a backslash, of course. For example, the following regular expression matches any string that includes a backslash: `/\\/`. (And if you use the RegExp() constructor, keep in mind that any backslashes in your regular expression need to be doubled, since strings also use backslashes as an escape character.)

CHARACTER CLASSES
Individual literal characters can be combined into character classes by placing them within square brackets. A character class matches any one character that is contained within it. Thus, the regular expression `/[abc]/` matches any one of the letters a, b, or c. Negated character classes can also be defined; these match any character except those contained within the brackets. A negated character class is specified by placing a caret (^) as the first character inside the left bracket. The RegExp `/[^abc]/` matches any one character other than a, b, or c. Character classes can use a hyphen to indicate a range of characters. To match any one lowercase character from the Latin alphabet, use `/[a-z]/`, and to match any letter or digit from the Latin alphabet, use `/[a-zA-Z0-9]/.` (And if you want to include an actual hyphen in your character class, simply make it the last character before the right bracket.)

Because certain character classes are commonly used, the JavaScript regular-expression syntax includes special characters and escape sequences to represent these common classes. For example, \s matches the space character, the tab character, and any other Unicode whitespace character; \S matches any character that is not Unicode whitespace. Table 11-2 lists these characters and summarizes character-class syntax. (Note that several of these character-class escape sequences match only ASCII characters and have not been extended to work with Unicode characters. You can, however, explicitly define your own Unicode character classes; for example, `/[\u0400-\u04FF]/` matches any one Cyrillic character.)

Table 11-2. Regular expression character classes
Character	Matches
[...]

Any one character between the brackets.

[^...]

Any one character not between the brackets.

.

Any character except newline or another Unicode line terminator. Or, if the RegExp uses the s flag, then a period matches any character, including line terminators.

\w

Any ASCII word character. Equivalent to [a-zA-Z0-9_].

\W

Any character that is not an ASCII word character. Equivalent to [^a-zA-Z0-9_].

\s

Any Unicode whitespace character.

\S

Any character that is not Unicode whitespace.

\d

Any ASCII digit. Equivalent to [0-9].

\D

Any character other than an ASCII digit. Equivalent to [^0-9].

[\b]

A literal backspace (special case).

Note that the special character-class escapes can be used within square brackets. \s matches any whitespace character, and \d matches any digit, so `/[\s\d]/` matches any one whitespace character or digit. Note that there is one special case. As you’ll see later, the \b escape has a special meaning. When used within a character class, however, it represents the backspace character. Thus, to represent a backspace character literally in a regular expression, use the character class with one element: `/[\b]/`.

UNICODE CHARACTER CLASSES
In ES2018, if a regular expression uses the u flag, then character classes \p{...} and its negation \P{...} are supported. (As of early 2020, this is implemented by Node, Chrome, Edge, and Safari, but not Firefox.) These character classes are based on properties defined by the Unicode standard, and the set of characters they represent may change as Unicode evolves.

The \d character class matches only ASCII digits. If you want to match one decimal digit from any of the world’s writing systems, you can use /\p{Decimal_Number}/u. And if you want to match any one character that is not a decimal digit in any language, you can capitalize the p and write \P{Decimal_Number}. If you want to match any number-like character, including fractions and roman numerals, you can use \p{Number}. Note that “Decimal_Number” and “Number” are not specific to JavaScript or to regular expression grammar: it is the name of a category of characters defined by the Unicode standard.

The \w character class only works for ASCII text, but with \p, we can approximate an internationalized version like this:
```js
/[\p{Alphabetic}\p{Decimal_Number}\p{Mark}]/u
```
(Though to be fully compatible with the complexity of the world’s languages, we really need to add in the categories “Connector_Punctuation” and “Join_Control” as well.)

As a final example, the \p syntax also allows us to define regular expressions that match characters from a particular alphabet or script:
```js
let greekLetter = /\p{Script=Greek}/u;
let cyrillicLetter = /\p{Script=Cyrillic}/u;
```
#### REPETITION
With the regular expression syntax you’ve learned so far, you can describe a two-digit number as `/\d\d/` and a four-digit number as `/\d\d\d\d/`. But you don’t have any way to describe, for example, a number that can have any number of digits or a string of three letters followed by an optional digit. These more complex patterns use regular expression syntax that specifies how many times an element of a regular expression may be repeated.

The characters that specify repetition always follow the pattern to which they are being applied. Because certain types of repetition are quite commonly used, there are special characters to represent these cases. For example, + matches one or more occurrences of the previous pattern.

Table 11-3 summarizes the repetition syntax.

Table 11-3. Regular expression repetition characters
Character	Meaning
{n,m}

Match the previous item at least n times but no more than m times.

{n,}

Match the previous item n or more times.

{n}

Match exactly n occurrences of the previous item.

?

Match zero or one occurrences of the previous item. That is, the previous item is optional. Equivalent to {0,1}.

+

Match one or more occurrences of the previous item. Equivalent to {1,}.

*

Match zero or more occurrences of the previous item. Equivalent to {0,}.

The following lines show some examples:
```js
let r = /\d{2,4}/; // Match between two and four digits
r = /\w{3}\d?/;    // Match exactly three word characters and an optional digit
r = /\s+java\s+/;  // Match "java" with one or more spaces before and after
r = /[^(]*/;       // Match zero or more characters that are not open parens
```
Note that in all of these examples, the repetition specifiers apply to the single character or character class that precedes them. If you want to match repetitions of more complicated expressions, you’ll need to define a group with parentheses, which are explained in the following sections.

Be careful when using the * and ? repetition characters. Since these characters may match zero instances of whatever precedes them, they are allowed to match nothing. For example, the regular expression /a*/ actually matches the string “bbbb” because the string contains zero occurrences of the letter a!

#### NON-GREEDY REPETITION
The repetition characters listed in Table 11-3 match as many times as possible while still allowing any following parts of the regular expression to match. We say that this repetition is “greedy.” It is also possible to specify that repetition should be done in a non-greedy way. Simply follow the repetition character or characters with a question mark: ??, +?, *?, or even {1,5}?. For example, the regular expression /a+/ matches one or more occurrences of the letter a. When applied to the string “aaa”, it matches all three letters. But /a+?/ matches one or more occurrences of the letter a, matching as few characters as necessary. When applied to the same string, this pattern matches only the first letter a.

Using non-greedy repetition may not always produce the results you expect. Consider the pattern /a+b/, which matches one or more a’s, followed by the letter b. When applied to the string “aaab”, it matches the entire string. Now let’s use the non-greedy version: /a+?b/. This should match the letter b preceded by the fewest number of a’s possible. When applied to the same string “aaab”, you might expect it to match only one a and the last letter b. In fact, however, this pattern matches the entire string, just like the greedy version of the pattern. This is because regular expression pattern matching is done by finding the first position in the string at which a match is possible. Since a match is possible starting at the first character of the string, shorter matches starting at subsequent characters are never even considered.

#### ALTERNATION, GROUPING, AND REFERENCES
The regular expression grammar includes special characters for specifying alternatives, grouping subexpressions, and referring to previous subexpressions. The | character separates alternatives. For example, `/ab|cd|ef/` matches the string “ab” or the string “cd” or the string “ef”. And `/\d{3}|[a-z]{4}/ `matches either three digits or four lowercase letters.

Note that alternatives are considered left to right until a match is found. If the left alternative matches, the right alternative is ignored, even if it would have produced a “better” match. Thus, when the pattern `/a|ab/` is applied to the string “ab”, it matches only the first letter.

Parentheses have several purposes in regular expressions. One purpose is to group separate items into a single subexpression so that the items can be treated as a single unit by `|, *, +, ?,` and so on. For example, `/java(script)?/` matches “java” followed by the optional “script”. And `/(ab|cd)+|ef/` matches either the string “ef” or one or more repetitions of either of the strings “ab” or “cd”.

Another purpose of parentheses in regular expressions is to define subpatterns within the complete pattern. When a regular expression is successfully matched against a target string, it is possible to extract the portions of the target string that matched any particular parenthesized subpattern. (You’ll see how these matching substrings are obtained later in this section.) For example, suppose you are looking for one or more lowercase letters followed by one or more digits. You might use the pattern `/[a-z]+\d+/`. But suppose you only really care about the digits at the end of each match. If you put that part of the pattern in parentheses (`/[a-z]+(\d+)/`), you can extract the digits from any matches you find, as explained later.

A related use of parenthesized subexpressions is to allow you to refer back to a subexpression later in the same regular expression. This is done by following a `\` character by a digit or digits. The digits refer to the position of the parenthesized subexpression within the regular expression. For example, `\1` refers back to the first subexpression, and `\3` refers to the third. Note that, because subexpressions can be nested within others, it is the position of the left parenthesis that is counted. In the following regular expression, for example, the nested subexpression `([Ss]cript)` is referred to as `\2`:
```js
/([Jj]ava([Ss]cript)?)\sis\s(fun\w*)/
```
A reference to a previous subexpression of a regular expression does not refer to the pattern for that subexpression but rather to the text that matched the pattern. Thus, references can be used to enforce a constraint that separate portions of a string contain exactly the same characters. For example, the following regular expression matches zero or more characters within single or double quotes. However, it does not require the opening and closing quotes to match (i.e., both single quotes or both double quotes):
```js
/['"][^'"]*['"]/
```
To require the quotes to match, use a reference:
```js
/(['"])[^'"]*\1/
```
The \1 matches whatever the first parenthesized subexpression matched. In this example, it enforces the constraint that the closing quote match the opening quote. This regular expression does not allow single quotes within double-quoted strings or vice versa. (It is not legal to use a reference within a character class, so you cannot write: `/(['"])[^\1]*\1/`.)

When we cover the RegExp API later, you’ll see that this kind of reference to a parenthesized subexpression is a powerful feature of regular-expression search-and-replace operations.

It is also possible to group items in a regular expression without creating a numbered reference to those items. Instead of simply grouping the items within ( and ), begin the group with (?: and end it with ). Consider the following pattern:
```js
/([Jj]ava(?:[Ss]cript)?)\sis\s(fun\w*)/
```
In this example, the subexpression `(?:[Ss]cript)` is used simply for grouping, so the ? repetition character can be applied to the group. These modified parentheses do not produce a reference, so in this regular expression, `\2` refers to the text matched by `(fun\w*)`.

Table 11-4 summarizes the regular expression alternation, grouping, and referencing operators.

Table 11-4. Regular expression alternation, grouping, and reference characters
Character	Meaning
|

Alternation: match either the subexpression to the left or the subexpression to the right.

(...)

Grouping: group items into a single unit that can be used with *, +, ?, |, and so on. Also remember the characters that match this group for use with later references.

(?:...)

Grouping only: group items into a single unit, but do not remember the characters that match this group.

\n

Match the same characters that were matched when group number n was first matched. Groups are subexpressions within (possibly nested) parentheses. Group numbers are assigned by counting left parentheses from left to right. Groups formed with (?: are not numbered.

NAMED CAPTURE GROUPS
ES2018 standardizes a new feature that can make regular expressions more self-documenting and easier to understand. This new feature is known as “named capture groups” and it allows us to associate a name with each left parenthesis in a regular expression so that we can refer to the matching text by name rather than by number. Equally important: using names allows someone reading the code to more easily understand the purpose of that portion of the regular expression. As of early 2020, this feature is implemented in Node, Chrome, Edge, and Safari, but not yet by Firefox.

To name a group, use (?<...> instead of ( and put the name between the angle brackets. For example, here is a regular expression that might be used to check the formatting of the final line of a US mailing address:
```js
/(?<city>\w+) (?<state>[A-Z]{2}) (?<zipcode>\d{5})(?<zip9>-\d{4})?/
```
Notice how much context the group names provide to make the regular expression easier to understand. In §11.3.2, when we discuss the String replace() and match() methods and the RegExp exec() method, you’ll see how the RegExp API allows you to refer to the text that matches each of these groups by name rather than by position.

If you want to refer back to a named capture group within a regular expression, you can do that by name as well. In the preceding example, we were able to use a regular expression “backreference” to write a RegExp that would match a single- or double-quoted string where the open and close quotes had to match. We could rewrite this RegExp using a named capturing group and a named backreference like this:
```js
/(?<quote>['"])[^'"]*\k<quote>/
```
The `\k<quote>` is a named backreference to the named group that captures the open quotation mark.

SPECIFYING MATCH POSITION
As described earlier, many elements of a regular expression match a single character in a string. For example, \s matches a single character of whitespace. Other regular expression elements match the positions between characters instead of actual characters. \b, for example, matches an ASCII word boundary—the boundary between a \w (ASCII word character) and a \W (nonword character), or the boundary between an ASCII word character and the beginning or end of a string.4 Elements such as \b do not specify any characters to be used in a matched string; what they do specify, however, are legal positions at which a match can occur. Sometimes these elements are called regular expression anchors because they anchor the pattern to a specific position in the search string. The most commonly used anchor elements are ^, which ties the pattern to the beginning of the string, and $, which anchors the pattern to the end of the string.

For example, to match the word “JavaScript” on a line by itself, you can use the regular expression /^JavaScript$/. If you want to search for “Java” as a word by itself (not as a prefix, as it is in “JavaScript”), you can try the pattern /\sJava\s/, which requires a space before and after the word. But there are two problems with this solution. First, it does not match “Java” at the beginning or the end of a string, but only if it appears with space on either side. Second, when this pattern does find a match, the matched string it returns has leading and trailing spaces, which is not quite what’s needed. So instead of matching actual space characters with \s, match (or anchor to) word boundaries with \b. The resulting expression is /\bJava\b/. The element \B anchors the match to a location that is not a word boundary. Thus, the pattern `/\B[Ss]cript/` matches “JavaScript” and “postscript”, but not “script” or “Scripting”.

You can also use arbitrary regular expressions as anchor conditions. If you include an expression within (?= and ) characters, it is a lookahead assertion, and it specifies that the enclosed characters must match, without actually matching them. For example, to match the name of a common programming language, but only if it is followed by a colon, you could use `/[Jj]ava([Ss]cript)?(?=\:)/`. This pattern matches the word “JavaScript” in “JavaScript: The Definitive Guide”, but it does not match “Java” in “Java in a Nutshell” because it is not followed by a colon.

If you instead introduce an assertion with (?!, it is a negative lookahead assertion, which specifies that the following characters must not match. For example, `/Java(?!Script)([A-Z]\w*)/` matches “Java” followed by a capital letter and any number of additional ASCII word characters, as long as “Java” is not followed by “Script”. It matches “JavaBeans” but not “Javanese”, and it matches “JavaScrip” but not “JavaScript” or “JavaScripter”. Table 11-5 summarizes regular expression anchors.

Table 11-5. Regular expression anchor characters
Character	Meaning
^

Match the beginning of the string or, with the m flag, the beginning of a line.

$

Match the end of the string and, with the m flag, the end of a line.

\b

Match a word boundary. That is, match the position between a \w character and a \W character or between a \w character and the beginning or end of a string. (Note, however, that [\b] matches backspace.)

\B

Match a position that is not a word boundary.

(?=p)

A positive lookahead assertion. Require that the following characters match the pattern p, but do not include those characters in the match.

(?!p)

A negative lookahead assertion. Require that the following characters do not match the pattern p.

#### LOOKBEHIND ASSERTIONS
ES2018 extends regular expression syntax to allow “lookbehind” assertions. These are like lookahead assertions but refer to text before the current match position. As of early 2020, these are implemented in Node, Chrome, and Edge, but not Firefox or Safari.

Specify a positive lookbehind assertion with (?<=...) and a negative lookbehind assertion with (?<!...). For example, if you were working with US mailing addresses, you could match a 5-digit zip code, but only when it follows a two-letter state abbreviation, like this:
```js
/(?<= [A-Z]{2} )\d{5}/
```
And you could match a string of digits that is not preceded by a Unicode currency symbol with a negative lookbehind assertion like this:
```js
/(?<![\p{Currency_Symbol}\d.])\d+(\.\d+)?/u
```
#### FLAGS
Every regular expression can have one or more flags associated with it to alter its matching behavior. JavaScript defines six possible flags, each of which is represented by a single letter. Flags are specified after the second / character of a regular expression literal or as a string passed as the second argument to the RegExp() constructor. The supported flags and their meanings are:

g
The g flag indicates that the regular expression is “global”—that is, that we intend to use it to find all matches within a string rather than just finding the first match. This flag does not alter the way that pattern matching is done, but, as we’ll see later, it does alter the behavior of the String match() method and the RegExp exec() method in important ways.

i
The i flag specifies that pattern matching should be case-insensitive.

m
The m flag specifies that matching should be done in “multiline” mode. It says that the RegExp will be used with multiline strings and that the ^ and $ anchors should match both the beginning and end of the string and also the beginning and end of individual lines within the string.

s
Like the m flag, the s flag is also useful when working with text that includes newlines. Normally, a “.” in a regular expression matches any character except a line terminator. When the s flag is used, however, “.” will match any character, including line terminators. The s flag was added to JavaScript in ES2018 and, as of early 2020, is supported in Node, Chrome, Edge, and Safari, but not Firefox.

u
The u flag stands for Unicode, and it makes the regular expression match full Unicode codepoints rather than matching 16-bit values. This flag was introduced in ES6, and you should make a habit of using it on all regular expressions unless you have some reason not to. If you do not use this flag, then your RegExps will not work well with text that includes emoji and other characters (including many Chinese characters) that require more than 16 bits. Without the u flag, the “.” character matches any 1 UTF-16 16-bit value. With the flag, however, “.” matches one Unicode codepoint, including those that have more than 16 bits. Setting the u flag on a RegExp also allows you to use the new \u{...} escape sequence for Unicode character and also enables the \p{...} notation for Unicode character classes.

y
The y flag indicates that the regular expression is “sticky” and should match at the beginning of a string or at the first character following the previous match. When used with a regular expression that is designed to find a single match, it effectively treats that regular expression as if it begins with ^ to anchor it to the beginning of the string. This flag is more useful with regular expressions that are used repeatedly to find all matches within a string. In this case, it causes special behavior of the String match() method and the RegExp exec() method to enforce that each subsequent match is anchored to the string position at which the last one ended.

These flags may be specified in any combination and in any order. For example, if you want your regular expression to be Unicode-aware to do case-insensitive matching and you intend to use it to find multiple matches within a string, you would specify the flags uig, gui, or any other permutation of these three letters.

### 11.3.2 String Methods for Pattern Matching
Until now, we have been describing the grammar used to define regular expressions, but not explaining how those regular expressions can actually be used in JavaScript code. We are now switching to cover the API for using RegExp objects. This section begins by explaining the string methods that use regular expressions to perform pattern matching and search-and-replace operations. The sections that follow this one continue the discussion of pattern matching with JavaScript regular expressions by discussing the RegExp object and its methods and properties.

SEARCH()
Strings support four methods that use regular expressions. The simplest is search(). This method takes a regular expression argument and returns either the character position of the start of the first matching substring or −1 if there is no match:
```js
"JavaScript".search(/script/ui)  // => 4
"Python".search(/script/ui)      // => -1
```
If the argument to search() is not a regular expression, it is first converted to one by passing it to the RegExp constructor. search() does not support global searches; it ignores the g flag of its regular expression argument.

REPLACE()
The replace() method performs a search-and-replace operation. It takes a regular expression as its first argument and a replacement string as its second argument. It searches the string on which it is called for matches with the specified pattern. If the regular expression has the g flag set, the replace() method replaces all matches in the string with the replacement string; otherwise, it replaces only the first match it finds. If the first argument to replace() is a string rather than a regular expression, the method searches for that string literally rather than converting it to a regular expression with the RegExp() constructor, as search() does. As an example, you can use replace() as follows to provide uniform capitalization of the word “JavaScript” throughout a string of text:
```js
// No matter how it is capitalized, replace it with the correct capitalization
text.replace(/javascript/gi, "JavaScript");
```
replace() is more powerful than this, however. Recall that parenthesized subexpressions of a regular expression are numbered from left to right and that the regular expression remembers the text that each subexpression matches. If a $ followed by a digit appears in the replacement string, replace() replaces those two characters with the text that matches the specified subexpression. This is a very useful feature. You can use it, for example, to replace quotation marks in a string with other characters:
```js
// A quote is a quotation mark, followed by any number of
// nonquotation mark characters (which we capture), followed
// by another quotation mark.
let quote = /"([^"]*)"/g;
// Replace the straight quotation marks with guillemets
// leaving the quoted text (stored in $1) unchanged.
'He said "stop"'.replace(quote, '«$1»')  // => 'He said «stop»'
```
If your RegExp uses named capture groups, then you can refer to the matching text by name rather than by number:
```js
let quote = /"(?<quotedText>[^"]*)"/g;
'He said "stop"'.replace(quote, '«$<quotedText>»')  // => 'He said «stop»'
```
Instead of passing a replacement string as the second argument to replace(), you can also pass a function that will be invoked to compute the replacement value. The replacement function is invoked with a number of arguments. First is the entire matched text. Next, if the RegExp has capturing groups, then the substrings that were captured by those groups are passed as arguments. The next argument is the position within the string at which the match was found. After that, the entire string that replace() was called on is passed. And finally, if the RegExp contained any named capture groups, the last argument to the replacement function is an object whose property names match the capture group names and whose values are the matching text. As an example, here is code that uses a replacement function to convert decimal integers in a string to hexadecimal:
```js
let s = "15 times 15 is 225";
s.replace(/\d+/gu, n => parseInt(n).toString(16))  // => "f times f is e1"
```
MATCH()
The match() method is the most general of the String regular expression methods. It takes a regular expression as its only argument (or converts its argument to a regular expression by passing it to the RegExp() constructor) and returns an array that contains the results of the match, or null if no match is found. If the regular expression has the g flag set, the method returns an array of all matches that appear in the string. For example:
```js
"7 plus 8 equals 15".match(/\d+/g)  // => ["7", "8", "15"]
```
If the regular expression does not have the g flag set, match() does not do a global search; it simply searches for the first match. In this nonglobal case, match() still returns an array, but the array elements are completely different. Without the g flag, the first element of the returned array is the matching string, and any remaining elements are the substrings matching the parenthesized capturing groups of the regular expression. Thus, if match() returns an array a, a[0] contains the complete match, a[1] contains the substring that matched the first parenthesized expression, and so on. To draw a parallel with the replace() method, a[1] is the same string as $1, a[2] is the same as $2, and so on.

For example, consider parsing a URL5 with the following code:
```js
// A very simple URL parsing RegExp
let url = /(\w+):\/\/([\w.]+)\/(\S*)/;
let text = "Visit my blog at http://www.example.com/~david";
let match = text.match(url);
let fullurl, protocol, host, path;
if (match !== null) {
    fullurl = match[0];   // fullurl == "http://www.example.com/~david"
    protocol = match[1];  // protocol == "http"
    host = match[2];      // host == "www.example.com"
    path = match[3];      // path == "~david"
}
```
In this non-global case, the array returned by match() also has some object properties in addition to the numbered array elements. The input property refers to the string on which match() was called. The index property is the position within that string at which the match starts. And if the regular expression contains named capture groups, then the returned array also has a groups property whose value is an object. The properties of this object match the names of the named groups, and the values are the matching text. We could rewrite the previous URL parsing example, for example, like this:
```js
let url = /(?<protocol>\w+):\/\/(?<host>[\w.]+)\/(?<path>\S*)/;
let text = "Visit my blog at http://www.example.com/~david";
let match = text.match(url);
match[0]               // => "http://www.example.com/~david"
match.input            // => text
match.index            // => 17
match.groups.protocol  // => "http"
match.groups.host      // => "www.example.com"
match.groups.path      // => "~david"
```
We’ve seen that match() behaves quite differently depending on whether the RegExp has the g flag set or not. There are also important but less dramatic differences in behavior when the y flag is set. Recall that the y flag makes a regular expression “sticky” by constraining where in the string matches can begin. If a RegExp has both the g and y flags set, then match() returns an array of matched strings, just as it does when g is set without y. But the first match must begin at the start of the string, and each subsequent match must begin at the character immediately following the previous match.

If the y flag is set without g, then match() tries to find a single match, and, by default, this match is constrained to the start of the string. You can change this default match start position, however, by setting the lastIndex property of the RegExp object at the index at which you want to match at. If a match is found, then this lastIndex will be automatically updated to the first character after the match, so if you call match() again, in this case, it will look for a subsequent match. (lastIndex may seem like a strange name for a property that specifies the position at which to begin the next match. We will see it again when we cover the RegExp exec() method, and its name may make more sense in that context.)
```js
let vowel = /[aeiou]/y;  // Sticky vowel match
"test".match(vowel)      // => null: "test" does not begin with a vowel
vowel.lastIndex = 1;     // Specify a different match position
"test".match(vowel)[0]   // => "e": we found a vowel at position 1
vowel.lastIndex          // => 2: lastIndex was automatically updated
"test".match(vowel)      // => null: no vowel at position 2
vowel.lastIndex          // => 0: lastIndex gets reset after failed match
```
It is worth noting that passing a non-global regular expression to the match() method of a string is the same as passing the string to the exec() method of the regular expression: the returned array and its properties are the same in both cases.

MATCHALL()
The matchAll() method is defined in ES2020, and as of early 2020 is implemented by modern web browsers and Node. matchAll() expects a RegExp with the g flag set. Instead of returning an array of matching substrings like match() does, however, it returns an iterator that yields the kind of match objects that match() returns when used with a non-global RegExp. This makes matchAll() the easiest and most general way to loop through all matches within a string.

You might use matchAll() to loop through the words in a string of text like this:
```js
// One or more Unicode alphabetic characters between word boundaries
const words = /\b\p{Alphabetic}+\b/gu; // \p is not supported in Firefox yet
const text = "This is a naïve test of the matchAll() method.";
for(let word of text.matchAll(words)) {
    console.log(`Found '${word[0]}' at index ${word.index}.`);
}
```
You can set the lastIndex property of a RegExp object to tell matchAll() what index in the string to begin matching at. Unlike the other pattern-matching methods, however, matchAll() never modifies the lastIndex property of the RegExp you call it on, and this makes it much less likely to cause bugs in your code.

SPLIT()
The last of the regular expression methods of the String object is split(). This method breaks the string on which it is called into an array of substrings, using the argument as a separator. It can be used with a string argument like this:
```js
"123,456,789".split(",")           // => ["123", "456", "789"]
```
The split() method can also take a regular expression as its argument, and this allows you to specify more general separators. Here we call it with a separator that includes an arbitrary amount of whitespace on either side:
```js
"1, 2, 3,\n4, 5".split(/\s*,\s*/)  // => ["1", "2", "3", "4", "5"]
```
Surprisingly, if you call split() with a RegExp delimiter and the regular expression includes capturing groups, then the text that matches the capturing groups will be included in the returned array. For example:
```js
const htmlTag = /<([^>]+)>/;  // < followed by one or more non->, followed by >
"Testing<br/>1,2,3".split(htmlTag)  // => ["Testing", "br/", "1,2,3"]
```
### 11.3.3 The RegExp Class
This section documents the RegExp() constructor, the properties of RegExp instances, and two important pattern-matching methods defined by the RegExp class.

The RegExp() constructor takes one or two string arguments and creates a new RegExp object. The first argument to this constructor is a string that contains the body of the regular expression—the text that would appear within slashes in a regular-expression literal. Note that both string literals and regular expressions use the \ character for escape sequences, so when you pass a regular expression to RegExp() as a string literal, you must replace each \ character with \\. The second argument to RegExp() is optional. If supplied, it indicates the regular expression flags. It should be g, i, m, s, u, y, or any combination of those letters.

For example:
```js
// Find all five-digit numbers in a string. Note the double \\ in this case.
let zipcode = new RegExp("\\d{5}", "g");
```
The RegExp() constructor is useful when a regular expression is being dynamically created and thus cannot be represented with the regular expression literal syntax. For example, to search for a string entered by the user, a regular expression must be created at runtime with RegExp().

Instead of passing a string as the first argument to RegExp(), you can also pass a RegExp object. This allows you to copy a regular expression and change its flags:
```js
let exactMatch = /JavaScript/;
let caseInsensitive = new RegExp(exactMatch, "i");
```
#### REGEXP PROPERTIES
RegExp objects have the following properties:

source
This read-only property is the source text of the regular expression: the characters that appear between the slashes in a RegExp literal.

flags
This read-only property is a string that specifies the set of letters that represent the flags for the RegExp.

global
A read-only boolean property that is true if the g flag is set.

ignoreCase
A read-only boolean property that is true if the i flag is set.

multiline
A read-only boolean property that is true if the m flag is set.

dotAll
A read-only boolean property that is true if the s flag is set.

unicode
A read-only boolean property that is true if the u flag is set.

sticky
A read-only boolean property that is true if the y flag is set.

lastIndex
This property is a read/write integer. For patterns with the g or y flags, it specifies the character position at which the next search is to begin. It is used by the exec() and test() methods, described in the next two subsections.

TEST()
The test() method of the RegExp class is the simplest way to use a regular expression. It takes a single string argument and returns true if the string matches the pattern or false if it does not match.

test() works by simply calling the (much more complicated) exec() method described in the next section and returning true if exec() returns a non-null value. Because of this, if you use test() with a RegExp that uses the g or y flags, then its behavior depends on the value of the lastIndex property of the RegExp object, which can change unexpectedly. See “The lastIndex Property and RegExp Reuse” for more details.

EXEC()
The RegExp exec() method is the most general and powerful way to use regular expressions. It takes a single string argument and looks for a match in that string. If no match is found, it returns null. If a match is found, however, it returns an array just like the array returned by the match() method for non-global searches. Element 0 of the array contains the string that matched the regular expression, and any subsequent array elements contain the substrings that matched any capturing groups. The returned array also has named properties: the index property contains the character position at which the match occurred, and the input property specifies the string that was searched, and the groups property, if defined, refers to an object that holds the substrings matching the any named capturing groups.

Unlike the String match() method, exec() returns the same kind of array whether or not the regular expression has the global g flag. Recall that match() returns an array of matches when passed a global regular expression. exec(), by contrast, always returns a single match and provides complete information about that match. When exec() is called on a regular expression that has either the global g flag or the sticky y flag set, it consults the lastIndex property of the RegExp object to determine where to start looking for a match. (And if the y flag is set, it also constrains the match to begin at that position.) For a newly created RegExp object, lastIndex is 0, and the search begins at the start of the string. But each time exec() successfully finds a match, it updates the lastIndex property to the index of the character immediately after the matched text. If exec() fails to find a match, it resets lastIndex to 0. This special behavior allows you to call exec() repeatedly in order to loop through all the regular expression matches in a string. (Although, as we’ve described, in ES2020 and later, the matchAll() method of String is an easier way to loop through all matches.) For example, the loop in the following code will run twice:
```js
let pattern = /Java/g;
let text = "JavaScript > Java";
let match;
while((match = pattern.exec(text)) !== null) {
    console.log(`Matched ${match[0]} at ${match.index}`);
    console.log(`Next search begins at ${pattern.lastIndex}`);
}
```
THE LASTINDEX PROPERTY AND REGEXP REUSE
As you have seen already, JavaScript’s regular expression API is complicated. The use of the lastIndex property with the g and y flags is a particularly awkward part of this API. When you use these flags, you need to be particularly careful when calling the match(), exec(), or test() methods because the behavior of these methods depends on lastIndex, and the value of lastIndex depends on what you have previously done with the RegExp object. This makes it easy to write buggy code.

Suppose, for example, that we wanted to find the index of all `<p>` tags within a string of HTML text. We might write code like this:
```js
let match, positions = [];
while((match = /<p>/g.exec(html)) !== null) { // POSSIBLE INFINITE LOOP
    positions.push(match.index);
}
```
This code does not do what we want it to. If the html string contains at least one `<p>` tag, then it will loop forever. The problem is that we use a RegExp literal in the while loop condition. For each iteration of the loop, we’re creating a new RegExp object with lastIndex set to 0, so exec() always begins at the start of the string, and if there is a match, it will keep matching over and over. The solution, of course, is to define the RegExp once, and save it to a variable so that we’re using the same RegExp object for each iteration of the loop.

On the other hand, sometimes reusing a RegExp object is the wrong thing to do. Suppose, for example, that we want to loop through all of the words in a dictionary to find words that contain pairs of double letters:
```js
let dictionary = [ "apple", "book", "coffee" ];
let doubleLetterWords = [];
let doubleLetter = /(\w)\1/g;

for(let word of dictionary) {
    if (doubleLetter.test(word)) {
        doubleLetterWords.push(word);
    }
}
doubleLetterWords  // => ["apple", "coffee"]: "book" is missing!
```
Because we set the g flag on the RegExp, the lastIndex property is changed after successful matches, and the test() method (which is based on exec()) starts searching for a match at the position specified by lastIndex. After matching the “pp” in “apple”, lastIndex is 3, and so we start searching the word “book” at position 3 and do not see the “oo” that it contains.

We could fix this problem by removing the g flag (which is not actually necessary in this particular example), or by moving the RegExp literal into the body of the loop so that it is re-created on each iteration, or by explicitly 
Download .txt
gitextract_0ubatihb/

├── .github/
│   └── workflows/
│       └── gh-pages.yml
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── archetypes/
│   └── default.md
├── config.toml
├── content/
│   └── posts/
│       ├── ch1.md
│       ├── ch10.md
│       ├── ch11.md
│       ├── ch12.md
│       ├── ch13.md
│       ├── ch14.md
│       ├── ch15.md
│       ├── ch16.md
│       ├── ch17.md
│       ├── ch2.md
│       ├── ch3.md
│       ├── ch4.md
│       ├── ch5.md
│       ├── ch6.md
│       ├── ch7.md
│       ├── ch8.md
│       └── ch9.md
├── layouts/
│   └── partials/
│       └── head/
│           └── seo.html
└── static/
    ├── CNAME
    ├── browserconfig.xml
    └── site.webmanifest
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,056K chars).
[
  {
    "path": ".github/workflows/gh-pages.yml",
    "chars": 1170,
    "preview": "name: Js\n\non:\n  push:\n    branches:\n      - master  # Set a branch to deploy\n  pull_request:\n\njobs:\n  deploy:\n    runs-o"
  },
  {
    "path": ".gitignore",
    "chars": 1620,
    "preview": "# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig\n\n# Created by https://www."
  },
  {
    "path": ".gitmodules",
    "chars": 96,
    "preview": "[submodule \"themes/LoveIt\"]\n\tpath = themes/LoveIt\n\turl = https://github.com/dillonzq/LoveIt.git\n"
  },
  {
    "path": "LICENSE",
    "chars": 1063,
    "preview": "MIT License\n\nCopyright (c) 2021 Mr.Ten\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
  },
  {
    "path": "README.md",
    "chars": 939,
    "preview": "# JavaScript-The-Definitive-Guide-7th-zh\n\n《JavaScript 权威指南第七版》中英对照\n\n在线阅读:[https://js.okten.cn/](https://js.okten.cn/)\n\n<"
  },
  {
    "path": "archetypes/default.md",
    "chars": 84,
    "preview": "---\ntitle: \"{{ replace .Name \"-\" \" \" | title }}\"\ndate: {{ .Date }}\ndraft: true\n---\n\n"
  },
  {
    "path": "config.toml",
    "chars": 17580,
    "preview": "# for build\nbaseURL = 'https://blog.okten.cn/'\ntheme = \"LoveIt\"\nminify = true\nhtml = \"keep\"\nWhitespace = false\n\ntitle = "
  },
  {
    "path": "content/posts/ch1.md",
    "chars": 36677,
    "preview": "---\ntitle: \"第 1 章 JavaScript 概述\"\ndate: 2020-11-02T22:18:42+08:00\n---\n\nJavaScript is the programming language of the web."
  },
  {
    "path": "content/posts/ch10.md",
    "chars": 49789,
    "preview": "---\ntitle: \"第 10 章 模块\"\ndate: 2020-11-02T22:18:33+08:00\n---\n\nThe goal of modular programming is to allow large programs t"
  },
  {
    "path": "content/posts/ch11.md",
    "chars": 156222,
    "preview": "---\ntitle: \"第 11 章 JavaScript 标准库\"\ndate: 2020-11-02T22:18:32+08:00\n---\n\nSome datatypes, such as numbers and strings (Cha"
  },
  {
    "path": "content/posts/ch12.md",
    "chars": 36814,
    "preview": "---\ntitle: \"第 12 章 迭代器和生成器\"\ndate: 2020-11-02T22:18:31+08:00\n---\n\nIterable objects and their associated iterators are a f"
  },
  {
    "path": "content/posts/ch13.md",
    "chars": 115560,
    "preview": "---\ntitle: \"第 13 章 异步 JavaScript\"\ndate: 2020-11-02T22:18:30+08:00\n---\n\nSome computer programs, such as scientific simula"
  },
  {
    "path": "content/posts/ch14.md",
    "chars": 69942,
    "preview": "---\ntitle: \"第 14 章 元编程\"\ndate: 2020-11-02T22:18:29+08:00\n---\n\nThis chapter covers a number of advanced JavaScript feature"
  },
  {
    "path": "content/posts/ch15.md",
    "chars": 403817,
    "preview": "---\ntitle: \"第 15 章 Web 浏览器中的 JavaScript\"\ndate: 2020-11-02T22:18:28+08:00\n---\n\nThe JavaScript language was created in 199"
  },
  {
    "path": "content/posts/ch16.md",
    "chars": 144905,
    "preview": "---\ntitle: \"第 16 章 服务器端 JavaScript\"\ndate: 2020-11-02T22:18:27+08:00\n---\n\nNode is JavaScript with bindings to the underly"
  },
  {
    "path": "content/posts/ch17.md",
    "chars": 71806,
    "preview": "---\ntitle: \"第 17 章 JavaScript 工具和扩展\"\ndate: 2020-11-02T22:18:26+08:00\n---\n\nCongratulations on reaching the final chapter "
  },
  {
    "path": "content/posts/ch2.md",
    "chars": 17690,
    "preview": "---\ntitle: \"第 2 章 词法结构\"\ndate: 2020-11-02T22:18:41+08:00\n---\n\nThe lexical structure of a programming language is the set "
  },
  {
    "path": "content/posts/ch3.md",
    "chars": 118995,
    "preview": "---\ntitle: \"第 3 章 类型、值和变量\"\ndate: 2020-11-02T22:18:40+08:00\n---\n\nComputer programs work by manipulating values, such as t"
  },
  {
    "path": "content/posts/ch4.md",
    "chars": 90673,
    "preview": "---\ntitle: \"第 4 章 表达式和运算符\"\ndate: 2020-11-02T22:18:39+08:00\n---\n\nThis chapter documents JavaScript expressions and the op"
  },
  {
    "path": "content/posts/ch5.md",
    "chars": 84712,
    "preview": "---\ntitle: \"第 5 章 语句\"\ndate: 2020-11-02T22:18:38+08:00\n---\n\nChapter 4 described expressions as JavaScript phrases. By tha"
  },
  {
    "path": "content/posts/ch6.md",
    "chars": 73941,
    "preview": "---\ntitle: \"第 6 章 对象\"\ndate: 2020-11-02T22:18:37+08:00\n---\n\nObjects are JavaScript’s most fundamental datatype, and you h"
  },
  {
    "path": "content/posts/ch7.md",
    "chars": 74880,
    "preview": "---\ntitle: \"第 7 章 数组\"\ndate: 2020-11-02T22:18:36+08:00\n---\n\nThis chapter documents arrays, a fundamental datatype in Java"
  },
  {
    "path": "content/posts/ch8.md",
    "chars": 111745,
    "preview": "---\ntitle: \"第 8 章 函数\"\ndate: 2020-11-02T22:18:35+08:00\n---\n\nThis chapter covers JavaScript functions. Functions are a fun"
  },
  {
    "path": "content/posts/ch9.md",
    "chars": 78336,
    "preview": "---\ntitle: \"第 9 章 类\"\ndate: 2020-11-02T22:18:34+08:00\n---\n\nJavaScript objects were covered in Chapter 6. That chapter tre"
  },
  {
    "path": "layouts/partials/head/seo.html",
    "chars": 5907,
    "preview": "{{- $params := .Scratch.Get \"params\" -}}\n\n{{- with .Site.Params.verification.google -}}\n    <meta name=\"google-site-veri"
  },
  {
    "path": "static/CNAME",
    "chars": 11,
    "preview": "js.okten.cn"
  },
  {
    "path": "static/browserconfig.xml",
    "chars": 246,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig>\n    <msapplication>\n        <tile>\n            <square150x150logo"
  },
  {
    "path": "static/site.webmanifest",
    "chars": 426,
    "preview": "{\n    \"name\": \"\",\n    \"short_name\": \"\",\n    \"icons\": [\n        {\n            \"src\": \"/android-chrome-192x192.png\",\n     "
  }
]

About this extraction

This page contains the full source code of the ten-ltw/JavaScript-The-Definitive-Guide-7th-zh GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (1.7 MB), approximately 453.6k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!