├── .changeset ├── README.md └── config.json ├── .eslintrc.json ├── .github └── workflows │ ├── changeset-version.yml │ └── ci.yml ├── .gitignore ├── .npmrc ├── .prettierrc ├── .vscode └── settings.json ├── README.md ├── deploy.sh ├── docs ├── .vitepress │ └── config.ts ├── guide │ ├── 第一章-心智模型.md │ ├── 第七章-平等的价值.md │ ├── 第三章-值与变量.md │ ├── 第九章-突变.md │ ├── 第二章-JavaScript宇宙.md │ ├── 第五章-会见原始值.md │ ├── 第八章-Properties(属性).md │ ├── 第六章-会见对象与函数.md │ ├── 第十章-原型.md │ └── 第四章-从内部学习.md └── index.md ├── package.json ├── packages └── javascript-mental-modules-doc │ ├── CHANGELOG.md │ ├── docs │ ├── .vitepress │ │ └── config.ts │ ├── guide │ │ ├── 小册简介.md │ │ ├── 第一章-心智模型.md │ │ ├── 第七章-平等的价值.md │ │ ├── 第三章-值与变量.md │ │ ├── 第九章-突变.md │ │ ├── 第二章-JavaScript宇宙.md │ │ ├── 第五章-会见原始值.md │ │ ├── 第八章-Properties(属性).md │ │ ├── 第六章-会见对象与函数.md │ │ ├── 第十章-原型.md │ │ └── 第四章-从内部学习.md │ └── index.md │ └── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── turbo.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "master", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [], 11 | "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { 12 | "updateInternalDependents": "always" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@sudongyuer" 3 | } 4 | -------------------------------------------------------------------------------- /.github/workflows/changeset-version.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | env: 8 | CI: true 9 | PNPM_CACHE_FOLDER: .pnpm-store 10 | 11 | jobs: 12 | release: 13 | name: Release 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: checkout code repository 17 | uses: actions/checkout@v2 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: setup node.js 22 | uses: actions/setup-node@v2 23 | with: 24 | node-version: 14 25 | 26 | - name: Setup pnpm 27 | uses: pnpm/action-setup@v2.2.2 28 | with: 29 | version: 6 30 | 31 | - name: install dependencies 32 | run: pnpm install 33 | 34 | - name: Create Release Pull Request or Publish to npm 35 | # https://github.com/changesets/action 36 | uses: changesets/action@v1 37 | with: 38 | # this expects you to have a script called release which does a build for your packages and calls changeset publish 39 | publish: pnpm run build 40 | version: pnpm run version 41 | commit: "chore: update versions" 42 | title: "chore: update versions" 43 | env: 44 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} 45 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Github Action Deploy docs 2 | on: 3 | push: 4 | branches: 5 | - master 6 | env: 7 | CI: true 8 | PNPM_CACHE_FOLDER: .pnpm-store 9 | 10 | permissions: 11 | contents: write 12 | jobs: 13 | build-and-deploy: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: checkout code repository 17 | uses: actions/checkout@v2 18 | with: 19 | fetch-depth: 0 20 | - name: setup node.js 21 | uses: actions/setup-node@v2 22 | with: 23 | node-version: 14 24 | - name: install pnpm 25 | run: npm i pnpm@latest -g 26 | - name: install dependencies 27 | run: | 28 | pnpm install 29 | pnpm run build 30 | - name: Build and Deploy 31 | uses: JamesIves/github-pages-deploy-action@v4.3.3 32 | with: 33 | token: ${{ secrets.ACCESS_TOKEN }} 34 | branch: gh-pages-deploy 35 | folder: packages/javascript-mental-modules-doc/docs/.vitepress/dist 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .pnpm-debug.log 4 | .DS_Store 5 | .idea 6 | .turbo 7 | dist/** 8 | build/** 9 | .next/** 10 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers=true 2 | registry=https://registry.npmmirror.com/ 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | semi: false 2 | singleQuote: true 3 | printWidth: 80 4 | trailingComma: 'none' 5 | arrowParens: 'avoid' 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.enable": false, 3 | "editor.codeActionsOnSave": { 4 | "source.fixAll.eslint": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript Mental Models 2 |

3 | 4 |

