b
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
由于这个赋值,变量first和second将收到数组的前两个整数作为它们的值。其余的整数被 "收集 "到它们自己的数组中,然后被分配给变量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
在本课程中,我们将使用箭头语法定义所有函数。