ES6(一):let和const、模板字符串、函数默认值、剩余参数、扩展运算符、箭头函数

2023-10-28

一、let和const声明变量

关于ES6的内容如果遇到不会的可以查阅文档:阮一峰ES6

1.变量不能重复声明

下面这样会报错,而var就不会

let a = 1;
let a = 2;

2.块儿级作用域

大括号里(if else while for等)使用let声明变量的话,在外边是找不到的。但是var就可以

if (1==1){
    let b = 2;
}
console.log(b);

3.不存在变量提升

这样写会报错

console.log(c);
let c = 3;

但是如果用var 的话,就相当于是

var c;
console.log(c);
c = 3;

4.const声明变量

const也不能重复声明,也是块级作用域,也不存在变量提升,但是和let不同的是,const赋值之后,不能再修改,还有就是for循环不能用const
比如我这么写,var和let都不会报错,但是const会报错

const d = 1;
d = 2;
console.log(d);

5.优点

(1)for循环中用let比较奶思

比如下面这个for循环,如果用var i = 0,那么最后结果是 5 5 5 5 5 ,这是因为用var是在全局只声明一个变量i,那么每一次循环,全局的i都会改变,里面的console.log(i)指向的都是同一个i,所以最后输出的是55555

for(let i = 0; i < 5; i++){
  setTimeout(()=>console.log(i),1000)//0 1 2 3 4
}

但是如果使用let声明就不一样了,i只在当前作用域生效,每一次循环都会重新声明变量,上面的代码其实就类似这样:

for(let i = 0; i < 5; i++){
	let i = 当前i的值;
  	setTimeout(()=>console.log(i),1000)//0 1 2 3 4
}

你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算(如+1运算)。

也正是因为如此,for循环中不能使用const定义变量,因为const定义后的变量不可更改,不能在上一轮循环的基础上进行计算(如++)再重新赋值给本轮声明

(2)for循环分为父作用域和子作用域

另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc

上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域(同一个作用域不可使用 let 重复声明同一个变量)。

(3)不会污染全局变量

除此之外,let和const不会污染全局变量,比如我如果这么写

var RegExp = 100;
console.log(RegExp);
console.log(window.RegExp);

那么最后的结果是两个100,window对象直接被改了,但是如果用let
第一个是100,第二个结果还是window对象的RegExp函数

let RegExp = 100;
console.log(RegExp);
console.log(window.RegExp);

综上所述:默认情况下用const,如果变量后面需要被修改再用let

二、模板字符串

比如有一个div,我想往里面塞下面这些内容:

<ul>
      <li>
            <p id="dj">从蓬莱写到仙台,哥们儿代码信手拈来</p>
      </li>
</ul> 

按照往常的写法,我们是这么写的:

const div = document.querySelector('div');
let id = 'dj';
let text = '从蓬莱写到仙台,哥们儿代码信手拈来';
div.innerHTML = "<ul><li><p id=" + id + ">" + text + "</p></li></ul>";

真的是相当麻烦啊,但是现在,我们可以这么写:
使用tab上面那个小点点包起来,里面的变量使用${变量名}来替换,非常奈斯

let id = 'dj';
let text = '从蓬莱写到仙台,哥们儿代码信手拈来';
div.innerHTML = `<ul>
    <li>
        <p id=${id}>${text}</p>
    </li>
</ul>`;

三、函数默认值、剩余参数、扩展运算符

1.函数默认值

如果我们想写一个求和函数,要给个默认值的话,搁以前得这么写:

function add(a, b) {
    a = a || 10;
    b = b || 20;
    return a + b;
}
console.log(add());

但是在ES6中,就非常easy了:

function addd(a = 10, b = 20) {
    return a + b;
}
console.log(addd());

当然啊,如果下面这么写会报错:

function addd(a = 10, b) {
    return a + b;
}
console.log(addd(20));