5 | 6 | ## 在线地址 7 | https://sudongyuer.github.io/javascript-mental-models/ 8 | 9 | 10 | ## 你好👋我是速冻鱼🐟 这个小册的作者 11 | 12 | 在我学习JavaScript心智模型后,我觉得它对我的帮助很大,于是我便写了这个小册子,旨在帮助您建立一个良好的JavaScript 本小册是学习Dan Abramov的JustJavaScript的一些学习心得及笔记📒希望能帮助大家更好的理解JavaScript 13 |
14 | 如何您觉得我的小册子给您带来了一些对JavaScript理解的帮助,你可以给我买一杯Coffee感谢一些我的辛苦付出 ☕️ 您也可以加我`VX:sudongyuer` 和我交流 15 |
16 | 持续更新中 ~ 🚀🚀🚀 17 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # abort on errors 4 | set -e 5 | 6 | # build 7 | npm run docs:build 8 | 9 | # navigate into the build output directory 10 | cd docs/.vitepress/dist 11 | 12 | # if you are deploying to a custom domain 13 | # echo 'www.example.com' > CNAME 14 | 15 | git init 16 | git add -A 17 | git commit -m 'deploy' 18 | 19 | # if you are deploying to https://.github.io 20 | # git push -f git@github.com:/.github.io.git master 21 | 22 | # if you are deploying to https://.github.io/ 23 | git push -f git@github.com:sudongyuer/javascript-mental-models.git master:gh-pages-deploy 24 | 25 | cd ../../../ && rm -rf docs/.vitepress/dist 26 | 27 | cd - 28 | -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress' 2 | 3 | export default defineConfig({ 4 | lang: 'en-US', 5 | title: 'JavaScript 心智💗模型', 6 | description: 'Vite & Vue powered static site generator.', 7 | lastUpdated: true, 8 | base:"/javascript-mental-models/", 9 | 10 | themeConfig: { 11 | repo: 'sudongyuer/javascript-mental-models', 12 | docsDir: 'docs', 13 | docsBranch: 'main', 14 | editLinks: false, 15 | editLinkText: 'Edit this page on GitHub', 16 | lastUpdated: 'Last Updated', 17 | 18 | algolia: { 19 | appId: '8J64VVRP8K', 20 | apiKey: 'a18e2f4cc5665f6602c5631fd868adfd', 21 | indexName: 'vitepress' 22 | }, 23 | 24 | nav: [ 25 | { text: 'Start', link: '/guide', activeMatch: '^/$|^/guide/' }, 26 | { 27 | text: 'About ME', 28 | link: 'https://github.com/sudongyuer' 29 | } 30 | ], 31 | 32 | sidebar: { 33 | '/guide/': getGuideSidebar(), 34 | '/': getGuideSidebar() 35 | } 36 | } 37 | }) 38 | 39 | function getGuideSidebar() { 40 | return [ 41 | { 42 | text: '开始探索我们的JavaScript宇宙吧🌈', 43 | 44 | 45 | }, 46 | { text: '第一章-什么是心智模型', link: '/guide/第一章-心智模型' }, 47 | { text: '第二章-JavaScript宇宙', link: '/guide/第二章-JavaScript宇宙' }, 48 | { text: '第三章-值与变量', link: '/guide/第三章-值与变量' }, 49 | { text: '第四章-从内部学习', link: '/guide/第四章-从内部学习' }, 50 | { text: '第五章-会见原始值', link: '/guide/第五章-会见原始值' }, 51 | { text: '第六章-会见对象与函数', link: '/guide/第六章-会见对象与函数' }, 52 | { text: '第七章-平等的价值', link: '/guide/第七章-平等的价值' }, 53 | { text: '第八章-Properties(属性)', link: '/guide/第八章-Properties(属性)' }, 54 | { text: '第九章-突变', link: '/guide/第九章-突变' }, 55 | { text: '第十章-原型', link: '/guide/第十章-原型' }, 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /docs/guide/第一章-心智模型.md: -------------------------------------------------------------------------------- 1 | # MentalaModels 2 | 3 | ```js 4 | let a = 10; 5 | let b = a; 6 | a = 0; 7 | ``` 8 | 9 | 它运行后`a` 的 值是 多少?`b`的值是多少?,在进一步阅读之前先在脑海中解决它。 10 | 11 | ## 什么是心智模型 12 | 13 | > 这些关于某些事物如何在您的脑海中运作的近似值被称为“心智模型”。 14 | 15 | 看看下面的代码 16 | 17 | ```js 18 | function duplicateSpreadsheet(original) { 19 | if (original.hasPendingChanges) { 20 | throw new Error('You need to save the file before you can duplicate it.'); 21 | } 22 | let copy = { 23 | created: Date.now(), 24 | author: original.author, 25 | cells: original.cells, 26 | metadata: original.metadata, 27 | }; 28 | copy.metadata.title = 'Copy of ' + original.metadata.title; 29 | return copy; 30 | } 31 | ``` 32 | 33 | 你能发现这段代码的错误么?,你能在脑海中构建出他们的模型么 34 | 35 | 如果你细心你就会发现 36 | 37 | ```js 38 | copy.metadata.title = 'Copy of ' + original.metadata.title; 39 | ``` 40 | 41 | 会改变原始值~ 42 | 43 | 您可能已经注意到: 44 | 45 | - 此函数复制电子表格。 46 | - 如果未保存原始电子表格,则会引发错误。 47 | - 它会在新电子表格的标题前添加“副本”。 48 | 49 | ## 总结 50 | 51 | 既然您知道存在错误,您会以不同的方式阅读代码吗?如果您一开始使用“快速”思维系统,当您意识到代码中存在错误时,您可能会切换到更费力的“慢”系统。 52 | 53 | 当我们使用“快速”系统时,我们会根据代码的整体结构、命名约定和注释来猜测代码的作用。使用“慢”系统,我们逐步追溯代码所做的事情——一个累人且耗时的过程。 54 | 55 | 这就是为什么拥有一个准确的心智模型如此重要的原因。在你的脑海中模拟一台计算机是很困难的,当你不得不退回到“慢”的思维系统时,你的思维模型就是你可以依赖的。使用错误的心智模型,您将从根本上误解您的代码期望什么,并且您的所有努力都将付诸东流。 56 | 57 | 如果您根本找不到错误,请不要担心——这只是意味着您将从本课程中获得最大收益!在接下来的模块中,我们将一起重建 JavaScript 的心智模型,以便您可以立即发现此类错误。 58 | 59 | 这就是为什么我们需要建立对JS的心智模型 60 | -------------------------------------------------------------------------------- /docs/guide/第七章-平等的价值.md: -------------------------------------------------------------------------------- 1 | # 平等的价值 2 | 3 | > 想象一下,参加一个只有几种面具的狂欢节。一群穿着相同服装的人会在房间里开玩笑、跳舞、走动。这会让人困惑!你可能会和两个人交谈,却没有意识到你实际上是在和同一个人交谈两次。或者你可能认为你在和一个人说话,但实际上,你在和两个不同的人说话! 4 | 5 | 如果您在JavaScript中没有一个明确的平等的心智模型,那么每天都像狂欢节一样——而且不是以一种好的方式。你无法确定你处理的是相同的值,还是两个不同的值。因此,您经常会犯错误——比如更改一个您不打算更改的值。 幸运的是,我们已经完成了在JavaScript中建立平等概念的大部分工作。它以一种非常自然的方式符合我们的心智模式。 6 | 7 | ## Kinds of Equality 8 | 9 | 在JavaScript中,有几种相等,如果你已经写过JS了,你应该对下面的几种相等不陌生 10 | 11 | - **Strict Equality:** `a === b` (triple equals). 12 | - **Loose Equality:** `a == b` (double equals). 13 | - **Same Value Equality:** `Object.is(a, b)`. 14 | 15 | ## Object.is(a,b) 16 | 17 | 在JavaScript中,`Object.is(a,b)`告诉我们a和b是否有相同的值 18 | 19 | ```js 20 | console.log(Object.is(2, 2)); // true 21 | console.log(Object.is({}, {})); // false 22 | ``` 23 | 24 | 这叫做相同的值相等。 25 | 26 | 尽管方法名是`Object.is`并不特定于对象。它可以比较任意两个值,不管它们是否是对象! 27 | 28 | 检查一下你的直觉🥳 29 | 30 | ```js 31 | let dwarves = 7; 32 | let continents = '7'; 33 | let worldWonders = 3 + 4; 34 | ``` 35 | 36 | 提示一下:这段代码的心智模型是如下的 37 | 38 | image-20220327131519517 39 | 40 | 现在,尝试告诉我一下下面代码的答案 41 | 42 | ```js 43 | console.log(Object.is(dwarves, continents)); // ? 44 | console.log(Object.is(continents, worldWonders)); // ? 45 | console.log(Object.is(worldWonders, dwarves)); // ? 46 | ``` 47 | 48 | 下面是答案👇: 49 | 50 | 1. `Object.is(dwarves, continents)` 是 **`false`** 因为 `dwarves` 和 `continents` **指向不同的值**. 51 | 2. `Object.is(continents, worldWonders)` 是 **`false`** 因为 `continents` 和 `worldWonders` **指向不同的值**. 52 | 3. `Object.is(worldWonders, dwarves)` 是 **`true`** 因为 `worldWonders` 和 `dwarves` **指向相同的值**. 53 | 54 | 但是对象呢? 到这里,您可能要担心对象了。您可能听说过等式对对象无效,或者它比较“引用”。如果你有这样的直觉,那就暂时把它们放在一边。 相反,请看下面的代码片段 55 | 56 | ```js 57 | let banana = {}; 58 | let cherry = banana; 59 | let chocolate = cherry; 60 | cherry = {}; 61 | ``` 62 | 63 | 记住,{}总是表示“创建一个新的对象值”。另外,请记住=的意思是“将左侧的导线指向右侧的值”。 64 | 65 | image-20220327132233221 66 | 67 | 现在再来解答一下下面的问题?👇 68 | 69 | ```js 70 | console.log(Object.is(banana, cherry)); // ? 71 | console.log(Object.is(cherry, chocolate)); // ? 72 | console.log(Object.is(chocolate, banana)); // ? 73 | ``` 74 | 75 | 1. `Object.is(banana, cherry)` 是 **`false`** 因为 `banana` 和 `cherry` **指向不同的值**. 76 | 2. `Object.is(cherry, chocolate)` 是 **`false`** 因为 `cherry` 和 `chocolate` **指向不同的值**. 77 | 3. `Object.is(chocolate, banana)` 是 **`true`** 因为 `chocolate` 和 `banana` **指向相同的值**. 78 | 79 | 正如您所看到的,我们不需要任何额外的概念来解释相同的值相等性如何适用于对象。它是通过我们的思维模式自然产生的。这就是我所知道的一切! 80 | 81 | ## 严格相等 82 | 83 | 您以前可能使用过严格相等操作符 84 | 85 | ```js 86 | console.log(2 === 2); // true 87 | console.log({} === {}); // false 88 | ``` 89 | 90 | #### 同值平等与严格平等 91 | 92 | `Object.is`和`===`有什么区别呢? 93 | 94 | - Object.is和我们心智模型中的值相等保持一致 95 | - 严格相同绝大部门情况下也和心智模型一致,但是也有一些特殊情况 96 | 97 | 这两种不寻常的情况都涉及我们过去讨论过的“特殊数字”: 98 | 99 | 1. **`NaN === NaN`是`false`**,尽管它们的值相同。 100 | 2. **`-0 === 0`并且`0 === -0`是`true`**,尽管它们是不同的值。 101 | 102 | #### 第一个特殊的例子: `NaN` 103 | 104 | `NaN` 是一个特殊的Number ,当我们做无效的数学时就会出现 `0 / 0`: 105 | 106 | ```js 107 | let width = 0 / 0; // NaN 108 | ``` 109 | 110 | 进一步的计算 `NaN` 也将会给你一个 `NaN` : 111 | 112 | ```js 113 | let height = width * 2; // NaN 114 | ``` 115 | 116 | 您可能不会故意这样做,但如果您的数据或计算存在缺陷,就会发生这种情况. 117 | 118 | **记住 `NaN === NaN` 永远都是 `false`:** 119 | 120 | ```js 121 | console.log(width === height); // false 122 | ``` 123 | 124 | 然而, `NaN` 和 `NaN`是同一个值: 125 | 126 | ```js 127 | console.log(Object.is(width, height)); // true 128 | ``` 129 | 130 | image-20220327133517952 131 | 132 | 这很令人困惑。 NaN === NaN 为假的原因很大程度上是历史原因,所以我建议接受它作为生活的事实。如果您尝试编写一些检查值是否为 NaN 的代码(例如,打印警告),您可能会遇到这种情况。 133 | 134 | ```js 135 | function resizeImage(size) { 136 | if (size === NaN) { 137 | // 这将永远不会被记录:检查永远是假的! 138 | console.log('Something is wrong.'); 139 | } 140 | // ... 141 | } 142 | ``` 143 | 144 | 相反,这里有一些方法(它们都有效!)来检查size是否为NaN: 145 | 146 | - **`Number.isNaN(size)`** 147 | - **`Object.is(size, NaN)`** 148 | - **`size !== size`** 149 | 150 | 最后一个可能特别令人惊讶。给它一些时间。如果您没有看到它是如何检测NaN的,请尝试重新阅读这一节并再次思考它。 151 | 152 | size !== size有效,因为NaN === NaN是假的,正如我们已经知道的。所以反过来(NaN !== NaN)一定是真的。 因为NaN是唯一不是严格等于自身的值,size !== size只能表示size是NaN。 153 | 154 | 一个简单的历史: https://stackoverflow.com/questions/1565164/what-is-the-rationale-for-all-comparisons-returning-false-for-ieee754-nan-values/1573715#1573715 。确保开发人员能够以这种方式检测 NaN 是使 NaN === NaN 返回 false 的最初原因之一!这是在JavaScript出现之前就决定的 155 | 156 | #### 第二个特殊的例子: `-0` 157 | 158 | 在普通数学中,没有“负0”这样的概念,但在浮点数学中却存在[实际原因](https://softwareengineering.stackexchange.com/a/280708)。这里有一个有趣的事实。 159 | 160 | **Both `0 === -0` and `-0 === 0` are always `true`:** 161 | 162 | ```js 163 | let width = 0; // 0 164 | let height = -width; // -0 165 | console.log(width === height); // true 166 | ``` 167 | 168 | 然而, `0` 和 `-0`不是同一个值: 169 | 170 | ```js 171 | console.log(Object.is(width, height)); // false 172 | ``` 173 | 174 | image-20220327134050806 175 | 176 | #### 编码练习 177 | 178 | 现在您知道了Object.is和=== 是如何工作的了,我有一个小的编码练习给你。你不需要完成它,但它是一个有趣的难题 179 | 180 | 写一个函数strictEquals(a, b),返回与a === b相同的值。你的实现不能使用===或!==操作符。 181 | 182 | 下面是我的答案 🐥 183 | 184 | ```js 185 | function strictEquals(a, b) { 186 | if (Object.is(a, b)) { 187 | if (Object.is(a, NaN) || Object.is(b, NaN)) { 188 | return false 189 | } else { 190 | return true 191 | } 192 | } else { 193 | if ((Object.is(a, -0) && Object.is(b, 0)) || 194 | Object.is(b, -0) && Object.is(a, 0)) { 195 | return true 196 | } else { 197 | return false 198 | } 199 | } 200 | 201 | } // sudongyuer🐟 202 | ``` 203 | 204 | 听到这些特殊的数字和他们的行为可能会让人不知所措。不要过分强调这些特殊情况! 它们并不常见。既然你知道它们的存在,你就会在实践中认识到它们。在大多数情况下,我们可以相信`Object.is (a, b)` 和 `a === b`。 205 | 206 | ## 宽松的平等 207 | 208 | > 松散等号(双等号)是JavaScript的梦魇。下面是几个让你起鸡皮疙瘩的例子: 209 | 210 | ```js 211 | console.log([[]] == ''); // true 212 | console.log(true == [1]); // true 213 | console.log(false == [0]); // true 214 | ``` 215 | 216 | 松散相等(也称为“抽象相等”)的规则是晦涩难懂的。许多编码标准完全禁止在代码中使用==和!=。 尽管Just JavaScript对应该或不应该使用哪些特性没有强烈的意见,但我们不会详细讨论松散相等性。它在现代代码库中并不常见,它的规则在语言或我们的思维模式中也没有发挥更大的作用 217 | 218 | 松散相等规则被广泛认为是JavaScript早期的一个糟糕设计决策,但如果您仍然好奇,可以在这里查看[它是如何工作的](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#Loose_equality_using)。不要觉得有压力要记住它——你需要记住其他话题! 219 | 220 | 值得了解的一个相对常见的用例是 221 | 222 | ```js 223 | if (x == null) { 224 | // ... 225 | } 226 | ``` 227 | 228 | 这段代码相当于这样写: 229 | 230 | ```js 231 | if (x === null || x === undefined) { 232 | // ... 233 | } 234 | ``` 235 | 236 | 然而,即使是==的使用也可能在一些团队中引起争议。在使用==之前,最好讨论一下在团队代码库中可以容忍多少==。 237 | 238 | ## 回顾 239 | 240 | > JavaScript有几种等号。它们包括相同价值平等、严格平等和松散平等。 241 | 242 | - 理解这种平等有助于防止错误! 243 | - 你经常需要知道什么时候你在处理相同的值,什么时候你在处理两个不同的值。 244 | - 当我们画一个值和变量的图时,相同的值不能出现两次。`Object.is(a,b)`当变量a和b指向图上相同的值时(a, b)为真。 245 | - 相同的值,编写起来有点麻烦,但它也是最容易解释的,这就是我们开始使用它的原因 246 | 247 | 在实践中,您将使用严格相等,或a === b,最常见的。除了两种罕见的特殊情况外,它等价于相同的值相等: 248 | 249 | - `NaN === NaN` 是 `false`, 即使它们是相同的值. 250 | - `0 === -0` 和 `-0 === 0` 是 `true`, 但它们是不同的值. 251 | 252 | - 你可以查看 `x` 是 `NaN` 通过 `Number.isNaN(x)`. 253 | - **Loose equality** (`==`) 使用一组神秘的规则,通常会被避免. -------------------------------------------------------------------------------- /docs/guide/第三章-值与变量.md: -------------------------------------------------------------------------------- 1 | # Values and Variables 2 | 3 | 先看一个小代码片段 4 | 5 | ```js 6 | let reaction = 'yikes'; 7 | reaction[0] = 'l'; 8 | console.log(reaction); 9 | ``` 10 | 11 | 你认为这段代码最后的结果是什么呢?我希望你花一点时间,一步一步地写下你对每一行代码的确切思考过程。注意你现有思维模式中的任何空白或不确定因素,也把它们写下来。如果你有任何疑问,尽量清楚地表达出来 12 | 13 | 带着疑问🤔️我们往下看吧! 14 | 15 | ## Primitive Values Are Immutable(原始值是不可变的) 16 | 17 | > 上面的那道题,你知道正确答案了么,你答对了吗?这似乎是一种只有在JavaScript面试中才会出现的琐碎问题。尽管如此,它说明了关于原始值的一个重要观点。 `我们不能改变原始值` 18 | 19 | 请记住! 我们不能改变原始值!!! 20 | 21 | 我将用一个小例子来解释这一点。字符串(原始的)和数组(不是)有一些表面上的相似之处。数组是项目序列,字符串是字符序列: 22 | 23 | ```js 24 | let arr = [212, 8, 506]; 25 | let str = 'hello'; 26 | ``` 27 | 28 | 我们可以类似地访问数组的第一项和字符串的第一个字符。几乎感觉字符串就是数组: 29 | 30 | ```js 31 | console.log(arr[0]); // 212 32 | console.log(str[0]); // "h" 33 | ``` 34 | 35 | 但他们不是。让我们仔细看看。我们可以更改数组的第一项: 36 | 37 | ```js 38 | arr [0] = 420; 39 | console.log(arr); // [420, 8, 506] 40 | ``` 41 | 42 | 直观地说,很容易假设我们可以对字符串做同样的事情: 43 | 44 | ```js 45 | str [0] = 'j'; // ??? 46 | ``` 47 | 48 | **但我们不能。** 49 | 50 | 这是我们需要添加到我们的心智模型中的一个重要细节。字符串是原始值,**所有原始值都是不可变的。**“不可变”是一种花哨的拉丁语方式来表达“不变”。只读。我们不能乱用原始值。完全没有。 51 | 52 | JavaScript 不允许我们在任何原始值上设置属性,无论是数字、字符串还是其他东西。它是否会默默地拒绝我们的请求或抛出错误取决于我们的代码处于[哪种模式](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)。但请放心,这永远不会起作用: 53 | 54 | ```js 55 | let fifty = 50; 56 | fifty.shades = 'gray'; // No! 57 | ``` 58 | 59 | 像任何数字一样,50是一个原始值。我们不能在上面设置属性。 记住,在我们的JavaScript世界中,所有的原始值都是遥远的星星,离我们的代码最远。我们可以指出它们,但它们将永远停留在原地,不变。 奇怪的是,我觉得这很安慰~ 60 | 61 | ![image-20220320093614875](https://tva1.sinaimg.cn/large/e6c9d24egy1h0g3jlk8a0j20zw0lkgmz.jpg) 62 | 63 | ## Variables and Values—A Contradiction?(变量和值 矛盾么) 64 | 65 | 看看下面代码你觉得答案是什么 66 | 67 | ```js 68 | let pet = 'Narwhal'; 69 | pet = 'The Kraken'; 70 | console.log(pet); // ? 71 | ``` 72 | 73 | > 如果你认为我想弄乱你的脑袋,那你是对的!答案是`"The Kraken"`。不变性在这里不起作用。 74 | > 75 | > 如果你弄错了,不要绝望!这个例子似乎与字符串不变性相矛盾,**但事实并非如此。** 76 | > 77 | > 当你刚接触一门语言时,有时有必要抛开矛盾,这样你就可以避免兔子洞并继续学习。但既然你致力于建立一个心智模型,你就需要质疑矛盾。 78 | > 79 | > *矛盾揭示了心智模型的差距。* 80 | 81 | ## Variables Are Wires(变量是连线) 82 | 83 | 让我们来看看解释 84 | 85 | ```js 86 | let pet = 'Narwhal'; 87 | pet = 'The Kraken'; 88 | console.log(pet); "The Kraken" 89 | ``` 90 | 91 | 我们知道字符串值不能改变,因为它们是原始的。但宠物变量确实变成了“海怪”。这是怎么回事? 这似乎是一个矛盾,但它不是。 92 | 93 | 我们说过原始值不能改变,但是我们没有说任何关于变量的东西!随着我们不断完善我们的思维模式. 94 | 95 | 我们需要理清几个相关的概念: 96 | 97 | - **变量不是值** 98 | - **变量指向值** 99 | 100 | ## Assigning a Value to a Variable () 101 | 102 | > 在我们的JavaScript世界中,变量是指向值的连线。 例如,我可以将pet变量指向“Narwhal”值。(我也可以说我把值“Narwhal”赋值给变量pet) 103 | 104 | ```js 105 | let pet = 'Narwhal'; 106 | pet = 'The Kraken'; 107 | console.log(pet); // "The Kraken" 108 | ``` 109 | 110 | 111 | 112 | iShot2022-03-20 10.06.59 113 | 114 | ```js 115 | pet = 'The Kraken'; 116 | ``` 117 | 118 | 119 | 120 | iShot2022-03-20 10.09.23 121 | 122 | - 我在这里所做的一切只是指示JavaScript将左侧(宠物)的变量或“连线”指向右侧(‘the Kraken’)的值。 123 | - 它会一直指向那个值,除非我以后再重新赋值。 124 | 125 | ## Rules of Assignment 126 | 127 | - 赋值的左侧必须是“连线”——例如pet变量。 128 | - 赋值的右边必须是一个表达式,所以它总是会产生一个值。我们的表达可以很简单,比如2或者“hello”。它也可以是一个更复杂的表达式——例如 pet = count +'sudongyuer' 129 | 130 | 在这里,count + ' sudongyuer'是一个表达式——一个JavaScript的问题。JavaScript将用一个值回答它(例如,“101 sudongyuer”)。从现在开始,宠物变量“wire”将指向那个特定的值 131 | 132 | 如果右边必须是一个表达式,这是否意味着简单的东西,如数字2或字符串,如' the Kraken',写在代码也是表达式?是的!这样的表达式被称为字面量——因为我们字面地写下它们产生的值。 133 | 134 | ## Reading a Value of a Variable 135 | 136 | > 我们当然可以从变量中读取值 137 | 138 | ```js 139 | console.log(pet); 140 | ``` 141 | 142 | 这是不足为奇的。 但是请注意,它不是我们传递给console.log的pet变量。 143 | 144 | 我们可以口头上这么说,但是我们不能把变量传递给函数。传递pet变量的当前值。 145 | 146 | 这是如何工作的? 事实证明,像pet这样的变量名也可以用作表达式!当我们编写pet时,我们要问JavaScript一个问题:“pet的当前值是多少?”为了回答我们的问题,JavaScript跟随宠物“wire”,并返回这个“wire”末尾的值。 147 | 148 | **所以同一个表达式可以在不同的时间给我们不同的值!** 149 | 150 | ### Nitpicking 151 | 152 | > JS中只有值传递!!!JS中只有值传递!!!JS中只有值传递!!! 153 | 154 | 谁在乎你说的是“传递一个变量”还是“传递一个值”?这两者之间的区别是不是太迂腐了?我当然不鼓励打断你的同事纠正他们。那是在浪费大家的时间。 但是您需要在头脑中清楚地了解每个JavaScript概念可以做什么。你不能骑自行车溜冰。你不能给蚊子唱歌。而且不能传递变量——至少在JavaScript中不能。 下面是一个小例子,说明为什么这些细节很重要。 155 | 156 | ```js 157 | function double(x) { 158 | x = x * 2; 159 | } 160 | 161 | let money = 10; 162 | double(money); 163 | console.log(money); // ? 164 | ``` 165 | 166 | ## 回顾 167 | 168 | - **原始值是不可变的**它们是我们 JavaScript 世界的永久组成部分——我们无法创建、销毁或更改它们。例如,我们不能在字符串值上设置属性,因为它是原始值。数组*不是*原始的,所以我们*可以*设置它们的属性。 169 | - **变量不是值**每个变量都*指向*一个特定的值。我们可以使用赋值运算符来更改*它*指向的值。`=` 170 | - **变量就像电线**“线”不是 JavaScript 的概念——但它可以帮助我们想象变量如何指向值。当我们进行赋值时,左边总是有一个连线,右边是一个表达式(产生一个值)。 171 | - **注意矛盾**如果你学到的两件事似乎相互矛盾,不要气馁。通常这是一个迹象,表明潜藏着更深层次的真相。 172 | - **语言很重要**我们正在建立一个心智模型,以便我们对宇宙中可能发生或不可能发生的*事情*充满信心,我们可能会以随意的方式谈论这些想法(吹毛求疵通常会适得其反),但我们对术语背后含义的理解需要准确。 173 | -------------------------------------------------------------------------------- /docs/guide/第九章-突变.md: -------------------------------------------------------------------------------- 1 | # Mutation 2 | 3 | > 在上一讲属性的模块中,我们介绍了夏洛克·福尔摩斯搬到马里布的秘密,但我们还没有解释它。 这一次,我们将一步一步地浏览代码,并一起绘制图表,以便检查您的心智模型。 4 | 5 | ## Step 1:声明 `sherlock` 变量 6 | 7 | ```js 8 | let sherlock = { 9 | surname: 'Holmes', 10 | address: { city: 'London' } 11 | }; 12 | ``` 13 | 14 | 请根据你之间建立的心智模型画一下图 15 | 16 | 这是我的图👇 17 | 18 | ![image-20220417171439346](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cu557ky7j20yg0lgabb.jpg) 19 | 20 | ### 没有嵌套的对象 21 | 22 | > 注意,我们这里不是一个,而是两个完全独立的对象。两对花括号表示两个对象。 23 | 24 | ## Step 2:描述 `john`变量 25 | 26 | ```js 27 | let john = { 28 | surname: 'Watson', 29 | address: sherlock.address 30 | }; 31 | ``` 32 | 33 | 请根据你之间理建立的心智模型画一下图 34 | 35 | 这是我的图👇 36 | 37 | ![image-20220417172052448](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cubmnym9j20ze0lo0uh.jpg) 38 | 39 | ### 属性总是指向值 40 | 41 | > 记住:属性总是指向一个值!它不能指向另一个属性或变量。通常,我们宇宙中的所有导线都指向值。 42 | 43 | ![image-20220417172311159](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cue14folj20zg0nkdhm.jpg) 44 | 45 | ## Step 3:改变属性 46 | 47 | ```js 48 | john.surname = 'Lennon'; 49 | john.address.city = 'Malibu'; 50 | ``` 51 | 52 | 请根据你之间理建立的心智模型画一下图 53 | 54 | 这是我的图👇 55 | 56 | ![image-20220417173136213](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cums23l2j21000li761.jpg) 57 | 58 | ```js 59 | console.log(sherlock.surname); // "Holmes" 60 | console.log(sherlock.address.city); // "Malibu" 61 | console.log(john.surname); // "Lennon" 62 | console.log(john.address.city); // "Malibu" 63 | ``` 64 | 65 | ## Mutation 66 | 67 | > 突变是"改变"的一种奇特说法 68 | 69 | 例如,我们可以说我们`change`了一个对象的属性,或者我们可以说我们`mutated`了那个对象(及其属性)。这是一样的。 70 | 71 | 人们喜欢说“mutate”,因为这个词有一种不祥的意味。它提醒你要格外小心。这并不意味着突变是“坏的”——它只是编程!但你需要非常有意识的去做。 72 | 73 | 让我们回顾一下最初的任务。我们想给约翰换个姓,然后把他搬到马里布。现在让我们看看我们的两个`mutations`: 74 | 75 | ```js 76 | // Step 3: Changing the Properties 77 | john.surname = 'Lennon'; 78 | john.address.city = 'Malibu'; 79 | ``` 80 | 81 | 哪些对象在这里被改变了? 82 | 83 | 第一行修改了john所指向的对象。它的意思是:我们想要改变约翰的姓氏。该对象表示John的数据,因此我们改变了它的姓氏属性。 84 | 85 | 然而,第二行做了一些非常不同的事情。它不会改变john指向的对象。相反,它改变了一个完全不同的对象——我们可以通过john.address访问的对象。如果我们看一下上面我们画的图,我们知道我们将通过`sherlock.address`和`john.address`将指向相同的对象! 86 | 87 | 通过改变程序中其他地方使用的对象,我们弄得一团糟。 88 | 89 | **有趣的事实** 90 | 91 | 这就是为什么对象被“嵌套”的直觉是如此错误!它让我们忘记了可能有很多对象指向我们更改过的对象。 92 | 93 | 所以对象只是值的集合,并不是值存在于对象 94 | 95 | ### 可能的解决方案:改变另一个对象 96 | 97 | ```js 98 | // Replace Step 3 with this code: 99 | john.surname = 'Lennon'; 100 | john.address = { city: 'Malibu' }; 101 | ``` 102 | 103 | ### 替代解决方案:没有对象突变 104 | 105 | ```js 106 | // Replace Step 3 with this code: 107 | john = { 108 | surname: 'Lennon', 109 | address: { city: 'Malibu' } 110 | }; 111 | ``` 112 | 113 | 现在在打印下面的值就会发现 114 | 115 | ```js 116 | console.log(sherlock.surname); // "Holmes" 117 | console.log(sherlock.address.city); // "London" 118 | console.log(john.surname); // "Lennon" 119 | console.log(john.address.city); // "Malibu" 120 | ``` 121 | 122 | 123 | 124 | ## Mutation 是很坏的么 125 | 126 | - 不要以为突变是 "坏事 "就走了。那是一种懒惰的过度简化,掩盖了真正的理解。如果数据随时间变化,某处就会发生变异。问题是,什么应该被突变,在哪里,以及什么时候? 127 | 128 | - 当你突变一个对象时,变量和属性可能已经指向它了。你的突变会影响以后 "跟随 "这些线的任何代码。 129 | 130 | - 这既是一种祝福也是一种诅咒。变异使改变一些数据变得很容易,并能立即在整个程序中 "看到 "这种变化。然而,无纪律的突变使我们更难预测程序会做什么。 131 | 132 | - 有一个学派认为,突变最好被限制在你的应用程序的一个非常狭窄的层。根据这一理念,其好处是你的程序的行为更容易预测。缺点是你要写更多的代码来 "传递东西 "和避免变异。 133 | 134 | - 值得注意的是,突变你刚刚创建的新对象总是可以的,因为现在还没有其他的线指向它们。在其他情况下,我建议你对你要突变的东西和时间要非常谨慎。你对变异的依赖程度取决于你的应用程序的架构。 135 | 136 | ## 回顾 137 | 138 | - 在我们的宇宙中,对象从来都是 不是"嵌套 "的 139 | - 改变一个对象的属性也被称为突变该对象。 140 | - 如果你改变了一个对象,你的代码将通过任何指向该对象的线来 "看到 "这一变化。有时,这可能是你想要的。然而,突变意外的共享数据可能会导致bug。 141 | - 你可以用const而不是let来声明一个变量。这允许你强制这个变量总是指向同一个值。但请记住,const并不能阻止对象的变异!你可以用const来声明一个变量,而不是用let。 142 | - 在代码中突变你刚刚创建的对象是安全的。大体上,你会在多大程度上使用变异取决于你的应用程序的架构。 -------------------------------------------------------------------------------- /docs/guide/第二章-JavaScript宇宙.md: -------------------------------------------------------------------------------- 1 | # JavaScript宇宙 2 | 3 | ## Values and Code 4 | 5 | > 虽然我们的值和我们的代码交互,但值是存在于完全独立的空间中 6 | 7 | - 值不存在于我们的代码中 8 | - 我们的代码中不包括值,我们的代码只是一堆指令,有`if`语句、变量声明、逗号和花括号。 9 | 10 | image-20220219194800337 11 | 12 | 可以想象一下我们是一个小人站在地球上,手里拿着要执行的code,上面有一条条指令,值存在于离我们比较远的星球上`boolean`、`number`、`string`、`symbol`、`null`、`undifined`、`object`、`function` 13 | 14 | image-20220219194727606 15 | 16 | ## Values 17 | 18 | 概括的说,值有两种类型 19 | 20 | - 原始类型 21 | - 原始的价值观就像星星——冰冷而遥远,但当我需要它们时,它们总是在那里。 22 | - 即使在我这颗小行星的表面,我也能找到它们,并指出它们。 23 | - 它们可以是数字和字符串等。所有原始值都有一个共同点:它们是JavaScript世界的永久组成部分。我可以指出它们,但我不能创造、破坏或改变它们 24 | 25 | ```js 26 | console.log(2); 27 | console.log("hello"); 28 | console.log(undefined); 29 | ``` 30 | 31 | - 对象和函数 32 | - 对象和函数也是值,但与原始值不同的是,我可以在代码中操作它们。 33 | - 如果原始值就像遥远的恒星,那么对象和函数就更像漂浮在我的行星周围的小行星。它们不是我代码的一部分,但它们离我很近,可以操纵 34 | 35 | ## Types of Values 36 | 37 | > JavaScript的值有9种类型 38 | 39 | ### Primitive Values(原始值) 40 | 41 | - Undefined 42 | - 用于无意中丢失的值 43 | - Null 44 | - 用于故意丢失值 45 | - Booleans 46 | - (true和false),用于逻辑运算。 47 | - Numbers 48 | - (-100, 3.14,等等),用于数学计算。 49 | - BigInts 50 | - (罕见且新颖),用于大数字的数学。 51 | - String 52 | - (“hello”,“abracadabra”等),用于文本。 53 | - Symbols 54 | - (不常见),用于举行仪式和隐藏秘密。 55 | 56 | ### Object and Functions(对象和函数) 57 | 58 | - Object({}和其他),用于对相关数据和代码进行分组。 59 | - Functions(x => x * 2 和其他),用于引用代码。 60 | 61 | ### No Other Types(没有其他类型) 62 | 63 | 在JavaScript中,除了我们刚刚列举的那些之外,没有其他的基本值类型。 64 | 65 | ```js 66 | console.log(typeof([])); // "object" 67 | console.log(typeof(new Date())); // "object" 68 | console.log(typeof(/(hello|goodbye)/)); // "object" 69 | ``` 70 | 71 | ⚠️ 你可能听说过everything 都是对象。这是一个流行的都市传说,但它不是真的。 72 | 73 | 虽然像 `"hi".toUpperCase()` 这样的代码使“hi”看起来像一个对象,但这只是一种错觉。当您这样做时,JavaScript会创建一个临时对象,然后立即丢弃它。如果这个机制还不适合你,那也没关系。这确实相当令人困惑! 现在,您只需要记住原始值(如数字和字符串)不是对象。 74 | 75 | ## Expressions 76 | 77 | > 有很多问题JavaScript无法回答。但是JavaScript很乐意回答一些问题。这些问题有一个特殊的名称,它们被称为表达式。 78 | 79 | 表达式是JavaScript可以回答的问题。JavaScript用它知道的唯一方式来回答表达式——用值。 80 | 81 | 总之记住一句话:**Expressions always result in a single value.** 82 | 83 | # 总结 84 | 85 | - 在JS世界中有值也有code,我们可以认为不同的值悬浮在JS的宇宙中,它们不存在于我们的代码中,但是我们可以在代码中引用它们。 86 | - 值有两种分类,一种是原始类型的值,一种是对象和方法的值,总共有九种不同的类型。每种类型都有特定的用途,但有些很少使用。 87 | - 有些值是孤独的,比如`null`是Null类型的,`undefined`是Undefined类型的,我们将在后面了解到,这两个孤独的值是很多错误的制造者! 88 | - 我们可以向JS宇宙询问表达式,表达式总是表示一个值 89 | - 我们可以用`typeof`来得到值的类型 -------------------------------------------------------------------------------- /docs/guide/第五章-会见原始值.md: -------------------------------------------------------------------------------- 1 | # Meeting the Primitive Values 2 | 3 | 到目前为止,我们一直在从地球表面观察我们的 JavaScript 宇宙。我们已经熟悉了远距离填充我们宇宙的价值观,但在这个模块中,我们正在改变它。我们将跳上宇宙飞船去探索,向我们介绍 JavaScript 世界中的每一个价值。 4 | 5 | *花时间详细查看每个值可能感觉没有必要,但是当您清楚地看到它们是两个不同的*苹果时,您只能说存在“两个苹果” 。区分值是理解JavaScript 中*相等性*的关键——这将是我们的下一个主题。 6 | 7 | 我们的宇宙飞船将引导我们穿越 JavaScript 的“天体”来满足不同的价值观。我们将首先遇到原始值:布尔值、数字、字符串等。稍后,我们将遇到对象和函数。将其视为观光旅游。 8 | 9 | image-20220320113551284 10 | 11 | ## Undefind 12 | 13 | > 我们将从未定义类型开始。这是一个非常简单的起点,因为这个类型只有一个值——undefined。 14 | 15 | ```js 16 | console.log(typeof(undefined)); // "undefined" 17 | ``` 18 | 19 | image-20220320113806163 20 | 21 | 22 | 23 | 它被称为未定义,因此您可能会认为它不存在——但它*是*一个值,并且是一个非常真实的值!像黑洞一样,`undefined`脾气暴躁,经常会带来麻烦。例如,从中读取一个属性会破坏你的程序: 24 | 25 | ```js 26 | let person = undefined; 27 | console.log(person.mood); // TypeError! 28 | ``` 29 | 30 | 哦,好吧。幸运的是,在整个JavaScript世界中只有一个undefined。你可能会想:它到底为什么会存在?在JavaScript中,它表示无意中丢失的值的概念。 你可以在你自己的代码中使用它,比如写undefined 2或“hello”。然而,undefined也通常“自然发生”。它会在JavaScript不知道你想要的值的情况下出现。例如,如果你忘记给一个变量赋值,它将指向undefined: 31 | 32 | ```js 33 | let bandersnatch; 34 | console.log(bandersnatch); // undefined 35 | ``` 36 | 37 | ![image-20220320114129996](https://tva1.sinaimg.cn/large/e6c9d24egy1h0g75vp9ghj208a028a9x.jpg) 38 | 39 | **然后你可以将它指向另一个值,或者如果你想的话,再次指向undefined。 别太在意它的名字。人们很容易把undefined看作是某种变量状态,例如“这个变量还没有定义”。但这完全是一种误导!实际上,如果读取的变量实际上没有定义(或在let声明之前),则会得到一个错误** 40 | 41 | ```js 42 | console.log(jabberwocky); // ReferenceError! 43 | let jabberwocky; 44 | ``` 45 | 46 | 实际上,undefined是一个常规的原始值,就像2或"hello"。 47 | 48 | 49 | 50 | ## Null 51 | 52 | image-20220320115014478 53 | 54 | 我们的下一站是`null`。你可以把null看作undefined的姐妹值;这种类型只有一个值 - null。它的行为与undefined很相似。例如,当您试图访问它的属性时,它也会引起混乱 55 | 56 | ```js 57 | let mimsy = null; 58 | console.log(mimsy.mood); // TypeError! 59 | ``` 60 | 61 | ![image-20220320115136416](https://tva1.sinaimg.cn/large/e6c9d24egy1h0g7ge0xctj20d004eweg.jpg) 62 | 63 | **Fun Fact** 64 | 65 | null是它自己的类型的唯一值。然而,null也是一个骗子。由于JavaScript中的一个bug,它假装是一个对象: 66 | 67 | ```js 68 | console.log(typeof(null)); // "object" (a lie!) 69 | ``` 70 | 71 | 您可能认为这意味着null是一个对象。不要落入这个陷阱!它是一个原始值,它的行为方式与对象完全不同。 不幸的是,typeof(null)是一个历史上的意外,我们将不得不永远忍受它。 72 | 73 | 在实践中,null用于故意丢失值。为什么null和undefined都有?这可以帮助您区分编码错误(可能导致未定义)和有效的缺失数据(可能表示为null)。然而,这只是一种约定,JavaScript并没有强制这种用法。有些人会尽量避免这两种情况! 74 | 75 | ## Booleans 76 | 77 | ![image-20220320120314654](https://tva1.sinaimg.cn/large/e6c9d24egy1h0g7si6qo1j20dw09wt8t.jpg) 78 | 79 | > 接下来,我们将学习布尔值!像白天和黑夜或开和关一样,只有两个布尔值:true和false。 80 | 81 | ```js 82 | console.log(typeof(true)); // "boolean" 83 | console.log(typeof(false)); // "boolean" 84 | ``` 85 | 86 | 我们可以用它们进行逻辑运算 87 | 88 | ```js 89 | let isSad = true; 90 | let isHappy = !isSad; // The opposite 91 | let isFeeling = isSad || isHappy; // Is at least one of them true? 92 | let isConfusing = isSad && isHappy; // Are both true? 93 | ``` 94 | 95 | image-20220320120540009 96 | 97 | ## Numbers 98 | 99 | image-20220320120616015 100 | 101 | > 到目前为止,我们已经介绍了四个值:null、undefined、true和false。 坚持住,当我们在我们的心智模型中增加18,000,000,437,000,000,736,874亿,4544,812,624个值的时候! 102 | 103 | ```js 104 | console.log(typeof(28)); // "number" 105 | console.log(typeof(3.14)); // "number" 106 | console.log(typeof(-140)); // "number" 107 | ``` 108 | 109 | 一开始,数字可能看起来没什么了不起,但让我们更好地了解它们! 110 | 111 | Math for Computers 112 | 113 | > JavaScript数字与普通数学数字的行为方式不完全相同。下面是一个演示代码片段 114 | 115 | ```js 116 | console.log(0.1 + 0.2 === 0.3); // false 117 | console.log(0.1 + 0.2 === 0.30000000000000004); // true 118 | ``` 119 | 120 | 1. 这可能看起来非常令人惊讶!与普遍的看法相反,这并不意味着JavaScript的数据被打破了。 121 | 2. 这种行为在不同的编程语言中很常见。它甚至还有个名字:浮点数学。 122 | 3. 你看,JavaScript并没有实现我们在现实生活中使用的那种数学。浮点数学是“计算机的数学”。 123 | 4. 不要太担心这个名字,也不要担心它到底是怎么工作的。很少有人知道它所有的微妙之处,这就是问题的关键! 124 | 5. 它在实践中运行得足够好,以至于大多数时候你都不会去想它。尽管如此,让我们快速地看看是什么使它变得不同的 125 | 126 | 你曾经用过扫描仪把一张实物照片或一份文件变成数码照片吗?这个类比可以帮助我们理解JavaScript数字。 扫描仪通常最多能分辨1600万种颜色。如果你用红色蜡笔画一幅画并扫描它,扫描出来的图像也应该是红色的——但它将是我们的扫描仪从这1600万种颜色中挑选出的最接近红色的颜色。所以,如果你有两支颜色稍有不同的红色蜡笔,扫描仪可能会误以为它们的颜色是完全一样的! 我们可以说扫描仪使用颜色的精度有限。 127 | 128 | 我们可以把所有的JavaScript数字想象成一个轴。我们越接近0,数字就越精确,它们之间的“位置”也越接近 129 | 130 | ![iShot2022-03-20 12.12.02](https://tva1.sinaimg.cn/large/e6c9d24egy1h0g81zxdd4g20zk0dsqv9.gif) 131 | 132 | ```js 133 | console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 134 | console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992 135 | console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992 (again!) 136 | console.log(Number.MAX_SAFE_INTEGER + 3); // 9007199254740994 137 | console.log(Number.MAX_SAFE_INTEGER + 4); // 9007199254740996 138 | console.log(Number.MAX_SAFE_INTEGER + 5); // 9007199254740996 (again!) 139 | ``` 140 | 141 | 幸运的是,任何在 `Number.MIN_SAFE_INTEGER` 和 `Number.MAX_SAFE_INTEGER` 之间的整数是正确的. 这就是为什么 `10 + 20 === 30`. 142 | 143 | 但是当我们写0.1或0.2的时候,我们不会得到精确的0.1和0.2。我们在JavaScript中得到了最接近的可用数字。它们几乎一模一样,但可能会有细微的差别。这些微小的差异加起来,这就是为什么0.1 + 0.2和写0.3的数字不完全一样。 144 | 145 | 为什么需要浮点数字呢? 146 | 147 | 按照我的理解,JS宇宙中保存不了那么多的number类型的值,因为内存有限 148 | 149 | 由于计算机内存有限,无论使用[二进制分数](https://floating-point-gui.de/formats/binary/)还是十进制分数,您都无法以无限精度存储数字:在某些时候您必须切断。但是需要多少准确度?它在*哪里*需要?有多少个整数位和多少个小数位? 150 | 151 | - 对于建造高速公路的工程师来说,它是 10 米还是 10.0001 米宽并不重要——他们的测量结果可能一开始就没有那么准确。 152 | - 对于设计微芯片的人来说,0.0001 米(十分之一毫米)是一个*巨大*的差异——但他们永远不必处理大于 0.1 米的距离。 153 | - 物理学家需要在同一个计算中同时使用[光速](http://en.wikipedia.org/wiki/Speed_of_light)(约 300000000)和[牛顿引力常数(约 0.0000000000667)。](http://en.wikipedia.org/wiki/Gravitational_constant) 154 | 155 | 为了满足工程师和芯片设计者的要求,数字格式必须为不同数量级的数字提供准确性。但是,只需要*相对*精度。为了让物理学家满意,必须能够进行涉及不同数量级的数字的计算。 156 | 157 | 基本上,具有固定数量的整数和小数位数是没有用的 - 解决方案是使用*浮点*格式。 158 | 159 | ## Special Numbers 160 | 161 | 值得注意的是,浮点数学包括一些特殊的数字。你可能会遇到NaN,∞,-∞和-0。 162 | 163 | 它们的存在是因为有时您可能会执行1 / 0之类的操作,而JavaScript需要以某种方式表示它们的结果。 164 | 165 | 浮点数学标准指定了它们如何工作,以及使用它们时会发生什么。 以下是在你的代码中可能出现的特殊数字: 166 | 167 | ```js 168 | let scale = 0; 169 | let a = 1 / scale; // Infinity 170 | let b = 0 / scale; // NaN 171 | let c = -a; // -Infinity 172 | let d = 1 / c; // -0 173 | ``` 174 | 175 | 在这些特殊的数字中,NaN特别有趣。NaN是0 / 0和其他一些无效数学运算的结果,表示“不是一个数字”。 176 | 177 | 您可能会困惑为什么它声称是一个数字 178 | 179 | ```js 180 | console.log(typeof(NaN)); // "number" 181 | ``` 182 | 183 | 然而,这里没有诀窍。从JavaScript的角度来看,NaN是一个数值。它不是null, undefined,字符串或其他类型。 184 | 185 | 但在浮点数学中,这个术语的名称是“非数字”。所以它是一个数值。它碰巧被称为“非数字”,因为它表示一个无效的结果。 186 | 187 | 使用这些特殊的数字编写代码并不常见。然而,它们可能由于编码错误而出现。所以很高兴知道它们的存在。 188 | 189 | ## BigInts 190 | 191 | > 普通数字不能精确地表示大整数,因此BigInts填补了这一空白(字面上)。在我们的宇宙中有多少个BigInts ?说明书上说它们有任意的精度。这意味着在我们的JavaScript世界中,有无数个bigint—数学中每个整数对应一个bigint。 192 | 193 | ![image-20220320135439290](https://tva1.sinaimg.cn/large/e6c9d24egy1h0gb0fkd1wj20ca08ajrh.jpg) 194 | 195 | 196 | 197 | ```js 198 | let alot = 9007199254740991n; // n at the end makes it a BigInt! 199 | console.log(alot + 1n); // 9007199254740992n 200 | console.log(alot + 2n); // 9007199254740993n 201 | console.log(alot + 3n); // 9007199254740994n 202 | console.log(alot + 4n); // 9007199254740995n 203 | console.log(alot + 5n); // 9007199254740996n 204 | ``` 205 | 206 | BigInts 是最近才添加到 JavaScript 中的,因此您不会看到它们被广泛使用,如果您使用旧版浏览器,它们将无法工作。 207 | 208 | ## Strings(字符串) 209 | 210 | > 我们的下一站是字符串,它表示 JavaScript 中的文本。字符串有三种写法(单引号、双引号和反引号),但它们指的是同一个概念。这三个字符串文字产生相同的字符串值: 211 | 212 | ![image-20220320135915477](https://tva1.sinaimg.cn/large/e6c9d24egy1h0gb57v8uzj208q06474a.jpg) 213 | 214 | ```js 215 | console.log(typeof("こんにちは")); // "string" 216 | console.log(typeof('こんにちは')); // "string" 217 | console.log(typeof(`こんにちは`)); // "string" 218 | ``` 219 | 220 | ```js 221 | console.log(typeof('')); // "string" 222 | ``` 223 | 224 | #### Strings Aren’t Objects(字符串不是对象) 225 | 226 | > 每个字符串都有一些内置属性 227 | 228 | ```js 229 | let cat = 'Cheshire'; 230 | console.log(cat.length); // 8 231 | console.log(cat[0]); // "C" 232 | console.log(cat[1]); // "h" 233 | ``` 234 | 235 | 这并不意味着字符串就是对象!字符串属性是特殊的,它的行为方式与对象属性不同。例如,您不能将任何内容分配给cat[0]。字符串是基本类型,所有基本类型都是不可变的。 236 | 237 | #### A Value for Every Conceivable String 238 | 239 | > 在我们的宇宙中,每一个可能的字符串都有一个不同的值。 240 | 241 | 字符串是否已经“存在”或“创建”的问题不是我们可以从代码中测试的。*在*我们的心智模型中,这个问题没有任何意义。我们无法建立一个实验来判断字符串在我们的 JavaScript 世界中是“被创建”还是“被召唤”。 242 | 243 | 为了使我们的心智模型简单,我们将说**所有可能的字符串值从一开始就已经存在——每个不同的字符串都有一个值。** 244 | 245 | ## Symbol(符号) 246 | 247 | > 我们已经探索了相当多的 JavaScript 世界,但在我们游览的第一部分还有一个(快速)停留:符号。 248 | 249 | 知道符号的存在很重要,但如果不深入研究对象和属性,就很难解释它们的作用和行为。符号的用途与门钥匙相似:它们让您隐藏对象内部的一些信息并控制代码的哪些部分可以访问它。它们也相对稀有,所以在这次宇宙之旅中,我们将跳过它们。对不起,符号! 250 | 251 | ```js 252 | let alohomora = Symbol(); 253 | console.log(typeof(alohomora)); // "symbol" 254 | ``` 255 | 256 | ## 回顾一下 257 | 258 | 现在我们已经满足了所有的原始值,我们将从我们的旅行中休息一下。让我们回顾一下到目前为止遇到的原始值! 259 | 260 | - **Undefined** 261 | - **Null** 262 | - **Booleans** 263 | - **Numbers** 264 | - **BigInts** 265 | - **Strings** 266 | - **Symbols** 267 | 268 | 我们还了解了一些关于JavaScript数字的有趣事实 269 | 270 | - 并非所有的数字都能在JavaScript中完美地表示出来。它们的小数部分在接近0时提供了更高的精度,而在远离0时提供了更低的精度。 271 | - 来自无效数学运算的数字,如1 / 0或0 / 0是特殊的。NaN就是这样一个数字。它们可能是由于编码错误而出现的。 272 | - typeof(NaN)是数字,因为NaN是数值。它被称为“非数字”,因为它代表了“无效”数字的概念。 273 | 274 | -------------------------------------------------------------------------------- /docs/guide/第八章-Properties(属性).md: -------------------------------------------------------------------------------- 1 | # Properties 2 | 3 | 让我们先来看看以下的代码吧 4 | 5 | ```js 6 | let sherlock = { 7 | surname: 'Holmes', 8 | address: { city: 'London' } 9 | }; 10 | ``` 11 | 12 | ```js 13 | let john = { 14 | surname: 'Watson', 15 | address: sherlock.address 16 | }; 17 | ``` 18 | 19 | ```js 20 | john.surname = 'Lennon'; 21 | john.address.city = 'Malibu'; 22 | ``` 23 | 24 | ```js 25 | console.log(sherlock.surname); // ? 26 | console.log(sherlock.address.city); // ? 27 | console.log(john.surname); // ? 28 | console.log(john.address.city); // ? 29 | ``` 30 | 31 | 上面的答案是什么?通过我们之前内容,尝试用JS的心智模型来回答它吧 32 | 33 | ```js 34 | console.log(sherlock.surname); // "Holmes" 35 | console.log(sherlock.address.city); // "Malibu" 36 | console.log(john.surname); // "Lennon" 37 | console.log(john.address.city); // "Malibu" 38 | ``` 39 | 40 | ## Properties 41 | 42 | > 我们之前讨论过Object,例如,这里有一个指向对象值的sherlock变量。我们通过写入{}来创建一个新的对象值 43 | 44 | ```js 45 | let sherlock = {}; 46 | ``` 47 | 48 | 在JavaScript的宇宙中它应该看来是是这样的 49 | 50 | ![image-20220410192322504](https://tva1.sinaimg.cn/large/e6c9d24egy1h14uixekb7j20wc0c2q3b.jpg) 51 | 52 | 然而,对象主要用于将相关数据分组在一起。例如,我们可能想把关于sherlock的不同事实归类,要记住对象只是将数据封装分组,数据并不是属于对象 53 | 54 | ```js 55 | let sherlock = { 56 | surname: 'Holmes', 57 | age: 64, 58 | }; 59 | ``` 60 | 61 | 在这里,sherlock仍然是一个`变量`,但surname和age不是。它们是属性,与变量不同,属性属于特定的对象。 62 | 63 | **在我们的JavaScript世界中,变量和属性的行为都类似于“连接”。但是,属性的连接是从对象开始的,而不是从代码开始的** 64 | 65 | ![image-20220410192656858](https://tva1.sinaimg.cn/large/e6c9d24egy1h14umnddcvj21120d8mxy.jpg) 66 | 67 | 在这里,我们可以看到sherlock变量指向我们创建的一个对象。该对象有两个属性。它的surname属性指向“Holmes”字符串值,它的age属性指向64个数字值。 68 | 69 | 重要的是,**属性不包含值——它们指向值!** 记住,我们的宇宙充满了`电线`。其中一些从代码(变量)开始,另一些从对象(属性)开始。所有导线总是指向值。 70 | 71 | 在阅读本文之前,您可能会认为值存在于对象“内部”,因为它们出现在代码的“内部”。这种直觉经常会导致错误,所以我们会“在电线中思考”。再看一下代码和图表。在继续之前,确保你对他们感到舒服。 72 | 73 | ## Property Names 74 | 75 | 在命名属性时要记住的一件重要事情是,一个对象不能有两个具有相同名称的属性。例如,我们的对象不能有两个名为age的属性。 76 | 77 | 属性名也总是区分大小写!例如,age和Age从JavaScript的角度来看是两个完全不同的属性。 78 | 79 | 如果我们事先不知道属性名,但我们在代码中把它作为字符串值,我们可以使用[]“括号表示法”从对象中读取它 80 | 81 | ```js 82 | let sherlock = { surname: 'Holmes', age: 64 }; 83 | let propertyName = prompt('What do you want to know?'); 84 | alert(sherlock[propertyName]); // Read property by its name 85 | 86 | ``` 87 | 88 | ## Assigning to a Property 89 | 90 | 当我们给属性赋值时会发生什么? 91 | 92 | ```js 93 | sherlock.age = 65; 94 | ``` 95 | 96 | 让我们把这段代码分成左右两部分,用=分隔。 97 | 98 | - 首先,我们找出左边哪根线是sherlock.age 99 | 100 | - 我们跟着sherlock的线,然后选age属性的线 101 | 102 | image-20220410195742272 103 | 104 | 注意,我们没有按照age线写上64岁。 105 | 106 | 我们不关心它的当前值是多少。 107 | 108 | 在分配语句的左边,我们要找导线本身。 还记得我们选了哪根线吗?让我们继续 109 | 110 | 接下来,我们找出右边的值:65。 111 | 112 | image-20220410195943038 113 | 114 | 现在我们准备好执行任务了。 115 | 116 | 最后,我们将左侧的导线指向右侧的值 117 | 118 | image-20220410200018165 119 | 120 | 我们完成了!从现在开始,读sherlock.age是65岁。 121 | 122 | ## Missing Properties(缺失属性) 123 | 124 | 您可能想知道,如果读取一个不存在的属性会发生什么 125 | 126 | ```js 127 | let sherlock = { surname: 'Holmes', age: 64 }; 128 | console.log(sherlock.boat); // ? 129 | ``` 130 | 131 | 我们知道`sherlock.boat`是一个属性表达式,但是我们的JavaScript宇宙如何决定用哪个值来“回答”我们呢? 132 | 133 | **JavaScript使用了一组类似于下面的规则:** 134 | 135 | 1. 算出点(' . ')前面的部分的值。 136 | 137 | 2. 如果该值为' null '或' undefined ',则立即抛出错误。 138 | 139 | 3. 检查对象中是否存在同名属性: 140 | 141 | a.如果**存在**,请使用此属性指向的值回答。 142 | 143 | b.如果**不存在**,则使用' undefined '值回答。 144 | 145 | 这些规则有点简单,但它们已经告诉了我们很多关于JavaScript如何工作的信息!例如,sherlock指向一个没有boat属性的对象。所以`sherlock.boat`给出的答案是undefined 146 | 147 | ```js 148 | let sherlock = { surname: 'Holmes', age: 64 }; 149 | console.log(sherlock.boat); // undefined 150 | ``` 151 | 152 | 注意,这并不意味着我们的对象有一个指向undefined的boat属性!它只有两个属性 surname 和 age 153 | 154 | 人们很容易想到`sherlock.boat`直接对应于我们心理模型中的属性概念,但这并不完全正确。这是一个`问题`,所以JavaScript`遵循规则`来回答这个问题。 155 | 156 | 它观察‘sherlock’指向的对象,发现它**没有**的‘boat’属性,并返回给我们一个`undefined`的值,因为**这就是规则所说的**。这其中没有更深层次的原因,`计算机遵循规则`。 157 | 158 | 事实上 159 | 160 | > 从根本上说,这是因为每个表达式都需要产生某个值,或者抛出一个错误。其他一些语言在访问不存在的属性时会抛出错误,但JavaScript不是其中之一! 161 | 162 | 现在再看看下面的代码,你能告诉我正确结果么? 163 | 164 | ```js 165 | let sherlock = { surname: 'Holmes', age: 64 }; 166 | console.log(sherlock.boat.name); // ? 167 | ``` 168 | 169 | 不要猜测哦,遵循JS的规则 170 | 171 | Hint: 这里有两个点,所以你需要遵守规则两次。 172 | 173 | 答案是会抛出一个错误,可以按照下面的代码来拆解问题就知道了👇 174 | 175 | ```js 176 | let sherlock = { surname: 'Holmes', age: 64 }; 177 | console.log(sherlock.boat); // undefined 178 | console.log(sherlock.boat.name); // TypeError! 179 | ``` 180 | 181 | ## 回顾 182 | 183 | - 属性是线——有点像变量。它们都指向值。与变量不同,属性在我们的宇宙中是从`对象`开始的,变量是从`代码`开始的 184 | 185 | - 属性属于特定的对象。一个对象上不能有多个具有相同名称的属性。 186 | 187 | - 一般来说,你可以用三个步骤来执行赋值: 188 | 189 | 1. 找出哪根线在左边 190 | 2. 找出右边的值 191 | 3. 把导线指向那个值 192 | 193 | - 一个表达式 像下面的 194 | 195 | ``` 196 | obj.property 197 | ``` 198 | 199 | 是分三步计算: 200 | 201 | 1. 找出`.`左边的值 202 | 2. 如果是` null`或` undefined `,则抛出一个错误。 203 | 3. 如果该属性存在,结果就是它所指向的值。如果该属性不存在,则结果为`undefined `。 204 | 205 | 注意,这种属性的心理模型仍然有些简化。目前它已经足够好了,但随着您对JavaScript领域的了解越来越多,它还需要进一步扩展。 206 | -------------------------------------------------------------------------------- /docs/guide/第六章-会见对象与函数.md: -------------------------------------------------------------------------------- 1 | # Meeting Objects and Functions 2 | 3 | > 在前面的模块中,我们已经了解了原始值:Undefined、Null、Booleans、Numbers、BigInts、Strings 和 Symbols 。现在我们将介绍非原始值—这些类型可以让我们创建自己的值。 4 | 5 | ![image-20220326184330896](https://tva1.sinaimg.cn/large/e6c9d24egy1h0nh2vwh6dj20u00z5q5i.jpg) 6 | 7 | ## Objects 8 | 9 | > 数组、正则等等非原始值都属于对象类型 10 | 11 | ```js 12 | console.log(typeof({})); // "object" 13 | console.log(typeof([])); // "object" 14 | console.log(typeof(new Date())); // "object" 15 | console.log(typeof(/\d+/)); // "object" 16 | console.log(typeof(Math)); // "object" 17 | ``` 18 | 19 | 与以前不同,对象不是原始值。这也意味着默认情况下,它们是可变的(我们可以更改它们)。我们可以用`.`或`[]` 20 | 21 | ```js 22 | let rapper = { name: 'Malicious' }; 23 | rapper.name = 'Malice'; // Dot notation 24 | rapper['name'] = 'No Malice'; // Bracket notation 25 | ``` 26 | 27 | 创建我们自己的对象 28 | 29 | 在前面介绍的原始值,我们只能召唤它们,而不能自己创造它们,但是对象类型,是我们可以创造的 30 | 31 | 每次使用{}对象字面量时,都会创建一个全新的对象值 32 | 33 | ```js 34 | let shrek = {}; 35 | let donkey = {}; 36 | ``` 37 | 38 | ![image-20220326184851319](https://tva1.sinaimg.cn/large/e6c9d24egy1h0nh8dw33xj20sg0iat9s.jpg) 39 | 40 | 我们创造的对象会消失么? 41 | 42 | 会的,当我们将指向对象的变量(线)指向null原始值的时候,我们创造的对象就没有变量指向,JavaScript的垃圾回收机制就在不经意间去回收掉它♻️ 43 | 44 | ## Functions 45 | 46 | > 我们定义函数是为了以后可以调用它们并在其中运行代码。然而,要真正理解JavaScript中的函数,我们需要暂时忘记它们为什么有用。相反,我们将把函数看作另一种类型的值:数字、对象、函数。 47 | 48 | 让我们来看看下面的代码,用你现在的心智模型思考一下🤔,它们有什么区别么 49 | 50 | ```js 51 | for (let i = 0; i < 7; i++) { 52 | console.log(2); 53 | } 54 | ``` 55 | 56 | 传递给log函数的永远只有一个value值就是2 57 | 58 | ```js 59 | for (let i = 0; i < 7; i++) { 60 | console.log({}); 61 | } 62 | ``` 63 | 64 | 这里会创建7个不同的对象类型的值 65 | 66 | ```js 67 | for (let i = 0; i < 7; i++) { 68 | console.log(function() {}); 69 | } 70 | ``` 71 | 72 | 这里同样也会创建7个不同对象类型的值,因为函数也是对象 73 | 74 | 从技术上讲,函数是JavaScript中的对象。我们将继续将它们作为一个独立的基本类型,因为与常规对象相比,它们具有独特的功能。但一般来说,如果你能对一个对象做些什么,你也能对一个函数做些什么。它们很特别 75 | 76 | ### 函数调用 77 | 78 | ```js 79 | let countDwarves = function() { return 7; }; 80 | let dwarves = countDwarves(); // () is a function call 81 | console.log(dwarves); 82 | ``` 83 | 84 | 添加()会改变代码的含义: 85 | 86 | - let dwarves = countDwarves表示“将dwarves指向countDwarves当前指向的值。” 87 | - let dwarves = countDwarves()表示“将 dwarves指向countDwarves当前指向的函数返回的值。” 88 | 89 | 事实上,countDwarves()也是一个表达式。它被称为调用表达式。要“回答”一个调用表达式,JavaScript在函数内部运行代码,并将返回值作为结果交给我们(在本例中是7)。 90 | 91 | ## 回顾 92 | 93 | 在前两个模块中,我们已经了解了JavaScript中的每种值类型。让我们回顾一下我们遇到的每种类型的值,从不同的原始类型开始 94 | 95 | ![image-20220326190008951](https://tva1.sinaimg.cn/large/e6c9d24egy1h0nhk53o1lj20we0fywfq.jpg) 96 | 97 | - Undefined 98 | - Null 99 | - Booleans 100 | - Numbers 101 | - BigInts 102 | - Strings 103 | - Symbols 104 | 105 | 然后这里还以欧两个特殊的类型,可以自己创造值的类型 106 | 107 | ![image-20220326190115441](https://tva1.sinaimg.cn/large/e6c9d24egy1h0nhlatcz4j20to0fut9b.jpg) 108 | 109 | - Objects 110 | - Functions 111 | 112 | 113 | 114 | - 原始值(字符串、数字等等)一直存在于我们的宇宙中。例如,写2或“hello”总是“召唤”相同的数字或字符串值。 115 | - 对象和函数的行为不同,并允许我们生成自己的值。编写{}或function(){}总是会创建一个全新的、不同的值。这个想法对于理解是至关重要的 -------------------------------------------------------------------------------- /docs/guide/第十章-原型.md: -------------------------------------------------------------------------------- 1 | # Prototypes 2 | 3 | > 在之前的模块中,我们已经讨论了对象属性和变异,但是还没有完全完成——我们仍然需要讨论原型! 4 | 5 | 这里有一个小谜语来检验我们的心智模式 6 | 7 | ```js 8 | let pizza = {}; 9 | console.log(pizza.taste); // "pineapple" 10 | ``` 11 | 12 | 问问你自己:这可能吗? 13 | 14 | 我们只是用`{}`创建了一个空对象。在`log`之前,我们肯定没有给它设置任何属性,所以看起来pizza.taste不能指向 "pineapple"。我们会期望pizza.taste给我们的是`undefined`,而不是当一个属性不存在的时候,我们通常会得到undefined,对吗? 15 | 16 | 然而,pizza.taste有可能是 `pineapple`!这可能是一个伪造的例子。这可能是一个矫揉造作的例子,但它表明我们的心理模型是不完整的。 17 | 18 | 在这个模块中,我们将介绍原型。原型解释了这个谜题中发生的事情,更重要的是,它是其他几个基本JavaScript特性的核心。偶尔人们会忽略对原型的学习,因为它们看起来太不寻常了,但其核心思想是非常简单的。 19 | 20 | ## Prototypes 21 | 22 | 这里有两个变量指向两个对象 23 | 24 | ```js 25 | let human = { 26 | teeth: 32 27 | }; 28 | 29 | 30 | let gwen = { 31 | age: 19 32 | }; 33 | ``` 34 | 35 | 我们可以用熟悉的方式来表示它们 36 | 37 | ![image-20220417175550659](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cvc0iyu0j211s0ga0tl.jpg) 38 | 39 | 在此例, `gwen` 指向一个没有 `teeth` 属性的对象.根据我们学过的规则,这个属性可以得到`undefined` 40 | 41 | ```js 42 | console.log(gwen.teeth); // undefined 43 | ``` 44 | 45 | 但故事不会就此结束。JavaScript的默认行为返回`undefined`,但我们可以指示它继续在另一个对象上搜索丢失的属性。我们可以用一行代码来完成 46 | 47 | ```js 48 | let human = { 49 | teeth: 32 50 | }; 51 | 52 | 53 | let gwen = { 54 | // We added this line: 55 | __proto__: human, 56 | age: 19 57 | }; 58 | ``` 59 | 60 | 那个神秘的`__proto__`属性是什么? 61 | 62 | 它代表了JavaScript原型的概念。任何JavaScript对象都可以选择另一个对象作为原型。我们将讨论这在实践中意味着什么,但现在,让我们把它看作一个特殊的`__proto__`导线 63 | 64 | ![image-20220417175958311](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cvgaz35sj21240kcjsq.jpg) 65 | 66 | 花点时间来验证图表与代码是否匹配。我们画的和以前一样。唯一的新东西是神秘的`__proto__`电线。 通过指定`__proto__`(也被称为对象的原型),我们指示JavaScript继续寻找该对象上缺失的属性。 67 | 68 | ## 原型在行动 69 | 70 | 我们去找`gwen.teeth`时,我们得到了`undefined`因为`teeth`属性在`Gwen`指向的对象上不存在。 但是由于`__proto__: human`,答案是不同的: 71 | 72 | ```js 73 | let human = { 74 | teeth: 32 75 | }; 76 | 77 | 78 | let gwen = { 79 | // "Look for other properties here" 80 | __proto__: human, 81 | age: 19 82 | }; 83 | 84 | 85 | console.log(gwen.teeth); // 32 86 | ``` 87 | 88 | - JS宇宙会去查找`gwen`导线找到指向的对象 89 | - 然后询问对象上是否有`teeth`属性 90 | - 得到没有的答案 91 | - 但是有一个原型属性,你可以去找找看 92 | - 于是就继续去原型属性指向的对象查找`teeth`属性 93 | - 是的,找到了teeth属性指向32 94 | - 所以 `gwen.teeth`的结果是32 95 | 96 | 这类似于说,“我不知道,但`XXX`可能知道。使用`__proto__`,你指示JavaScript询问另一个对象 97 | 98 | 为了检查你到目前为止的理解,写下你的答案: 99 | 100 | ```js 101 | let human = { 102 | teeth: 32 103 | }; 104 | 105 | 106 | let gwen = { 107 | __proto__: human, 108 | age: 19 109 | }; 110 | 111 | 112 | console.log(human.age); // ? 113 | console.log(gwen.age); // ? 114 | 115 | 116 | console.log(human.teeth); // ? 117 | console.log(gwen.teeth); // ? 118 | 119 | 120 | console.log(human.tail); // ? 121 | console.log(gwen.tail); // ? 122 | ``` 123 | 124 | 答案如下 125 | 126 | ```js 127 | console.log(human.age); // undefined 128 | console.log(gwen.age); // 19 129 | 130 | console.log(human.teeth); // 32 131 | console.log(gwen.teeth); // 32 132 | 133 | console.log(human.tail); // undefined 134 | console.log(gwen.tail); // undefined 135 | ``` 136 | 137 | 上面的例子提醒了我们,`gwen.teeth`只是一个表达——一个对我们JavaScript世界的问题——JavaScript将遵循一系列步骤来回答它。现在我们知道这些步骤包括查看原型。 138 | 139 | ## Prototype Chain(原型链) 140 | 141 | > 原型在JavaScript中并不是一个特殊的“东西”。原型更像是一种关系。一个对象可以指向另一个对象作为它的原型。 这自然会引出一个问题:但是如果我的对象的原型有它自己的原型呢?那个原型有自己的原型吗?会工作吗? 答案是肯定的——这就是它的工作原理! 142 | 143 | ```js 144 | let mammal = { 145 | brainy: true, 146 | }; 147 | 148 | 149 | let human = { 150 | __proto__: mammal, 151 | teeth: 32 152 | }; 153 | 154 | 155 | let gwen = { 156 | __proto__: human, 157 | age: 19 158 | }; 159 | 160 | 161 | console.log(gwen.brainy); // true 162 | ``` 163 | 164 | 我们可以看到JavaScript将在对象上搜索属性,然后在对象的原型上搜索属性,然后在对象的原型上搜索属性,以此类推。如果我们没有原型,也没有找到我们的属性,我们就得到`undefined`。 165 | 166 | 这类似于说,“我不知道,但爱丽丝可能知道。”然后爱丽丝可能会说:“实际上,我也不知道——问鲍勃吧。”最终,你要么找到答案,要么找不到人问了! 这个要“访问”的对象序列被称为我们的对象原型链。(然而,与你可能佩戴的链条不同,原型链条不可能是圆形的!) 167 | 168 | ## Shadowing 遮蔽 169 | 170 | 考虑一下这个稍微修改过的例子 171 | 172 | ```js 173 | let human = { 174 | teeth: 32 175 | }; 176 | 177 | 178 | let gwen = { 179 | __proto__: human, 180 | // This object has its own teeth property: 181 | teeth: 31 182 | }; 183 | ``` 184 | 185 | ```js 186 | console.log(human.teeth); // 32 187 | console.log(gwen.teeth); // 31 188 | ``` 189 | 190 | 注意,`gwen.teen`是31。如果gwen没有自己的 `teeth` 属性,我们会看看原型。但是因为gwen指向的对象有它自己的teeth属性,我们不需要继续寻找答案 191 | 192 | 换句话说,一旦找到我们的属性,我们就停止搜索。 193 | 194 | 如果你想要检查一个对象是否有它自己的属性线与特定的名称,你可以调用一个内置的函数`hasOwnProperty`。对于“own”属性,它返回true,而不查看原型。在我们的最后一个例子中,两个对象都有自己的牙齿线,所以这对两个都是正确的 195 | 196 | ```js 197 | console.log(human.hasOwnProperty('teeth')); // true 198 | console.log(gwen.hasOwnProperty('teeth')); // true 199 | ``` 200 | 201 | ## Assignment 202 | 203 | 考虑一下这个例子 204 | 205 | ```js 206 | let human = { 207 | teeth: 32 208 | }; 209 | 210 | 211 | let gwen = { 212 | __proto__: human, 213 | // Note: no own teeth property 214 | }; 215 | 216 | 217 | gwen.teeth = 31; 218 | 219 | 220 | console.log(human.teeth); // ? 221 | console.log(gwen.teeth); // ? 222 | ``` 223 | 224 | 答案如下 225 | 226 | ```js 227 | console.log(human.teeth); // 32 228 | console.log(gwen.teeth); // 31 229 | ``` 230 | 231 | 当我们*读取*对象上不存在的属性时,我们将继续在原型链上查找它。如果我们没有找到它,我们会得到`undefined`. 232 | 233 | 但是当我们*编写*一个在我们的对象上不存在的属性时,它会在我们的对象上*创建*该属性。一般来说,原型*不会*发挥作用。 234 | 235 | ## Object原型 236 | 237 | 这个对象没有原型,对吧? 238 | 239 | ```js 240 | let obj = {}; 241 | ``` 242 | 243 | 在浏览器打印一下 244 | 245 | ```js 246 | let obj = {}; 247 | console.log(obj.__proto__); //玩一下 248 | ``` 249 | 250 | 令人惊讶的是,`obj.__proto__`不是`null`或`undefined`!相反,您将看到一个奇怪的对象,它有一堆属性,包括`hasOwnProperty` 251 | 252 | 我们将这个特殊对象称为`Object Prototype` 253 | 254 | ![image-20220417182152539](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cw33bc5oj212o0qe40u.jpg) 255 | 256 | 一直以来,我们都认为{}创建了一个`空`对象,但它毕竟不是那么空!它有一个隐藏的`__proto__`线,默认情况下指向`对象原型`。 这就解释了为什么JavaScript对象似乎有“内置”属性: 257 | 258 | ```js 259 | let human = { 260 | teeth: 32 261 | }; 262 | console.log(human.hasOwnProperty); // (function) 263 | console.log(human.toString); // // (function) 264 | ``` 265 | 266 | 这些`内置`属性只不过是对象原型上的普通属性。因为我们的对象原型是object prototype,所以我们可以访问它们。 267 | 268 | ## 没有原型的对象 269 | 270 | 我们刚刚了解到,所有使用`{}`语法创建的对象都有特殊的`__proto__`导线指向一个默认的`对象原型`。但是我们也知道我们可以自定义`__proto__`你可能会想:我们能把它设为`null`吗? 271 | 272 | ```js 273 | let weirdo = { 274 | __proto__: null 275 | }; 276 | ``` 277 | 278 | 当然可以 279 | 280 | ```js 281 | console.log(weirdo.hasOwnProperty); // undefined 282 | console.log(weirdo.toString); // undefined 283 | ``` 284 | 285 | 你可能不想创建这样的对象,但是对象原型正是这样的——一个没有原型的对象。 286 | 287 | ## 原型污染 288 | 289 | 现在我们知道,所有JavaScript对象默认情况下都有相同的原型。让我们简要地回顾一下关于突变模块的例子: 290 | 291 | ![image-20220417182746505](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cw98aid5j213j0u00v1.jpg) 292 | 293 | 这幅图给了我们一个有趣的见解。如果JavaScript在原型中搜索丢失的属性,而大多数对象都共享相同的原型,那么我们是否可以通过改变原型使新属性“出现”在所有对象上? 294 | 295 | ```js 296 | let obj = {}; 297 | obj.__proto__.smell = 'banana'; 298 | ``` 299 | 300 | ```js 301 | console.log(sherlock.smell); // "banana" 302 | console.log(watson.smell); // "banana" 303 | ``` 304 | 305 | ![image-20220417182834694](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cwa26wzgj20zd0u0go6.jpg) 306 | 307 | - 像我们刚才做的那样,改变一个共享的原型叫做原型污染。 308 | - 在过去,原型污染是自定义特性扩展JavaScript的一种流行方式。 309 | - 然而,多年来,网络社区意识到它是脆弱的,很难添加新的语言特性,所以我们宁愿避免它 310 | 311 | ### 有趣的事实 312 | 313 | 在JavaScript添加类之前,通常是将它们编写为生成对象的函数,例如: 314 | 315 | ```js 316 | function Donut() { 317 | return { shape: 'round' }; 318 | } 319 | 320 | 321 | let donutProto = { 322 | eat() { 323 | console.log('Nom nom nom'); 324 | } 325 | }; 326 | 327 | 328 | let donut1 = Donut(); 329 | donut1.__proto__ = donutProto; 330 | let donut2 = Donut(); 331 | donut2.__proto__ = donutProto; 332 | 333 | donut1.eat(); 334 | donut2.eat(); 335 | ``` 336 | 337 | 这就是为什么JavaScript有一个`new`的关键字。当您将`new`关键字放在Donut()函数调用之前时,会发生两件事 338 | 339 | - 该对象是自动创建的,所以您不需要从Donut返回它。(可以这样使用。) 340 | - 该对象的`__proto__`将被设置为你放入函数的prototype属性中的任何值。 341 | 342 | ```js 343 | function Donut() { 344 | this.shape = 'round'; 345 | } 346 | Donut.prototype = { 347 | eat() { 348 | console.log('Nom nom nom'); 349 | } 350 | }; 351 | 352 | 353 | let donut1 = new Donut(); // __proto__: Donut.prototype 354 | let donut2 = new Donut(); // __proto__: Donut.prototype 355 | 356 | 357 | donut1.eat(); 358 | donut2.eat(); 359 | ``` 360 | 361 | 362 | 363 | **ES6 Class** 364 | 365 | Class.js 366 | 367 | ```js 368 | class Spiderman { 369 | lookOut() { 370 | alert('My Spider-Sense is tingling.'); 371 | } 372 | } 373 | 374 | let miles = new Spiderman(); 375 | miles.lookOut(); 376 | ``` 377 | 378 | Prototypes.js 379 | 380 | ```js 381 | // class Spiderman { 382 | let SpidermanPrototype = { 383 | lookOut() { 384 | alert('My Spider-Sense is tingling.'); 385 | } 386 | }; 387 | 388 | // let miles = new Spiderman(); 389 | let miles = { __proto__: SpidermanPrototype }; 390 | miles.lookOut(); 391 | ``` 392 | 393 | ## 回顾 394 | 395 | - 读取时`obj.something`,如果`obj`没有`something`属性,JavaScript 会查找`obj.__proto__.something`. 然后它会寻找`obj.__proto__.__proto__.something`,依此类推,直到找到我们的属性或到达原型链的末端。 396 | - 写入时`obj.something`,JavaScript 通常会直接写入对象,而不是遍历原型链。 397 | - 我们可以使用它`obj.hasOwnProperty('something')`来确定我们的对象是否有*自己的*属性,称为`something`. 398 | - 我们可以通过变异来“污染”许多对象共享的原型。我们甚至可以对对象原型(对象的默认原型)执行此操作`{}`!(但我们不应该这样做,除非我们在恶作剧我们的同事。) 399 | - 在实践中,您可能不会直接使用原型。但是,它们是 JavaScript 对象的基础,因此理解它们的底层机制很方便。一些高级的 JavaScript 特性,包括类,可以用原型来表达。 400 | -------------------------------------------------------------------------------- /docs/guide/第四章-从内部学习.md: -------------------------------------------------------------------------------- 1 | # Studying from the Inside(从内部学习) 2 | 3 | ## 从外面学习 4 | 5 | 研究我们的 JavaScript 世界的一种方法是*从外部研究它*。 6 | 7 | 我们可能会专注于模拟我们的世界——一个 JavaScript 引擎——是如何“真正”工作的。例如,我们可能会了解到这个文本字符串——*我们世界中的一个值*——是存储在硅芯片中的字节序列。 8 | 9 | 这种方法将我们的注意力集中在人和计算机的物理世界上。我们的方法不同。 10 | 11 | 比如我们去阅读JS的引擎如何执行的,计算机如何分配内存的等等 12 | 13 | ## 从内部学习 14 | 15 | 我们将从*内部*研究我们的世界。将自己的精神转移到 JavaScript 世界中,并站在我旁边。我们将观察我们的宇宙法则,并像物理宇宙中的物理学家一样进行实验。 16 | 17 | **我们将了解我们的 JavaScript 世界,而无需考虑它是如何实现的。这类似于物理学家讨论恒星的属性而不质疑\*物理\*世界是否真实。没关系!无论我们是在研究物理世界还是 JavaScript 世界,我们都可以用它们自己的术语来描述它们。** 18 | 19 | 我们的心智模型不会试图解释一个值是如何在计算机的内存中表示的。答案一直在变化![当您的程序运行时](https://v8.dev/blog/react-cliff),答案甚至会发生变化。如果你听过关于 JavaScript 如何“真正”表示内存中的数字、字符串或对象的简单解释,那很可能是错误的。 20 | 21 | 对我们来说,每个字符串都是一个值——不是“指针”或“内存地址”——一个*值*。**在我们的宇宙中,一个值就足够了。** 不要让“记忆细胞”和其他低级隐喻分散您构建 JavaScript 的准确高级心智模型的注意力。反正都是乌龟! 22 | 23 | 如果您来自较低级别的语言,请抛开您对“通过引用传递”、“在堆栈上分配”、“在写入时复制”等的直觉。这些关于计算机如何工作的模型通常使人们*更难以*对 JavaScript 中可以发生或不能发生的事情充满信心。我们将查看一些较低级别的细节,但只关注[真正重要的地方](https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/)。它们可以作为我们心智模型的*补充*,而不是它的基础。 24 | 25 | **我们心智模型的基础是价值观。** 每个值都属于一个类型。原始值是不可变的。我们可以使用我们称之为变量的“连线”来指向值。这个基础——对价值观的理解——将帮助我们继续建立我们的心智模型。 26 | 27 | 至于这些奇异的景象,我不再多想了。我有电线要指出,有问题要问。我最好得到它! 28 | 29 | 当我看着星星时,它们是明亮的。 30 | 31 | 我眨眼时它们还在吗? 32 | 33 | 我耸了耸肩。 34 | 35 | *“实施细节。”* 36 | 37 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # JavaScript Mental Models 2 |

