本文介绍了学习React Native需要了解的JavaScript基本语法及概念。
《无题·相见时难别亦难》
相见时难别亦难,东风无力百花残。
春蚕到死丝方尽,蜡炬成灰泪始干。
晓镜但愁云鬓改,夜吟应觉月光寒。
蓬山此去无多路,青鸟殷勤为探看。
—唐,李商隐
JavaScript基本语法及概念
JavaScript是一种脚本语言,ECMA(European Computer Manufacturers Association)组织定制了JavaScript语言的标准,被称为ECMAScript标准。最新版ECMAScript 7标准(简称ES7)已经在2016年6月正式发布了,所以,讲到JavaScript的版本,实际上就是说它实现了ECMAScript标准的哪个版本。
由于JavaScript的语法和Java语言类似,这里我们只介绍有区别或需要了解的语法及概念。
变量
声明一个变量用 var 语句,比如:
1 | var a; // 声明了变量a,此时a的值为undefined |
常量
ES6标准引入了新的关键字 const 来定义常量, const 与 let 都具有块级作用域:1
const PI = 3.14;
对象
JavaScript的对象是一种无序的集合数据类型,它由若干键值对组成。JavaScript的对象用于描述现实世界中的某个对象。例如,为了描述小明这个淘气的小朋友,我们可以用若干键值对来描述他:1
2
3
4
5
6
7
8var xiaoming = {
name: '小明',
birth: 1990,
school: 'No.1 Middle School',
height: 1.70,
weight: 65,
score: null
}
JavaScript用一个 {…} 表示一个对象,键值对以 xxx: xxx 形式声明,用,
隔开。获取属性值:
1 | xiaoming.name; // '小明' |
函数
在JavaScript中,定义函数的方式如下:
1 | function abs(x) { |
如果没有 return 语句,函数执行完毕后也会返回结果,只是结果为 undefined 。由于JavaScript的函数也是一个对象,上述定义的 abs() 函数实际上是一个函数对象,而函数名 abs 可以视为指向该函数的变量。
因此,第二种定义函数的方式如下:1
2
3
4
5
6
7var abs = function (x) {
if (x >= 0) {
return x;
} else {
return -x;
}
};
在这种方式下, function (x) { … } 是一个匿名函数,它没有函数名。但是,这个匿名函数赋值给了变量 abs ,所以通过变量 abs 就可以调用该函数。
上述两种定义完全等价,注意第二种方式按照完整语法需要在函数体末尾加一个;
,表示赋值语句结束。
调用定义的函数:1
abs(10); // 返回10
abs(-9); // 返回9
箭头函数
ES6标准新增了一种新的函数:Arraw Function(箭头函数)。为什么叫Arrow Function?因为它的定义用的就是一个箭头:
1 | x => x * x |
上面的箭头函数相当于:
1 | function (x) { |
箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种像上面的,只包含一个表达式,连 { … } 和 return 都省略掉了。还有一种可以包含多条语句,这时候就不能省略 { … } 和 return :
1 | x => { |
上面的例子是只有一个参数的箭头函数,无参的箭头函数或多个参数的箭头函数需要使用圆括号:1
2
3var a = () => 4 * 4;
var xy = (x,y) => x * y;
strict模式
strict模式是用于避免由于JavaScript设计缺陷而导致潜在的严重的bug,启用strict模式的方法是在JavaScript代码的第一行写上:
1 | 'use strict'; |
这是一个字符串,不支持strict模式的浏览器会把它当做一个字符串语句执行,支持strict模式的浏览器将开启strict模式运行JavaScript。
变量作用域
在JavaScript中,用 var 声明的变量实际上是有作用域的。如果一个变量在函数体内部声明,则该变量的作用域为整个函数体,在函数体外不可引用该变量:
1 | 'use strict'; |
块级作用域 let
由于JavaScript的变量作用域实际上是函数内部,我们在 for 循环等语句块中是无法定义具有局部作用域的变量的:
1 | 'use strict'; |
为了解决块级作用域,ES6引入了新的关键字 let ,用 let 替代 var 可以声明一个块级作用域的变量:
1 | 'use strict'; function foo() { var sum = 0; for (let i=0; i<100; i++) { sum += i; } i += 1; // SyntaxError } |
比较运算符
JavaScript在设计时,有两种比较运算符:
第一种是 == 比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;
第二种是 === 比较,它不会自动转换数据类型,如果数据类型不一致,返回 false ,如果一致,再比较。
由于JavaScript这个设计缺陷,不要使用 == 进行比较,始终坚持使用 === 进行比较。
模块
ES6引入了模块的概念,一个模块就是一个独立的js文件。ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。模块功能主要由两个命令构成:export 和 import。export 命令用于规定模块的对外接口,import 命令用于输入其他模块提供的功能。
profile.js1
2
3
4
5
6
7
8export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
// or
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
下面的 import 命令,用于加载 profile.js 文件,并从中输入变量。大括号里面的变量名,必须与被导入模块( profile.js )对外接口的名称相同。import 后面的 from 指定模块文件的位置,可以是相对路径,也可以是绝对路径,.js后缀可以省略。如果 import 指定的是文件夹的路径,默认会去找指定目录下的index.js文件。如果只是模块名,不带有路径,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。
main.js1
2
3
4
5
6// main.js
import {firstName, lastName, year} from './profile.js';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
除此之外,我们还可以使用 export default 命令为模块指定默认输出:
export-default.js1
2
3export default function () {
console.log('foo');
}
其他模块加载该模块时,import 命令可以为该匿名函数指定任意名字:
1 | import customName from './export-default'; |
上面代码的 import 命令,可以用任意名称指向export-default.js输出的方法,这时就不需要知道原模块输出的函数名。需要注意的是,这时import命令后面不使用大括号。export default 命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此 export deault 命令只能使用一次。所以, import 命令后面才不用加大括号,因为只可能对应一个方法。
如果想在一条 import 语句中,同时输入默认方法和其他变量,可以写成下面这样:
1 | import customName, { otherMethod } from './export-default'; |
class
ES6 引入了 Class(类)这个概念,通过使用 class 关键字,可以定义一个类:
1 | class Point { constructor(x, y) { this.x = x; this.y = y; } |
上面代码定义了一个类,可以看到里面有一个 constructor 方法,这就是构造方法,而 this 关键字则代表实例对象。Point 类除了构造方法,还定义了一个 toString 方法。
注意,定义类的方法的时候,前面不需要加上 function 这个关键字,直接把函数定义放进去了就可以了。
使用 new 关键字创建一个类的实例。
1 | var point = new Point(2, 3); |
创建匿名类:1
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('张三');
person.sayName(); // "张三"
使用 extends 关键字继承一个类:
1 | class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 调用父类的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 调用父类的toString() } } |
在Class内部可以使用 get 和 set 关键字自定义属性的getter和setter方法:
1 | class MyClass { constructor() { // ... } get prop() { return 'getter'; } set prop(value) { console.log('setter: '+value); } } let inst = new MyClass(); inst.prop = 123; // setter: 123 console.log(inst.prop); // 'getter' |
使用 static 关键字定义静态方法,静态方法可以直接使用类名调用:1
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'