这是因为啥捏,调用函数给的值是按顺序的,这么写a是20,b没赋值,所以不行

当然啊,这个默认值也可以是一个函数

function add2(a = 10, b = getVal(5)) {
    return a + b;
}

function getVal(val) {
    return val + 5;
}
console.log(add2());  //20

2.剩余参数

我把它理解为代替arguments伪数组的一个真正的数组
比如说我要干一件事儿,我要干啥呢,我要把一个对象儿中的属性名和属性值拿出来传给另一个对象。按照ES5的写法,如果不确定用户传几个参数,我们肯定是使用arguments伪数组来接收

let book = {
    title: '前端之王',
    name: 'zzy',
    age: 18
}

function pick(obj) {
    let result = {};
    for (let i = 1; i < arguments.length; i++) {
        result[arguments[i]] = obj[arguments[i]];
    }
    return result;
}
let bookData = pick(book, 'title', 'name', 'age');
console.log(bookData);

但是在ES6中,我们可以获取一个真正的数组。使用(…+名字)这种语法来做形参,返回的是一个数组,但是这个一定要写在形参的最后一个。比如下面这个例子,obj对应book,args就是后面那三个属性名构成的数组
慢慢理解理解,问题不大。

let book = {
    title: '前端之王',
    name: 'zzy',
    age: 18
}

function pick(obj, ...args) {
	console.log(args);  //['title', 'name', 'age']
    let result = {}; // 定义一个空对象
    for (let i = 0; i < args.length; i++) {
    //把对象中的属性值拿过来,给空对象(如果该对象没有这个属性,那么就添加一个)
        result[args[i]] = obj[args[i]];
    }
    return result;
}
let bookData = pick(book, 'title', 'name', 'age');
console.log(bookData);

3.扩展运算符

扩展运算符:将一个数组(或对象)分割,并将数组的各个项作为分离的参数传给函数。其实就是把数组或对象拆开
1、比如我要获取数组的最大值,以前我们会使用apply

const arr = [123, 545, 34, 234, 5];
console.log(Math.max.apply(null, arr));

但是现在我们可以这样写

console.log(Math.max(...arr));

2、其实也可以把对象里的东西拆开

let obj1 = {x:100, y:200};
let obj2 = {
    a:1,
    ...obj1,
    b:2
}
console.log(obj2);  //{a: 1, x: 100, y: 200, b: 2}

四、箭头函数

1.箭头函数的语法

在ES5中这样定义函数:

let add = function (a,b) {
    return a + b;
}

但是在ES6中,我们可以这么写:

let add = (a,b) => {
    return a + b;
}

如果只有一个返回值,我们甚至可以把return省略:

let add = (a, b) => a + b;

如果我们返回的是一个对象或者数组什么的,要加()把它包起来才行,不然会报错

let getObj = id => ({ id: id, name: 'zzy' });
console.log(getObj(007)); // id: 007, name: 'zzy'

2.对象中的函数和箭头函数

比如一个Person对象,里面有eat方法:

let person = {
    name: "jack",
    // 以前:
    eat: function (food) {
        console.log(this.name + "在吃" + food);
    },
    // 箭头函数版:
    eat2: food => console.log(person.name + "在吃" + food),// 这里拿不到this
    // 简写版:
    eat3(food){
        console.log(this.name + "在吃" + food);
    }
}

3.箭头函数的this指向

简单来说,想知道箭头函数中的this指向谁,就看这个箭头函数外边有没有包裹函数,如果它外面有函数,那么this指向的就是外层包裹函数的this,如果没有包裹函数,this指向的就是window。

看几个例子就明白了:

(1)例1

定义一个函数,定义一个对象,使用bind让函数中的this指向这个对象
这样的话,fn里的this指向的就是obj,返回值返回一个匿名箭头函数,箭头函数中的this指向的和包裹它的fn中的this指向相同,也是obj。
这里还要注意bind的特性,bind不会更改原函数,而是复制一份原函数生成一个新函数,新函数中this的指向改变,所以如果bind更改绑定后,调用fn的话,还是输出window

