跳到内容

b

JavaScript

在课程中,除了网络开发,我们还有一个目标和需求,就是学习足够多的JavaScript。

在过去的几年里,JavaScript进步很快,在这个课程中,我们使用了较新版本的功能。JavaScript标准的官方名称是ECMAScript。目前,最新的版本是2021年6月发布的版本,名称为ECMAScript®2021,又称ES12。

浏览器还不支持JavaScript的所有最新功能。由于这个事实,很多在浏览器中运行的代码都是从较新版本的JavaScript转写成较旧的、更兼容的版本。

如今,最流行的方式是通过使用Babel进行转码。在用create-react-app创建的React应用中,转译是自动配置的。我们将在本课程的第7章节中仔细研究转码的配置问题。

Node.js是一个基于Google's Chrome V8JavaScript引擎的JavaScript运行环境,几乎可以在任何地方工作--从服务器到手机应用。让我们练习一下使用Node编写一些JavaScript。假定你机器上安装的Node.js版本至少是16.13.2。最新版本的Node已经能够理解最新版本的JavaScript,所以代码不需要转码。

代码被写入以.js结尾的文件中,通过键入node nameoffile.js命令来运行。

也可以将JavaScript代码写入Node.js控制台,该控制台可以通过在命令行中输入node打开,也可以写入浏览器的开发者工具控制台。Chrome浏览器的最新版本可以很好地处理JavaScript的新功能,而无需转写代码。另外,你可以使用JS Bin这样的工具。

JavaScript在名字和语法上都有点让人想起了Java。但是当涉及到语言的核心机制时,它们是非常不同的。来自Java背景的人,对JavaScript的行为可能会感到有点陌生,尤其是当你没有努力去研究它的特性时。

在某些圈子里,还流行尝试在JavaScript中 "模拟 "Java的特性和设计模式。我们不建议这样做,因为这两种语言和各自的生态系统最终都是非常不同的。

Variables

在JavaScript中,有几种方法来定义变量。

const x = 1
let y = 5

console.log(x, y)   // 1, 5 are printed
y += 10
console.log(x, y)   // 1, 15 are printed
y = 'sometext'
console.log(x, y)   // 1, sometext are printed
x = 4               // causes an error

const实际上并没有定义一个变量,而是一个常量,其值不能再被改变。相对应的,let定义了一个普通变量。

在上面的例子中,我们还看到分配给变量的数据类型在执行过程中可以改变。在开始时y存储的是一个整数,在结束时是一个字符串。

在JavaScript中也可以使用关键字var来定义变量。在很长一段时间内,var是定义变量的唯一方法。 const和let是最近才在ES6版本中加入的。在特定情况下,与大多数语言中的变量定义相比,var的工作方式有所不同--更多信息请参见JavaScript变量--你应该使用let、var还是const? on Medium关键词:var vs. let on JS Tips 。在本课程中,使用var是不明智的,你应该坚持使用const和let!

你可以在YouTube上找到更多关于这个主题的信息--例如 var, let and const - ES6 JavaScript Features

Arrays

一个数组和几个使用它的例子。

const t = [1, -1, 3]

t.push(5)

console.log(t.length) // 4 is printed
console.log(t[1])     // -1 is printed

t.forEach(value => {
  console.log(value)  // numbers 1, -1, 3, 5 are printed, each to own line
})

在这个例子中值得注意的是,数组的内容可以被修改,尽管它被定义为const。因为数组是一个对象,这个变量总是指向同一个对象。然而, 数组的内容随着新项目的加入而改变.

遍历数组项目的一种方法是使用forEach,如图所示。forEach接收一个用箭头语法定义的函数作为参数。

value => {
  console.log(value)
}

forEach为数组中的每个项调用函数,总是传递单个项作为参数。作为forEach参数的函数也可以接收其他参数

在前面的例子中,使用方法push将一个新项添加到数组中。在使用React时,经常使用函数式编程的技术。函数式编程范式的一个特点是使用不可变的数据结构。在React代码中,最好使用concat方法,该方法不会将项目添加到数组中,而是创建一个新的数组,其中同时包含旧数组和新项目的内容。

const t = [1, -1, 3]

const t2 = t.concat(5)

console.log(t)  // [1, -1, 3] is printed
console.log(t2) // [1, -1, 3, 5] is printed

