React教案

ECMAScript6

1、ES6简介

1.1、什么是ES6

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

1.2、ECMAScript和JavaScript的关系

一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系?

要讲清楚这个问题,需要回顾历史。1996 年 11 月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给标准化组织 ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是 1.0 版。该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫 JavaScript,有两个原因。一是商标,Java 是 Sun 公司的商标,根据授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 JScript 和 ActionScript)。日常场合,这两个词是可以互换的。

1.3、为什么要学习ES6?

这个问题可以转换一种问法,就是学完es6会给我们的开发带来什么样便利?chrome解释javascript的引擎叫做V8,有一个人把V8引擎转移到了服务器,于是服务器端也可以写javascript,这种在服务器端运行的js语言,就是Node.js。Node.js一经问世,它优越的性能就表现了出了,很多基于nodejs的web框架也应运而生,express就是之一,随之而来的就是全栈MEAN mogoDB,Express,Vue.js,Node.js开发,javaScript越来越多的使用到web领域的各个角落,js能做的事情也越来越多。Babel是一个广泛使用的ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。这意味着,你可以用ES6的方式编写程序,又不用担心现有环境是否支持。nodejs是一种开发趋势,Vue.js这种前端框架是一种开发趋势,ES6被普及使用也是趋势。目前一些前端框架都在使用ES6语法,例如Vue、React、D3等等,所以ES6也是学习好前端框架的基础。

2、ES6环境搭建

由于有些低版本的浏览器还不支持ES6的语法,所以在不使用框架的情况下,需要将ES6语法转换为ES5语法。

2.1、前期准备

先创建一个项目,项目中有两个文件夹,src和dist,一个html文件

src:将编写的ES6的js文件放到此文件夹中(这里是index.js文件)

dist:将通过Babel编译成的ES5的js文件放到此文件中(这里是index.js文件)

html:注意:将dist中编译好的文件引入到HTML文件中,而不是src中的js文件

DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Documenttitle>
	<script src="./dist/index.js">script>
head>
<body>
	Hello ES6
body>
html>
2.2、ES6环境搭建

第一步

在src目录下,新建index.js文件。这个文件很简单,我们只作一个a变量的声明,并用console.log()打印出来。

let a = 1;
console.log(a);

第二步

在项目的根目录初始化项目并生成package.json文件(可以根据自己的需求进行修改)

cnpm init -y
{
   
    "name": "es6",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
   
        "test": "echo \"Error: no test specified\" && exit 1"
     },
    "keywords": [],
    "author": "",
    "license": "ISC"
}

第三步

安装Babel插件(将ES6语法转换为ES5)

cnpm install -g babel-cli

第四步

当然现在还不能正常转换,还需要安装ES5所需的一个包

cnpm install --save-dev babel-preset-es2015 babel-cli 安装完成后,package.json会有所变化
{
   
    "name": "es6",
     "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
   
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
   
    "babel-cli": "^6.26.0",
    "babel-preset-es2015": "^6.24.1"
    }
}

第五步:

在项目的根目录添加一个 .babelrc 文件,并添加内容

{
   
    "presets":[
        "es2015"
    ],
    "plugins": []
}

在windows系统中创建.babelrc文件的方法

方法一:根目录下,创建“.babelrc.”文件名就可以了!(前后共两个点)

方法二:cmd进入根目录,输入“type null>.babelrc”,回车即可!

第六步:

安装完成后我们可以通过命令进行转换

babel src/index.js -o dist/index.js

第七步:

可以将命令进行简化(package.json进行配置)

"scripts": {
   
    "test": "echo "Error: no test specified" && exit 1"
},

修改为:

{
   
  	"name": "es6",
  	"version": "1.0.0",
  	"description": "",
  	"main": "index.js",
  	"scripts": {
   
        "test": "babel src/index.js -o dist/index.js"
  	},
  	"keywords": [],
  	"author": "",
  	"license": "ISC",
  	"devDependencies": {
   
		"babel-cli": "^6.26.0",
		"babel-preset-es2015": "^6.24.1"
  	}
}

然后我们可以通过下面命令转义代码:

npm run test

3、let与const

