目录
JavaScript
JavaScript 在过去的几年里发展非常迅速,其标准的正式名称是ECMAScript。
浏览器还不能支持所有 JavaScript 的最新特性,许多在浏览器中运行的代码需要从一个新版本的 JavaScript 转译到了一个更旧、更兼容的版本。
现在最流行的转译方法是使用 Babel。 在使用 create-React-app 创建的 React 应用中转译是自动配置好的。
Node.js是一个基于谷歌的 chrome V8 引擎的 JavaScript 运行时环境,可以在任何地方工作,从服务端到移动端。 代码文件以 .js结尾,通过 node 文件名.js 命令以运行文件。
还可以将 JavaScript 代码编写到 Node.js 控制台(通过在命令行中键入 node 打开),或者浏览器的开发工具控制台中。 最新版本的 Chrome 能 很好地 处理 JavaScript 的新特性,而且不需要转译代码。作为替代品,也可以选择 JS Bin这样的工具。
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 存储了一个整数,但最后存储一个字符串。
也可以使用关键字var在 JavaScript 中定义变量。 在很长一段时间里,var 是定义变量的唯一方法。 const 和 let 是最近才在 ES6版本中添加的。 在一些特定情况,var 的工作方式与大多数语言中的变量定义相比是十分不同的。 推荐参考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 字符串的数组。
数组中的单个元素可以很容易地通过解构赋值赋给变量。
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版本开始,增加了类语法,这在某些情况下有助于构造面向对象的类。
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]
这个箭头函数是几年前随 ES6 一起添加到 JavaScript 中。 在此之前,定义函数的唯一方法是使用关键字 function。
有两种方法可定义函数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
Object methods and "this"
【对象方法以及“ this”关键字】
箭头函数和使用function关键字的函数,在涉及到 this 关键字(指向对象本身)的行为上,有很大的不同。
我们可以通过给一个对象定义函数属性,来给对象分配方法:
const arto = {
name: 'Arto Hellas',
age: 35,
education: 'PhD',
greet: function() { console.log('hello, my name is ' + this.name) },}
arto.greet() // "hello, my name is Arto Hellas" gets printed
方法甚至可以在对象创建之后再赋值给对象:
const arto = {
name: 'Arto Hellas',
age: 35,
education: 'PhD',
greet: function() {
console.log('hello, my name is ' + this.name)
},
}
arto.growOlder = function() { this.age += 1}
console.log(arto.age) // 35 is printed
arto.growOlder()
console.log(arto.age) // 36 is printed
让我们稍微修改一下对象
const arto = {
name: 'Arto Hellas',
age: 35,
education: 'PhD',
greet: function() {
console.log('hello, my name is ' + this.name)
},
doAddition: function(a, b) { console.log(a + b) },}
arto.doAddition(1, 4) // 5 is printed
const referenceToAddition = arto.doAddition
referenceToAddition(10, 15) // 25 is printed
现在对象有了 doAddition 方法,该方法将传递给他的参数进行求和。 该方法通常使用对象的 arto.doAddition(1, 4) 来调用,或者通过赋值给变量的 方法引用 ,referenceToAddition(10, 15)来调用该方法
如果我们用同样的方式调用greet函数,我们就会遇到一个问题:
arto.greet() // "hello, my name is Arto Hellas" gets printed
const referenceToGreet = arto.greet
referenceToGreet() // prints "hello, my name is undefined"
当通过引用调用referenceToGreet() 方法时,该方法已经不认识原始的this是什么了。 与其他语言相反,在 JavaScript 中,this的值是根据 方法如何调用 来定义的。 当通过引用调用该方法时, this 的值就变成了所谓的全局对象 ,而最终结果往往不是软件开发者设想的那样。
失去对this 关键字的追踪,在编写 JavaScript 代码时会带来一些潜在的问题。 通常情况下,React 或 Node (或者更确切地说是 web 浏览器的 JavaScript 引擎) 需要调用开发人员定义的对象中的某个方法。 然而,在本课程中,我们会使用“ 去this” (避免使用this关键字)的JavaScript 来避免这些问题。
例如,当我们使用setTimeout方法,让arto对象1秒钟后调用greet。
const arto = {
name: 'Arto Hellas',
greet: function() {
console.log('hello, my name is ' + this.name)
},
}
setTimeout(arto.greet, 1000)
如上所述,在 JavaScript 中,this 的值是根据方法的调用方式来定义的。 当 setTimeout 调用该方法时,实际上是JavaScript引擎在调用该方法,此时的this是指向的是全局对象。
有几种机制可以保留这种原始的 this 。 其中一个是使用bind方法:
setTimeout(arto.greet.bind(arto), 1000)
调用 arto.greet.bind(arto) 创建了一个新函数,它将 this 绑定指向到了 Arto,这与方法的调用位置和方式无关。
使用箭头函数可以解决与 this相关的一系列问题, 但是它不能当做对象的方法来使用。
Classes
【类】
正如前面提到的,JavaScript 中并没有像面向对象程序语言中的类机制。 然而,JavaScript 中的一些新特性使得它能够“模拟”面向对象中的类。
与 ES6一起引入到 JavaScript 中的类语法,在很大程度上简化了 JavaScript 中的类(或者说像是类)的定义。
下面的代码,定义了一个名为 Person 的“类”和两个 Person 对象。
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
greet() {
console.log('hello, my name is ' + this.name)
}
}
const adam = new Person('Adam Ondra', 35)
adam.greet()
const janja = new Person('Janja Garnbret', 22)
janja.greet()
在语法方面,类以及由它们创建的对象非常类似于 Java 的类和对象。 它们的行为也非常类似于 Java 对象。 但在本质上,它们仍然是基于 JavaScript 的原型继承的对象。 这两个对象的类型实际上都是 Object,因为 JavaScript 实质上只定义了Boolean,Null,Undefined,Number,String,Symbol,BigInt,以及 Object几种类型。