Web前端系列技术之JavaScript进阶(从ES6开始)①

ES6 基础学习一

在之前的内容学习中,已经学会了基础的 DOM操作BOM操作 ,同时也认识了 对象函数事件 的基本概念和用法,有兴趣的小伙伴可以❤️ 订阅APIs专栏免费学习 ❤️哟;那么接下来就是会结合目前市面上最流行的 ES6 对这些 基础的JS 进行更细化的学习;


说到 ES6 ,我想大家应该都有所耳闻;其实,它就是在2015年6月份迎来的 ECMAScript标准的第六版 的简称,之前的 ES5 都已经可以追溯到2009年了。厚积薄发的ES6可以称得上是内容变化最大的一个版本,它提供了许多的新语法和新特性,在很大程度上帮助开发者们处理更为复杂的业务逻辑,并且还能有效的 增强代码的可读性减少代码的错误率;那么接下来本专栏 ES6基础学习 主要介绍的就是耳熟能详的 Promise箭头函数模板字符串 、以及 let解构,当然也包括陌生的 代理反射迭代器生成器 等,欢迎大家订阅关注啦❤️❤️

那么今天主要会学习的内容是:
一、let和const
二、作用域
三、闭包
四、预解析
五、参数
六、箭头函数


文章目录

  • ES6 基础学习一
  • 一、let和const
    • 1. let
    • 2. const
  • 二、作用域
    • 1. 全局作用域
    • 2. 函数作用域
    • 3. 块作用域
    • 4. 作用域链
  • 三、闭包
  • 四、预解析
    • 1. 变量提升
    • 2. 函数提升
  • 五、参数
    • 1. 形参 / 实参
    • 2. 动态参数
    • 3. 剩余参数
  • 六、箭头函数
  • 总结


一、let和const

ES6 之前的版本中,用于声明变量的关键字只有var,并且没有块作用域,只有函数作用域和全局作用域,但是在 ES6 中由于提出了letconst,就彻底改变了以往的局面;它们不仅可以用于声明变量,还可以将变量绑定到当前所处在的任意作用域中,如此一来就形成了块作用域。因此,可简单的了解到letconst这两关键字与var大体的区别:

  1. 不允许声明提升;
  2. 不允许重复声明;
  3. 不覆盖全局变量;

当然,这里也会细讲letconst

1. let

其实,对于let可以理解为是var的升级版本,摒弃和纠正了var的一些导致代码混乱的特性,从而使代码的逻辑更清晰,可维护性更高;

特性一:其无法实现变量提升;

代码样例:

{
  let inner = true
  console.log(inner)  //true
}
console.log(inner)  //抛出未定义的引用错误

特性二:存在临时死区;

临时死区:也叫暂时性死区,英文简写为 TDZ ;用letconst声明的变量,在声明之前是都会被放到 TDZ 中的,如果对于这些变量未赋值便访问的话,就会产生运行错误;

特性三:不允许重复声明;
当然,这里有一个前置条件:就是只有在相同的作用域时,才不允许同一个变量的重复声明;,这完全是因为 ES6 引入了一个重复声明的检查机制,促使着避免项目开发的过程中存在的同名变量影响代码逻辑的情况;

特性四:不覆盖全局变量;
在使用var在全局作用域中声明变量的时候,该变量不但会成为全局变量,而且还会成为全局对象,而在ES6中,针对这种情况影响函数封装导致不利于代码模块化的问题,规定了使用let可将全局变量和全局对象断开练习;

代码样例:

var global = true
var Math = true
console.log(window.global)// true
console.log(window.Math)// true

let whole = true
let Math = true
console.log(window.whole)//undefined
console.log(window.Math)//Math对象

2. const

对于const来说,它不但具有let所具有的四大特性,它自己本身也有一个很重要的特性:声明常量

不过,此处需要强调的一点就是,const限制的其实是变量和内存地址之间的绑定,也就是说:

  1. 如果是基本类型(如布尔值、数字等)的变量,那么对应的内存地址中保存的就是值,不可更改
  2. 如果是引用类型(如对象)的变量,那么对应的内存地址中保存的就是指向实际数据的一个指针,可以修改其内部的属性或方法