let obj = { name: 'zzy' };  //定义一个对象,让fn的this指向这个对象
function fn() {
    console.log(this);
    return () => {
        console.log(this);
    }
}
//更改fn函数的this指向
let newfn = fn.bind(obj);  //bind不会更改原函数,复制一份新的给newfn
fn();  //window
newfn(); //obj

(2)例2

如果我定义一个对象,里面写个方法,方法里面的this指向的是函数的调用者,也就是Person这个对象,所以最后输出的结果是18

let Person = {
    name: 'zzy',
    age: 18,
    getAge: function () {
        console.log(this.age);
    }
}
Person.getAge();  //18

但是我如果把这个函数写成箭头函数,那么this就往外边查找,指向window

let Person = {
    name: 'zzy',
    age: 18,
    getAge: () => {
        console.log(this.age);
    }
}
Person.getAge();  //undefined

这里还有个注意点:对象有大括号,但不产生作用域

(3)例3

比如我定义一个Dj对象,里面有个事件监听,点击页面调用另一个方法
那么这么写会报错,因为this指向的是document,document里面根本没有dance方法

let Dj = {
    id: 007,
    drop: function () {
        document.addEventListener('click', function () {
        	console.log(this);  //document
            this.dance();
        })
    },
    dance: function () {
        console.log('drop the beat');
    }
}
Dj.drop();  //报错

但是如果我把监听事件里的函数写成箭头函数,this的绑定就消失了,此时this指向的是外层包裹函数的this,也就是drop函数里的this,也就是Dj对象,这样的话就可以找到dance方法了

let Dj = {
    id: 007,
    drop: function () {
        document.addEventListener('click', () => {
            console.log(this);  //Dj
            this.dance();
        })
    },
    dance: function () {
        console.log('drop the beat');
    }
}
Dj.drop();  //drop the beat

那如果我把drop也写成箭头函数呢?因为箭头函数没有this绑定,此时就会往上找,而对象的大括号没有作用域,所以this就会找到window,懂了吗baby?

let Dj = {
    id: 007,
    drop: () => {
        document.addEventListener('click', () => {
            console.log(this);  //window
            this.dance();
        })
    },
    dance: function () {
        console.log('drop the beat');
    }
}
Dj.drop();  //报错

4.箭头函数的注意事项

