ECMAScript基础

3 篇文章 0 订阅
2 篇文章 0 订阅

ECMAScript基础

本文目录

一、学习目标

  1. 了解ECMAScript和JavaScript的关系
  2. 能熟练使用let和const命令
  3. 能熟练进行数组和对象的解构赋值
  4. 能熟练使用箭头函数

二、ECMAScript 6简介

ECMAScript 6.0

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

目标

使JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

版本升级

ECMAScript 2015
ECMAScript 2016
ECMAScript 2017

ECMAScript和JavaScript关系

前者是后者的规格,后者是前者的一种实现。

ES6与ES2015的关系

  • ES6
    • 是5.1版以后的JavaScript的下一代标准
    • 涵盖了ES2015、ES2016、ES2017等等
  • ES2015
    • 是ES6第一个版本的正式名称
    • 特指该年发布的正式版本的语言标准

浏览器对ES6的支持情况

在这里插入图片描述


三、Babel转码器

3.1作用

Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,从而在现有环境执行

3.2Babel在线转码工具

链接地址:babel在线转码工具

3.3Nodejs安装部署

Babel本地安装部署,需要事先安装 nodejs ,nodejs官网下载地址:nodejs下载,我们可以根据自己电脑选择32位还是64位。

在这里插入图片描述

此处我选择的是Windows安装包(.mis)64位的,点击下载后将会得到一个安装包如下:

在这里插入图片描述

1.双击安装包打开,点击 “Next”按钮。

在这里插入图片描述

2.勾选 I accept the terms in the License Agreement 点击 “Next”按钮,进入下一步

在这里插入图片描述

3.选择要安装的目录位置,这里我选择的是 D:\Program Files\nodejs\目录,点击 “Next” 按钮,进入下一步

在这里插入图片描述

4.点击 “Next” 按钮,进入下一步

在这里插入图片描述

5.直接点击 “Next” 按钮,进入下一步

在这里插入图片描述

6.单击Install开始安装。

在这里插入图片描述

7.点击Finish按钮,到这里我们就已经完成node.js的安装了

在这里插入图片描述

8.开始菜单中输入cmd进入命令打卡提示符窗口,分别输入以下命令,显示版本号,则安装成功

node -v 显示安装的nodejs版本
npm -v 显示安装的npm版本

在这里插入图片描述

9.修改全局模块路径和缓存路径(这里不是必须的)

注意: 当我们在执行npm install express -g命令时,g表示 global 全局。会默认下载到c盘,c盘一般作为系统盘,尽量把一些程序安装到其他盘,来减少c盘空间的占用

它的默认路径为:C:\Users\用户名\AppData\Roaming\npm如图所示:

在这里插入图片描述

接下来我们开始进行修改,这里将全局模块(文件夹名:node_global)和缓存(文件夹名:node_cache)放在了nodejs安装目录下,在nodejs安装目录下创建创建两个文件夹,名称分别为:node_globalnode_cache,在node_global文件夹下再建一个node_modules文件夹,用来进行配置环境变量 如图所示

在这里插入图片描述

接下来开始修改默认配置:

nodejs的安装目录下,进入node_modules—>npm文件夹—>找到npmrc文件,右键打开

在这里插入图片描述

接下来在npmrc文件中添加如下配置:

prefix=创建的node_global文件夹所在路径
cache=创建的node_cache文件夹所在路径

npmrc文件修改示例:

在这里插入图片描述

10.接下来修改系统环境变量

  1. 右键此电脑—>高级系统设置—>环境变量

  2. 在系统变量中,新建,变量名:NODE_PATH 变量值:node_global文件夹下的node_modules文件夹。如:D:\Program Files\nodejs\node_global\node_modules

在这里插入图片描述

  1. 修改用户变量中的Path变量,将默认的npm路径修改为新建的node_global路径

    在这里插入图片描述

3.4Babel ES6转ES5案例

ES6代码如下(示例):

// 转码前
input.map(item => item + 1);

// 转码后
input.map(function (item) {
  return item + 1;
});
3.5Babel转换代码实现步骤
3.5.1.开启终端

