JS初学

开始学习JavaScript

1、

  • 我们可以使用一个 <script> 标签将 JavaScript 代码添加到页面中。
  • type 和 language 属性不是必需的。
  • 外部的脚本可以通过 <script src="path/to/script.js"></script> 的方式插入。

2、在大多数的编辑器中,一行代码可以使用 Ctrl+/ 热键进行单行注释,诸如 Ctrl+Shift+/ 的热键可以进行多行注释(选择代码,然后按下热键)。对于 Mac 电脑,应使用 Cmd 而不是 Ctrl,使用 Option 而不是 Shift。

 

3、

  1. "use strict" 指令将浏览器引擎转换为“现代”模式,改变一些内建特性的行为。我们会在之后的学习中了解这些细节。
  2. 严格模式通过将 "use strict" 放置在整个脚本或函数的顶部来启用。一些新语言特性诸如 “classes” 和 “modules” 也会自动开启严格模式。
  3. 所有的现代浏览器都支持严格模式。
  4. 我们建议始终使用 "use strict" 启动脚本。本教程的所有例子都默认采用严格模式,除非特别指定(非常少)

4、

我们可以使用 varlet 或 const 声明变量来存储数据。

  • let — 现代的变量声明方式。
  • var — 老旧的变量声明方式。一般情况下,我们不会再使用它。但是,我们会在 旧时的 "var" 章节介绍 var 和 let 的微妙差别,以防你需要它们。
  • const — 类似于 let,但是变量的值无法被修改。

变量应当以一种容易理解变量内部是什么的方式进行命名。

 

5、

JavaScript 中有八种基本的数据类型(译注:前七种为基本数据类型,也称为原始类型,而 object 为复杂数据类型)。

  • number 用于任何类型的数字:整数或浮点数,在 ±253 范围内的整数。
  • bigint 用于任意长度的整数。
  • string 用于字符串:一个字符串可以包含一个或多个字符,所以没有单独的单字符类型。
  • boolean 用于 true 和 false
  • null 用于未知的值 —— 只有一个 null 值的独立类型。
  • undefined 用于未定义的值 —— 只有一个 undefined 值的独立类型。
  • symbol 用于唯一的标识符。
  • object 用于更复杂的数据结构。

我们可以通过 typeof 运算符查看存储在变量中的数据类型。

  • 两种形式:typeof x 或者 typeof(x)
  • 以字符串的形式返回类型名称,例如 "string"
  • typeof null 会返回 "object" —— 这是 JavaScript 编程语言的一个错误,实际上它并不是一个 object

ps:专门试了一下 typeof null 会返回 "object"

6、

有三种常用的类型转换:转换为 string 类型、转换为 number 类型和转换为 boolean 类型。

字符串转换 —— 转换发生在输出内容的时候,也可以通过 String(value) 进行显式转换。原始类型值的 string 类型转换通常是很明显的。

数字型转换 —— 转换发生在进行算术操作时,也可以通过 Number(value) 进行显式转换。

数字型转换遵循以下规则:

变成……
undefinedNaN
null0
true / false1 / 0
string“按原样读取”字符串,两端的空白会被忽略。空字符串变成 0。转换出错则输出 NaN

布尔型转换 —— 转换发生在进行逻辑操作时,也可以通过 Boolean(value) 进行显式转换。

布尔型转换遵循以下规则:

变成……
0nullundefinedNaN""false
其他值true

上述的大多数规则都容易理解和记忆。人们通常会犯错误的值得注意的例子有以下几个:

  • 对 undefined 进行数字型转换时,输出结果为 NaN,而非 0
  • 对 "0" 和只有空格的字符串(比如:" ")进行布尔型转换时,输出结果为 true

 

7、

  • 比较运算符始终返回布尔值。
  • 字符串的比较,会按照“词典”顺序逐字符地比较大小。
  • 当对不同类型的值进行比较时,它们会先被转化为数字(不包括严格相等检测)再进行比较。
  • 在非严格相等 == 下,null 和 undefined 相等且各自不等于任何其他的值。
  • 在使用 > 或 < 进行比较时,需要注意变量可能为 null/undefined 的情况。比较好的方法是单独检查变量是否等于 null/undefined

 

8、

我们学习了与用户交互的 3 个浏览器的特定函数:

alert

显示信息。

prompt

显示信息要求用户输入文本。点击确定返回文本,点击取消或按下 Esc 键返回 null

confirm

显示信息等待用户点击确定或取消。点击确定返回 true,点击取消或按下 Esc 键返回 false

这些方法都是模态的:它们暂停脚本的执行,并且不允许用户与该页面的其余部分进行交互,直到窗口被解除。