3 | 4 |

5 | 6 | 7 | ## 你好👋我是速冻鱼🐟 这个小册的作者 8 | 9 | 在我学习JavaScript心智模型后,我觉得它对我的帮助很大,于是我便写了这个小册子,旨在帮助您建立一个良好的JavaScript 心智模型 10 |
11 | 如何您觉得我的小册子给您带来了一些对JavaScript理解的帮助,你可以给我买一杯Coffee感谢一些我的辛苦付出 ☕️ 您也可以加我`VX:sudongyuer` 和我交流 12 |
13 | 持续更新中 ~ 🚀🚀🚀 14 |

15 | 16 |

17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-mental-models-monorepo", 3 | "version": "1.0.0", 4 | "description": "JavaScript mental models", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1", 7 | "build": "pnpm exec turbo run build", 8 | "changeset": "changeset add", 9 | "version": "changeset version", 10 | "lint": "eslint .", 11 | "lint-fix": "eslint . --fix" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/sudongyuer/javascript-mental-models" 16 | }, 17 | "keywords": [ 18 | "javascript", 19 | "mental models", 20 | "study" 21 | ], 22 | "main": "index.js", 23 | "author": "sudongyuer", 24 | "license": "MIT", 25 | "devDependencies": { 26 | "@changesets/cli": "^2.23.0", 27 | "@sudongyuer/eslint-config":"^0.1.2", 28 | "eslint": "^8.19.0", 29 | "turbo": "^1.2.16" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # javascript-mental-models-doc 2 | 3 | ## 1.1.0 4 | 5 | ### Minor Changes 6 | 7 | - a42af0c: upgrade vitepress 8 | -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import type { DefaultTheme } from 'vitepress' 2 | import { defineConfig } from 'vitepress' 3 | 4 | export default defineConfig({ 5 | lang: 'zh-CN', 6 | title: 'JavaScript 心智💗模型', 7 | description: 'JavaScript Mental Modules', 8 | lastUpdated: true, 9 | base:"/javascript-mental-models/", 10 | head: [ 11 | ['link', { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }], 12 | ], 13 | themeConfig: { 14 | siteTitle: '❤️JavaScript心智模型', 15 | nav: [ 16 | { text: 'Start', link: '/' }, 17 | { text: 'AboutMe', link: 'https://github.com/sudongyuer' }, 18 | ], 19 | socialLinks: [ 20 | { icon: 'github', link: 'https://github.com/sudongyuer/javascript-mental-models' }, 21 | ], 22 | editLink: { 23 | pattern: 'https://github.com/sudongyuer/javascript-mental-models/tree/master/packages/javascript-mental-modules-doc/docs/:path', 24 | text: 'Edit this page on GitHub', 25 | }, 26 | lastUpdatedText: '最后更新', 27 | algolia: { 28 | appId: '8J64VVRP8K', 29 | apiKey: 'a18e2f4cc5665f6602c5631fd868adfd', 30 | indexName: 'vitepress', 31 | }, 32 | footer: { 33 | message: 'Released under the MIT License.', 34 | copyright: 'Copyright © 2022-present sudongyuer', 35 | }, 36 | sidebar: getSideBar(), 37 | }, 38 | }) 39 | 40 | function getSideBar(): DefaultTheme.Sidebar { 41 | return [ 42 | { 43 | text: '开始探索我们的JavaScript宇宙吧🌈', 44 | collapsible: true, 45 | items: [ 46 | { text: '第一章-什么是心智模型', link: '/guide/第一章-心智模型' }, 47 | 48 | { text: '第二章-JavaScript宇宙', link: '/guide/第二章-JavaScript宇宙' }, 49 | 50 | { text: '第三章-值与变量', link: '/guide/第三章-值与变量' }, 51 | 52 | { text: '第四章-从内部学习', link: '/guide/第四章-从内部学习' }, 53 | 54 | { text: '第五章-会见原始值', link: '/guide/第五章-会见原始值' }, 55 | 56 | { text: '第六章-会见对象与函数', link: '/guide/第六章-会见对象与函数' }, 57 | 58 | { text: '第七章-平等的价值', link: '/guide/第七章-平等的价值' }, 59 | 60 | { text: '第八章-Properties(属性)', link: '/guide/第八章-Properties(属性)' }, 61 | 62 | { text: '第九章-突变', link: '/guide/第九章-突变' }, 63 | 64 | { text: '第十章-原型', link: '/guide/第十章-原型' }, 65 | ], 66 | } 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/guide/小册简介.md: -------------------------------------------------------------------------------- 1 | # JavaScript Mental Models 2 |