在WebStorm工具中创建一个文件目录StudyBabel,然后右键点击该目录,选择终端命令 如图所示

在这里插入图片描述

3.5.2.安装babel

2.1.在终端中输入npm init -y 命令——>执行后该目录下会生成一个package.json文件
2.2.在文件夹StudyBabel中创建一个src目录(用来存放你需要转换的ES6的js文件)

在这里插入图片描述

3.5.3.修改package.json脚本的scripts属性

3.1.正在scripts属性添加build属性配置:"build": "babel src -d dist"
3.2.在刚才的终端窗口中继续输入:npm i @babel/core @babel/cli @babel/preset-env -D命令
3.3.此时文件夹StudyBabel目录下会生成一个 node_modules文件夹和一个dist文件夹

{
  "name": "studybabel",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "babel src -d dist" //添加配置
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.17.10",
    "@babel/core": "^7.18.2",
    "@babel/preset-env": "^7.18.2"
  }
}

执行 npm i @babel/core @babel/cli @babel/preset-env -D命令

在这里插入图片描述

3.5.4.添加配置

在文件夹StudyBabel目录下创建 src 的目录并在src目录下创建一个后缀名为.babelrc的文件,添加如下json配置信息:

{
	"presets":["@babel/preset-env"]
}

在这里插入图片描述

3.5.5.执行

5.1.在src目录下随便新建一个ES6的js文件用于测试
5.2.在刚才的cmd命令窗口继续输入:npm run build命令

let title='测试ES6代码转换';
let calculate=(x,y)=>x+y;
let result=calculate(5,10);
console.log(title+':'+result);

在这里插入图片描述

3.5.6.观察dist文件下,将会生成ES6转换成ES5的js文件

在这里插入图片描述


四、ES6新增命令

回顾ES5中,我们通常使用var声明一个变量或常量而ES6中我们拥有更多声明变量的关键命令

let命令

1.声明变量
3.用法类似于var
3.但是,所声明的变量,只在let命令所在的代码块内有效

const命令

1.声明一个只读的常量
2.一旦声明,常量的值就不能改变

4.1let命令

使用let命令声明变量示例:

{
    var a = 1;
    let b = 2;
}
console.log(a);   //1,在ES5中,没有块级作用域,且有预解析的概念,即变量可提升
console.log(b);   //ReferenceError: b is not defined

let命令-不存在变量提升示例:

//使用var的情况
console.log(a);  //undefined
var a = 1;

//使用let的情况
console.log(b);  //ReferenceError: b is not defined
let b = 2;

结论: let命令所声明的变量一定要在声明后使用,否则报错。


let命令-暂时性死区示例:

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

if (true) {
    // 暂时性死区开始
    tmp = 'abc'; // ReferenceError
    console.log(tmp); // ReferenceError
    let tmp; // TDZ结束
    console.log(tmp); // undefined
    tmp = 123;
    console.log(tmp); // 123
}

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


let命令-不允许重复声明示例:

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

结论: 使用var命令可以重复声明,即后声明的变量覆盖之前声明的变量,ES6中的let不允许在相同作用域内,重复声明同一个变量。


let命令-块级作用域

问题: 在ES6中,为什么新增了块级作用域?

  1. 内层变量可能会覆盖外层变量
  2. 用来计数的循环变量泄露为全局变量
function f1() {
    let n = 5;
    if (true) {
        let n = 10;
    }
    console.log(n); // 5
}
f1();

结论: 通过示例以上代码发现,有两个代码块,且都声明了变量 n,运行后输出的值为5。这表示外层的代码块不受内层代码块的影响。那如果说将let两次声明的变量n改为使用 var 声明,最后输出的值为10。


4.2const命令

使用const命令定义变量示例

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

结论:

使用const命令声明一个常量,然后再改变常量的值,会发现:改变常量的值会报错,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

例如,const foo;这段代码就只有声明没有赋值,就一定会报错。

const命令其他特性

  1. 只在声明所在的块级作用域内有效
  2. const命令声明的常量不提升
  3. 存在暂时性死区,只能在声明的位置后面使用
  4. 声明的常量,也与let一样不可重复声明