⭐注意:const在声明时必须初始化(即赋值),并且在设定后,其值无法再改变;


二、作用域

基础概念: 作用域(scope)规定了变量能够被访问的 “范围” ,离开了这个 “范围” ,变量便不能被访问了,作用域通常也被分为 全局作用域局部作用域

其中,局部作用域 又分为 函数作用域块作用域,具体如图所示:

Web前端系列技术之JavaScript进阶(从ES6开始)①_第1张图片

而这里学习作用域的主要目的是:了解作用域对程序执行的影响及作用域链的查找机制

1. 全局作用域

基础概念: 当打开浏览器加载 HTML 页面的时候,浏览器首先会提供一个供全局JS代码执行的环境 这个环境就被称为 全局作用域,也就是说script 标签和 .js 文件的【最外层】就是所谓的全局作用域;

代码样例:

<script>
  // 此处是全局
  function sayHi() {
    // 此处为局部
  }
  // 此处为全局
script>

说明:

  1. 在全局作用域中声明的变量在函数内部也可以被访问;
  2. 尽可能少的声明全局变量,防止全局变量被污染;
  3. window 对象动态添加的属性默认也是全局的(了解即可,不推荐操作);
  4. 函数中未使用任何关键字声明的变量为全局变量(了解即可,不推荐操作);

2. 函数作用域

特点: 函数每次执行都会开辟新的内存空间,当函数执行完毕之后,就会关闭这块空间,并且里面的一些变量都会销毁;所以,在函数内部声明的变量只能在函数内部被访问,外部无法直接访问;

代码样例:

<script>
  // 声明 counter 函数
  function counter(x, y) {
    // 局部作用域
    // 函数内部声明的变量,为局部变量
    let s = x + y;
    console.log(s); // 18
  }
  // 调用 counter 函数
  counter(10, 8);
  // 访问变量 s
  console.log(s); // 报错
script>

说明:

  1. 函数内部声明的变量,在函数外部无法被访问;
  2. 函数的参数也是函数内部的局部变量;
  3. 不同函数内部声明的变量无法互相访问;
  4. 函数执行完毕后,函数内部的变量实际被清空了;

3. 块作用域

这是在 ES6 中新加入的一个概念,常被理解为是在 JavaScript 中使用 {} 包裹范围区域,这其中的代码也被称为 代码块代码块内部声明的变量外部将【有可能】无法被访问;

⭐注意:这里标注的【有可能】是因为:在使用var声明变量时,是会被提升为全局变量的,而使用let声明变量时才具有块级作用域的特性;

代码样例:

<script>
  {
    // age 只能在该代码块中被访问
    let age = 18;
    console.log(age); // 正常
  }
  // 超出了 age 的作用域
  console.log(age); // 报错
script>

当然,JavaScript 中除了 变量 外还有 常量 ,常量与变量本质的区别是 【常量必须要有值且不允许被重新赋值】 ,常量值为对象时其属性和方法允许重新赋值,这里常用到声明常量的关键词是:const

代码样例:

<script>
  // 声明常量,必须要有值
  const version = '1.0.0';

  // 不能重新赋值
  version = '1.0.1';//报错
  // 常量值为对象类型
  const user = {
    name: '小明',
    age: 18
  }
  // 不能重新赋值
  user = {};
  // 属性和方法允许被修改
  user.name = '小小明';
  user.gender = '男';
script>

说明:

  1. let 声明的变量会产生块作用域,var 不会产生块作用域;
  2. varlet都可以只定义不赋值【拓展:对象为空 是返回null变量为空 返回undefined
  3. const 声明的常量也会产生块作用域;
  4. const定义的常量不允许更改值;
  5. 不同代码块之间的变量无法互相访问;
  6. 推荐使用 letconst,不推荐使用var

⭐注意:开发中 letconst 经常不加区分的使用,如果担心某个值会不小被修改时,则只能使用 const 声明成常量

4. 作用域链

基础概念: 本质上是底层的变量查找机制,在函数被执行时,会优先查找当前函数作用域中所要查找的变量,如果当前作用域查找不到则会依次逐级查找 父级作用域 直到全局作用域;

代码样例:

<script>
  // 全局作用域
  let a = 1;
  let b = 2;
  // 局部作用域
  function f() {
    let c;
    // 局部作用域
    function g() {
      let d = 'yo';
    }
  }
script>

由代码可知:函数内部允许创建新的函数,f 函数内部创建的新函数 g,会产生新的函数作用域,至此函数作用域产生了 嵌套的关系 ;这种由父子关系的作用域关联在一起形成了链状的结构,便是作用域链这个名字的由来;

说明:

  1. 嵌套关系 的作用域串联起来形成了作用域链;
  2. 相同作用域链中按着从小到大的规则查找变量;
  3. 子作用域能够访问父作用域,父级作用域无法访问子级作用域(就近原则);

⭐注意:作用域链的主要作用就是提供查找变量的机制;


三、闭包

基础概念: 闭包是一种比较特殊的 函数,使用闭包能够访问函数作用域中的变量;例如递归函数就是闭包;

简单理解:闭包就是一个作用域能够有权访问另外一个作用域的局部变量;

从代码形式上理解闭包,其实就是子函数能够访问父函数的变量,并且返回子函数

代码样例:

<script>
  function foo() {
    let i = 0;
    // 函数内部分函数
    function bar() {
      console.log(++i);
    }
    // 将函数做为返回值
    return bar;
  }
  // fn 即为闭包函数
  let fn = foo();
  
  fn(); // 1
script>

说明:

  1. 闭包本质仍是函数,只不过不是从函数内部返回的;
  2. 闭包能够创建外部可访问的隔离作用域,避免全局变量污染;
  3. 主要缺点就是过度使用闭包可能造成 内存泄漏
  4. 主要优势就是能够使变量的作用范围得到延伸

⭐注意:回调函数也能访问函数内部的局部变量


四、预解析

基础概念: 在代码执行之前,先要解析整个代码,提取变量或者函数之后再执行代码,它可以不用遵循先定义后执行的规则

通常,任何一个作用域在执行代码之前都需要先进行预解析这个步骤;在预解析的过程中,通常也会由于变量和函数的存在,被分为两类预解析过程:变量提升函数提升

1. 变量提升

基础概念: 变量提升其实是 JavaScript 中比较【奇怪】的现象,它是允许在变量声明之前被访问的,也可被称为【变量预解析】;一般情况下仅适用于var声明的变量;

具体过程:

  1. 变量解析,变量提升;
  2. JS会把声明的变量,提升到当前作用域的最前面,只声明不赋值

代码样例:

<script>
  // 访问变量 str
  console.log(str + ' world!');//返回值为:undefined world!

  // 声明变量 str
  var str = 'hello ';
script>

说明:

  1. 变量在未声明即被访问时会报语法错误;
  2. 变量在声明之前即被访问,变量的值为 undefined
  3. let 声明的变量不存在变量提升,推荐使用 let【也有认为具有提升但是不赋值不能使用】;
  4. 变量提升出现在相同作用域当中;
  5. 在实际开发中还是推荐 先声明再访问变量

⭐注意:之所以认为let具有提升但是不赋值不能使用的原因是什么?

  • let 声明的变量在没有初始化之前不能使用,因为此时的变量处于一个 死区 ,不可被使用,只有被赋值之后才能被使用;

2. 函数提升

基础概念: 函数提升与变量提升比较类似,是指函数在声明之前即可被调用,通常也被称为【函数预解析】;

代码样例:

<script>
  // 调用函数
  foo();
  
  // 声明函数
  function foo() {
    console.log('声明之前即被调用...');
  }
  
  // 不存在提升现象
  bar();
  var bar = function () {
    console.log('函数表达式不存在提升现象...');
  }
script>

说明:

  1. 函数提升能够使函数的声明调用更灵活;
  2. 函数表达式不存在提升的现象;
  3. 函数提升出现在相同作用域当中;

五、参数

参数通常被使用在函数当中,在函数参数的使用细节里,能够提升函数应用的灵活度,这里主要也含有 形参实参动态参数 、以及 剩余参数 的概念;

1. 形参 / 实参

首先对于 形参实参 我想小伙伴应该都不陌生,形参通常在函数定义的时候使用,而实参往往都在函数调用的时候使用;