上述所有方法共有两个限制:

  1. 模态窗口的确切位置由浏览器决定。通常在页面中心。
  2. 窗口的确切外观也取决于浏览器。我们不能修改它。

这就是简单的代价。还有其他一些方法可以显示更漂亮的窗口,并与用户进行更丰富的交互,但如果“花里胡哨”不是非常重要,那使用本节讲的这些方法也挺好。

 

9、

我们学习了三种循环:

  • while —— 每次迭代之前都要检查条件。
  • do..while —— 每次迭代后都要检查条件。
  • for (;;) —— 每次迭代之前都要检查条件,可以使用其他设置。

通常使用 while(true) 来构造“无限”循环。这样的循环和其他循环一样,都可以通过 break 指令来终止。

如果我们不想在当前迭代中做任何事,并且想要转移至下一次迭代,那么可以使用 continue 指令。

break/continue 支持循环前的标签。标签是 break/continue 跳出嵌套循环以转到外部的唯一方法。

 

10、

函数声明方式如下所示:

function name(parameters, delimited, by, comma) {
  /* code */
}
  • 作为参数传递给函数的值,会被复制到函数的局部变量。
  • 函数可以访问外部变量。但它只能从内到外起作用。函数外部的代码看不到函数内的局部变量。
  • 函数可以返回值。如果没有返回值,则其返回的结果是 undefined

为了使代码简洁易懂,建议在函数中主要使用局部变量和参数,而不是外部变量。

与不获取参数但将修改外部变量作为副作用的函数相比,获取参数、使用参数并返回结果的函数更容易理解。

函数命名:

  • 函数名应该清楚地描述函数的功能。当我们在代码中看到一个函数调用时,一个好的函数名能够让我们马上知道这个函数的功能是什么,会返回什么。
  • 一个函数是一个行为,所以函数名通常是动词。
  • 目前有许多优秀的函数名前缀,如 create…show…get…check… 等等。使用它们来提示函数的作用吧。

函数是脚本的主要构建块。现在我们已经介绍了基本知识,现在我们就可以开始创建和使用函数了。但这只是学习和使用函数的开始。我们将继续学习更多函数的相关知识,更深入地研究它们的先进特征。

 

11、

  • 函数是值。它们可以在代码的任何地方被分配,复制或声明。
  • 如果函数在主代码流中被声明为单独的语句,则称为“函数声明”。
  • 如果该函数是作为表达式的一部分创建的,则称其“函数表达式”。
  • 在执行代码块之前,内部算法会先处理函数声明。所以函数声明在其被声明的代码块内的任何位置都是可见的。
  • 函数表达式在执行流程到达时创建。

在大多数情况下,当我们需要声明一个函数时,最好使用函数声明,因为函数在被声明之前也是可见的。这使我们在代码组织方面更具灵活性,通常也会使得代码可读性更高。

 

12、

对于一行代码的函数来说,箭头函数是相当方便的。它具体有两种:

  1. 不带花括号:(...args) => expression — 右侧是一个表达式:函数计算表达式并返回其结果。
  2. 带花括号:(...args) => { body } — 花括号允许我们在函数中编写多个语句,但是我们需要显式地 return 来返回一些内容。

 

13、

对象是具有一些特殊特性的关联数组。

它们存储属性(键值对),其中:

  • 属性的键必须是字符串或者 symbol(通常是字符串)。
  • 值可以是任何类型。

我们可以用下面的方法访问属性:

  • 点符号: obj.property
  • 方括号 obj["property"],方括号允许从变量中获取键,例如 obj[varWithKey]

其他操作:

  • 删除属性:delete obj.prop
  • 检查是否存在给定键的属性:"key" in obj
  • 遍历对象:for(let key in obj) 循环。

对象是通过引用被赋值或复制的。换句话说,变量存储的不是“对象的值”,而是值的“引用”(内存地址)。所以复制这样的变量或者将其作为函数参数进行传递时,复制的是引用,而不是对象。基于复制的引用(例如添加/删除属性)执行的所有的操作,都是在同一个对象上执行的。

我们可以使用 Object.assign 或者 _.cloneDeep(obj) 进行“真正的复制”(一个克隆)。

我们在这一章学习的叫做“基本对象”,或者就叫对象。

JavaScript 中还有很多其他类型的对象:

  • Array 用于存储有序数据集合,
  • Date 用于存储时间日期,
  • Error 用于存储错误信息。
  • ……等等。

它们有着各自特别的特性,我们将在后面学习到。有时候大家会说“数组类型”或“日期类型”,但其实它们并不是自身所属的类型,而是属于一个对象类型即 “object”。它们以不同的方式对 “object” 做了一些扩展。