ES2015(ES6) 新增加了两个重要的 JavaScript 关键字: letconst

let 声明的变量只在 let 命令所在的代码块内有效,const 声明一个只读的常量,一旦声明,常量的值就不能改变。

3.1、let命令

let命令有以下特点:

(1)代码块内有效

ES2015(ES6) 新增加了两个重要的 JavaScript 关键字: letconst。let 声明的变量只在 let 命令所在的代码块内有效,const 声明一个只读的常量,一旦声明,常量的值就不能改变。

{
   
    let a = 1;
    var b = 2;
    console.log(a);//输出1
    console.log(b);//输出2
}
console.log(a);//报错 ReferenceError: a is not defined
console.log(b);//输出2

(2)不能重复声明

let 只能声明一次 var 可以声明多次:

let a = 1;
let a = 2;//报错 Identifier 'a' has already been declared
var b = 3;
var b = 4;
console.log(a);
console.log(b);//输出4

for 循环计数器很适合用 let

for (var i = 0; i < 10; i++) {
   
  setTimeout(function(){
   
    console.log(i);
  })
}
// 输出十个 10
for (let j = 0; j < 10; j++) {
   
  setTimeout(function(){
   
    console.log(j);
  })
}
// 输出 0123456789

变量 i 是用 var 声明的,在全局范围内有效,所以全局中只有一个变量 i, 每次循环时,setTimeout 定时器里面的 i 指的是全局变量 i ,而循环里的十个 setTimeout 是在循环结束后才执行,所以此时的 i 都是 10。

变量 j 是用 let 声明的,当前的 j 只在本轮循环中有效,每次循环的 j 其实都是一个新的变量,所以 setTimeout 定时器里面的 j 其实是不同的变量,即最后输出 12345。(若每次循环的变量 j 都是重新声明的,如何知道前一个循环的值?这是因为 JavaScript 引擎内部会记住前一个循环的值)。

(3)不存在变量提升

let 不存在变量提升,var 会变量提升:

console.log(a);  //ReferenceError: a is not defined
let a = "apple";

console.log(b);  //undefined
var b = "banana";

变量 b 用 var 声明存在变量提升,所以当脚本开始运行的时候,b 已经存在了,但是还没有赋值,所以会输出 undefined。变量 a 用 let 声明不存在变量提升,在声明变量 a 之前,a 不存在,所以会报错。

(4)暂时性死区

只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

var tmp = 123;

if (true) {
   
  tmp = 'abc'; // ReferenceError
  let tmp;
}

上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。

ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

if (true) {
   
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ结束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}

上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。

“暂时性死区”也意味着typeof不再是一个百分之百安全的操作。

typeof x; // ReferenceError
let x;

另外,下面的代码也会报错,与var的行为不同。

// 不报错
var x = x;

// 报错
let x = x;
// ReferenceError: x is not defined

上面代码报错,也是因为暂时性死区。使用let声明变量时,只要变量在还没有声明完成前使用,就会报错。上面这行就属于这个情况,在变量x的声明语句还没有执行完成前,就去取x的值,导致报错”x 未定义“。

ES6 规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。

总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

3.2、const命令

const 声明一个只读变量,声明之后不允许改变。意味着,一旦声明必须初始化,否则会报错。

基本用法:

const PI = "3.1415926";
PI  // 3.1415926

const MY_AGE;  // 报错 SyntaxError: Missing initializer in const declaration

const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

const foo;
// 报错 SyntaxError: Missing initializer in const declaration

上面代码表示,对于const来说,只声明不赋值,就会报错。const的作用域与let命令相同:只在声明所在的块级作用域内有效。

if (true) {
   
  const MAX = 5;
}

MAX // Uncaught ReferenceError: MAX is not defined

const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。

if (true) {
   
  console.log(MAX); // ReferenceError
  const MAX = 5;
}

上面代码在常量MAX声明之前就调用,结果报错。const声明的常量,也与let一样不可重复声明。

var message = "Hello!";
let age = 25;

// 以下两行都会报错
const message = "Goodbye!";
const age = 30;

暂时性死区:

var PI = "a";
if(true){
   
  console.log(PI);  //报错 ReferenceError: PI is not defined
  const PI = "3.1415926";
}

ES6 明确规定,代码块内如果存在 let 或者 const,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。代码块内,在声明变量 PI 之前使用它会报错。

注意要点

const 如何做到变量在声明初始化之后不允许改变的?其实 const 其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不允许改动。此时,你可能已经想到,简单类型和复合类型保存值的方式是不同的。是的,对于简单类型(数值 number、字符串 string 、布尔值 boolean),值就保存在变量指向的那个内存地址,因此 const 声明的简单类型变量等同于常量。而复杂类型(对象 object,数组 array,函数 function),变量指向的内存地址其实是保存了一个指向实际数据的指针,所以 const 只能保证指针是固定的,至于指针指向的数据结构变不变就无法控制了,所以使用 const 声明复杂类型对象时要慎重。

4、ES6解构赋值

4.1、解构赋值概述

解构赋值是对赋值运算符的扩展。

它是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。

4.2、解构模型

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

在解构中,有下面两部分参与:

解构的源,解构赋值表达式的右边部分;

解构目标,解构赋值表达式的左边部分;

在ES5中,为变量赋值只能直接指定变量的值:

let a = 1;
let b = 2;

在ES6中,变量赋值允许写成:

let [a,b,c] = [1,2,3];
console.log(a); // 输出1
console.log(b); // 输出2
console.log(c); // 输出3

面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。

本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

4.3、数组的解构赋值

基本用法

let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3

可嵌套

let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3

可忽略

let [a, , b] = [1, 2, 3];
// a = 1
// b = 3

不完全解构

let [a = 1, b] = []; // a = 1, b = undefined

如果解构不成功,变量的值就等于undefined。

let [foo] = [];
let [bar, foo] = [1];

以上两种情况都属于解构不成功,foo的值都会等于undefined。

另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。

let [x, y] = [1, 2, 3];
x // 1
y // 2

let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4

上面两个例子,都属于不完全解构,但是可以成功。

如果等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错。

// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {
   };

上面的语句都会报错,因为等号右边的值,要么转为对象以后不具备 Iterator 接口(前五个表达式),要么本身就不具备 Iterator 接口(最后一个表达式)。

剩余运算符

let [a, ...b] = [1, 2, 3];
//a = 1
//b = [2, 3]

字符串

在数组的解构中,解构的目标若为可遍历对象,皆可进行解构赋值。可遍历对象即实现 Iterator 接口的数据。

let [a, b, c, d, e] = 'hello';
// a = 'h'
// b = 'e'
// c = 'l'
// d = 'l'
// e = 'o'

解构默认值

解构赋值允许指定默认值。

let [foo = true] = [];
foo // true

let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果。

let [a = 2] = [undefined]; // a = 2

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

let [x = 1] = [undefined];
x // 1

let [x = 1] = [null];
x // null

上面代码中,如果一个数组成员是null,默认值就不会生效,因为null不严格等于undefined。

如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。

function f() {
   
  console.log('aaa');
}

let [x = f()] = [1];

上面代码中,因为x能取到值,所以函数f根本不会执行。上面的代码其实等价于下面的代码。

let x;
if ([1][0] === undefined) {
   
  x = f();
} else {
   
  x = [1][0];
}

默认值可以引用解构赋值的其他变量,但该变量必须已经声明。

let [a = 3, b = a] = []; // a = 3, b = 3
let [a = 3, b = a] = [1];// a = 1, b = 1
let [a = 3, b = a] = [1, 2]; // a = 1, b = 2
let [a = b, b = 1] = []; // ReferenceError: y is not defined

上述代码解释:

  • a 与 b 匹配结果为 undefined ,触发默认值:a = 3; b = a =3;
  • a 正常解构赋值,匹配结果:a = 1,b 匹配结果 undefined ,触发默认值:b = a =1;
  • a 与 b 正常解构赋值,匹配结果:a = 1,b = 2;
  • 上面最后一个表达式之所以会报错,是因为x用y做默认值时,y还没有声明。
4.4、对象的解构赋值

(1)基本用法

解构不仅可以用于数组,还可以用于对象。

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

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

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