对于形参而言,也相当于局部变量,通常在没有接受实参的情况下,形参默认是undefined,但是形参是可以存在默认值的;

代码样例:

//这里的形参被赋了默认值
function getSum (num1 = 0, num2 = 0) {
     let he = num1 + num2;
     console.log(he);
}
//实参
getSum(6, 3);

说明:

  1. 声明函数时为形参赋值即为参数的默认值;
  2. 如果参数未自定义默认值时,参数的默认值为 undefined
  3. 调用函数时没有传入对应实参时,参数的默认值被当做实参传入;

2. 动态参数

基础概念: 在函数传递参数时,实参的个数不固定的情况下,这种参数就被称为动态参数;

那么面对参数无法固定的情况,该如何实现参数的传递呢?

这里就需要用到函数内部的一个 特殊对象 ——【arguments】,它是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参;

代码样例:

<script>
  // 求和函数,计算所有参数的和
  function sum() {
    // console.log(arguments);
    let s = 0;
    for(let i = 0; i < arguments.length; i++) {
      s += arguments[i];
    }
    console.log(s);
  }

  // 调用求和函数
  sum(5, 10); // 两个参数
  sum(1, 2, 4); // 两个参数
script>

说明:

  1. arguments 是一个伪数组;
  2. arguments 的作用是动态获取函数的实参;
  3. 通常情况下,如果参数固定用形参,参数不固定用arguments

⭐注意:在传参的过程中,如果有不想传的参数,可以直接用undefined的替代即可;

3. 剩余参数

基础概念: 剩余参数也叫做 rest参数 ,通常的写法是...

代码样例:

<script>
  function config(baseURL, ...other) {
    console.log(baseURL);
    // other 是真数组,动态获取实参
    console.log(other);
  }

  // 调用函数
  config('http://baidu.com', 'get', 'json');
script>

说明:

  1. ... 是语法符号,置于最末函数形参之前,用于获取多余的实参;
  2. 借助 ... 获取的剩余实参;

六、箭头函数

基础概念: 箭头函数是一种声明函数的简洁语法,它与普通函数并无本质的区别,差异性更多体现在语法格式上;

基础语法结构:

//箭头函数的定义
let 变量名 = ( n个参数 ) => { 函数体 }
//调用
变量名()

场景的应用场景:涉及到 回调函数 ,基本上都可以使用箭头函数;例如定时器、遍历数组的forEach()

当然,箭头函数也有其他形式的写法,遇见了得需要认识:

  1. 写法一:箭头函数只有一个参数时可以省略圆括号 (),代码样式如下:
let 变量名 = 一个参数 => { 函数体 }
  1. 写法二:箭头函数函数体 只有一行代码 时可以省略花括号 {},并自动做为返回值被返回,代码样式如下:
let 变量名 = 一个参数 => 一行代码的函数体 

说明:

  1. 箭头函数属于表达式函数,因此不存在函数提升,主要原因是由于表达式函数不会被预解析,它预解析的是变量;
  2. 箭头函数this指向上级作用域的this,所有如果函数中用到this,不建议使用箭头函数;要知道,函数中的this指向调用者(事件源),默认的functionwindow调用的;
  3. 箭头函数中没有 arguments,只能使用 ... 动态获取实参;

总结

今天是学习 ES6 的第一天,内容还是挺多的,使用练习和理解极为重要,今天所总结出来的所有知识,希望对大家有用,同时也希望这篇文章可以有一个好的展现量和得到更多人的支持,谢谢每一位浏览文章的人,要相信小柴码文,必是好文,欢迎各位 点赞+收藏+关注 啦! ! !


有兴趣的小伙伴们可以回顾之前所学习的Web APIs基础学习的内容,后续即将更新前端开发的学习目标。感谢关注和支持,让我们一起成长!

有兴趣可回顾一下JavaScript基础学习的文章内容,再结合之前所介绍的CSS基础学习以及HTML基础学习,大脑里的内容会更加丰富而充实的,毕竟综合性复习和学习是更会加深印象的哟!!!

你可能感兴趣的:(ES6基础学习,javascript,前端,es6,开发语言,web)