1.使用箭头函数,函数内部没有arguments
2.箭头函数不能用来构造函数,因为function函数是一个对象,但是箭头函数不是一个对象,实际上箭头函数就是个语法糖

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ES6(一):let和const、模板字符串、函数默认值、剩余参数、扩展运算符、箭头函数 的相关文章

  • onYouTubeIframeAPIReady 函数未调用

    我想打电话onYouTubeIframeAPIReady函数 但这没有触发 我只得到frameID在控制台中 但其他函数没有被调用 document ready function var player var ytsrc video hol
  • Javascript:如何检查异步操作是否仍在挂起/正在进行中?

    我想知道是否可以以某种方式检查 Javascript 中的异步操作是否仍处于待处理状态 因为我正在执行调用特定 URL 的数据库请求 虽然 db 调用仍在进行中 但我想停止任何其他传入的 db 调用 这意味着 停止对该 URL 的任何进一步
  • 如何从ArrayBuffer中获取二进制字符串?

    JavaScript中如何从ArrayBuffer中获取二进制字符串 我不想对字节进行编码 只需将二进制表示形式获取为字符串 提前致谢 以下代码将一致地转换ArrayBuffer to a String并再次返回 而不会丢失或添加任何额外的
  • browserify 错误 /usr/bin/env: 节点: 没有这样的文件或目录

    我通过 apt get install 安装了 node js 和 npm 以及所有依赖项 然后安装了 browserify npm install browserify g 它完成了整个过程 看起来安装正确 但是当我尝试为此做一个简单的捆
  • 从 DOM 中删除后,动态添加的 JavaScript 脚本会继续执行

    因此 我正在创建一个 SPA 并使用 AJAX 将 HTML 页面加载到我网站的索引页面中 问题是 当包含我的一个页面时 它似乎会徘 徊并执行其中的 JavaScript 代码 即使它随后从 DOM 中删除 索引 html 正文 div d
  • 通过 HTML5 文件和 URL API 正确创建和提供 PDF Blob

    好吧 假设我有文档数据存储在某处 让我们任意取this pdf http www grida no climate ipcc tar wg1 pdf tar 01 pdf 问题 1 我想要做的是对此 URL 进行 AJAX 调用 因为我需要
  • 区分单击与 mousedown/mouseup

    我已经阅读了有关这种情况的 stackoverflow 上的几个答案 但没有一个解决方案有效 我尝试根据用户是否单击某个元素或使用 jQuery 将鼠标按住该元素来执行不同的操作 有可能做到这一点吗 onMouseDown 将在按下左侧或右
  • JSDoc:如何在生成的文档中包含自定义 css 文件模板?

    JS文档docs https jsdoc app about configuring default template html say 将图像目录复制到输出目录 复制全部 将 myproject static 中的静态文件复制到输出目录
  • WebDriver:更改事件未触发

    我有一个使用 KnockoutJS 的应用程序 我正在尝试编写一些测试表单的测试 如果您不了解 KnockoutJS 简单来说 它提供了从我的视图到我的数据模型的绑定 这意味着当我在输入字段中键入值时 我的基础对象会自动使用该输入字段值进行
  • 正则表达式 - 避免表达式中出现字符串

    我正在尝试创建一个应该匹配以下情况的正则表达式 如果单词完全匹配 first second third 那么匹配应该失败 但如果它周围有任何字符 那么应该匹配该字符串 我还需要避免字符串中的某些字符集 如果这些字符是字符串的一部分 则匹配结
  • 禁用任何类型的浏览器窗口滚动?

    有没有办法禁用滚动 不仅仅是滚动条 还有浏览器窗口的全部功能 根据您对 Keit 的回答 您不想在打开灯箱时滚动处于活动状态 如果是这种情况 您可以使用以下 css 在打开灯箱的同时向正文添加一个类 这个解决方案的好处是它保留了滚动 空间
  • Rxjs 可观察等待直到满足某些条件

    我有以下重试逻辑来重试操作 对于单个请求来说它工作得很好 对于多个正在进行的请求 我想在重试之前等待现有的重试逻辑完成 handleError errors Observable
  • 如何检查jquery数据表中的每个复选框?

    我有一个第一列带有复选框的表格 我使用 jQuery DataTable 插件显示我的表格 我制作了 2 个链接来选择 取消选择每个复选框 这是选择全部的一个 a href Select all a 和 JavaScript functio
  • 在外部单击时关闭弹出 div

    我有一个弹出 div 仅在单击特定按钮时显示 单击同一按钮时它甚至会隐藏 我的问题是 我还想在单击外部任何地方时隐藏 div 我无法这样做 因为弹出 div 位于主包装类内部 并且无法通过在包装类上使用 click 事件并使其隐藏来做到这一
  • 如何在 OpenLayers 3 中删除监听器

    我做了一个copy https gis stackexchange com questions 178222 how to delete a listener in openlayers 3我在 stackoverflow 上提出的问题 因
  • 优化正则表达式以过滤数千个 HTML 选择选项

    背景 我开发了一个基于 jQuery 的穿梭小部件 https stackoverflow com a 13557000 59087对于 HTMLselect元素 因为我找不到一个经过最低限度编码并提供正则表达式过滤器来补偿的元素变音符号
  • 单击react.js 切换列表的背景颜色

    我正在尝试创建一个具有以下功能的列表 悬停时更改列表项的背景颜色 单击时更改列表项的背景颜色 在单击的元素之间切换背景颜色 即列表中只有一个元素可以具有 clicked 属性 我已经执行了 onhover 1 和 2 功能 但无法实现第三个
  • 获取 2 个日期之间的月份名称

    我有两个约会from and to 我想获取这两个日期之间的所有月份名称 以下是我的代码 var monthNames January February March April May June July August September
  • Chrome Prerender 功能每次都会被取消

    我正在尝试 Chrome 中的预渲染功能 但是当我检查网络时 我可以看到任何链接的请求都被取消 我使用以下语法 我尝试了现场演示http prerender test appspot com http prerender test apps
  • Angular 5 中 Observable.ForkJoin 的重复 Http 请求

    我有一个 Angular 5 应用程序 组件中包含以下代码 ngOnInit Observable forkJoin this highlightedInsight this insightService getHighlightedIns