JavaScript 中的对象非常强大。这里我们只接触了冰山一角。在后面的章节中,我们将频繁使用对象进行编程,并学习更多关于对象的知识。

 

14、

Symbol 是唯一标识符的基本类型

Symbol 是使用带有可选描述(name)的 Symbol() 调用创建的。

Symbol 总是不同的值,即使它们有相同的名字。如果我们希望同名的 Symbol 相等,那么我们应该使用全局注册表:Symbol.for(key) 返回(如果需要的话则创建)一个以 key 作为名字的全局 Symbol。使用 Symbol.for 多次调用 key 相同的 Symbol 时,返回的就是同一个 Symbol。

Symbol 有两个主要的使用场景:

  1. “隐藏” 对象属性。 如果我们想要向“属于”另一个脚本或者库的对象添加一个属性,我们可以创建一个 Symbol 并使用它作为属性的键。Symbol 属性不会出现在 for..in 中,因此它不会意外地被与其他属性一起处理。并且,它不会被直接访问,因为另一个脚本没有我们的 symbol。因此,该属性将受到保护,防止被意外使用或重写。

    因此我们可以使用 Symbol 属性“秘密地”将一些东西隐藏到我们需要的对象中,但其他地方看不到它。

  2. JavaScript 使用了许多系统 Symbol,这些 Symbol 可以作为 Symbol.* 访问。我们可以使用它们来改变一些内置行为。例如,在本教程的后面部分,我们将使用 Symbol.iterator 来进行 迭代 操作,使用 Symbol.toPrimitive 来设置 对象原始值的转换 等等。

从技术上说,Symbol 不是 100% 隐藏的。有一个内置方法 Object.getOwnPropertySymbols(obj) 允许我们获取所有的 Symbol。还有一个名为 Reflect.ownKeys(obj) 的方法可以返回一个对象的 所有 键,包括 Symbol。所以它们并不是真正的隐藏。但是大多数库、内置方法和语法结构都没有使用这些方法。

 

15、

  • 存储在对象属性中的函数被称为“方法”。
  • 方法允许对象进行像 object.doSomething() 这样的“操作”。
  • 方法可以将对象引用为 this

this 的值是在程序运行时得到的。

  • 一个函数在声明时,可能就使用了 this,但是这个 this 只有在函数被调用时才会有值。
  • 可以在对象之间复制函数。
  • 以“方法”的语法调用函数时:object.method(),调用过程中的 this 值是 object

请注意箭头函数有些特别:它们没有 this。在箭头函数内部访问到的 this 都是从外部获取的。

 

 

16、

对象到原始值的转换,是由许多期望以原始值作为值的内建函数和操作符自动调用的。

这里有三种类型(hint):

  • "string"(对于 alert 和其他需要字符串的操作)
  • "number"(对于数学运算)
  • "default"(少数操作符)

规范明确描述了哪个操作符使用哪个 hint。很少有操作符“不知道期望什么”并使用 "default" hint。通常对于内建对象,"default" hint 的处理方式与 "number" 相同,因此在实践中,最后两个 hint 常常合并在一起。

转换算法是:

  1. 调用 obj[Symbol.toPrimitive](hint) 如果这个方法存在,
  2. 否则,如果 hint 是 "string"
    • 尝试 obj.toString() 和 obj.valueOf(),无论哪个存在。
  3. 否则,如果 hint 是 "number" 或者 "default"
    • 尝试 obj.valueOf() 和 obj.toString(),无论哪个存在。

在实践中,为了便于进行日志记录或调试,对于所有能够返回一种“可读性好”的对象的表达形式的转换,只实现以 obj.toString() 作为全能转换的方法就够了。

 

17、

  • 构造函数,或简称构造器,就是常规函数,但大家对于构造函数有个共同的约定,就是其命名首字母要大写。
  • 构造函数只能使用 new 来调用。这样的调用意味着在开始时创建了空的 this,并在最后返回填充了值的 this

 

18、

  • 除 null 和 undefined 以外的原始类型都提供了许多有用的方法。我们将在即将到来的章节中研究这些内容。
  • 从形式上讲,这些方法通过临时对象工作,但 JavaScript 引擎可以很好地调整以在内部对齐进行优化,因此调用它们并不需要太高的成本。

 

19、

写非常大的数字:

  • 附加 "e" 来省略 0,e 后面的数字就是零的个数。就像:123e6 是 123 后面接 6 个零。
  • "e" 后面的负数将导致数字除以 1 后面接着给定数量的零。e-6 那是一百万分之一。

