前言
《React学习笔记》系列,旨在总结自己学习React的过程,也希望能给刚接触React框架的同学带来一点有益的启发。
本系列笔记,初步将内容规划为以下几个部分:
一、ES6语法学习
二、环境搭建:Babel + Webpack
三、React简介和JSX语法
四、React组件
五、React数据流
六、受控组件与非受控组件
七、React组件生命周期
那好,接下来我们先为学习React做一下知识储备,开始学习ES6的一些语法。
一、ES6简介
提到ES6的学习,先推荐阮一峰大神的 《ECMAScript 6 入门》,一本开源电子书,学习ES6的好手册。
ES6标准发布于2015年6月,因此也叫ES2015。
ES6中有很多新的语法特性,本文只择出一些 常用的 、 React中会使用到的 新特性进行介绍。
- let和const变量
- 变量的解构赋值
- 箭头函数
- 数组的新方法
- 对象的新特性
- Promise和async
- class
- Module
注:每种新特性只介绍简单用法,不包全面只包实用,不求理解原理只求快速上手。
二、let和const变量
2.1 let
ES5中变量用var
声明,只有全局作用域和函数作用域,没有块级作用域。
ES6中用let
来定义块级作用域变量。它的用法类似于var
,但是所声明的变量,只在let
命令所在的代码块内有效。
所谓的块级作用域,就是由{}
包裹的区域,比如我们常用的for
循环,if
判断。
{
var a = 10;
let b = 20;
}
console.log(a); // 10
console.log(b); // 报错:a is not defined.
复制代码
以后的for
循环,我们可以使用let
来定义变量,这样变量只在for
循环体内有效,在循环体外引用就会报错。
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i); // 报错:i is not defined
复制代码
2.2 const
const
声明一个只读的常量。一旦声明,常量的值就不能改变。
const a = 10;
a = 20;
console.log(a); // 报错:TypeError: Assignment to constant variable.
复制代码
const
声明的变量不得改变值,这意味着,const
一旦声明变量,就必须立即初始化,不能留到以后赋值。
const a; // 报错:SyntaxError: Missing initializer in const declaration
复制代码
一般来,说函数的定义都是不变的,所以今后的函数一律用const
定义。
const fun = function () {
// ...
}
复制代码
注意一点:
let
和const
定义的变量都 不存在变量声明提升。也就是说,今后你想使用一个变量,必须先声明后使用了。这样可以避免一些意料之外的错误。
console.log(a); // 报错
let a = 10;
复制代码
三、变量的解构赋值
3.1 数组和对象的解构赋值
解构赋值,字面意思很容易理解,分解结构再进行赋值。
以前想得到数组或者对象中的某个值,数组需要使用 下标索引,对象要使用 点语法。
var arr = [10, 20, 30];
var a = arr[0];
var b = arr[1];
var c = arr[2];
var obj = {
name:"小明",
age:18,
sex:"男"
};
var name = obj.name;
var age = obj.age;
var sex = obj.sex;
复制代码
ES6中,允许从数组中提取值,按照对应位置,对变量赋值。对象也是一样。
解构赋值语法是一个 Javascript 表达式,这使得可以将 值从数组 或 属性从对象 提取到不同的变量中。 ---火狐MDN
let [a, b, c] = [10, 20, 30];
console.log(a); // 10
console.log(b); // 20
console.log(v); // 30
let {name, age, sex} = {name:"小明", age:18, sex:"男"};
console.log(name); // 小明
console.log(age); // 18
console.log(sex); // 男
复制代码
注意,数组的解构赋值使用的是方括号[]
,对象的解构赋值使用的是大括号{}
。
解构赋值有个很大的用处,就是用在函数的参数上。
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
function ajax({url, method, })
复制代码
3.2 ...操作符
是的,你没看错,这三个点...
是一个操作符,不可分割。
...
作为操作符,有两种用途。
一种叫做扩展操作符
,英文Spread Operator
;另一种叫做剩余操作符
,英文(Rest Parameters
。两者可以互为逆运算。
- 扩展操作符
先说说扩展操作符
,可以 将数组或者对象里面的值展开。
let arr = [10, 20, 30]
console.log(...arr) // 1 2 3
复制代码
用在对象身上,一般用来 创建一个对象的副本,仅仅改变原对象的个别属性,不影响原对象。
// 这是去年的小明,17岁
let obj = {
name: "小明",
age: 17,
sex: "男"
}
// 这是今年的小明,长了一岁
let obj2 = {
...obj,
age:18
}
console.log(obj); // Object { name: "小明", age: 17, sex: "男" }
console.log(obj2); // Object { name: "小明", age: 18, sex: "男" }
复制代码
- 剩余操作符
与扩展操作符
相反,剩余操作符
将多个值收集为一个变量,而扩展操作符
是将一个数组或对象扩展成多个值。
回想上面数组的解构赋值,如果用来接收值的变量不够,那么数组中剩下的值就拿不到了。
// 数组中只有10,20,30这三个元素被变量a, b, c接走了,剩下的40,,50,60没人管
let [a, b, c] = [10, 20, 30, 40, 50 ,60];
// 使用剩余操作符,让c接走剩下的元素
// 现在,数组中的10,20被变量a, b接走了,剩下的打包成一个数组给了c
let [a, b, ...c] = [10, 20, 30, 40, 50 ,60];
console.log(c); // [30, 40, 50, 60]
复制代码
需要注意的是,既然叫做剩余操作符,那么只能打包剩下的元素,...
出现在其他位置就会报错。
let [a, ...b, c] = [10, 20, 30, 40, 50 ,60]; // 报错
复制代码
四、箭头函数
4.1 参数默认值
ES6允许给形参设置默认值,直接写在形参的后面。
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
复制代码
4.2 箭头函数
ES6允许使用“箭头”=>
定义函数。可以看作是省略了function
关键字。
注意=>
是一个整体,中间不能有空格。
const add = (a, b) => {
reutrn a + b;
};
// 等同于
cosnt add = function (a, b) {
return a + b;
};
add(10, 20); // 30
复制代码
如果函数体只有一条语句并且是return语句,不需要写大括号{}
,还可以省略return
。 反之,如果函数体有多条语句必须使用{}
包裹。
cosnt add = (a, b) => a + b;
复制代码
如果函数体需要换行,必须使用{}
包裹。
var add = (a, b) => {
return a + b;
}
复制代码
如果函数的参数只有一个,可以省略()
。
const fun = v => v;
// 等价于
const fun = function(v){
return v;
}
复制代码
如果函数返回值为一个对象,必须使用()
包裹,否则会报错。因为对象的{}
会被认为是代码块。
// 报错
const fun = id => { id: id, name: "Temp" };
// 不报错
const fun = id => ({ id: id, name: "Temp" });
复制代码
4.3 箭头函数的this
我们知道,在JavaScript这门神奇的语言中,只要函数还没有被调用,你永远也不知道this
到底指向谁!
关于this
指向的问题,这里不再赘述,还没弄明白的同学可以找找相关资料学习下再来看下面的内容。
到了箭头函数,关于this
的指向问题,又多了一种情况。
箭头函数没有自己的this
, 它的this
是继承而来的。也就是说,箭头函数的this
在定义函数的时候就能够确定,并且不会改变。
let a = 10;
let obj = {
a :20,
fn: ()=> {
console.log(this.a);
}
}
obj.fn(); // 10
复制代码
上面例子中,为什么打印结果是10呢? 我们说过,箭头函数中的this
是继承而来,它的上一级是obj
,也就是说箭头函数中的this
其实是obj
的this
,那么obj
的this
又是谁呢?毫无疑问是window
对象,所以最终打印出来的a
的值其实是window
对象下的变量a
。
以前用call
、apply
、bind
能改变this
的指向,到了箭头函数这里,不好使!
let a = 10;
let obj = {
a :20,
fn: ()=> {
console.log(this.a);
}
}
obj.fn.call(obj); // 10
obj.fn.apply(obj); // 10
obj.fn.bind(obj)(); // 10
复制代码
注意,这里的环境是浏览器环境,全局对象为window
。若在node
环境中,打印结果为undefined
。
关于箭头函数的使用,总结下需要注意的点:
(1)函数体内的
this
对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new
命令,否则会抛出一个错误。
(3)不可以使用arguments
对象,该对象在函数体内不存在。如果要用,可以用rest
参数代替。
(4)不可以使用yield
命令,因此箭头函数不能用作Generator
函数。
上面四点中,第一点尤其值得注意。this
对象的指向是可变的,但是在箭头函数中,它是固定的。
五、数组的新方法
下面这些方法并不都是ES6中的新特性,大部分都是ES5中的方法。
因为这些方法在React中很常用,所以在此罗列一下。
5.1 Array.map()
map
这个单词不仅有“地图”的意思,在计算机技术中更常用的意思是“映射”。
Array.map()
方法返回一个新的数组,新数组的每一项都是原数组的映射。简单理解就是,map()
方法的参数是一个函数,原数组中的每一项会经过这个函数处理后返回,组成一个新的数组。
// 返回一个数组,数组中元素为原来数组的两倍:
let arr = [4, 9, 16, 25];
// newArr用来接收返回的新数组
//item参数就是原数组中的元素,每次遍历对它进行操作
let newArr = arr.map((item)=>{
return item * 2;
})
console.log(newArr); // [ 8, 18, 32, 50 ]
复制代码
5.2 Array.filter()
filter
为“过滤器”的意思。很明显,filter()
方法是对数组进行一些“过滤”操作。
filter()
的参数同样是一个函数,表示对原数组中的元素进行的操作,用火狐MDN的说法,叫做“测试”,通过测试的元素就会被返回,没有通过的就被“过滤”掉了。通过测试的元素会返回成一个新数组。
// 有这么一个数组,我们需要筛选一下年龄大于(包括)18岁的学生去参加一项社会活动
let students = [
{name:"小明", age:18},
{name:"小强", age:15},
{name:"小刚", age:19},
{name:"壮壮", age:20},
{name:"小凡", age:14},
{name:"小光", age:16}
]
// 使用filter()
// item就是原来数组的每个学生
// 当某个学生通过我们的“测试”(item.age>=18)时,其检测结果为true,那么就返回这一项
// 检测结果为false的,就不会返回
let newStudents = students.filter((item)=>{
return item.age >= 18;
});
console.log(newStudents);
/* [ { name: '小明', age: 18 },
{ name: '小刚', age: 19 },
{ name: '壮壮', age: 20 } ]
*/
复制代码
5.3 Array.forEach
一个新的遍历数组的方法。可以在一定程度上替代原来的for
循环语法。
let arr = [1, 2, 3, 4, 5, 6];
for(let i=0; i<arr.length; i++){
console.log(arr[i]);
}
arr.forEach((item,index)=>{
console.log(item);
})
复制代码
但forEach()
有缺点。
不能使用break
语句中断循环:
在forEach()
中使用break
会报错,在for
循环中使用break
会终止循环。
不能使用return语句返回到外层函数:
在forEach()
中使用return
没有任何作用,在for
循环中使用return
会打断循环,并且打断同层级下面语句的执行。
let arr = [1, 2, 3, 4, 5, 6];
arr.forEach((item,index)=>{
if(item== 4){
return;
}
console.log(item);
})
for(let i=0; i<arr.length; i++){
if(arr[i]== 4){
return;
}
console.log(arr[i]);
}
复制代码
5.4 for-of
ES6强烈推荐的方法。
这是最简洁、最直接的遍历数组元素的语法,避开了for-in
循环的所有缺陷。 并且,与forEach()
不同的是,它可以正确响应break
、continue
和return
语句。
for-of
可以遍历数组,字符串,类数组对象和其他集合。
let arr = [10, "hello", 20, {a: 20}];
let string = 'hello world';
// v就是数组中的每一项元素
for(let v of arr){
console.log(v);
}
for(let v of string){
console.log(v);
}
// 结果
10
hello
20
{ a: 20 }
h
e
l
l
o
w
o
r
l
d
复制代码
注意,for-of
不支持遍历对象。这点上还是要使用for-in
,毕竟这也是它的本职工作。
5.5 Array.from()
Array.from()
方法用于将类数组对象转为真正的数组。
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
复制代码
小结
本文总结了ES6中出现的新特性,包括变量、解构赋值、箭头函数、数组。
其中数组这一章节中大部分都是ES5中的方法,但在React中很常用,所以算是“新瓶装老酒”了。