随机推荐

  • 时序分析/约束(一):相关概念

    由 zme 于 星期四 02 20 2014 15 03 发表 http xilinx eetrend com blog 6631 时序分析时FPGA设计中永恒的话题 也是FPGA开发人员设计进阶的必由之路 慢慢来 先介绍时序分析中的一些基
  • uniapp 登入功能 vuex使用 通俗易懂

    目录 功能介绍 运行效果 未登入状态 登入页面 进行登入完后 代码演示 功能结构 请求封装 详细文章 部分api ts 关于 用户登入接口 store index ts store user ts main js App vue type
  • 2018-12-12 Pycharm git clone 密码错误

    在新建工程的时候选择了 clone from git 不小心把密码输错了 再次clone 不再提示输入密码 只提示错误 微信截图 20181212003707 png 进入 windows 凭据 点击下三角 删除即可 微信截图 201812
  • nodejs express multer 中文名乱码

    找了半天找不到 科学上网出去秒解决 哎 无力吐槽 不bb那么多 直接上代码 上传时前端正常 但是后端接收文件时乱码 const uploads multer 文件上传的位置 dest path join dirname public upl
  • Java中字符串与byte数组之间的转换方法

    在Java编程中 我们常常需要对字符串和byte数组进行转换 字符串一般是用来表示文本信息 而byte数组则是用来表示二进制数据 如图片 音频等 本文将详细介绍Java中字符串和byte数组之间的转换方法 包括将字符串转换为byte数组和将
  • Failed to find Build Tools revision 27.0.3

    因为电脑系统的问题 导致运行一下Android Studio整个8G的电脑内存都给吃没了 索性又重装了下电脑 重新安装了下Android Studio 错误信息 11 44 Gradle sync failed Failed to find
  • @Value值注入及配置文件组件扫描方式

    spring配置文件对应的是父容器 springMVC配置文件产生的是子容器 前者一般配置数据源 事务 注解等 当然还可以进一步将一些配置细化到其他xml中 后者一般配置控制层相关的 如静态资源 视图解析器等 系统启动的时候 先初始化父容器
  • 神经网络的计算量(FLOPs)、参数量(Params)、推理时间(FPS)的定义及实现方法

    目录 1 定义 2 实现方法 2 1 计算参数量 2 2 计算参数量和FLOPs 2 3 计算推理时间 FPS 3 数据大小对参数量和FLOPs的影响 4 参数量和FLOPs对于硬件要求 参考 1 定义 在评价深度学习模型的大小 计算量 推
  • CentOS7搭建Redis Sentinel

    目录 什么是Redis Sentinel 搭建Redis Sentinel 启动与验证 什么是Redis Sentinel 官方文档 Redis 的 Sentinel 文档 搭建Redis Sentinel 首先需要在CentOS环境下准备
  • GCC Coverage代码分析-GCC如何编译生成gcov/gcov-dump程序及其bug分析

    本博客 http blog csdn net livelylittlefish 贴出作者 阿波 相关研究 学习内容所做的笔记 欢迎广大朋友指正 Content 0 序 1 编译gcov gcov dump 2 额外的话 3 gcov dum
  • vue项目打包部署-手把手教程

    vue项目打包部署 1 购买服务器 可选阿里云 腾讯云 华为云 等等 购买时选择镜像 我们这里以CentOS为例 2 配置服务器 2 1 安装FinalShell 需要本地使用一些软件来操作服务器 例如 FinalShell Xshell
  • npm遇到的各种坑(errno)及解决办法

    建议从底下向上来看 9 8 npm警告可选跳过可选依赖 fsevents 1 2 7 node modules fsevents npm WARN notsup跳过可选依赖项 Unsupported platform fo fsevents
  • JAVA Swing

    Swing简介 Swing 是 Java 为图形界面应用开发提供的一组工具包 是 Java 基础类的一部分 Swing 包含了构建图形界面 GUI 的各种组件 如 窗口 标签 按钮 文本框等 Swing 提供了许多比 AWT 更好的屏幕显示
  • 制造业MES&R语言数据挖掘之设备故障序列关联分析

    本案例针对于铅酸电池行业的设备故障进行R语言数据挖掘 找到故障之间的时间先后规律 比如A故障发生后 多大概率会发生B故障 目录 一 数据准备 二 选择算法 三 编程建模 四 分析 五 评价 步骤 1 数据准备 2 选择算法 3 编程建模 4
  • 什么是梯形凸块布线,什么是10度走线,什么是zig-zag走线,什么是任意角度走线? 为什么要走梯形凸块布线,为什么要走10度走线,为什么要走zig-zag走线,为什么要走任意角度走线?

    来自群友的疑难杂症 加杨老师V信 PCB206 可入群 请教下各位老师 你们有见过以下面这样的走线方式吗 科普下有什么作用 杨老师简单阐述下 虽然绝大部分人暂时还没用到 说不定将来也不会用到 但是对于了解这些走线方式还是有帮助的 梯形凸块布
  • 不同图像的噪声,选用什么滤波器去噪,图像处理的噪声和处理方法

    不同图像的噪声 选用什么滤波器去噪 图像处理的噪声和处理方法 提示 据说是科大讯飞的算法面试题 知道哪些噪声 分别用什么滤波器处理 文章目录 不同图像的噪声 选用什么滤波器去噪 图像处理的噪声和处理方法 TOC 文章目录 图像噪声 椒盐噪声
  • Java深拷贝浅拷贝终极总结

    目录 万物之始 大道至简 衍化至繁 道德经 概念 浅拷贝 结果分析 深拷贝 万物之始 大道至简 衍化至繁 道德经 概念 浅拷贝是拷贝了源对象的地址 任何源对象的任何值发生改变时 拷贝对象的值也会发生改变 深拷贝只是拷贝了源对象的所有值而不是
  • 微信小程序——开篇

    开篇 前言 锻造兵器 开发者账号 微信开发者工具 写在最后 前言 如今微信小程序已经成为我们日常生活中不可或缺的 介质 如我们的出行 购物 餐饮 社交 娱乐等活动的小程序已经因有尽有 相比于去安装一个app人们自然更加倾向于在微信中去直接访
  • 【qt】信号,使用自定义的结构作为参数传递

    错误 解决办法 方法1 注册改向量 1 注册位置 在第一次使用此类链接跨线程的signal slot之前 一般在当前类的构造函数中进行注册 2 注册方法 在当前类的顶部包含 include
  • ES6(一):let和const、模板字符串、函数默认值、剩余参数、扩展运算符、箭头函数

    ES6 一 一 let和const声明变量 1 变量不能重复声明 2 块儿级作用域 3 不存在变量提升 4 const声明变量 5 优点 1 for循环中用let比较奶思 2 for循环分为父作用域和子作用域 3 不会污染全局变量 二 模板