3 | 4 |

5 | 6 | ## 在线地址 7 | https://sudongyuer.github.io/javascript-mental-models/ 8 | 9 | 10 | ## 你好👋我是速冻鱼🐟 这个小册的作者 11 | 12 | 在我学习JavaScript心智模型后,我觉得它对我的帮助很大,于是我便写了这个小册子,旨在帮助您建立一个良好的JavaScript 心智模型 本小册是学习Dan Abramov的JustJavaScript的一些学习心得及笔记📒希望能帮助大家更好的理解JavaScript 13 |
14 | 如何您觉得我的小册子给您带来了一些对JavaScript理解的帮助,你可以给我买一杯Coffee感谢一些我的辛苦付出 ☕️ 您也可以加我`VX:sudongyuer` 和我交流 15 |
16 | 持续更新中 ~ 🚀🚀🚀 17 | -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/guide/第一章-心智模型.md: -------------------------------------------------------------------------------- 1 | # MentalaModels 2 | 3 | ```js 4 | let a = 10; 5 | let b = a; 6 | a = 0; 7 | ``` 8 | 9 | 它运行后`a` 的 值是 多少?`b`的值是多少?,在进一步阅读之前先在脑海中解决它。 10 | 11 | ## 什么是心智模型 12 | 13 | > 这些关于某些事物如何在您的脑海中运作的近似值被称为“心智模型”。 14 | 15 | 看看下面的代码 16 | 17 | ```js 18 | function duplicateSpreadsheet(original) { 19 | if (original.hasPendingChanges) { 20 | throw new Error('You need to save the file before you can duplicate it.'); 21 | } 22 | let copy = { 23 | created: Date.now(), 24 | author: original.author, 25 | cells: original.cells, 26 | metadata: original.metadata, 27 | }; 28 | copy.metadata.title = 'Copy of ' + original.metadata.title; 29 | return copy; 30 | } 31 | ``` 32 | 33 | 你能发现这段代码的错误么?,你能在脑海中构建出他们的模型么 34 | 35 | 如果你细心你就会发现 36 | 37 | ```js 38 | copy.metadata.title = 'Copy of ' + original.metadata.title; 39 | ``` 40 | 41 | 会改变原始值~ 42 | 43 | 您可能已经注意到: 44 | 45 | - 此函数复制电子表格。 46 | - 如果未保存原始电子表格,则会引发错误。 47 | - 它会在新电子表格的标题前添加“副本”。 48 | 49 | ## 总结 50 | 51 | 既然您知道存在错误,您会以不同的方式阅读代码吗?如果您一开始使用“快速”思维系统,当您意识到代码中存在错误时,您可能会切换到更费力的“慢”系统。 52 | 53 | 当我们使用“快速”系统时,我们会根据代码的整体结构、命名约定和注释来猜测代码的作用。使用“慢”系统,我们逐步追溯代码所做的事情——一个累人且耗时的过程。 54 | 55 | 这就是为什么拥有一个准确的心智模型如此重要的原因。在你的脑海中模拟一台计算机是很困难的,当你不得不退回到“慢”的思维系统时,你的思维模型就是你可以依赖的。使用错误的心智模型,您将从根本上误解您的代码期望什么,并且您的所有努力都将付诸东流。 56 | 57 | 如果您根本找不到错误,请不要担心——这只是意味着您将从本课程中获得最大收益!在接下来的模块中,我们将一起重建 JavaScript 的心智模型,以便您可以立即发现此类错误。 58 | 59 | 这就是为什么我们需要建立对JS的心智模型 60 | -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/guide/第七章-平等的价值.md: -------------------------------------------------------------------------------- 1 | # 平等的价值 2 | 3 | > 想象一下,参加一个只有几种面具的狂欢节。一群穿着相同服装的人会在房间里开玩笑、跳舞、走动。这会让人困惑!你可能会和两个人交谈,却没有意识到你实际上是在和同一个人交谈两次。或者你可能认为你在和一个人说话,但实际上,你在和两个不同的人说话! 4 | 5 | 如果您在JavaScript中没有一个明确的平等的心智模型,那么每天都像狂欢节一样——而且不是以一种好的方式。你无法确定你处理的是相同的值,还是两个不同的值。因此,您经常会犯错误——比如更改一个您不打算更改的值。 幸运的是,我们已经完成了在JavaScript中建立平等概念的大部分工作。它以一种非常自然的方式符合我们的心智模式。 6 | 7 | ## Kinds of Equality 8 | 9 | 在JavaScript中,有几种相等,如果你已经写过JS了,你应该对下面的几种相等不陌生 10 | 11 | - **Strict Equality:** `a === b` (triple equals). 12 | - **Loose Equality:** `a == b` (double equals). 13 | - **Same Value Equality:** `Object.is(a, b)`. 14 | 15 | ## Object.is(a,b) 16 | 17 | 在JavaScript中,`Object.is(a,b)`告诉我们a和b是否有相同的值 18 | 19 | ```js 20 | console.log(Object.is(2, 2)); // true 21 | console.log(Object.is({}, {})); // false 22 | ``` 23 | 24 | 这叫做相同的值相等。 25 | 26 | 尽管方法名是`Object.is`并不特定于对象。它可以比较任意两个值,不管它们是否是对象! 27 | 28 | 检查一下你的直觉🥳 29 | 30 | ```js 31 | let dwarves = 7; 32 | let continents = '7'; 33 | let worldWonders = 3 + 4; 34 | ``` 35 | 36 | 提示一下:这段代码的心智模型是如下的 37 | 38 | image-20220327131519517 39 | 40 | 现在,尝试告诉我一下下面代码的答案 41 | 42 | ```js 43 | console.log(Object.is(dwarves, continents)); // ? 44 | console.log(Object.is(continents, worldWonders)); // ? 45 | console.log(Object.is(worldWonders, dwarves)); // ? 46 | ``` 47 | 48 | 下面是答案👇: 49 | 50 | 1. `Object.is(dwarves, continents)` 是 **`false`** 因为 `dwarves` 和 `continents` **指向不同的值**. 51 | 2. `Object.is(continents, worldWonders)` 是 **`false`** 因为 `continents` 和 `worldWonders` **指向不同的值**. 52 | 3. `Object.is(worldWonders, dwarves)` 是 **`true`** 因为 `worldWonders` 和 `dwarves` **指向相同的值**. 53 | 54 | 但是对象呢? 到这里,您可能要担心对象了。您可能听说过等式对对象无效,或者它比较“引用”。如果你有这样的直觉,那就暂时把它们放在一边。 相反,请看下面的代码片段 55 | 56 | ```js 57 | let banana = {}; 58 | let cherry = banana; 59 | let chocolate = cherry; 60 | cherry = {}; 61 | ``` 62 | 63 | 记住,{}总是表示“创建一个新的对象值”。另外,请记住=的意思是“将左侧的导线指向右侧的值”。 64 | 65 | image-20220327132233221 66 | 67 | 现在再来解答一下下面的问题?👇 68 | 69 | ```js 70 | console.log(Object.is(banana, cherry)); // ? 71 | console.log(Object.is(cherry, chocolate)); // ? 72 | console.log(Object.is(chocolate, banana)); // ? 73 | ``` 74 | 75 | 1. `Object.is(banana, cherry)` 是 **`false`** 因为 `banana` 和 `cherry` **指向不同的值**. 76 | 2. `Object.is(cherry, chocolate)` 是 **`false`** 因为 `cherry` 和 `chocolate` **指向不同的值**. 77 | 3. `Object.is(chocolate, banana)` 是 **`true`** 因为 `chocolate` 和 `banana` **指向相同的值**. 78 | 79 | 正如您所看到的,我们不需要任何额外的概念来解释相同的值相等性如何适用于对象。它是通过我们的思维模式自然产生的。这就是我所知道的一切! 80 | 81 | ## 严格相等 82 | 83 | 您以前可能使用过严格相等操作符 84 | 85 | ```js 86 | console.log(2 === 2); // true 87 | console.log({} === {}); // false 88 | ``` 89 | 90 | #### 同值平等与严格平等 91 | 92 | `Object.is`和`===`有什么区别呢? 93 | 94 | - Object.is和我们心智模型中的值相等保持一致 95 | - 严格相同绝大部门情况下也和心智模型一致,但是也有一些特殊情况 96 | 97 | 这两种不寻常的情况都涉及我们过去讨论过的“特殊数字”: 98 | 99 | 1. **`NaN === NaN`是`false`**,尽管它们的值相同。 100 | 2. **`-0 === 0`并且`0 === -0`是`true`**,尽管它们是不同的值。 101 | 102 | #### 第一个特殊的例子: `NaN` 103 | 104 | `NaN` 是一个特殊的Number ,当我们做无效的数学时就会出现 `0 / 0`: 105 | 106 | ```js 107 | let width = 0 / 0; // NaN 108 | ``` 109 | 110 | 进一步的计算 `NaN` 也将会给你一个 `NaN` : 111 | 112 | ```js 113 | let height = width * 2; // NaN 114 | ``` 115 | 116 | 您可能不会故意这样做,但如果您的数据或计算存在缺陷,就会发生这种情况. 117 | 118 | **记住 `NaN === NaN` 永远都是 `false`:** 119 | 120 | ```js 121 | console.log(width === height); // false 122 | ``` 123 | 124 | 然而, `NaN` 和 `NaN`是同一个值: 125 | 126 | ```js 127 | console.log(Object.is(width, height)); // true 128 | ``` 129 | 130 | image-20220327133517952 131 | 132 | 这很令人困惑。 NaN === NaN 为假的原因很大程度上是历史原因,所以我建议接受它作为生活的事实。如果您尝试编写一些检查值是否为 NaN 的代码(例如,打印警告),您可能会遇到这种情况。 133 | 134 | ```js 135 | function resizeImage(size) { 136 | if (size === NaN) { 137 | // 这将永远不会被记录:检查永远是假的! 138 | console.log('Something is wrong.'); 139 | } 140 | // ... 141 | } 142 | ``` 143 | 144 | 相反,这里有一些方法(它们都有效!)来检查size是否为NaN: 145 | 146 | - **`Number.isNaN(size)`** 147 | - **`Object.is(size, NaN)`** 148 | - **`size !== size`** 149 | 150 | 最后一个可能特别令人惊讶。给它一些时间。如果您没有看到它是如何检测NaN的,请尝试重新阅读这一节并再次思考它。 151 | 152 | size !== size有效,因为NaN === NaN是假的,正如我们已经知道的。所以反过来(NaN !== NaN)一定是真的。 因为NaN是唯一不是严格等于自身的值,size !== size只能表示size是NaN。 153 | 154 | 一个简单的历史: https://stackoverflow.com/questions/1565164/what-is-the-rationale-for-all-comparisons-returning-false-for-ieee754-nan-values/1573715#1573715 。确保开发人员能够以这种方式检测 NaN 是使 NaN === NaN 返回 false 的最初原因之一!这是在JavaScript出现之前就决定的 155 | 156 | #### 第二个特殊的例子: `-0` 157 | 158 | 在普通数学中,没有“负0”这样的概念,但在浮点数学中却存在[实际原因](https://softwareengineering.stackexchange.com/a/280708)。这里有一个有趣的事实。 159 | 160 | **Both `0 === -0` and `-0 === 0` are always `true`:** 161 | 162 | ```js 163 | let width = 0; // 0 164 | let height = -width; // -0 165 | console.log(width === height); // true 166 | ``` 167 | 168 | 然而, `0` 和 `-0`不是同一个值: 169 | 170 | ```js 171 | console.log(Object.is(width, height)); // false 172 | ``` 173 | 174 | image-20220327134050806 175 | 176 | #### 编码练习 177 | 178 | 现在您知道了Object.is和=== 是如何工作的了,我有一个小的编码练习给你。你不需要完成它,但它是一个有趣的难题 179 | 180 | 写一个函数strictEquals(a, b),返回与a === b相同的值。你的实现不能使用===或!==操作符。 181 | 182 | 下面是我的答案 🐥 183 | 184 | ```js 185 | function strictEquals(a, b) { 186 | if (Object.is(a, b)) { 187 | if (Object.is(a, NaN) || Object.is(b, NaN)) { 188 | return false 189 | } else { 190 | return true 191 | } 192 | } else { 193 | if ((Object.is(a, -0) && Object.is(b, 0)) || 194 | Object.is(b, -0) && Object.is(a, 0)) { 195 | return true 196 | } else { 197 | return false 198 | } 199 | } 200 | 201 | } // sudongyuer🐟 202 | ``` 203 | 204 | 听到这些特殊的数字和他们的行为可能会让人不知所措。不要过分强调这些特殊情况! 它们并不常见。既然你知道它们的存在,你就会在实践中认识到它们。在大多数情况下,我们可以相信`Object.is (a, b)` 和 `a === b`。 205 | 206 | ## 宽松的平等 207 | 208 | > 松散等号(双等号)是JavaScript的梦魇。下面是几个让你起鸡皮疙瘩的例子: 209 | 210 | ```js 211 | console.log([[]] == ''); // true 212 | console.log(true == [1]); // true 213 | console.log(false == [0]); // true 214 | ``` 215 | 216 | 松散相等(也称为“抽象相等”)的规则是晦涩难懂的。许多编码标准完全禁止在代码中使用==和!=。 尽管Just JavaScript对应该或不应该使用哪些特性没有强烈的意见,但我们不会详细讨论松散相等性。它在现代代码库中并不常见,它的规则在语言或我们的思维模式中也没有发挥更大的作用 217 | 218 | 松散相等规则被广泛认为是JavaScript早期的一个糟糕设计决策,但如果您仍然好奇,可以在这里查看[它是如何工作的](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#Loose_equality_using)。不要觉得有压力要记住它——你需要记住其他话题! 219 | 220 | 值得了解的一个相对常见的用例是 221 | 222 | ```js 223 | if (x == null) { 224 | // ... 225 | } 226 | ``` 227 | 228 | 这段代码相当于这样写: 229 | 230 | ```js 231 | if (x === null || x === undefined) { 232 | // ... 233 | } 234 | ``` 235 | 236 | 然而,即使是==的使用也可能在一些团队中引起争议。在使用==之前,最好讨论一下在团队代码库中可以容忍多少==。 237 | 238 | ## 回顾 239 | 240 | > JavaScript有几种等号。它们包括相同价值平等、严格平等和松散平等。 241 | 242 | - 理解这种平等有助于防止错误! 243 | - 你经常需要知道什么时候你在处理相同的值,什么时候你在处理两个不同的值。 244 | - 当我们画一个值和变量的图时,相同的值不能出现两次。`Object.is(a,b)`当变量a和b指向图上相同的值时(a, b)为真。 245 | - 相同的值,编写起来有点麻烦,但它也是最容易解释的,这就是我们开始使用它的原因 246 | 247 | 在实践中,您将使用严格相等,或a === b,最常见的。除了两种罕见的特殊情况外,它等价于相同的值相等: 248 | 249 | - `NaN === NaN` 是 `false`, 即使它们是相同的值. 250 | - `0 === -0` 和 `-0 === 0` 是 `true`, 但它们是不同的值. 251 | 252 | - 你可以查看 `x` 是 `NaN` 通过 `Number.isNaN(x)`. 253 | - **Loose equality** (`==`) 使用一组神秘的规则,通常会被避免. -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/guide/第三章-值与变量.md: -------------------------------------------------------------------------------- 1 | # Values and Variables 2 | 3 | 先看一个小代码片段 4 | 5 | ```js 6 | let reaction = 'yikes'; 7 | reaction[0] = 'l'; 8 | console.log(reaction); 9 | ``` 10 | 11 | 你认为这段代码最后的结果是什么呢?我希望你花一点时间,一步一步地写下你对每一行代码的确切思考过程。注意你现有思维模式中的任何空白或不确定因素,也把它们写下来。如果你有任何疑问,尽量清楚地表达出来 12 | 13 | 带着疑问🤔️我们往下看吧! 14 | 15 | ## Primitive Values Are Immutable(原始值是不可变的) 16 | 17 | > 上面的那道题,你知道正确答案了么,你答对了吗?这似乎是一种只有在JavaScript面试中才会出现的琐碎问题。尽管如此,它说明了关于原始值的一个重要观点。 `我们不能改变原始值` 18 | 19 | 请记住! 我们不能改变原始值!!! 20 | 21 | 我将用一个小例子来解释这一点。字符串(原始的)和数组(不是)有一些表面上的相似之处。数组是项目序列,字符串是字符序列: 22 | 23 | ```js 24 | let arr = [212, 8, 506]; 25 | let str = 'hello'; 26 | ``` 27 | 28 | 我们可以类似地访问数组的第一项和字符串的第一个字符。几乎感觉字符串就是数组: 29 | 30 | ```js 31 | console.log(arr[0]); // 212 32 | console.log(str[0]); // "h" 33 | ``` 34 | 35 | 但他们不是。让我们仔细看看。我们可以更改数组的第一项: 36 | 37 | ```js 38 | arr [0] = 420; 39 | console.log(arr); // [420, 8, 506] 40 | ``` 41 | 42 | 直观地说,很容易假设我们可以对字符串做同样的事情: 43 | 44 | ```js 45 | str [0] = 'j'; // ??? 46 | ``` 47 | 48 | **但我们不能。** 49 | 50 | 这是我们需要添加到我们的心智模型中的一个重要细节。字符串是原始值,**所有原始值都是不可变的。**“不可变”是一种花哨的拉丁语方式来表达“不变”。只读。我们不能乱用原始值。完全没有。 51 | 52 | JavaScript 不允许我们在任何原始值上设置属性,无论是数字、字符串还是其他东西。它是否会默默地拒绝我们的请求或抛出错误取决于我们的代码处于[哪种模式](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)。但请放心,这永远不会起作用: 53 | 54 | ```js 55 | let fifty = 50; 56 | fifty.shades = 'gray'; // No! 57 | ``` 58 | 59 | 像任何数字一样,50是一个原始值。我们不能在上面设置属性。 记住,在我们的JavaScript世界中,所有的原始值都是遥远的星星,离我们的代码最远。我们可以指出它们,但它们将永远停留在原地,不变。 奇怪的是,我觉得这很安慰~ 60 | 61 | ![image-20220320093614875](https://tva1.sinaimg.cn/large/e6c9d24egy1h0g3jlk8a0j20zw0lkgmz.jpg) 62 | 63 | ## Variables and Values—A Contradiction?(变量和值 矛盾么) 64 | 65 | 看看下面代码你觉得答案是什么 66 | 67 | ```js 68 | let pet = 'Narwhal'; 69 | pet = 'The Kraken'; 70 | console.log(pet); // ? 71 | ``` 72 | 73 | > 如果你认为我想弄乱你的脑袋,那你是对的!答案是`"The Kraken"`。不变性在这里不起作用。 74 | > 75 | > 如果你弄错了,不要绝望!这个例子似乎与字符串不变性相矛盾,**但事实并非如此。** 76 | > 77 | > 当你刚接触一门语言时,有时有必要抛开矛盾,这样你就可以避免兔子洞并继续学习。但既然你致力于建立一个心智模型,你就需要质疑矛盾。 78 | > 79 | > *矛盾揭示了心智模型的差距。* 80 | 81 | ## Variables Are Wires(变量是连线) 82 | 83 | 让我们来看看解释 84 | 85 | ```js 86 | let pet = 'Narwhal'; 87 | pet = 'The Kraken'; 88 | console.log(pet); "The Kraken" 89 | ``` 90 | 91 | 我们知道字符串值不能改变,因为它们是原始的。但宠物变量确实变成了“海怪”。这是怎么回事? 这似乎是一个矛盾,但它不是。 92 | 93 | 我们说过原始值不能改变,但是我们没有说任何关于变量的东西!随着我们不断完善我们的思维模式. 94 | 95 | 我们需要理清几个相关的概念: 96 | 97 | - **变量不是值** 98 | - **变量指向值** 99 | 100 | ## Assigning a Value to a Variable () 101 | 102 | > 在我们的JavaScript世界中,变量是指向值的连线。 例如,我可以将pet变量指向“Narwhal”值。(我也可以说我把值“Narwhal”赋值给变量pet) 103 | 104 | ```js 105 | let pet = 'Narwhal'; 106 | pet = 'The Kraken'; 107 | console.log(pet); // "The Kraken" 108 | ``` 109 | 110 | 111 | 112 | iShot2022-03-20 10.06.59 113 | 114 | ```js 115 | pet = 'The Kraken'; 116 | ``` 117 | 118 | 119 | 120 | iShot2022-03-20 10.09.23 121 | 122 | - 我在这里所做的一切只是指示JavaScript将左侧(宠物)的变量或“连线”指向右侧(‘the Kraken’)的值。 123 | - 它会一直指向那个值,除非我以后再重新赋值。 124 | 125 | ## Rules of Assignment 126 | 127 | - 赋值的左侧必须是“连线”——例如pet变量。 128 | - 赋值的右边必须是一个表达式,所以它总是会产生一个值。我们的表达可以很简单,比如2或者“hello”。它也可以是一个更复杂的表达式——例如 pet = count +'sudongyuer' 129 | 130 | 在这里,count + ' sudongyuer'是一个表达式——一个JavaScript的问题。JavaScript将用一个值回答它(例如,“101 sudongyuer”)。从现在开始,宠物变量“wire”将指向那个特定的值 131 | 132 | 如果右边必须是一个表达式,这是否意味着简单的东西,如数字2或字符串,如' the Kraken',写在代码也是表达式?是的!这样的表达式被称为字面量——因为我们字面地写下它们产生的值。 133 | 134 | ## Reading a Value of a Variable 135 | 136 | > 我们当然可以从变量中读取值 137 | 138 | ```js 139 | console.log(pet); 140 | ``` 141 | 142 | 这是不足为奇的。 但是请注意,它不是我们传递给console.log的pet变量。 143 | 144 | 我们可以口头上这么说,但是我们不能把变量传递给函数。传递pet变量的当前值。 145 | 146 | 这是如何工作的? 事实证明,像pet这样的变量名也可以用作表达式!当我们编写pet时,我们要问JavaScript一个问题:“pet的当前值是多少?”为了回答我们的问题,JavaScript跟随宠物“wire”,并返回这个“wire”末尾的值。 147 | 148 | **所以同一个表达式可以在不同的时间给我们不同的值!** 149 | 150 | ### Nitpicking 151 | 152 | > JS中只有值传递!!!JS中只有值传递!!!JS中只有值传递!!! 153 | 154 | 谁在乎你说的是“传递一个变量”还是“传递一个值”?这两者之间的区别是不是太迂腐了?我当然不鼓励打断你的同事纠正他们。那是在浪费大家的时间。 但是您需要在头脑中清楚地了解每个JavaScript概念可以做什么。你不能骑自行车溜冰。你不能给蚊子唱歌。而且不能传递变量——至少在JavaScript中不能。 下面是一个小例子,说明为什么这些细节很重要。 155 | 156 | ```js 157 | function double(x) { 158 | x = x * 2; 159 | } 160 | 161 | let money = 10; 162 | double(money); 163 | console.log(money); // ? 164 | ``` 165 | 166 | ## 回顾 167 | 168 | - **原始值是不可变的**它们是我们 JavaScript 世界的永久组成部分——我们无法创建、销毁或更改它们。例如,我们不能在字符串值上设置属性,因为它是原始值。数组*不是*原始的,所以我们*可以*设置它们的属性。 169 | - **变量不是值**每个变量都*指向*一个特定的值。我们可以使用赋值运算符来更改*它*指向的值。`=` 170 | - **变量就像电线**“线”不是 JavaScript 的概念——但它可以帮助我们想象变量如何指向值。当我们进行赋值时,左边总是有一个连线,右边是一个表达式(产生一个值)。 171 | - **注意矛盾**如果你学到的两件事似乎相互矛盾,不要气馁。通常这是一个迹象,表明潜藏着更深层次的真相。 172 | - **语言很重要**我们正在建立一个心智模型,以便我们对宇宙中可能发生或不可能发生的*事情*充满信心,我们可能会以随意的方式谈论这些想法(吹毛求疵通常会适得其反),但我们对术语背后含义的理解需要准确。 173 | -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/guide/第九章-突变.md: -------------------------------------------------------------------------------- 1 | # Mutation 2 | 3 | > 在上一讲属性的模块中,我们介绍了夏洛克·福尔摩斯搬到马里布的秘密,但我们还没有解释它。 这一次,我们将一步一步地浏览代码,并一起绘制图表,以便检查您的心智模型。 4 | 5 | ## Step 1:声明 `sherlock` 变量 6 | 7 | ```js 8 | let sherlock = { 9 | surname: 'Holmes', 10 | address: { city: 'London' } 11 | }; 12 | ``` 13 | 14 | 请根据你之间建立的心智模型画一下图 15 | 16 | 这是我的图👇 17 | 18 | ![image-20220417171439346](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cu557ky7j20yg0lgabb.jpg) 19 | 20 | ### 没有嵌套的对象 21 | 22 | > 注意,我们这里不是一个,而是两个完全独立的对象。两对花括号表示两个对象。 23 | 24 | ## Step 2:描述 `john`变量 25 | 26 | ```js 27 | let john = { 28 | surname: 'Watson', 29 | address: sherlock.address 30 | }; 31 | ``` 32 | 33 | 请根据你之间理建立的心智模型画一下图 34 | 35 | 这是我的图👇 36 | 37 | ![image-20220417172052448](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cubmnym9j20ze0lo0uh.jpg) 38 | 39 | ### 属性总是指向值 40 | 41 | > 记住:属性总是指向一个值!它不能指向另一个属性或变量。通常,我们宇宙中的所有导线都指向值。 42 | 43 | ![image-20220417172311159](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cue14folj20zg0nkdhm.jpg) 44 | 45 | ## Step 3:改变属性 46 | 47 | ```js 48 | john.surname = 'Lennon'; 49 | john.address.city = 'Malibu'; 50 | ``` 51 | 52 | 请根据你之间理建立的心智模型画一下图 53 | 54 | 这是我的图👇 55 | 56 | ![image-20220417173136213](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cums23l2j21000li761.jpg) 57 | 58 | ```js 59 | console.log(sherlock.surname); // "Holmes" 60 | console.log(sherlock.address.city); // "Malibu" 61 | console.log(john.surname); // "Lennon" 62 | console.log(john.address.city); // "Malibu" 63 | ``` 64 | 65 | ## Mutation 66 | 67 | > 突变是"改变"的一种奇特说法 68 | 69 | 例如,我们可以说我们`change`了一个对象的属性,或者我们可以说我们`mutated`了那个对象(及其属性)。这是一样的。 70 | 71 | 人们喜欢说“mutate”,因为这个词有一种不祥的意味。它提醒你要格外小心。这并不意味着突变是“坏的”——它只是编程!但你需要非常有意识的去做。 72 | 73 | 让我们回顾一下最初的任务。我们想给约翰换个姓,然后把他搬到马里布。现在让我们看看我们的两个`mutations`: 74 | 75 | ```js 76 | // Step 3: Changing the Properties 77 | john.surname = 'Lennon'; 78 | john.address.city = 'Malibu'; 79 | ``` 80 | 81 | 哪些对象在这里被改变了? 82 | 83 | 第一行修改了john所指向的对象。它的意思是:我们想要改变约翰的姓氏。该对象表示John的数据,因此我们改变了它的姓氏属性。 84 | 85 | 然而,第二行做了一些非常不同的事情。它不会改变john指向的对象。相反,它改变了一个完全不同的对象——我们可以通过john.address访问的对象。如果我们看一下上面我们画的图,我们知道我们将通过`sherlock.address`和`john.address`将指向相同的对象! 86 | 87 | 通过改变程序中其他地方使用的对象,我们弄得一团糟。 88 | 89 | **有趣的事实** 90 | 91 | 这就是为什么对象被“嵌套”的直觉是如此错误!它让我们忘记了可能有很多对象指向我们更改过的对象。 92 | 93 | 所以对象只是值的集合,并不是值存在于对象 94 | 95 | ### 可能的解决方案:改变另一个对象 96 | 97 | ```js 98 | // Replace Step 3 with this code: 99 | john.surname = 'Lennon'; 100 | john.address = { city: 'Malibu' }; 101 | ``` 102 | 103 | ### 替代解决方案:没有对象突变 104 | 105 | ```js 106 | // Replace Step 3 with this code: 107 | john = { 108 | surname: 'Lennon', 109 | address: { city: 'Malibu' } 110 | }; 111 | ``` 112 | 113 | 现在在打印下面的值就会发现 114 | 115 | ```js 116 | console.log(sherlock.surname); // "Holmes" 117 | console.log(sherlock.address.city); // "London" 118 | console.log(john.surname); // "Lennon" 119 | console.log(john.address.city); // "Malibu" 120 | ``` 121 | 122 | 123 | 124 | ## Mutation 是很坏的么 125 | 126 | - 不要以为突变是 "坏事 "就走了。那是一种懒惰的过度简化,掩盖了真正的理解。如果数据随时间变化,某处就会发生变异。问题是,什么应该被突变,在哪里,以及什么时候? 127 | 128 | - 当你突变一个对象时,变量和属性可能已经指向它了。你的突变会影响以后 "跟随 "这些线的任何代码。 129 | 130 | - 这既是一种祝福也是一种诅咒。变异使改变一些数据变得很容易,并能立即在整个程序中 "看到 "这种变化。然而,无纪律的突变使我们更难预测程序会做什么。 131 | 132 | - 有一个学派认为,突变最好被限制在你的应用程序的一个非常狭窄的层。根据这一理念,其好处是你的程序的行为更容易预测。缺点是你要写更多的代码来 "传递东西 "和避免变异。 133 | 134 | - 值得注意的是,突变你刚刚创建的新对象总是可以的,因为现在还没有其他的线指向它们。在其他情况下,我建议你对你要突变的东西和时间要非常谨慎。你对变异的依赖程度取决于你的应用程序的架构。 135 | 136 | ## 回顾 137 | 138 | - 在我们的宇宙中,对象从来都是 不是"嵌套 "的 139 | - 改变一个对象的属性也被称为突变该对象。 140 | - 如果你改变了一个对象,你的代码将通过任何指向该对象的线来 "看到 "这一变化。有时,这可能是你想要的。然而,突变意外的共享数据可能会导致bug。 141 | - 你可以用const而不是let来声明一个变量。这允许你强制这个变量总是指向同一个值。但请记住,const并不能阻止对象的变异!你可以用const来声明一个变量,而不是用let。 142 | - 在代码中突变你刚刚创建的对象是安全的。大体上,你会在多大程度上使用变异取决于你的应用程序的架构。 -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/guide/第二章-JavaScript宇宙.md: -------------------------------------------------------------------------------- 1 | # JavaScript宇宙 2 | 3 | ## Values and Code 4 | 5 | > 虽然我们的值和我们的代码交互,但值是存在于完全独立的空间中 6 | 7 | - 值不存在于我们的代码中 8 | - 我们的代码中不包括值,我们的代码只是一堆指令,有`if`语句、变量声明、逗号和花括号。 9 | 10 | image-20220219194800337 11 | 12 | 可以想象一下我们是一个小人站在地球上,手里拿着要执行的code,上面有一条条指令,值存在于离我们比较远的星球上`boolean`、`number`、`string`、`symbol`、`null`、`undifined`、`object`、`function` 13 | 14 | image-20220219194727606 15 | 16 | ## Values 17 | 18 | 概括的说,值有两种类型 19 | 20 | - 原始类型 21 | - 原始的价值观就像星星——冰冷而遥远,但当我需要它们时,它们总是在那里。 22 | - 即使在我这颗小行星的表面,我也能找到它们,并指出它们。 23 | - 它们可以是数字和字符串等。所有原始值都有一个共同点:它们是JavaScript世界的永久组成部分。我可以指出它们,但我不能创造、破坏或改变它们 24 | 25 | ```js 26 | console.log(2); 27 | console.log("hello"); 28 | console.log(undefined); 29 | ``` 30 | 31 | - 对象和函数 32 | - 对象和函数也是值,但与原始值不同的是,我可以在代码中操作它们。 33 | - 如果原始值就像遥远的恒星,那么对象和函数就更像漂浮在我的行星周围的小行星。它们不是我代码的一部分,但它们离我很近,可以操纵 34 | 35 | ## Types of Values 36 | 37 | > JavaScript的值有9种类型 38 | 39 | ### Primitive Values(原始值) 40 | 41 | - Undefined 42 | - 用于无意中丢失的值 43 | - Null 44 | - 用于故意丢失值 45 | - Booleans 46 | - (true和false),用于逻辑运算。 47 | - Numbers 48 | - (-100, 3.14,等等),用于数学计算。 49 | - BigInts 50 | - (罕见且新颖),用于大数字的数学。 51 | - String 52 | - (“hello”,“abracadabra”等),用于文本。 53 | - Symbols 54 | - (不常见),用于举行仪式和隐藏秘密。 55 | 56 | ### Object and Functions(对象和函数) 57 | 58 | - Object({}和其他),用于对相关数据和代码进行分组。 59 | - Functions(x => x * 2 和其他),用于引用代码。 60 | 61 | ### No Other Types(没有其他类型) 62 | 63 | 在JavaScript中,除了我们刚刚列举的那些之外,没有其他的基本值类型。 64 | 65 | ```js 66 | console.log(typeof([])); // "object" 67 | console.log(typeof(new Date())); // "object" 68 | console.log(typeof(/(hello|goodbye)/)); // "object" 69 | ``` 70 | 71 | ⚠️ 你可能听说过everything 都是对象。这是一个流行的都市传说,但它不是真的。 72 | 73 | 虽然像 `"hi".toUpperCase()` 这样的代码使“hi”看起来像一个对象,但这只是一种错觉。当您这样做时,JavaScript会创建一个临时对象,然后立即丢弃它。如果这个机制还不适合你,那也没关系。这确实相当令人困惑! 现在,您只需要记住原始值(如数字和字符串)不是对象。 74 | 75 | ## Expressions 76 | 77 | > 有很多问题JavaScript无法回答。但是JavaScript很乐意回答一些问题。这些问题有一个特殊的名称,它们被称为表达式。 78 | 79 | 表达式是JavaScript可以回答的问题。JavaScript用它知道的唯一方式来回答表达式——用值。 80 | 81 | 总之记住一句话:**Expressions always result in a single value.** 82 | 83 | # 总结 84 | 85 | - 在JS世界中有值也有code,我们可以认为不同的值悬浮在JS的宇宙中,它们不存在于我们的代码中,但是我们可以在代码中引用它们。 86 | - 值有两种分类,一种是原始类型的值,一种是对象和方法的值,总共有九种不同的类型。每种类型都有特定的用途,但有些很少使用。 87 | - 有些值是孤独的,比如`null`是Null类型的,`undefined`是Undefined类型的,我们将在后面了解到,这两个孤独的值是很多错误的制造者! 88 | - 我们可以向JS宇宙询问表达式,表达式总是表示一个值 89 | - 我们可以用`typeof`来得到值的类型 -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/guide/第五章-会见原始值.md: -------------------------------------------------------------------------------- 1 | # Meeting the Primitive Values 2 | 3 | 到目前为止,我们一直在从地球表面观察我们的 JavaScript 宇宙。我们已经熟悉了远距离填充我们宇宙的价值观,但在这个模块中,我们正在改变它。我们将跳上宇宙飞船去探索,向我们介绍 JavaScript 世界中的每一个价值。 4 | 5 | *花时间详细查看每个值可能感觉没有必要,但是当您清楚地看到它们是两个不同的*苹果时,您只能说存在“两个苹果” 。区分值是理解JavaScript 中*相等性*的关键——这将是我们的下一个主题。 6 | 7 | 我们的宇宙飞船将引导我们穿越 JavaScript 的“天体”来满足不同的价值观。我们将首先遇到原始值:布尔值、数字、字符串等。稍后,我们将遇到对象和函数。将其视为观光旅游。 8 | 9 | image-20220320113551284 10 | 11 | ## Undefind 12 | 13 | > 我们将从未定义类型开始。这是一个非常简单的起点,因为这个类型只有一个值——undefined。 14 | 15 | ```js 16 | console.log(typeof(undefined)); // "undefined" 17 | ``` 18 | 19 | image-20220320113806163 20 | 21 | 22 | 23 | 它被称为未定义,因此您可能会认为它不存在——但它*是*一个值,并且是一个非常真实的值!像黑洞一样,`undefined`脾气暴躁,经常会带来麻烦。例如,从中读取一个属性会破坏你的程序: 24 | 25 | ```js 26 | let person = undefined; 27 | console.log(person.mood); // TypeError! 28 | ``` 29 | 30 | 哦,好吧。幸运的是,在整个JavaScript世界中只有一个undefined。你可能会想:它到底为什么会存在?在JavaScript中,它表示无意中丢失的值的概念。 你可以在你自己的代码中使用它,比如写undefined 2或“hello”。然而,undefined也通常“自然发生”。它会在JavaScript不知道你想要的值的情况下出现。例如,如果你忘记给一个变量赋值,它将指向undefined: 31 | 32 | ```js 33 | let bandersnatch; 34 | console.log(bandersnatch); // undefined 35 | ``` 36 | 37 | ![image-20220320114129996](https://tva1.sinaimg.cn/large/e6c9d24egy1h0g75vp9ghj208a028a9x.jpg) 38 | 39 | **然后你可以将它指向另一个值,或者如果你想的话,再次指向undefined。 别太在意它的名字。人们很容易把undefined看作是某种变量状态,例如“这个变量还没有定义”。但这完全是一种误导!实际上,如果读取的变量实际上没有定义(或在let声明之前),则会得到一个错误** 40 | 41 | ```js 42 | console.log(jabberwocky); // ReferenceError! 43 | let jabberwocky; 44 | ``` 45 | 46 | 实际上,undefined是一个常规的原始值,就像2或"hello"。 47 | 48 | 49 | 50 | ## Null 51 | 52 | image-20220320115014478 53 | 54 | 我们的下一站是`null`。你可以把null看作undefined的姐妹值;这种类型只有一个值 - null。它的行为与undefined很相似。例如,当您试图访问它的属性时,它也会引起混乱 55 | 56 | ```js 57 | let mimsy = null; 58 | console.log(mimsy.mood); // TypeError! 59 | ``` 60 | 61 | ![image-20220320115136416](https://tva1.sinaimg.cn/large/e6c9d24egy1h0g7ge0xctj20d004eweg.jpg) 62 | 63 | **Fun Fact** 64 | 65 | null是它自己的类型的唯一值。然而,null也是一个骗子。由于JavaScript中的一个bug,它假装是一个对象: 66 | 67 | ```js 68 | console.log(typeof(null)); // "object" (a lie!) 69 | ``` 70 | 71 | 您可能认为这意味着null是一个对象。不要落入这个陷阱!它是一个原始值,它的行为方式与对象完全不同。 不幸的是,typeof(null)是一个历史上的意外,我们将不得不永远忍受它。 72 | 73 | 在实践中,null用于故意丢失值。为什么null和undefined都有?这可以帮助您区分编码错误(可能导致未定义)和有效的缺失数据(可能表示为null)。然而,这只是一种约定,JavaScript并没有强制这种用法。有些人会尽量避免这两种情况! 74 | 75 | ## Booleans 76 | 77 | ![image-20220320120314654](https://tva1.sinaimg.cn/large/e6c9d24egy1h0g7si6qo1j20dw09wt8t.jpg) 78 | 79 | > 接下来,我们将学习布尔值!像白天和黑夜或开和关一样,只有两个布尔值:true和false。 80 | 81 | ```js 82 | console.log(typeof(true)); // "boolean" 83 | console.log(typeof(false)); // "boolean" 84 | ``` 85 | 86 | 我们可以用它们进行逻辑运算 87 | 88 | ```js 89 | let isSad = true; 90 | let isHappy = !isSad; // The opposite 91 | let isFeeling = isSad || isHappy; // Is at least one of them true? 92 | let isConfusing = isSad && isHappy; // Are both true? 93 | ``` 94 | 95 | image-20220320120540009 96 | 97 | ## Numbers 98 | 99 | image-20220320120616015 100 | 101 | > 到目前为止,我们已经介绍了四个值:null、undefined、true和false。 坚持住,当我们在我们的心智模型中增加18,000,000,437,000,000,736,874亿,4544,812,624个值的时候! 102 | 103 | ```js 104 | console.log(typeof(28)); // "number" 105 | console.log(typeof(3.14)); // "number" 106 | console.log(typeof(-140)); // "number" 107 | ``` 108 | 109 | 一开始,数字可能看起来没什么了不起,但让我们更好地了解它们! 110 | 111 | Math for Computers 112 | 113 | > JavaScript数字与普通数学数字的行为方式不完全相同。下面是一个演示代码片段 114 | 115 | ```js 116 | console.log(0.1 + 0.2 === 0.3); // false 117 | console.log(0.1 + 0.2 === 0.30000000000000004); // true 118 | ``` 119 | 120 | 1. 这可能看起来非常令人惊讶!与普遍的看法相反,这并不意味着JavaScript的数据被打破了。 121 | 2. 这种行为在不同的编程语言中很常见。它甚至还有个名字:浮点数学。 122 | 3. 你看,JavaScript并没有实现我们在现实生活中使用的那种数学。浮点数学是“计算机的数学”。 123 | 4. 不要太担心这个名字,也不要担心它到底是怎么工作的。很少有人知道它所有的微妙之处,这就是问题的关键! 124 | 5. 它在实践中运行得足够好,以至于大多数时候你都不会去想它。尽管如此,让我们快速地看看是什么使它变得不同的 125 | 126 | 你曾经用过扫描仪把一张实物照片或一份文件变成数码照片吗?这个类比可以帮助我们理解JavaScript数字。 扫描仪通常最多能分辨1600万种颜色。如果你用红色蜡笔画一幅画并扫描它,扫描出来的图像也应该是红色的——但它将是我们的扫描仪从这1600万种颜色中挑选出的最接近红色的颜色。所以,如果你有两支颜色稍有不同的红色蜡笔,扫描仪可能会误以为它们的颜色是完全一样的! 我们可以说扫描仪使用颜色的精度有限。 127 | 128 | 我们可以把所有的JavaScript数字想象成一个轴。我们越接近0,数字就越精确,它们之间的“位置”也越接近 129 | 130 | ![iShot2022-03-20 12.12.02](https://tva1.sinaimg.cn/large/e6c9d24egy1h0g81zxdd4g20zk0dsqv9.gif) 131 | 132 | ```js 133 | console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 134 | console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992 135 | console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992 (again!) 136 | console.log(Number.MAX_SAFE_INTEGER + 3); // 9007199254740994 137 | console.log(Number.MAX_SAFE_INTEGER + 4); // 9007199254740996 138 | console.log(Number.MAX_SAFE_INTEGER + 5); // 9007199254740996 (again!) 139 | ``` 140 | 141 | 幸运的是,任何在 `Number.MIN_SAFE_INTEGER` 和 `Number.MAX_SAFE_INTEGER` 之间的整数是正确的. 这就是为什么 `10 + 20 === 30`. 142 | 143 | 但是当我们写0.1或0.2的时候,我们不会得到精确的0.1和0.2。我们在JavaScript中得到了最接近的可用数字。它们几乎一模一样,但可能会有细微的差别。这些微小的差异加起来,这就是为什么0.1 + 0.2和写0.3的数字不完全一样。 144 | 145 | 为什么需要浮点数字呢? 146 | 147 | 按照我的理解,JS宇宙中保存不了那么多的number类型的值,因为内存有限 148 | 149 | 由于计算机内存有限,无论使用[二进制分数](https://floating-point-gui.de/formats/binary/)还是十进制分数,您都无法以无限精度存储数字:在某些时候您必须切断。但是需要多少准确度?它在*哪里*需要?有多少个整数位和多少个小数位? 150 | 151 | - 对于建造高速公路的工程师来说,它是 10 米还是 10.0001 米宽并不重要——他们的测量结果可能一开始就没有那么准确。 152 | - 对于设计微芯片的人来说,0.0001 米(十分之一毫米)是一个*巨大*的差异——但他们永远不必处理大于 0.1 米的距离。 153 | - 物理学家需要在同一个计算中同时使用[光速](http://en.wikipedia.org/wiki/Speed_of_light)(约 300000000)和[牛顿引力常数(约 0.0000000000667)。](http://en.wikipedia.org/wiki/Gravitational_constant) 154 | 155 | 为了满足工程师和芯片设计者的要求,数字格式必须为不同数量级的数字提供准确性。但是,只需要*相对*精度。为了让物理学家满意,必须能够进行涉及不同数量级的数字的计算。 156 | 157 | 基本上,具有固定数量的整数和小数位数是没有用的 - 解决方案是使用*浮点*格式。 158 | 159 | ## Special Numbers 160 | 161 | 值得注意的是,浮点数学包括一些特殊的数字。你可能会遇到NaN,∞,-∞和-0。 162 | 163 | 它们的存在是因为有时您可能会执行1 / 0之类的操作,而JavaScript需要以某种方式表示它们的结果。 164 | 165 | 浮点数学标准指定了它们如何工作,以及使用它们时会发生什么。 以下是在你的代码中可能出现的特殊数字: 166 | 167 | ```js 168 | let scale = 0; 169 | let a = 1 / scale; // Infinity 170 | let b = 0 / scale; // NaN 171 | let c = -a; // -Infinity 172 | let d = 1 / c; // -0 173 | ``` 174 | 175 | 在这些特殊的数字中,NaN特别有趣。NaN是0 / 0和其他一些无效数学运算的结果,表示“不是一个数字”。 176 | 177 | 您可能会困惑为什么它声称是一个数字 178 | 179 | ```js 180 | console.log(typeof(NaN)); // "number" 181 | ``` 182 | 183 | 然而,这里没有诀窍。从JavaScript的角度来看,NaN是一个数值。它不是null, undefined,字符串或其他类型。 184 | 185 | 但在浮点数学中,这个术语的名称是“非数字”。所以它是一个数值。它碰巧被称为“非数字”,因为它表示一个无效的结果。 186 | 187 | 使用这些特殊的数字编写代码并不常见。然而,它们可能由于编码错误而出现。所以很高兴知道它们的存在。 188 | 189 | ## BigInts 190 | 191 | > 普通数字不能精确地表示大整数,因此BigInts填补了这一空白(字面上)。在我们的宇宙中有多少个BigInts ?说明书上说它们有任意的精度。这意味着在我们的JavaScript世界中,有无数个bigint—数学中每个整数对应一个bigint。 192 | 193 | ![image-20220320135439290](https://tva1.sinaimg.cn/large/e6c9d24egy1h0gb0fkd1wj20ca08ajrh.jpg) 194 | 195 | 196 | 197 | ```js 198 | let alot = 9007199254740991n; // n at the end makes it a BigInt! 199 | console.log(alot + 1n); // 9007199254740992n 200 | console.log(alot + 2n); // 9007199254740993n 201 | console.log(alot + 3n); // 9007199254740994n 202 | console.log(alot + 4n); // 9007199254740995n 203 | console.log(alot + 5n); // 9007199254740996n 204 | ``` 205 | 206 | BigInts 是最近才添加到 JavaScript 中的,因此您不会看到它们被广泛使用,如果您使用旧版浏览器,它们将无法工作。 207 | 208 | ## Strings(字符串) 209 | 210 | > 我们的下一站是字符串,它表示 JavaScript 中的文本。字符串有三种写法(单引号、双引号和反引号),但它们指的是同一个概念。这三个字符串文字产生相同的字符串值: 211 | 212 | ![image-20220320135915477](https://tva1.sinaimg.cn/large/e6c9d24egy1h0gb57v8uzj208q06474a.jpg) 213 | 214 | ```js 215 | console.log(typeof("こんにちは")); // "string" 216 | console.log(typeof('こんにちは')); // "string" 217 | console.log(typeof(`こんにちは`)); // "string" 218 | ``` 219 | 220 | ```js 221 | console.log(typeof('')); // "string" 222 | ``` 223 | 224 | #### Strings Aren’t Objects(字符串不是对象) 225 | 226 | > 每个字符串都有一些内置属性 227 | 228 | ```js 229 | let cat = 'Cheshire'; 230 | console.log(cat.length); // 8 231 | console.log(cat[0]); // "C" 232 | console.log(cat[1]); // "h" 233 | ``` 234 | 235 | 这并不意味着字符串就是对象!字符串属性是特殊的,它的行为方式与对象属性不同。例如,您不能将任何内容分配给cat[0]。字符串是基本类型,所有基本类型都是不可变的。 236 | 237 | #### A Value for Every Conceivable String 238 | 239 | > 在我们的宇宙中,每一个可能的字符串都有一个不同的值。 240 | 241 | 字符串是否已经“存在”或“创建”的问题不是我们可以从代码中测试的。*在*我们的心智模型中,这个问题没有任何意义。我们无法建立一个实验来判断字符串在我们的 JavaScript 世界中是“被创建”还是“被召唤”。 242 | 243 | 为了使我们的心智模型简单,我们将说**所有可能的字符串值从一开始就已经存在——每个不同的字符串都有一个值。** 244 | 245 | ## Symbol(符号) 246 | 247 | > 我们已经探索了相当多的 JavaScript 世界,但在我们游览的第一部分还有一个(快速)停留:符号。 248 | 249 | 知道符号的存在很重要,但如果不深入研究对象和属性,就很难解释它们的作用和行为。符号的用途与门钥匙相似:它们让您隐藏对象内部的一些信息并控制代码的哪些部分可以访问它。它们也相对稀有,所以在这次宇宙之旅中,我们将跳过它们。对不起,符号! 250 | 251 | ```js 252 | let alohomora = Symbol(); 253 | console.log(typeof(alohomora)); // "symbol" 254 | ``` 255 | 256 | ## 回顾一下 257 | 258 | 现在我们已经满足了所有的原始值,我们将从我们的旅行中休息一下。让我们回顾一下到目前为止遇到的原始值! 259 | 260 | - **Undefined** 261 | - **Null** 262 | - **Booleans** 263 | - **Numbers** 264 | - **BigInts** 265 | - **Strings** 266 | - **Symbols** 267 | 268 | 我们还了解了一些关于JavaScript数字的有趣事实 269 | 270 | - 并非所有的数字都能在JavaScript中完美地表示出来。它们的小数部分在接近0时提供了更高的精度,而在远离0时提供了更低的精度。 271 | - 来自无效数学运算的数字,如1 / 0或0 / 0是特殊的。NaN就是这样一个数字。它们可能是由于编码错误而出现的。 272 | - typeof(NaN)是数字,因为NaN是数值。它被称为“非数字”,因为它代表了“无效”数字的概念。 273 | 274 | -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/guide/第八章-Properties(属性).md: -------------------------------------------------------------------------------- 1 | # Properties 2 | 3 | 让我们先来看看以下的代码吧 4 | 5 | ```js 6 | let sherlock = { 7 | surname: 'Holmes', 8 | address: { city: 'London' } 9 | }; 10 | ``` 11 | 12 | ```js 13 | let john = { 14 | surname: 'Watson', 15 | address: sherlock.address 16 | }; 17 | ``` 18 | 19 | ```js 20 | john.surname = 'Lennon'; 21 | john.address.city = 'Malibu'; 22 | ``` 23 | 24 | ```js 25 | console.log(sherlock.surname); // ? 26 | console.log(sherlock.address.city); // ? 27 | console.log(john.surname); // ? 28 | console.log(john.address.city); // ? 29 | ``` 30 | 31 | 上面的答案是什么?通过我们之前内容,尝试用JS的心智模型来回答它吧 32 | 33 | ```js 34 | console.log(sherlock.surname); // "Holmes" 35 | console.log(sherlock.address.city); // "Malibu" 36 | console.log(john.surname); // "Lennon" 37 | console.log(john.address.city); // "Malibu" 38 | ``` 39 | 40 | ## Properties 41 | 42 | > 我们之前讨论过Object,例如,这里有一个指向对象值的sherlock变量。我们通过写入{}来创建一个新的对象值 43 | 44 | ```js 45 | let sherlock = {}; 46 | ``` 47 | 48 | 在JavaScript的宇宙中它应该看来是是这样的 49 | 50 | ![image-20220410192322504](https://tva1.sinaimg.cn/large/e6c9d24egy1h14uixekb7j20wc0c2q3b.jpg) 51 | 52 | 然而,对象主要用于将相关数据分组在一起。例如,我们可能想把关于sherlock的不同事实归类,要记住对象只是将数据封装分组,数据并不是属于对象 53 | 54 | ```js 55 | let sherlock = { 56 | surname: 'Holmes', 57 | age: 64, 58 | }; 59 | ``` 60 | 61 | 在这里,sherlock仍然是一个`变量`,但surname和age不是。它们是属性,与变量不同,属性属于特定的对象。 62 | 63 | **在我们的JavaScript世界中,变量和属性的行为都类似于“连接”。但是,属性的连接是从对象开始的,而不是从代码开始的** 64 | 65 | ![image-20220410192656858](https://tva1.sinaimg.cn/large/e6c9d24egy1h14umnddcvj21120d8mxy.jpg) 66 | 67 | 在这里,我们可以看到sherlock变量指向我们创建的一个对象。该对象有两个属性。它的surname属性指向“Holmes”字符串值,它的age属性指向64个数字值。 68 | 69 | 重要的是,**属性不包含值——它们指向值!** 记住,我们的宇宙充满了`电线`。其中一些从代码(变量)开始,另一些从对象(属性)开始。所有导线总是指向值。 70 | 71 | 在阅读本文之前,您可能会认为值存在于对象“内部”,因为它们出现在代码的“内部”。这种直觉经常会导致错误,所以我们会“在电线中思考”。再看一下代码和图表。在继续之前,确保你对他们感到舒服。 72 | 73 | ## Property Names 74 | 75 | 在命名属性时要记住的一件重要事情是,一个对象不能有两个具有相同名称的属性。例如,我们的对象不能有两个名为age的属性。 76 | 77 | 属性名也总是区分大小写!例如,age和Age从JavaScript的角度来看是两个完全不同的属性。 78 | 79 | 如果我们事先不知道属性名,但我们在代码中把它作为字符串值,我们可以使用[]“括号表示法”从对象中读取它 80 | 81 | ```js 82 | let sherlock = { surname: 'Holmes', age: 64 }; 83 | let propertyName = prompt('What do you want to know?'); 84 | alert(sherlock[propertyName]); // Read property by its name 85 | 86 | ``` 87 | 88 | ## Assigning to a Property 89 | 90 | 当我们给属性赋值时会发生什么? 91 | 92 | ```js 93 | sherlock.age = 65; 94 | ``` 95 | 96 | 让我们把这段代码分成左右两部分,用=分隔。 97 | 98 | - 首先,我们找出左边哪根线是sherlock.age 99 | 100 | - 我们跟着sherlock的线,然后选age属性的线 101 | 102 | image-20220410195742272 103 | 104 | 注意,我们没有按照age线写上64岁。 105 | 106 | 我们不关心它的当前值是多少。 107 | 108 | 在分配语句的左边,我们要找导线本身。 还记得我们选了哪根线吗?让我们继续 109 | 110 | 接下来,我们找出右边的值:65。 111 | 112 | image-20220410195943038 113 | 114 | 现在我们准备好执行任务了。 115 | 116 | 最后,我们将左侧的导线指向右侧的值 117 | 118 | image-20220410200018165 119 | 120 | 我们完成了!从现在开始,读sherlock.age是65岁。 121 | 122 | ## Missing Properties(缺失属性) 123 | 124 | 您可能想知道,如果读取一个不存在的属性会发生什么 125 | 126 | ```js 127 | let sherlock = { surname: 'Holmes', age: 64 }; 128 | console.log(sherlock.boat); // ? 129 | ``` 130 | 131 | 我们知道`sherlock.boat`是一个属性表达式,但是我们的JavaScript宇宙如何决定用哪个值来“回答”我们呢? 132 | 133 | **JavaScript使用了一组类似于下面的规则:** 134 | 135 | 1. 算出点(' . ')前面的部分的值。 136 | 137 | 2. 如果该值为' null '或' undefined ',则立即抛出错误。 138 | 139 | 3. 检查对象中是否存在同名属性: 140 | 141 | a.如果**存在**,请使用此属性指向的值回答。 142 | 143 | b.如果**不存在**,则使用' undefined '值回答。 144 | 145 | 这些规则有点简单,但它们已经告诉了我们很多关于JavaScript如何工作的信息!例如,sherlock指向一个没有boat属性的对象。所以`sherlock.boat`给出的答案是undefined 146 | 147 | ```js 148 | let sherlock = { surname: 'Holmes', age: 64 }; 149 | console.log(sherlock.boat); // undefined 150 | ``` 151 | 152 | 注意,这并不意味着我们的对象有一个指向undefined的boat属性!它只有两个属性 surname 和 age 153 | 154 | 人们很容易想到`sherlock.boat`直接对应于我们心理模型中的属性概念,但这并不完全正确。这是一个`问题`,所以JavaScript`遵循规则`来回答这个问题。 155 | 156 | 它观察‘sherlock’指向的对象,发现它**没有**的‘boat’属性,并返回给我们一个`undefined`的值,因为**这就是规则所说的**。这其中没有更深层次的原因,`计算机遵循规则`。 157 | 158 | 事实上 159 | 160 | > 从根本上说,这是因为每个表达式都需要产生某个值,或者抛出一个错误。其他一些语言在访问不存在的属性时会抛出错误,但JavaScript不是其中之一! 161 | 162 | 现在再看看下面的代码,你能告诉我正确结果么? 163 | 164 | ```js 165 | let sherlock = { surname: 'Holmes', age: 64 }; 166 | console.log(sherlock.boat.name); // ? 167 | ``` 168 | 169 | 不要猜测哦,遵循JS的规则 170 | 171 | Hint: 这里有两个点,所以你需要遵守规则两次。 172 | 173 | 答案是会抛出一个错误,可以按照下面的代码来拆解问题就知道了👇 174 | 175 | ```js 176 | let sherlock = { surname: 'Holmes', age: 64 }; 177 | console.log(sherlock.boat); // undefined 178 | console.log(sherlock.boat.name); // TypeError! 179 | ``` 180 | 181 | ## 回顾 182 | 183 | - 属性是线——有点像变量。它们都指向值。与变量不同,属性在我们的宇宙中是从`对象`开始的,变量是从`代码`开始的 184 | 185 | - 属性属于特定的对象。一个对象上不能有多个具有相同名称的属性。 186 | 187 | - 一般来说,你可以用三个步骤来执行赋值: 188 | 189 | 1. 找出哪根线在左边 190 | 2. 找出右边的值 191 | 3. 把导线指向那个值 192 | 193 | - 一个表达式 像下面的 194 | 195 | ``` 196 | obj.property 197 | ``` 198 | 199 | 是分三步计算: 200 | 201 | 1. 找出`.`左边的值 202 | 2. 如果是` null`或` undefined `,则抛出一个错误。 203 | 3. 如果该属性存在,结果就是它所指向的值。如果该属性不存在,则结果为`undefined `。 204 | 205 | 注意,这种属性的心理模型仍然有些简化。目前它已经足够好了,但随着您对JavaScript领域的了解越来越多,它还需要进一步扩展。 206 | -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/guide/第六章-会见对象与函数.md: -------------------------------------------------------------------------------- 1 | # Meeting Objects and Functions 2 | 3 | > 在前面的模块中,我们已经了解了原始值:Undefined、Null、Booleans、Numbers、BigInts、Strings 和 Symbols 。现在我们将介绍非原始值—这些类型可以让我们创建自己的值。 4 | 5 | ![image-20220326184330896](https://tva1.sinaimg.cn/large/e6c9d24egy1h0nh2vwh6dj20u00z5q5i.jpg) 6 | 7 | ## Objects 8 | 9 | > 数组、正则等等非原始值都属于对象类型 10 | 11 | ```js 12 | console.log(typeof({})); // "object" 13 | console.log(typeof([])); // "object" 14 | console.log(typeof(new Date())); // "object" 15 | console.log(typeof(/\d+/)); // "object" 16 | console.log(typeof(Math)); // "object" 17 | ``` 18 | 19 | 与以前不同,对象不是原始值。这也意味着默认情况下,它们是可变的(我们可以更改它们)。我们可以用`.`或`[]` 20 | 21 | ```js 22 | let rapper = { name: 'Malicious' }; 23 | rapper.name = 'Malice'; // Dot notation 24 | rapper['name'] = 'No Malice'; // Bracket notation 25 | ``` 26 | 27 | 创建我们自己的对象 28 | 29 | 在前面介绍的原始值,我们只能召唤它们,而不能自己创造它们,但是对象类型,是我们可以创造的 30 | 31 | 每次使用{}对象字面量时,都会创建一个全新的对象值 32 | 33 | ```js 34 | let shrek = {}; 35 | let donkey = {}; 36 | ``` 37 | 38 | ![image-20220326184851319](https://tva1.sinaimg.cn/large/e6c9d24egy1h0nh8dw33xj20sg0iat9s.jpg) 39 | 40 | 我们创造的对象会消失么? 41 | 42 | 会的,当我们将指向对象的变量(线)指向null原始值的时候,我们创造的对象就没有变量指向,JavaScript的垃圾回收机制就在不经意间去回收掉它♻️ 43 | 44 | ## Functions 45 | 46 | > 我们定义函数是为了以后可以调用它们并在其中运行代码。然而,要真正理解JavaScript中的函数,我们需要暂时忘记它们为什么有用。相反,我们将把函数看作另一种类型的值:数字、对象、函数。 47 | 48 | 让我们来看看下面的代码,用你现在的心智模型思考一下🤔,它们有什么区别么 49 | 50 | ```js 51 | for (let i = 0; i < 7; i++) { 52 | console.log(2); 53 | } 54 | ``` 55 | 56 | 传递给log函数的永远只有一个value值就是2 57 | 58 | ```js 59 | for (let i = 0; i < 7; i++) { 60 | console.log({}); 61 | } 62 | ``` 63 | 64 | 这里会创建7个不同的对象类型的值 65 | 66 | ```js 67 | for (let i = 0; i < 7; i++) { 68 | console.log(function() {}); 69 | } 70 | ``` 71 | 72 | 这里同样也会创建7个不同对象类型的值,因为函数也是对象 73 | 74 | 从技术上讲,函数是JavaScript中的对象。我们将继续将它们作为一个独立的基本类型,因为与常规对象相比,它们具有独特的功能。但一般来说,如果你能对一个对象做些什么,你也能对一个函数做些什么。它们很特别 75 | 76 | ### 函数调用 77 | 78 | ```js 79 | let countDwarves = function() { return 7; }; 80 | let dwarves = countDwarves(); // () is a function call 81 | console.log(dwarves); 82 | ``` 83 | 84 | 添加()会改变代码的含义: 85 | 86 | - let dwarves = countDwarves表示“将dwarves指向countDwarves当前指向的值。” 87 | - let dwarves = countDwarves()表示“将 dwarves指向countDwarves当前指向的函数返回的值。” 88 | 89 | 事实上,countDwarves()也是一个表达式。它被称为调用表达式。要“回答”一个调用表达式,JavaScript在函数内部运行代码,并将返回值作为结果交给我们(在本例中是7)。 90 | 91 | ## 回顾 92 | 93 | 在前两个模块中,我们已经了解了JavaScript中的每种值类型。让我们回顾一下我们遇到的每种类型的值,从不同的原始类型开始 94 | 95 | ![image-20220326190008951](https://tva1.sinaimg.cn/large/e6c9d24egy1h0nhk53o1lj20we0fywfq.jpg) 96 | 97 | - Undefined 98 | - Null 99 | - Booleans 100 | - Numbers 101 | - BigInts 102 | - Strings 103 | - Symbols 104 | 105 | 然后这里还有两个特殊的类型,可以自己创造值的类型 106 | 107 | ![image-20220326190115441](https://tva1.sinaimg.cn/large/e6c9d24egy1h0nhlatcz4j20to0fut9b.jpg) 108 | 109 | - Objects 110 | - Functions 111 | 112 | 113 | 114 | - 原始值(字符串、数字等等)一直存在于我们的宇宙中。例如,写2或“hello”总是“召唤”相同的数字或字符串值。 115 | - 对象和函数的行为不同,并允许我们生成自己的值。编写{}或function(){}总是会创建一个全新的、不同的值。这个想法对于理解是至关重要的 116 | -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/guide/第十章-原型.md: -------------------------------------------------------------------------------- 1 | # Prototypes 2 | 3 | > 在之前的模块中,我们已经讨论了对象属性和变异,但是还没有完全完成——我们仍然需要讨论原型! 4 | 5 | 这里有一个小谜语来检验我们的心智模式 6 | 7 | ```js 8 | let pizza = {}; 9 | console.log(pizza.taste); // "pineapple" 10 | ``` 11 | 12 | 问问你自己:这可能吗? 13 | 14 | 我们只是用`{}`创建了一个空对象。在`log`之前,我们肯定没有给它设置任何属性,所以看起来pizza.taste不能指向 "pineapple"。我们会期望pizza.taste给我们的是`undefined`,而不是当一个属性不存在的时候,我们通常会得到undefined,对吗? 15 | 16 | 然而,pizza.taste有可能是 `pineapple`!这可能是一个伪造的例子。这可能是一个矫揉造作的例子,但它表明我们的心理模型是不完整的。 17 | 18 | 在这个模块中,我们将介绍原型。原型解释了这个谜题中发生的事情,更重要的是,它是其他几个基本JavaScript特性的核心。偶尔人们会忽略对原型的学习,因为它们看起来太不寻常了,但其核心思想是非常简单的。 19 | 20 | ## Prototypes 21 | 22 | 这里有两个变量指向两个对象 23 | 24 | ```js 25 | let human = { 26 | teeth: 32 27 | }; 28 | 29 | 30 | let gwen = { 31 | age: 19 32 | }; 33 | ``` 34 | 35 | 我们可以用熟悉的方式来表示它们 36 | 37 | ![image-20220417175550659](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cvc0iyu0j211s0ga0tl.jpg) 38 | 39 | 在此例, `gwen` 指向一个没有 `teeth` 属性的对象.根据我们学过的规则,这个属性可以得到`undefined` 40 | 41 | ```js 42 | console.log(gwen.teeth); // undefined 43 | ``` 44 | 45 | 但故事不会就此结束。JavaScript的默认行为返回`undefined`,但我们可以指示它继续在另一个对象上搜索丢失的属性。我们可以用一行代码来完成 46 | 47 | ```js 48 | let human = { 49 | teeth: 32 50 | }; 51 | 52 | 53 | let gwen = { 54 | // We added this line: 55 | __proto__: human, 56 | age: 19 57 | }; 58 | ``` 59 | 60 | 那个神秘的`__proto__`属性是什么? 61 | 62 | 它代表了JavaScript原型的概念。任何JavaScript对象都可以选择另一个对象作为原型。我们将讨论这在实践中意味着什么,但现在,让我们把它看作一个特殊的`__proto__`导线 63 | 64 | ![image-20220417175958311](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cvgaz35sj21240kcjsq.jpg) 65 | 66 | 花点时间来验证图表与代码是否匹配。我们画的和以前一样。唯一的新东西是神秘的`__proto__`电线。 通过指定`__proto__`(也被称为对象的原型),我们指示JavaScript继续寻找该对象上缺失的属性。 67 | 68 | ## 原型在行动 69 | 70 | 我们去找`gwen.teeth`时,我们得到了`undefined`因为`teeth`属性在`Gwen`指向的对象上不存在。 但是由于`__proto__: human`,答案是不同的: 71 | 72 | ```js 73 | let human = { 74 | teeth: 32 75 | }; 76 | 77 | 78 | let gwen = { 79 | // "Look for other properties here" 80 | __proto__: human, 81 | age: 19 82 | }; 83 | 84 | 85 | console.log(gwen.teeth); // 32 86 | ``` 87 | 88 | - JS宇宙会去查找`gwen`导线找到指向的对象 89 | - 然后询问对象上是否有`teeth`属性 90 | - 得到没有的答案 91 | - 但是有一个原型属性,你可以去找找看 92 | - 于是就继续去原型属性指向的对象查找`teeth`属性 93 | - 是的,找到了teeth属性指向32 94 | - 所以 `gwen.teeth`的结果是32 95 | 96 | 这类似于说,“我不知道,但`XXX`可能知道。使用`__proto__`,你指示JavaScript询问另一个对象 97 | 98 | 为了检查你到目前为止的理解,写下你的答案: 99 | 100 | ```js 101 | let human = { 102 | teeth: 32 103 | }; 104 | 105 | 106 | let gwen = { 107 | __proto__: human, 108 | age: 19 109 | }; 110 | 111 | 112 | console.log(human.age); // ? 113 | console.log(gwen.age); // ? 114 | 115 | 116 | console.log(human.teeth); // ? 117 | console.log(gwen.teeth); // ? 118 | 119 | 120 | console.log(human.tail); // ? 121 | console.log(gwen.tail); // ? 122 | ``` 123 | 124 | 答案如下 125 | 126 | ```js 127 | console.log(human.age); // undefined 128 | console.log(gwen.age); // 19 129 | 130 | console.log(human.teeth); // 32 131 | console.log(gwen.teeth); // 32 132 | 133 | console.log(human.tail); // undefined 134 | console.log(gwen.tail); // undefined 135 | ``` 136 | 137 | 上面的例子提醒了我们,`gwen.teeth`只是一个表达——一个对我们JavaScript世界的问题——JavaScript将遵循一系列步骤来回答它。现在我们知道这些步骤包括查看原型。 138 | 139 | ## Prototype Chain(原型链) 140 | 141 | > 原型在JavaScript中并不是一个特殊的“东西”。原型更像是一种关系。一个对象可以指向另一个对象作为它的原型。 这自然会引出一个问题:但是如果我的对象的原型有它自己的原型呢?那个原型有自己的原型吗?会工作吗? 答案是肯定的——这就是它的工作原理! 142 | 143 | ```js 144 | let mammal = { 145 | brainy: true, 146 | }; 147 | 148 | 149 | let human = { 150 | __proto__: mammal, 151 | teeth: 32 152 | }; 153 | 154 | 155 | let gwen = { 156 | __proto__: human, 157 | age: 19 158 | }; 159 | 160 | 161 | console.log(gwen.brainy); // true 162 | ``` 163 | 164 | 我们可以看到JavaScript将在对象上搜索属性,然后在对象的原型上搜索属性,然后在对象的原型上搜索属性,以此类推。如果我们没有原型,也没有找到我们的属性,我们就得到`undefined`。 165 | 166 | 这类似于说,“我不知道,但爱丽丝可能知道。”然后爱丽丝可能会说:“实际上,我也不知道——问鲍勃吧。”最终,你要么找到答案,要么找不到人问了! 这个要“访问”的对象序列被称为我们的对象原型链。(然而,与你可能佩戴的链条不同,原型链条不可能是圆形的!) 167 | 168 | ## Shadowing 遮蔽 169 | 170 | 考虑一下这个稍微修改过的例子 171 | 172 | ```js 173 | let human = { 174 | teeth: 32 175 | }; 176 | 177 | 178 | let gwen = { 179 | __proto__: human, 180 | // This object has its own teeth property: 181 | teeth: 31 182 | }; 183 | ``` 184 | 185 | ```js 186 | console.log(human.teeth); // 32 187 | console.log(gwen.teeth); // 31 188 | ``` 189 | 190 | 注意,`gwen.teen`是31。如果gwen没有自己的 `teeth` 属性,我们会看看原型。但是因为gwen指向的对象有它自己的teeth属性,我们不需要继续寻找答案 191 | 192 | 换句话说,一旦找到我们的属性,我们就停止搜索。 193 | 194 | 如果你想要检查一个对象是否有它自己的属性线与特定的名称,你可以调用一个内置的函数`hasOwnProperty`。对于“own”属性,它返回true,而不查看原型。在我们的最后一个例子中,两个对象都有自己的牙齿线,所以这对两个都是正确的 195 | 196 | ```js 197 | console.log(human.hasOwnProperty('teeth')); // true 198 | console.log(gwen.hasOwnProperty('teeth')); // true 199 | ``` 200 | 201 | ## Assignment 202 | 203 | 考虑一下这个例子 204 | 205 | ```js 206 | let human = { 207 | teeth: 32 208 | }; 209 | 210 | 211 | let gwen = { 212 | __proto__: human, 213 | // Note: no own teeth property 214 | }; 215 | 216 | 217 | gwen.teeth = 31; 218 | 219 | 220 | console.log(human.teeth); // ? 221 | console.log(gwen.teeth); // ? 222 | ``` 223 | 224 | 答案如下 225 | 226 | ```js 227 | console.log(human.teeth); // 32 228 | console.log(gwen.teeth); // 31 229 | ``` 230 | 231 | 当我们*读取*对象上不存在的属性时,我们将继续在原型链上查找它。如果我们没有找到它,我们会得到`undefined`. 232 | 233 | 但是当我们*编写*一个在我们的对象上不存在的属性时,它会在我们的对象上*创建*该属性。一般来说,原型*不会*发挥作用。 234 | 235 | ## Object原型 236 | 237 | 这个对象没有原型,对吧? 238 | 239 | ```js 240 | let obj = {}; 241 | ``` 242 | 243 | 在浏览器打印一下 244 | 245 | ```js 246 | let obj = {}; 247 | console.log(obj.__proto__); //玩一下 248 | ``` 249 | 250 | 令人惊讶的是,`obj.__proto__`不是`null`或`undefined`!相反,您将看到一个奇怪的对象,它有一堆属性,包括`hasOwnProperty` 251 | 252 | 我们将这个特殊对象称为`Object Prototype` 253 | 254 | ![image-20220417182152539](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cw33bc5oj212o0qe40u.jpg) 255 | 256 | 一直以来,我们都认为{}创建了一个`空`对象,但它毕竟不是那么空!它有一个隐藏的`__proto__`线,默认情况下指向`对象原型`。 这就解释了为什么JavaScript对象似乎有“内置”属性: 257 | 258 | ```js 259 | let human = { 260 | teeth: 32 261 | }; 262 | console.log(human.hasOwnProperty); // (function) 263 | console.log(human.toString); // // (function) 264 | ``` 265 | 266 | 这些`内置`属性只不过是对象原型上的普通属性。因为我们的对象原型是object prototype,所以我们可以访问它们。 267 | 268 | ## 没有原型的对象 269 | 270 | 我们刚刚了解到,所有使用`{}`语法创建的对象都有特殊的`__proto__`导线指向一个默认的`对象原型`。但是我们也知道我们可以自定义`__proto__`你可能会想:我们能把它设为`null`吗? 271 | 272 | ```js 273 | let weirdo = { 274 | __proto__: null 275 | }; 276 | ``` 277 | 278 | 当然可以 279 | 280 | ```js 281 | console.log(weirdo.hasOwnProperty); // undefined 282 | console.log(weirdo.toString); // undefined 283 | ``` 284 | 285 | 你可能不想创建这样的对象,但是对象原型正是这样的——一个没有原型的对象。 286 | 287 | ## 原型污染 288 | 289 | 现在我们知道,所有JavaScript对象默认情况下都有相同的原型。让我们简要地回顾一下关于突变模块的例子: 290 | 291 | ![image-20220417182746505](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cw98aid5j213j0u00v1.jpg) 292 | 293 | 这幅图给了我们一个有趣的见解。如果JavaScript在原型中搜索丢失的属性,而大多数对象都共享相同的原型,那么我们是否可以通过改变原型使新属性“出现”在所有对象上? 294 | 295 | ```js 296 | let obj = {}; 297 | obj.__proto__.smell = 'banana'; 298 | ``` 299 | 300 | ```js 301 | nsole.log(sherlock.smell); // "banana" 302 | console.log(watson.smell); // "banana" 303 | ``` 304 | 305 | ![image-20220417182834694](https://tva1.sinaimg.cn/large/e6c9d24egy1h1cwa26wzgj20zd0u0go6.jpg) 306 | 307 | - 像我们刚才做的那样,改变一个共享的原型叫做原型污染。 308 | - 在过去,原型污染是自定义特性扩展JavaScript的一种流行方式。 309 | - 然而,多年来,网络社区意识到它是脆弱的,很难添加新的语言特性,所以我们宁愿避免它 310 | 311 | ### 有趣的事实 312 | 313 | 在JavaScript添加类之前,通常是将它们编写为生成对象的函数,例如: 314 | 315 | ```js 316 | function Donut() { 317 | return { shape: 'round' }; 318 | } 319 | 320 | 321 | let donutProto = { 322 | eat() { 323 | console.log('Nom nom nom'); 324 | } 325 | }; 326 | 327 | 328 | let donut1 = Donut(); 329 | donut1.__proto__ = donutProto; 330 | let donut2 = Donut(); 331 | donut2.__proto__ = donutProto; 332 | 333 | donut1.eat(); 334 | donut2.eat(); 335 | ``` 336 | 337 | 这就是为什么JavaScript有一个`new`的关键字。当您将`new`关键字放在Donut()函数调用之前时,会发生两件事 338 | 339 | - 该对象是自动创建的,所以您不需要从Donut返回它。(可以这样使用。) 340 | - 该对象的`__proto__`将被设置为你放入函数的prototype属性中的任何值。 341 | 342 | ```js 343 | function Donut() { 344 | this.shape = 'round'; 345 | } 346 | Donut.prototype = { 347 | eat() { 348 | console.log('Nom nom nom'); 349 | } 350 | }; 351 | 352 | 353 | let donut1 = new Donut(); // __proto__: Donut.prototype 354 | let donut2 = new Donut(); // __proto__: Donut.prototype 355 | 356 | 357 | donut1.eat(); 358 | donut2.eat(); 359 | ``` 360 | 361 | 362 | 363 | **ES6 Class** 364 | 365 | Class.js 366 | 367 | ```js 368 | class Spiderman { 369 | lookOut() { 370 | alert('My Spider-Sense is tingling.'); 371 | } 372 | } 373 | 374 | let miles = new Spiderman(); 375 | miles.lookOut(); 376 | ``` 377 | 378 | Prototypes.js 379 | 380 | ```js 381 | // class Spiderman { 382 | let SpidermanPrototype = { 383 | lookOut() { 384 | alert('My Spider-Sense is tingling.'); 385 | } 386 | }; 387 | 388 | // let miles = new Spiderman(); 389 | let miles = { __proto__: SpidermanPrototype }; 390 | miles.lookOut(); 391 | ``` 392 | 393 | ## 回顾 394 | 395 | - 读取时`obj.something`,如果`obj`没有`something`属性,JavaScript 会查找`obj.__proto__.something`. 然后它会寻找`obj.__proto__.__proto__.something`,依此类推,直到找到我们的属性或到达原型链的末端。 396 | - 写入时`obj.something`,JavaScript 通常会直接写入对象,而不是遍历原型链。 397 | - 我们可以使用它`obj.hasOwnProperty('something')`来确定我们的对象是否有*自己的*属性,称为`something`. 398 | - 我们可以通过变异来“污染”许多对象共享的原型。我们甚至可以对对象原型(对象的默认原型)执行此操作`{}`!(但我们不应该这样做,除非我们在恶作剧我们的同事。) 399 | - 在实践中,您可能不会直接使用原型。但是,它们是 JavaScript 对象的基础,因此理解它们的底层机制很方便。一些高级的 JavaScript 特性,包括类,可以用原型来表达。 -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/guide/第四章-从内部学习.md: -------------------------------------------------------------------------------- 1 | # Studying from the Inside(从内部学习) 2 | 3 | ## 从外面学习 4 | 5 | 研究我们的 JavaScript 世界的一种方法是*从外部研究它*。 6 | 7 | 我们可能会专注于模拟我们的世界——一个 JavaScript 引擎——是如何“真正”工作的。例如,我们可能会了解到这个文本字符串——*我们世界中的一个值*——是存储在硅芯片中的字节序列。 8 | 9 | 这种方法将我们的注意力集中在人和计算机的物理世界上。我们的方法不同。 10 | 11 | 比如我们去阅读JS的引擎如何执行的,计算机如何分配内存的等等 12 | 13 | ## 从内部学习 14 | 15 | 我们将从*内部*研究我们的世界。将自己的精神转移到 JavaScript 世界中,并站在我旁边。我们将观察我们的宇宙法则,并像物理宇宙中的物理学家一样进行实验。 16 | 17 | **我们将了解我们的 JavaScript 世界,而无需考虑它是如何实现的。这类似于物理学家讨论恒星的属性而不质疑\*物理\*世界是否真实。没关系!无论我们是在研究物理世界还是 JavaScript 世界,我们都可以用它们自己的术语来描述它们。** 18 | 19 | 我们的心智模型不会试图解释一个值是如何在计算机的内存中表示的。答案一直在变化![当您的程序运行时](https://v8.dev/blog/react-cliff),答案甚至会发生变化。如果你听过关于 JavaScript 如何“真正”表示内存中的数字、字符串或对象的简单解释,那很可能是错误的。 20 | 21 | 对我们来说,每个字符串都是一个值——不是“指针”或“内存地址”——一个*值*。**在我们的宇宙中,一个值就足够了。** 不要让“记忆细胞”和其他低级隐喻分散您构建 JavaScript 的准确高级心智模型的注意力。反正都是乌龟! 22 | 23 | 如果您来自较低级别的语言,请抛开您对“通过引用传递”、“在堆栈上分配”、“在写入时复制”等的直觉。这些关于计算机如何工作的模型通常使人们*更难以*对 JavaScript 中可以发生或不能发生的事情充满信心。我们将查看一些较低级别的细节,但只关注[真正重要的地方](https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/)。它们可以作为我们心智模型的*补充*,而不是它的基础。 24 | 25 | **我们心智模型的基础是价值观。** 每个值都属于一个类型。原始值是不可变的。我们可以使用我们称之为变量的“连线”来指向值。这个基础——对价值观的理解——将帮助我们继续建立我们的心智模型。 26 | 27 | 至于这些奇异的景象,我不再多想了。我有电线要指出,有问题要问。我最好得到它! 28 | 29 | 当我看着星星时,它们是明亮的。 30 | 31 | 我眨眼时它们还在吗? 32 | 33 | 我耸了耸肩。 34 | 35 | *“实施细节。”* 36 | 37 | -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | title: JavaScript Mental Modules 5 | titleTemplate: Vite & Vue Powered Static Site Generator 6 | 7 | hero: 8 | name: JavaScript心智模型 9 | text: 探索JavaScript宇宙🌍 10 | tagline: 从内到外重建你的思维模式~~~🏄‍♂️ 本小册是学习Dan Abramov的JustJavaScript的一些学习心得及笔记📒希望能帮助大家更好的理解JavaScript 11 | actions: 12 | - theme: brand 13 | text: Get Started 14 | link: /guide/小册简介.html 15 | - theme: alt 16 | text: View on GitHub 17 | link: https://github.com/sudongyuer/javascript-mental-models 18 | 19 | features: 20 | - title: "🌈仅仅是JavaScript" 21 | details: 发现并重建你的 JavaScript 心智模型 22 | - title: "💗获得真正的信心" 23 | details: 小册会给你的是信心,确信您知道JavaScript代码在做什么 24 | --- 25 | -------------------------------------------------------------------------------- /packages/javascript-mental-modules-doc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-mental-models-doc", 3 | "version": "1.1.0", 4 | "description": "JavaScript mental models", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1", 7 | "dev": "vitepress dev docs", 8 | "build": "vitepress build docs", 9 | "serve": "vitepress serve docs" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/sudongyuer/javascript-mental-models" 14 | }, 15 | "keywords": [ 16 | "javascript", 17 | "mental models", 18 | "study" 19 | ], 20 | "main": "index.js", 21 | "author": "sudongyuer", 22 | "license": "MIT", 23 | "devDependencies": { 24 | "vitepress": "^1.0.0-alpha.4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "baseBranch": "origin/master", 4 | "pipeline": { 5 | "build": { 6 | "dependsOn": [ 7 | "^build" 8 | ], 9 | "outputs": [ 10 | ".next/**", 11 | "dist/**", 12 | "build/**" 13 | ] 14 | }, 15 | "test":{ 16 | "dependsOn": ["build"], 17 | "outputs": [], 18 | "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"] 19 | }, 20 | "deploy":{ 21 | "dependsOn": ["build", "test"], 22 | "outputs": [] 23 | } 24 | } 25 | } 26 | --------------------------------------------------------------------------------