对于不同的进制:

  • 可以在十六进制(0x),八进制(0o)和二进制(0b)系统中直接写入数字。
  • parseInt(str,base) 解析来自任何数字系统的整数,其基数为:2≤base≤36
  • num.toString(base) 将数字转换为数字系统中具有给定 base 的字符串。

将 12pt 和 100px 等值转换为数字:

  • 使用 parseInt / parseFloat 进行  转换,它从字符串中读取一个数字,然后返回错误发生前可以读取的值。

分数:

  • 使用 Math.floorMath.ceilMath.truncMath.round 或 num.toFixed(precision) 截取。
  • 请记住,使用分数时会损失精度。

更多的数学函数:

  • 需要时请参阅 Math 对象,虽然这个文档非常小,但是它可以满足基础的要求。

 

20、

数组是一种特殊的对象,适用于存储和管理有序的数据项。

  • 声明:

    // 方括号 (常见用法)
    let arr = [item1, item2...];
    
    // new Array (极其少见)
    let arr = new Array(item1, item2...);

    调用 new Array(number) 会创建一个给定长度的数组,但不含有任何项。

  • length 属性是数组的长度,准确地说,它是数组最后一个数字索引值加一。它由数组方法自动调整。

  • 如果我们手动缩短 length,那么数组就会被截断。

我们可以通过下列操作以双端队列的方式使用数组:

  • push(...items) 在末端添加 items 项。
  • pop() 从末端移除并返回该元素。
  • shift() 从前端移除并返回该元素。
  • unshift(...items) 从前端添加 items 项。

遍历数组的元素:

  • for (let i=0; i<arr.length; i++) — 运行得最快,可兼容旧版本浏览器。
  • for (let item of arr) — 现代语法,只能访问 items。
  • for (let i in arr) — 永远不要用这个。

 

21、

组方法备忘单:

  • 添加/删除元素:

    • push(...items) — 向尾端添加元素,
    • pop() — 从尾端提取一个元素,
    • shift() — 从首端提取一个元素,
    • unshift(...items) — 向首端添加元素,
    • splice(pos, deleteCount, ...items) — 从 index 开始删除 deleteCount 个元素,并在当前位置插入 items
    • slice(start, end) — 创建一个新数组,将从位置 start 到位置 end(但不包括 end)的元素复制进去。
    • concat(...items) — 返回一个新数组:复制当前数组的所有元素,并向其中添加 items。如果 items 中的任意一项是一个数组,那么就取其元素。
  • 搜索元素:

    • indexOf/lastIndexOf(item, pos) — 从位置 pos 开始搜索 item,搜索到则返回该项的索引,否则返回 -1
    • includes(value) — 如果数组有 value,则返回 true,否则返回 false
    • find/filter(func) — 通过 func 过滤元素,返回使 func 返回 true 的第一个值/所有值。
    • findIndex 和 find 类似,但返回索引而不是值。
  • 遍历元素:

    • forEach(func) — 对每个元素都调用 func,不返回任何内容。
  • 转换数组:

    • map(func) — 根据对每个元素调用 func 的结果创建一个新数组。
    • sort(func) — 对数组进行原位(in-place)排序,然后返回它。
    • reverse() — 原位(in-place)反转数组,然后返回它。
    • split/join — 将字符串转换为数组并返回。
    • reduce(func, initial) — 通过对每个元素调用 func 计算数组上的单个值,并在调用之间传递中间结果。
  • 其他:  – Array.isArray(arr) 检查 arr 是否是一个数组。

请注意,sortreverse 和 splice 方法修改的是数组本身。

这些是最常用的方法,它们覆盖 99% 的用例。但是还有其他几个:

  • arr.some(fn)/arr.every(fn) 检查数组。

    与 map 类似,对数组的每个元素调用函数 fn。如果任何/所有结果为 true,则返回 true,否则返回 false

  • arr.fill(value, start, end) — 从索引 start 到 end,用重复的 value 填充数组。

  • arr.copyWithin(target, start, end) — 将从位置 start 到 end 的所有元素复制到 自身 的 target 位置(覆盖现有元素)。

有关完整列表,请参阅 手册

乍看起来,似乎有很多方法,很难记住。但实际上这比看起来要容易得多。

浏览这个备忘单,以了解这些方法。然后解决本章中的习题来进行练习,以便让你有数组方法的使用经验。

然后,每当你需要对数组进行某些操作,而又不知道怎么做的时候,请回到这儿,查看这个备忘单,然后找到正确的方法。示例将帮助你正确编写它。用不了多久,你就自然而然地记住这些方法了,根本不需要你死记硬背。

 

22、可迭代对象没看懂

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值