五、变量的解构赋值


解构赋值是对赋值运算符的扩展,针对数组或者对象先进行模式匹配,然后对其中的变量进行赋值。

  1. 数组的解构赋值
  2. 对象的解构赋值
  3. 字符串的解构赋值
  4. 数值和布尔值的解构赋值
  5. 函数参数的解构赋值

5.1数组的解构赋值-基本用法

在ES5中,为变量赋值,只能直接指定值,在ES6中,可以从数组中提取值,按照对应位置,对变量赋值

数组的解构赋值演示示例:

let a = 1;
let b = 2;
let c = 3;
//ES6:解构赋值
let [a,b,c]=[1,2,3];

数组还有其他的一些操作,比如 可嵌套可忽略不完全解构剩余运算符解构默认值 等。


5.2数组的解构赋值-其他特性
//可嵌套
let [a, [[b], c]] = [1, [[2], 3]];
console.log(a); //1
console.log(b); //2
console.log(c); //3
//可忽略
let [a, , b] = [1, 2, 3];
console.log(a); //1
console.log(b); //3
//不完全解构
let [a, b] = [1, 2, 3]; 
console.log(a); //1
console.log(b); //2

let [a, [b], c] = [1, [2, 3], 4];
console.log(a); //1
console.log(b); //2
console.log(c); //4
//剩余运算符
let [a, ...b] = [1, 2, 3];
console.log(a); //1
console.log(b); //[2, 3]

//解构默认值
let [x, y = 'b'] = ['a']; 
console.log(x); //'a'
console.log(y); //'b'

结论: 通过以上示例代码可以发现:不管是在数组中嵌套数组,还是左右两边的数组缺少变量或值,均可解构成功,如果解构不成功,变量的值就等于undefined,解构不仅可以用于数组,还可以用于对象接下来一起学习对象的对象的解构赋值~。


5.3对象的解构赋值-基本用法

对象的解构赋值示例

let {bar, foo} = {foo: "ECMAScript 6.0", bar: "hello"};
console.log(bar); //hello
console.log(foo); //ECMAScript 6.0
let {abc} = {foo: "ECMAScript 6.0", bar: "hello"};
console.log(abc); //undefined
5.4对象的解构赋值-其他特性
//可嵌套
let obj = {p: ['hello', {y: 'world'}] }; 
let {p: [x, { y }] } = obj;
console.log(x); //hello
console.log(y); //world
//可忽略
let obj = {p: ['hello', {y: 'world'}, 'ECMAScript'] }; 
let {p: [x, { y }] } = obj;
console.log(x); //hello
console.log(y); //world
//不完全解构
let obj = {p: [{y: 'world'}, 'hello'] }; 
let {p: [{ y }, x, z ] } = obj;
console.log(x,y,z); //hello ,world,undefined
//剩余运算符
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}; 
console.log(a,b); //10,20
console.log(rest); //{c: 30, d: 40}
//解构默认值
let {a = 10, b = 5} = {a: 3};
console.log(a); //3
console.log(b); //5
let {a: aa = 10, b: bb = 5} = {a: 3}; 
console.log(aa); //3
console.log(bb); //5

结论: 不管是在对象中嵌套对象,还是左右两边的对象缺少变量,均可解构成功,默认值生效的条件是,对象的属性值严格等于undefined。


5.5字符串、数值和布尔值的解构赋值

字符串解构赋值,字符串被转换成了一个类似数组的对象。

字符串解构赋值示例:

const [a, b, c] = 'yes';
console.log(a)  //y
console.log(b)  //e
console.log(c)  //s

数值和布尔值的解构赋值

如果等号右边是数值和布尔值,会先转为对象

let {toString: s} = 123;
console.log(s === Number.prototype.toString)// true
let {toString: s} = true;
console.log(s === Boolean.prototype.toString) // true

字符串、数值和布尔值的解构赋值,不作为重点讲解,了解即可。


5.6变量解构赋值的用途
  1. 交换变量的值
  2. 从函数返回多个值
  3. 函数参数的定义
  4. 提取 JSON 数据