let {
    baz } = {
    foo: 'aaa', bar: 'bbb' };
baz // undefined

上面代码的第一个例子,等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没有影响。第二个例子的变量没有对应的同名属性,导致取不到值,最后等于undefined。

如果解构失败,变量的值等于undefined。

let {
   foo} = {
   bar: 'baz'};
foo // undefined

上面代码中,等号右边的对象没有foo属性,所以变量foo取不到值,所以等于undefined。

对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。

// 例一
let {
    log, sin, cos } = Math;

// 例二
const {
    log } = console;
log('hello') // hello

上面代码的例一将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多。例二将console.log赋值到log变量。

如果变量名与属性名不一致,必须写成下面这样。

let {
    foo: baz } = {
    foo: 'aaa', bar: 'bbb' };
baz // "aaa"

let obj = {
    first: 'hello', last: 'world' };
let {
    first: f, last: l } = obj;
f // 'hello'
l // 'world'

这实际上说明,对象的解构赋值是下面形式的简写。

let {
    foo: foo, bar: bar } = {
    foo: 'aaa', bar: 'bbb' };

也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

let {
    foo: baz } = {
    foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined

上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。

(2)嵌套对象的解构赋值

与数组一样,解构也可以用于嵌套结构的对象。

let obj = {
   
  p: [
    'Hello',
    {
    y: 'World' }
  ]
};

let {
    p: [x, {
    y }] } = obj;
x // "Hello"
y // "World"

注意,这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以写成下面这样。

let obj = {
   
  p: [
    'Hello',
    {
    y: 'World' }
  ]
};

let {
    p, p: [x, {
    y }] } = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]

下面是另一个例子。

4.5、解构赋值注意事项

(1)如果要将一个已经声明的变量用于解构赋值,必须非常小心。

// 错误的写法
let x;
{
   x} = {
   x: 1};
// SyntaxError: syntax error

上面代码的写法会报错,因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。

// 正确的写法
let x;
({
   x} = {
   x: 1});

上面代码将整个解构赋值语句,放在一个圆括号里面,就可以正确执行。关于圆括号与解构赋值的关系,参见下文。

(2)解构赋值允许等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式。

({
   } = [true, false]);
({
   } = 'abc');
({
   } = []);

上面的表达式虽然毫无意义,但是语法是合法的,可以执行。

(3)由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。

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

上面代码对数组进行对象解构。数组arr的0键对应的值是1,[arr.length - 1]就是2键,对应的值是3。方括号这种写法,属于“属性名表达式”。

4.6、解构赋值的用途

变量的解构赋值用途很多。

(1)交换变量的值

let x = 1;
let y = 2;

[x, y] = [y, x];

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

(2)从函数返回多个值

函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。

// 返回一个数组

function example() {
   
  return [1, 2, 3];
}
let [a, b, c] = example();

// 返回一个对象

function example() {
   
  return {
   
    foo: 1,
    bar: 2
  };
}
let {
    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 对象中的数据,尤其有用。

let 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 接口,配合变量的解构赋值,获取键名和键值就非常方便。

const 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");

5、字符串、函数、数组、对象的扩展

5.1、模板字符串

传统的 JavaScript 语言,输出模板通常是这样写的(下面使用了 jQuery 的方法)。

$('#result').append(
  'There are ' + basket.count + ' ' +
  'items in your basket, ' +
  '' + basket.onSale +
  ' are on sale!'
);

上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。

$('#result').append(`
  There are ${
     basket.count} items
   in your basket, ${
     basket.onSale}
  are on sale!
`);

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

// 普通字符串
`In JavaScript '\n' is a line-feed.`

// 多行字符串
`In JavaScript this is
 not legal.`

console.log(`string text line 1
string text line 2`);

// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${
     name}, how are you ${
     time}?`

上面代码中的模板字符串,都是用反引号表示。

转义符号

如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。

let greeting = `\`Yo\` World!`;

多行字符串

如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。

$('#list').html(`
  • first
  • second
`
);