方法调用t.concat(5)并没有向旧数组添加一个新的项,而是返回一个新的数组,这个数组除了包含旧数组的项之外,还包含新的项。

数组定义了很多有用的方法。让我们看看一个使用map方法的简短例子。

const t = [1, 2, 3]

const m1 = t.map(value => value * 2)
console.log(m1)   // [2, 4, 6] is printed

基于旧数组,map创建了一个新数组,对于这个数组,作为参数的函数被用来创建项目。在这个例子中,原始值被乘以了2。

Map也可以将数组转化为完全不同的东西。

const m2 = t.map(value => '<li>' + value + '</li>')
console.log(m2)
// [ '<li>1</li>', '<li>2</li>', '<li>3</li>' ] is printed

在这里,一个充满整数值的数组通过map方法被转化为一个包含HTML字符串的数组。在本课程的第二章节中,我们将看到map在React中的使用相当频繁。

解构赋值的帮助下,数组中的单个项目很容易被赋值给变量。

const t = [1, 2, 3, 4, 5]

const [first, second, ...rest] = t

console.log(first, second)  // 1, 2 is printed
console.log(rest)          // [3, 4, 5] is printed

由于这个赋值,变量firstsecond将收到数组的前两个整数作为它们的值。其余的整数被 "收集 "到它们自己的数组中,然后被分配给变量rest

Objects

在JavaScript中,有几种不同的方法来定义对象。一种非常常见的方法是使用对象字面量,它通过在大括号内列出其属性来实现。

const object1 = {
  name: 'Arto Hellas',
  age: 35,
  education: 'PhD',
}

const object2 = {
  name: 'Full Stack web application development',
  level: 'intermediate studies',
  size: 5,
}

const object3 = {
  name: {
    first: 'Dan',
    last: 'Abramov',
  },
  grades: [2, 3, 5, 3],
  department: 'Stanford University',
}

属性的值可以是任何类型的,比如整数、字符串、数组、对象......

一个对象的属性是通过使用 "点 "符号或通过使用大括号来引用的。

console.log(object1.name)         // Arto Hellas is printed
const fieldName = 'age'
console.log(object1[fieldName])    // 35 is printed

你也可以通过使用点符号或方括号来为一个对象即时添加属性。

object1.address = 'Helsinki'
object1['secret number'] = 12341

后者的添加必须使用括号,因为当使用点符号时,由于有空格字符,secret number 不是一个有效的属性名称。

自然地,JavaScript中的对象也可以有方法。然而,在这个课程中,我们不需要定义任何有自己方法的对象。这就是为什么在本课程中只简单地讨论它们。

对象也可以用所谓的构造函数来定义,这导致了一种让人想起许多其他编程语言的机制,例如Java的类。尽管有这种相似性,JavaScript并没有与面向对象的编程语言一样的类。然而,从ES6版本开始,增加了class语法,这在某些情况下有助于构造面向对象的类。

Functions

我们已经熟悉了定义箭头函数的方法。在不走弯路的情况下,定义一个箭头函数的完整过程如下。

const sum = (p1, p2) => {
  console.log(p1)
  console.log(p2)
  return p1 + p2
}

且函数的调用是可以预期的。

const result = sum(1, 5)
console.log(result)

如果只有一个参数,我们可以在定义中排除括号。

const square = p => {
  console.log(p)
  return p * p
}

如果函数只包含一个表达式,那么大括号就不需要了。在这种情况下,函数只返回其唯一表达式的结果。现在,如果我们去掉控制台打印,我们可以进一步缩短函数定义。

const square = p => p * p

这种形式在操作数组时特别方便--例如使用map方法时。

const t = [1, 2, 3]
const tSquared = t.map(p => p * p)
// tSquared is now [1, 4, 9]

箭头函数的功能是在几年前才加入到JavaScript中的,版本ES6。在这之前,定义函数的唯一方法是使用关键字function

有两种方式来引用函数;一种是在函数声明中给出一个名称。

function product(a, b) {
  return a * b
}

const result = product(2, 6)
// result is now 12

另一种定义函数的方式是使用函数表达式。在这种情况下,不需要给函数一个名字,定义可以存在于代码的其他部分。

const average = function(a, b) {
  return (a + b) / 2
}

const result = average(2, 5)
// result is now 3.5

在本课程中,我们将使用箭头语法定义所有函数。