React-Native_02:语法篇

Posted zhchoutai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React-Native_02:语法篇相关的知识,希望对你有一定的参考价值。

1.简单介绍


ECMAScript 6.0(以下简称ES6)是javascript语言的下一代标准,已经在2015年6月正式公布了。它的目标。是使得JavaScript语言能够用来编写复杂的大型应用程序,成为企业级开发语言。

JavaScript的创造者Netscape公司,之后将JavaScript提交给国际标准化组织ECMA。希望这种语言能够成为国际标准。ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现,

之所以不叫JavaScript。有两个原因。


一是商标。Java是Sun公司的商标,依据授权协议,仅仅有Netscape公司能够合法地使用JavaScript这个名字,且JavaScript本身也已经被Netscape公司注冊为商标。
二是想体现这门语言的制定者是ECMA,不是Netscape。这样有利于保证这门语言的开放性和中立性。

在线将ES6代码转为ES5代码(https://babeljs.io/repl/)
Babel是一个广泛使用的ES6转码器,能够将ES6代码转为ES5代码,从而在现有环境运行。这意味着。你能够用ES6的方式编敲代码,又不用操心现有环境是否支持。

2.变量声明
ES5仅仅有2种声明变量的方法:var、function。
ES6共同拥有6种声明变量的方法:var、function、let、const、import(require)、class。

2.1 var命令

var a = 10;
var b = 20;
var c = 30;

var a = 10,b = 20,c = 30;

var arr = [1,2,3,4,5];
var a = arr[0];
var b = arr[1];
var c = arr[3];

var obj = {
? name: ‘gary‘,
? age: 20
}
var a = obj.name;
var b = obj.age;

没实用varkeyword。使用直接赋值方式声明的是全局变量,比如:
a = 10;

全局对象是最顶层的对象,在浏览器环境指的是window对象,在Node.js指的是global对象。

ES5之中,全局对象的属性与全局变量是等价的。
window.a = 1;
a // 1
a = 2;
window.a // 2



2.2 function命令
var 方式定义的函数,不能先调用函数。后声明,仅仅能先声明函数。然后调用。
function方式定义函数能够先调用,后声明。

aaa();//这样调用就会出错 ??
var aaa = function(){ ??
? ? ? alert("aaa"); ??
? } ??
aaa();//这样就不会出错 ??
? ??
//先调用后声明 ??
bbb(); ??
function bbb(){ ??
? ? ? alert("bbb"); ??
} ??


2.3 let命令

块级有效
ES5仅仅有全局作用域和函数作用域,没有块级作用域,在ES6中,let实际上为JavaScript新增了块级作用域。
用来声明变量,使用方法相似于var,可是所声明的变量,仅仅在let命令所在的代码块内有效。

function f1() {
? let n = 5;
? if (true) {
? ? let n = 10;
? }
? console.log(n); // 5
}

for循环的计数器,就很合适使用let命令。比如:
var a = [];
for (var i = 0; i < 10; i++) {
? a[i] = function () {
? ? console.log(i);
? };
}
a[5](); //10
a[6](); // 10

var a = [];
for (let i = 0; i < 10; i++) {
? a[i] = function () {
? ? console.log(i);
? };
}
a[5](); //5
a[6](); // 6

变量提升
let不像var那样会发生“变量提升”现象??

????


console.log(foo); // 输出undefined
console.log(bar); // 报错ReferenceError
var foo = 2;
let bar = 2;

实測结果两个都是undefined,应该是网上资料错误,能够通过Babel来了解底层原理


临时性死区
仅仅要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
var tmp=‘dev‘;
if (true) {?
? console.log(tmp);

? let tmp;?
? console.log(tmp);

? tmp = 123;
? console.log(tmp);
}
在let命令声明变量tmp之前,都属于变量tmp的“死区”。




不同意反复声明
let不同意在同样作用域内,反复声明同一个变量。
// 报错
function test() {
? let a = 10;
? var a = 1;
}
// 报错
function test() {
? let a = 10;
? let a = 1;
}
因此,不能在函数内部又一次声明參数。
function func(arg) {
? let arg; // 报错
}
function func(arg) {
? {
? ? let arg; // 不报错
? }
}


2.4 const命令

const声明一个仅仅读的常量。一旦声明,就必须马上初始化。不能留到以后赋值。

也不能改变。


const PI = 3.1415;
console.log(PI); // 3.1415
PI = 3;// TypeError: Assignment to constant variable.

const的作用域与let命令同样:仅仅在声明所在的块级作用域内有效。声明的常量,也与let一样不可反复声明。

const命令仅仅是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须很小心。
const foo = {};
foo.prop = 123;
console.log(foo.prop);// 123
foo = {}; // TypeError: "foo" is read-only

var命令和function命令声明的全局变量,依然是全局对象的属性。
let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。也就是说。从ES6開始。全局变量将逐步与全局对象的属性脱钩。



var a = 1;
// 假设在Node的REPL环境,能够写成global.a
// 或者採用通用方法。写成this.a
window.a // 1
let b = 1;
window.b // undefined


2.5 import命令

模块的功能主要由 export 和 import 组成.每个模块都有自己单独的作用域。模块之间的相互调用关系是通过 export 来规定模块对外暴露的接口,通过import来引用其他模块提供的接口。

同一时候还为模块创造了命名空间。防止函数的命名冲突。

ES6将一个文件视为一个模块,通过export 向外输出了一个变量。一个模块也能够同一时候往外面输出多个变量。
//test.js
var name = ‘Rainbow‘;
var age = ‘24‘;
export {name, age};

定义好模块的输出以后就能够在另外一个模块通过import引用。


import {name, age} from ‘./test.js‘

总体输入,module指令
//test.js
export function getName() {
? return name;
}
export function getAge(){
? return age;
}?
通过 import * as 就完毕了模块总体的导入。
import * as test form ‘./test.js‘;

通过指令 module 也能够达到总体的输入。
?module test from ‘test.js‘;
?test.getName();

export default
不用关系模块输出了什么。通过 export default 指令就能载入到默认模块。不须要通过 花括号来指定输出的模块,一个模块仅仅能使用 export default 一次

// default 导出
export default function getAge() {}?
?
// 或者写成
function getAge() {}
export default getAge;

// 导入的时候不须要花括号
import test from ‘./test.js‘;

一条import 语句能够同一时候导入默认方法和其他变量.
import defaultMethod, { otherMethod } from ‘xxx.js‘;

2.6 class命令

假设你用过纯面向对象语言,那么你会对class的语法很熟悉。


class People {
? ? constructor(name) { //构造函数
? ? ? ? ? this.name = name;
? ? }
? ? sayName() {
? ? ? ? ? console.log(this.name);
? ? }
}

var p = new People("Tom");
p.sayName();

上面定义了一个People类,他有一个属性 name 和一个方法 sayName(),另一个构造函数;?
就像函数有函数声明和函数表达式两种定义方式,类也能够通过类表达式来定义:
let People = class {
? ? constructor(name) { //构造函数
? ? ? ? ? this.name = name;
? ? }
? ? sayName() {
? ? ? ? ? console.log(this.name);
? ? }
? }

你可能以为类声明和类表达式的差别在于变量提升的不同。可是事实是不管是类声明还是类表达式的方式来定义,都不会有变量提升。


通过keyword extends 来继承一个类。并且,能够通过 super keyword来引用父类。
class Student extends People {
? ? constructor(name, grade) { //构造函数
? ? ? ? super(name); ? ?//通过 super 调用父类的构造函数的。
? ? ? ? ? this.grade = grade;
? ? }
? ? sayGrade() {
? ? ? ? ? console.log(this.grade);
? ? }
}
上面的样例中我们定义了一个 Student 。他是 People 的子类。




以下我们给 name 属性定义 getter 和 setter
class People {
? ? constructor(name) { //构造函数
? ? ? ? ? this.name = name;
? ? }
? ? get name() {
? ? ? ? return this._name.toUpperCase();
? ? }
? ? set name(name) {
? ? ? ? this._name = name;
? ? }
? ? sayName() {
? ? ? ? ? console.log(this.name);
? ? }
}
var p = new People("tom");
console.log(p.name); ? ?//TOM
console.log(p._name); ? ?//tom
p.sayName(); ? ?//TOM
细致看上面的样例,搞清晰最后三行分别会输出什么,就明确getter 和 setter该怎么用了。



主要是要区分 this._name 和 this.name 的差别。由于我们定义了 name 的读写器,而未定义 _name 的读写器。所以訪问这两个属性的结果是不同的。

可是要注意一点,不要这样写:

set name(name) {
? ? this.name = name;
}
由于给 this.name 赋值的时候会调用 set name ,这样会导致无限递归直到栈溢出。

通过 static keyword定义静态方法:
class People {
? ? constructor(name) { //构造函数
? ? ? ? ? this.name = name;
? ? }
? ? sayName() {
? ? ? ? ? console.log(this.name);
? ? }
? ? static formatName(name) {
? ? ? ? return name[0].toUpperCase() + name.sustr(1).toLowerCase();
? ? }
}
console.log(People.formatName("tom"));


3.解构赋值

ES6同意依照一定模式。从数组和对象中提取值。对变量进行赋值。这被称为解构(Destructuring)。

“模式匹配”,仅仅要等号两边的模式同样,左边的变量就会被赋予相应的值。以下是一些使用嵌套数组进行解构的样例。
假设解构不成功,变量的值就等于undefined。


let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3

let [x, , y] = [1, 2, 3];//不全然解构
x // 1
y // 3

let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]