上面代码中,所有模板字符串的空格和换行,都是被保留的,比如

    标签前面会有一个换行。如果你不想要这个换行,可以使用 trim 方法消除它。

    $('#list').html(`
    
    • first
    • second
    `
    .trim());

    插入变量

    模板字符串中嵌入变量,需要将变量名写在${}之中。

    function authorize(user, action) {
       
      if (!user.hasPrivilege(action)) {
       
        throw new Error(
          // 传统写法为
          // 'User '
          // + user.name
          // + ' is not authorized to do '
          // + action
          // + '.'
          `User ${
         user.name} is not authorized to do ${
         action}.`);
      }
    }
    
    

    插入表达式

    大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。

    let x = 1;
    let y = 2;
    
    `${
         x} + ${
         y} = ${
         x + y}`
    // "1 + 2 = 3"
    
    `${
         x} + ${
         y * 2} = ${
         x + y * 2}`
    // "1 + 4 = 5"
    
    let obj = {
       x: 1, y: 2};
    `${
         obj.x + obj.y}`
    // "3"
    
    

    调用函数

    模板字符串之中还能调用函数。

    function fn() {
       
      return "Hello World";
    }
    
    `foo ${
         fn()} bar`
    // foo Hello World bar
    
    

    如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的 toString 方法。

    如果模板字符串中的变量没有声明,将报错。

    // 变量place没有声明
    let msg = `Hello, ${
         place}`;
    // 报错
    

    由于模板字符串的大括号内部,就是执行 JavaScript 代码,因此如果大括号内部是一个字符串,将会原样输出。

    `Hello ${
         'World'}`
    // "Hello World"
    

    注意要点

    模板字符串中的换行和空格都是会被保留的

    innerHtml = `
    • menu
    • mine
    `
    ; console.log(innerHtml); // 输出 <ul> <li>menu</li> <li>mine</li> </ul>
    5.2、字符串扩展方法

    (1)子串的识别

    ES6 之前判断字符串是否包含子串,用 indexOf 方法,ES6 新增了子串的识别方法。

    • includes():返回布尔值,判断是否找到参数字符串。
    • startsWith():返回布尔值,判断参数字符串是否在原字符串的头部。
    • endsWith():返回布尔值,判断参数字符串是否在原字符串的尾部。

    以上三个方法都可以接受两个参数,需要搜索的字符串,和可选的搜索起始位置索引。

    let s = 'Hello world!';
    
    s.startsWith('Hello') // true
    s.endsWith('!') // true
    s.includes('o') // true
    
    
    

    这三个方法都支持第二个参数,表示开始搜索的位置。

    let s = 'Hello world!';
    
    s.startsWith('world', 6) // true
    s.endsWith('Hello', 5) // true
    s.includes('Hello', 6) // false
    
    
    

    上面代码表示,使用第二个参数 n 时, endsWith 的行为与其他两个方法有所不同。它针对前 n 个字符,而其他两个方法针对从第 n 个位置直到字符串结束。

    注意点:

    • 这三个方法只返回布尔值,如果需要知道子串的位置,还是得用 indexOf 和 lastIndexOf 。
    • 这三个方法如果传入了正则表达式而不是字符串,会抛出错误。而 indexOf 和 lastIndexOf 这两个方法,它们会将正则表达式转换为字符串并搜索它。

    (2)字符串重复

    repeat():返回新的字符串,表示将字符串重复指定次数返回。

    'x'.repeat(3) // "xxx"
    'hello'.repeat(2) // "hellohello"
    'na'.repeat(0) // ""
    
    

    参数如果是小数,会被向下取整。

    'na'.repeat(2.9) // "nana"
    
    

    如果 repeat 的参数是负数或者 Infinity ,会报错。

    'na'.repeat(Infinity)
    // RangeError
    'na'.repeat(-1)
    // RangeError
    
    

    但是,如果参数是 0 到-1 之间的小数,则等同于 0,这是因为会先进行取整运算。0 到-1 之间的小数,取整以后等于 -0repeat 视同为 0。

    'na'.repeat(-0.9) // ""
    
    

    参数 NaN 等同于 0。

    'na'.repeat(NaN) // ""
    
    

    如果 repeat

你可能感兴趣的:(react.js,前端,前端框架)