解析jsonData的值示例:

let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]   
};
let { id, status, data: number } = jsonData;

细节可参考:阮一峰的电子书中内容,网址:http://es6.ruanyifeng.com/#docs/destructuring

当然变量解构赋值除了这些用途以外,还有遍历 Map 结构、输入模块的指定方法这两个用途,在这里了解即可,后续会讲解到Map数据结构以及模块。


六、函数参数的解构赋值


6.1函数参数的解构赋值-基本用法

函数参数的解构赋值示例:

function add([x,y]){
    console.log(x + y); //3
}
add([1,2]);

结论: 定义一个add函数,传入x,y两个参数,会发现结果为3。原因是:函数add的参数表面上是一个数组,但是在传入参数的那一刻,数组的参数就被解构成变量x和y,对于函数内部的代码来说,它们能感受到参数就是x和y。除了基本用法外,函数参数的解构还可以使用默认值。


6.2函数参数的解构赋值-使用默认值
  1. 基本用法
  2. 优点和局限性
  3. 与解构赋值默认值结合使用
  4. 其他特性

问题: 在ES6之前,是否可以直接为函数的参数指定默认值?

ES5示例:

function foo(x, y) {
  y = y || 'World';
  console.log(x, y);
}
foo('Hello') // Hello World
foo('Hello', 'ES6') // Hello ES6
foo('Hello', '') // Hello World

ES6示例:

function foo(x, y = 'World') {
  console.log(x, y);
}
foo('Hello') // Hello World
foo('Hello', 'ES6') // Hello ES6
foo('Hello', '') // Hello

结论:

​ 在ES6之前,不可以直接为函数的参数指定默认值,只能采用变通的方法。**(ES5示例)**上面代码检查函数foo的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。

​ 通过ES6之间的变通方式为函数的参数指定默认值后,引出ES6直接给函数的参数指定默认值**(ES6示例)** 通过ES6的方法直接为函数的参数指定默认值,因此可得出函数参数默认值-优点:简洁、提高代码的阅读性、有利于将来的代码优化


6.3函数参数默认值-局限性
  1. 参数变量是默认声明的,所以不能用let或const再次声明
  2. 使用参数默认值时,函数不能有同名参数

代码块1:

function foo(x = 5) {
    let x = 1; // error
    const x = 2; // error
}
foo();

代码块2:

// 不报错
function foo(x, x, y) {// ...}
foo();
// 报错
function foo(x, x, y = 1) { // ...}
foo();

结论:

1、参数变量是默认声明的,所以不能用let或const再次声明:代码块1中,由于参数变量x是默认声明的,所以在函数体中,不能用let或const再次声明,否则会报错。

2、使用参数默认值时,函数不能有同名参数:代码块2中,不报错的情况:虽有同名参数,但不报错,是因为没有使用参数默认值;报错的情况:有同名参数,但报错,是因为使用了参数默认值。这是通过函数参数指定默认值的基本用法,总结的函数参数默认值的优点和局限性,其实,除此之外,函数参数默认值还可以与解构赋值默认值结合使用。


6.4函数参数默认值-与解构赋值默认值结合

函数参数默认值与结构赋值默认值使用示例:

function foo({x, y = 5}) {
  console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined

上面代码只使用了对象的解构赋值默认值。只有当函数foo的参数是一个对象时,变量x和y才会通过解构赋值生成。如果函数foo调用时没提供参数,变量x和y就不会生成,从而报错


6.5函数参数默认值-其他特性

1.参数默认值的位置

2.函数的 length 属性

​ 返回没有指定默认值的参数个数

3.作用域(context)

​ 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域
​ 等到初始化结束,这个作用域就会消失

4.rest 参数

​ 用于获取函数的多余参数

示例1:

//参数默认值的位置
function foo(x = 1, y) {console.log(x,y);}
foo(); // [1, undefined]

//foo(2); // [2, undefined])
//foo(, 1); // 报错
//foo(undefined, 1); // [1, 1]
//foo(null, 1);

//函数的 length 属性
console.log((function (a) {}).length) // 1
console.log((function (a = 5) {}).length) // 0
console.log((function (a, b, c = 5) {}).length) // 2

示例2:

//作用域
var x = 5;
function f(x, y = x) {console.log(y);}
f(10); // 10
//rest 参数实现求和
function add(...values) {
    let sum = 0;
    for (var val of values) {sum += val;}
    console.log(sum);
}
add(1, 2, 3) // 6

参数默认值的位置:

通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非 尾部的参数设置默认值,实际上这个参数是没法省略的。上面代码中,有默认值的参数都不是尾参数。这时,无法只省略该参数,而不省略它后面的参数,除非显式输入undefined。如果传入undefined,将触发该参数等于默认值,null则没有这个效果。

函数的 length 属性:

​ 指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。上面代码中,length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。比如,上面最后一个函数,定义了 3 个参数,其中有一个参数c指定了默认值,因此length属性等于3减去1,最后得到2。

作用域 :

​ 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。上面代码中,参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是10。

rest参数:

​ ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。上面代码的add函数是一个求和函数,利用 rest 参数,可以向该函数传入任意数目的参数。


七、箭头函数


7.1箭头函数基本使用

使用“箭头”(=>)定义函数

let f = v => v;
// 等同于
let f = function (v) {
  return v;
};

特点 : 简洁、参数、函数体的大括号


箭头函数-参数

  1. 没有参数
let fun1 = () => console.log('fun1()'); //()不能省略
  1. 一个参数
let fun1 = a => console.log(a); //()可以省略
  1. 两个及两个以上的参数
let fun1 = (x,y) => console.log(x,y); // ()不能省略

箭头函数-函数体

  1. 只有一条语句或者表达式
 //{}可以省略,会自动返回语句执行的结果
let fun1 = (x,y) => console.log(x+y); 
  1. 不止一条语句或者表达式
// 添加{ }时返回结果一定要有return
let fun1 = (x,y) => {
    console.log(x,y);
    return x+y;
}

7.2this出现的原因

问题: 在ES6中,为什么会出现箭头函数this?

原因: 解决了匿名函数this指向的问题

setTimeout()和setInterval()函数中使用this所造成的问题

function Timer() {
    this.s1 = 0;
    this.s2 = 0;
    // 箭头函数 
    setInterval(() => this.s1++, 1000);
    // 普通函数 
    setInterval(function () { 
        this.s2++;    
    }, 1000);
}

var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);// s1: 3
setTimeout(() => console.log('s2: ', timer.s2), 3100);// s2: 0

结论:

上面代码中,Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(即Timer函数),后者的this指向运行时所在的作用域(即全局对象)。所以,3100 毫秒之后,timer.s1被更新了 3 次,而timer.s2一次都没更新。

因此,箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域

箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this。

扩展理解:

  1. 箭头函数的this判断外层是否有函数
  2. 如果有,外层函数的this就是内部箭头函数的this
  3. 如果没有,则this是window

7.3箭头函数-使用注意事项

  1. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
  2. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误
  3. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替
    其中,第一点尤其重要,需要记住,前面也已给大家讲解

7.4箭头函数-不适用场合

  1. 定义对象的方法,且该方法内部包括this
  2. 需要动态this的时候,也不应使用箭头函数

cat.jumps()方法是一个箭头函这句话准确吗?

const cat = {
    lives: 9,
    jumps: () => {
        this.lives--;
    }
}

解释: cat.jumps()方法是一个箭头函数,这是错误的。调用cat.jumps()时,如果是普通函数,该方法内部的this指向cat;如果写成上面那样的箭头函数,使得this指向全局对象,因此不会得到预期结果。这是因为对象不构成单独的作用域,导致jumps箭头函数定义时的作用域就是全局作用域。

点击按钮触发单击事件会出错吗?

<button id="press">测试箭头函数</button>
<script>
	var button = document.getElementById('press');
	button.addEventListener('click', () => {
   		 this.classList.toggle('on');
	});
</script>

解释: 点击按钮会报错,因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。


八、总结

思维导图

在这里插入图片描述

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

众生云海,一念初见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值