let [x, y, ...z] = [‘a‘];
x // "a"
y // undefined
z // []

解构赋值同意指定默认值。
var [foo = true] = [];
foo // true

[x, y = ‘b‘] = [‘a‘]; // x=‘a‘, y=‘b‘

[x, y = ‘b‘] = [‘a‘, undefined]; // x=‘a‘, y=‘b‘

注意。ES6内部使用严格相等运算符(===)。推断一个位置是否有值。所以,假设一个数组成员不严格等于undefined。默认值是不会生效的。



默认值能够引用解构赋值的其他变量,但该变量必须已经声明。
let [x = 1, y = x] = []; ? ? // x=1; y=1
let [x = 1, y = x] = [2]; ? ?// x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = []; ? ? // ReferenceError

解构赋值不仅适用于var命令,也适用于let和const命令。


var [v1, v2, ..., vN ] = array;
let [v1, v2, ..., vN ] = array;
const [v1, v2, ..., vN ] = array;

对于Set结构。也能够使用数组的解构赋值。


let [x, y, z] = new Set(["a", "b", "c"]);
x // "a"

对象的解构赋值
解构不仅能够用于数组。还能够用于对象。


var { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定。而对象的属性没有次序,变量必须与属性同名,才干取到正确的值。


var { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

var { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined

假设变量名与属性名不一致。必须写成以下这样。
var { foo: baz } = { foo: ‘aaa‘, bar: ‘bbb‘ };
baz // "aaa"
let obj = { first: ‘hello‘, last: ‘world‘ };
let { first: f, last: l } = obj;
f // ‘hello‘
l // ‘world‘
这实际上说明。对象的解构赋值是以下形式的简写。


var { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
也就是说,对象的解构赋值的内部机制。是先找到同名属性,然后再赋给相应的变量。

真正被赋值的是后者,而不是前者。
var { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined

变量的声明和赋值是一体的。

对于let和const来说,变量不能又一次声明,所以一旦赋值的变量曾经声明过,就会报错。
let foo;
let {foo} = {foo: 1}; // SyntaxError: Duplicate declaration "foo"
let baz;
let {bar: baz} = {bar: 1}; // SyntaxError: Duplicate declaration "baz"
上面代码中。解构赋值的变量都会又一次声明,所以报错了。只是,由于var命令同意又一次声明。所以这个错误仅仅会在使用let和const命令时出现。假设没有第二个let命令。上面的代码就不会报错。
let foo;
({foo} = {foo: 1}); // 成功
let baz;
({bar: baz} = {bar: 1}); // 成功
上面代码中,let命令以下一行的圆括号是必须的,否则会报错。由于解析器会将起首的大括号。理解成一个代码块,而不是赋值语句。
?
和数组一样,解构也能够用于嵌套结构的对象。
var obj = {
? p: [
? ? ‘Hello‘,
? ? { y: ‘World‘ }
? ]
};
var { p: [x, { y }] } = obj;
x // "Hello"
y // "World"

注意,这时p是模式,不是变量,因此不会被赋值。
var node = {
? loc: {
? ? start: {
? ? ? line: 1,
? ? ? column: 5
? ? }
? }
};
var { loc: { start: { line }} } = node;
line // 1
loc ?// error: loc is undefined
start // error: start is undefined

默认值生效的条件是。对象的属性值严格等于undefined。



假设要将一个已经声明的变量用于解构赋值。必须很小心。
// 错误的写法
var x;
{x} = {x: 1};
// SyntaxError: syntax error
上面代码的写法会报错。由于JavaScript引擎会将{x}理解成一个代码块。从而发生语法错误。仅仅有不将大括号写在行首。避免JavaScript将其解释为代码块,才干解决问题。
// 正确的写法
({x} = {x: 1});

由于数组本质是特殊的对象,因此能够对数组进行对象属性的解构。


var arr = [1, 2, 3];
var {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3

字符串的解构赋值
字符串也能够解构赋值。

这是由于此时,字符串被转换成了一个相似数组的对象。
const [a, b, c, d, e] = ‘hello‘;
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
相似数组的对象都有一个length属性。因此还能够对这个属性解构赋值。
let {length : len} = ‘hello‘;
len // 5

数值和布尔值的解构赋值
解构赋值时。假设等号右边是数值和布尔值,则会先转为对象。


let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
上面代码中,数值和布尔值的包装对象都有toString属性。因此变量s都能取到值。

解构赋值的规则是。仅仅要等号右边的值不是对象,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError

函数參数的解构也能够使用默认值。


function move({x = 0, y = 0} = {}) {
? return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]


函数move的參数是一个对象,通过对这个对象进行解构,得到变量x和y的值。

假设解构失败。x和y等于默认值。


function move({x, y} = { x: 0, y: 0 }) {
? return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

函数move的參数指定默认值,而不是为变量x和y指定默认值,所以会得到与前一种写法不同的结果。

其实,仅仅要某种数据结构具有Iterator接口,都能够採用数组形式的解构赋值。
function* fibs() {
? var a = 0;
? var b = 1;
? while (true) {
? ? yield a;
? ? [a, b] = [b, a + b];
? }
}
var [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5

解构赋值用途:

(1)交换变量的值
[x, y] = [y, x];

上面代码交换变量x和y的值。这种写法不仅简洁。并且易读,语义很清晰。


(2)从函数返回多个值
函数仅仅能返回一个值。假设要返回多个值,仅仅能将它们放在数组或对象里返回。有了解构赋值,取出这些值就很方便。
// 返回一个数组
function example() {
? return [1, 2, 3];
}
var [a, b, c] = example();
// 返回一个对象
function example() {
? return {
? ? foo: 1,
? ? bar: 2
? };
}

var { foo, bar } = example();


(3)函数參数的定义

解构赋值能够方便地将一组參数与变量名相应起来。
// 參数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 參数是一组无次序的值
function f({x, y, z}) { ... }

f({z: 3, y: 2, x: 1});


(4)提取JSON数据

解构赋值对提取JSON对象中的数据,尤其实用。
var jsonData = {
? id: 42,
? status: "OK",
? data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]

上面代码能够高速提取JSON数据的值。


(5)函数參数的默认值
jQuery.ajax = function (url, {
? async = true,
? beforeSend = function () {},
? cache = true,
? complete = function () {},
? crossDomain = false,
? global = true,
? // ... more config
}) {
? // ... do stuff
};

指定參数的默认值,就避免了在函数体内部再写var foo = config.foo || ‘default foo‘;这种语句。


(6)遍历Map结构
不论什么部署了Iterator接口的对象,都能够用for...of循环遍历。Map结构原生支持Iterator接口,配合变量的解构赋值。获取键名和键值就很方便。


var map = new Map();
map.set(‘first‘, ‘hello‘);
map.set(‘second‘, ‘world‘);
for (let [key, value] of map) {
? console.log(key + " is " + value);
}
// first is hello
// second is world
假设仅仅想获取键名,或者仅仅想获取键值,能够写成以下这样。
// 获取键名
for (let [key] of map) {
? // ...
}
// 获取键值
for (let [,value] of map) {
? // ...

}


(7)输入模块的指定方法
载入模块时。往往须要指定输入那些方法。解构赋值使得输入语句很清晰。


const { SourceMapConsumer, SourceNode } = require("source-map");

4.编程风格
4.1 採用严格模式:‘use strict‘;

主要有以下限制:
变量必须声明后再使用
函数的參数不能有同名属性,否则报错
不能使用with语句
不能对仅仅读属性赋值,否则报错
不能使用前缀0表示八进制数,否则报错
不能删除不可删除的属性,否则报错
不能删除变量delete prop。会报错,仅仅能删除属性delete global[prop]
eval不会在它的外层作用域引入变量
eval和arguments不能被又一次赋值
arguments不会自己主动反映函数參数的变化
不能使用arguments.callee
不能使用arguments.caller
禁止this指向全局对象
不能使用fn.caller和fn.arguments获取函数调用的堆栈
添加了保留字(比方protected、static和interface)


4.2 let代替var

在块级作用域下,let全然能够代替var。由于两者语义同样。并且let没有副作用。

4.3 全局常量和线程安全

在let和const之间,建议优先使用const,尤其是在全局环境。不应该设置变量,仅仅应设置常量。全部的函数都应该设置为常量。这符合函数式编程思想。有利于将来的分布式运算。
const声明常量还有两个优点,一是阅读代码的人立马会意识到不应该改动这个值,二是防止了无意间改动变量值所导致的错误。


长远来看。JavaScript可能会有多线程的实现(比方Intel的River Trail那一类的项目),这时let表示的变量,仅仅应出如今单线程运行的代码中,不能是多线程共享的,这样有利于保证线程安全。



4.4 字符串
静态字符串一律使用单引號或反引號,不使用双引號。动态字符串使用反引號。

4.5 解构赋值
使用数组成员对变量赋值时,优先使用解构赋值。

函数的參数假设是对象的成员,优先使用解构赋值。

假设函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于以后加入返回值,以及更改返回值的顺序。

4.6 对象
单行定义的对象。最后一个成员不以逗号结尾。

多行定义的对象。最后一个成员以逗号结尾。

对象尽量静态化。一旦定义。就不得任意加入新的属性。假设加入属性不可避免,要使用Object.assign方法。

假设对象的属性名是动态的,能够在创造对象的时候。使用属性表达式定义。

另外,对象的属性和方法。尽量採用简洁表达法,这样易于描写叙述和书写。

4.7 数组
使用扩展运算符(...)拷贝数组。



使用Array.from方法,将相似数组的对象转为数组。



4.8 函数
马上运行函数能够写成箭头函数的形式。

那些须要使用函数表达式的场合,尽量用箭头函数代替。

由于这样更简洁。并且绑定了this。

简单的、单行的、不会复用的函数,建议採用箭头函数。假设函数体较为复杂。行数较多。还是应该採用传统的函数写法。



全部配置项都应该集中在一个对象,放在最后一个參数。布尔值不能够直接作为參数。



不要在函数体内使用arguments变量。使用rest运算符(...)代替。由于rest运算符显式表明你想要获取參数,并且arguments是一个相似数组的对象,而rest运算符能够提供一个真正的数组。

4.9 Map结构
注意区分Object和Map。仅仅有模拟现实世界的实体对象时,才使用Object。假设仅仅是须要key: value的数据结构,使用Map结构。由于Map有内建的遍历机制。



4.10 Class
总是用Class,代替须要prototype的操作。由于Class的写法更简洁,更易于理解。



使用extends实现继承,由于这样更简单,不会有破坏instanceof运算的危急。

4.11 模块
首先。Module语法是JavaScript模块的标准写法,坚持使用这种写法。

使用import代替require。



假设模块仅仅有一个输出值。就使用export default,假设模块有多个输出值。就不使用export default,不要export default与普通的export同一时候使用。



不要在模块输入中使用通配符。由于这样能够确保你的模块之中,有一个默认输出(export default)。

假设模块默认输出一个对象。对象名的首字母应该大写。

5.网络资源
http://www.w3school.com.cn/js/index.asp
http://es6.ruanyifeng.com/
http://babeljs.io/

以上是关于React-Native_02:语法篇的主要内容,如果未能解决你的问题,请参考以下文章

React-Native_02:语法篇

React-Native_02:语法篇

React-Native_01:开山篇

React-Native_01:开山篇

react-native 学习之Image篇

react-native 命令给出语法错误