es6 笔记(1)

306 阅读12分钟

「这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战」。

ECMAScript2015

摘要

1.新的标准规范

ECMAScript2015 是 js 的一种的新的标准规范,就是对 js 的写法上提出了新的语法要求和写法格式。

2.ECMAScript 和 js 关系

ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。javascript 是 netscape 创 造的并交给了国际标准化组织 ECMA,之所以不叫做 JavaScript 由于商标的问题,java 是 sun 公司的商标,根据 授权协议只有 Netscape 公司可以合法使用 JavaScript 这个名字,另外就是为了体现 JavaScript 的标准的制定者 不是 ECMA 所以取名为 ECMAScript。

3.ES6 与 ECMAScript 2015 的关系

ES6 是 ECMA 的为 JavaScript 制定的第 6 个版本的标准,标准委员会最终决定,标准在每年的 6 月份正式发布一 次,作为当年的正式版本。ECMAscript 2015 是在 2015 年 6 月份发布的 ES6 的第一个版本。依次类推 ECMAscript 2016 是 ES6 的第二个版本、 ECMAscript 2017 是 ES6 的第三个版本……

块级作用域

1.块级作用域的种类

ECMAScript2015 为 js 提出的第三个作用域,凡是带{}的都是一个块级作用域。

if 语句的{},for 循环中的{},while 中的{},或者是我们单独写的{} try{}catch(error){}这些都提供了块级作用域。

块级作用域分析

1.为什么需要块级作用域?
  • 内层变量会覆盖外层变量

    var lagou = "拉勾";
    function fn() {
      console.log(lagou); //undefined
      if (false) {
        var lagou = "hello";
      }
    }
    fn();
    

这是因为 fn 函数体内以及有 var 声明的变量 lagou,只是还没有赋值,默认为 undefined。

  • 用来计数的循环变量泄露为全局变量

    for (var i = 0; i < 10; i++) {}
    // 10
    console.log(i);
    
2.块级作用域的成员

块级作用域内的成员需要使用 let 或 const 命令定义的变量。

var lagou = "拉勾";
function fn() {
  console.log(lagou); //拉勾
  if (true) {
    let lagou = "hello";
  }
}
fn();

let const

1.let

基本用法

ECMAScript2015 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

let 主要声明块级作用域下的成员,这个成员变量的作用域范围只能在当前块级作用域下。

  • wer
  • er

2.const

const 声明变量的同时必须要赋值。

const 声明之后,不允许去修改它的值,这里面的值说的是不允许修改它,是声明之后不允许重新指向一个新

的内存地址,可以去修改内存地址中的属性成员。

数组

1.数组的解构

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

  • 完全解构 将数组中的每一个值都对应上相应的变量。

    var arr = ["lagou", "edu", "web"];
    let [com, ind, work] = arr;
    console.log(work); //web
    
  • 不完全解构 数组中的部分值对应上了相应的变量。

    var arr = ["lagou", "edu", "web"];
    let [, , work] = arr;
    console.log(work); //web
    

    注意:模式没有匹配上的可以不填,但是必须要加逗号隔开。

  • 扩展运算符...

    • 展开运算符说明

      • 三个点(…)是一个展开运算符,其功能为对三个点后面的变量进行展开操作

      • 三个点(…)展开运算符:只能对具有 Iterator 接口的对象进行展开操作

      • 使用场景

        ![image-20200508180111209](imgs/image-20200508180111209.png)
        
        案例一
        
        ```js
        

        var arr=["拉勾","edu","web"]; fn(...arr)//web 案例二js let a = [88,99,100]; let b = [101,102,103]; a.push(...b); console.log(a);//[88, 99, 100, 101, 102, 103] 案例三js let d={ e:"拉钩教育", f:"www.lagou.com" } let obj={ g:"web", h:1100, ...d }; console.log(obj); {g: "web", h: 1100, e: "拉钩教育", f: "www.lagou.com"} 案例四js var st="拉钩 edu"; var arr=[...st]; console.log(arr)//["拉", "钩", "e", "d", "u"] 掌握了展开运算符的使用规则,我们看一下数组的特殊解构。js let [a,...b] = [1,2,3,4,5,6]; console.log(a)//1 console.log(b)//[2,3,4,5,6]

  • 解构不成功

    右边的变量的个数超过了等号左边中数组的元素

    let [a, b, c] = [12];
    console.log(b); //undefined
    

    如果解构没有成功,则变量的值是 undefined,如果是展开运算的变量则是空数组。

    let [a, b, ...c] = [12];
    console.log(c); //[]
    

2.数组的扩展

  • 扩展运算符...

    • 扩展运算符(spread)是三个点(...)它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。

    • 替代 apply()的使用技巧

      我们之前在求一个数组中的最大值得时候采用得方式是 Math.max.apply(null,[12,34,56,43]) ==56

      var max = Math.max.apply(null, [12, 34, 56, 43]);
      console.log(max); //56
      var max2 = Math.max(...[12, 34, 56, 43]);
      console.log(max2); //56
      
  • Array 类的扩展方法

    • Array.from()

    Array.from 方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)

    var arraylike = {
      0: "lagou",
      1: "edu",
      2: "web",
      length: 3,
    };
    var arr = Array.from(arraylike);
    ["lagou", "edu", "web"];
    

    Array.from 还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组

    var arr = Array.from([1, 2, 3], function (x) {
      return x * x;
    });
    console.log(arr); //[1,4,9]
    
    • Array.of 方法用于将一组值,转换为数组,这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。

      var arr = Array(3); //[emptyx3]
      

      这里面 3 表示数组中元素的长度。

      var arr = Array(2, 3, 4); //[2,3,4]
      

      Array()里的参数个数大于 1 的时候,表示的是数组元素。

      Array.of()方法不管里面参数的个数多少,都将其转为数组的元素。

      var arr = Array.of(3);
      console.log(arr); //[3]
      

对象

1.对象中有关变量的解构赋值

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

let { name, work } = { name: "lagou", work: "web" };
console.log(name, work); //lagou web

2.对象的扩展

  • 对象的简写

    • 当变量名和属性名同名式,省略同名的属性值

      const foo = "bar";
      const baz = { foo };
      // 等同于
      const baz = { foo: foo };
      
  • 省略方法中的 function

    const obj = {
      method() {
        return "拉勾!";
      },
    };
    // 等同于
    const obj = {
      method: function () {
        return "拉勾!";
      },
    };
    
    • 属性的赋值器(setter)和取值器(getter)

      const lagou = {
        name: "拉勾",
        get com() {
          return this.name;
        },
        set work(value) {
          this.name = this.name + value;
        },
      };
      console.log(lagou.com); //拉勾
      lagou.work = "招聘";
      console.log(lagou.name); //拉勾招聘
      
    • 属性名表达式

      es5 中定义对象的属性有两种方法,一种是用标识符做属性,一种是用表达式做属性

      方法一;
      obj.name = "拉勾";
      // 方法二
      obj["name"] = "拉勾";
      
      var lagou = {
        name: "拉勾",
      };
      

      如果使用大括号定义对象,那么在 es5 中只能使用标识符定义属性。

      var lagou = {
        name: "拉勾",
      };
      

      但是 ECMAScript2015 在使用大括号定义对象的时候,允许使用表达式定义属性,把表达式放在方括号中。

      let name = "lagou";
      const lagou = {
        [name]: "web",
      };
      console.log(lagou); //{lagou: "web"}
      

3.三点运算在对象中的用途

  • 用于对象的解构

    • 对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。

      let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
      console.log(z); //{a: 3, b: 4}
      

      上面代码中,变量z是解构赋值所在的对象。它获取等号右边的所有尚未读取的键(ab),将它们连同值一起拷贝过来。

      注意:1.解构赋值必须是最后一个参数。2.解构赋值的拷贝是浅拷贝。
      
  • 用于扩展运算

    • 对象的扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。

      let z = { name: "lagou", work: "web" };
      let n = { ...z };
      n; // { name: "lagou", work: "web" }
      

字符串

1.字符串模板

  • 传统的字符串里不能使用换行符,必须使用转义符\n 替代,字符串模板里可以使用。模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

  • 模板字符串中嵌入变量,需要将变量名写在${}之中。大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性

    var name = "拉勾";
    var st = `欢迎来到${name}`;
    console.log(st);
    

2.标签模板

  • 模板字符串的功能,不仅仅是上面这些。它可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能。

    console.log`hello`;
    等同于;
    console.log(["hello"]);
    

    标签模板其实不是模板,而是函数调用的一种特殊形式。“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数。

    注意:如果模板字符里面有变量,就不是简单的调用了,而是会将模板字符串先处理成多个参数,再调用函数。

    var name="laogou";
    var work="web"
    function tag(st,a,b){
        console.log(st);
        console.log(a);
        console.log(b)
        return "hello lagou";
    }
    var st=tag`hello${name},职业${work}开发`
    console.log(st)//hello lagou
    ["hello", ",职业", "开发", raw: Array(3)]
    laogou
    web
    

    函数内的返回值,就是tag函数处理模板字符串后的返回值。

    var name = "laogou";
    var work = "web";
    function tag(st, a, b) {
      console.log(st);
      console.log(a);
      console.log(b);
      return "hello lagou"; //如果没有返回值,则默认是undefined
    }
    var st = tag`hello${name},职业${work}开发`;
    console.log(st); //hello lagou
    

3.扩展的方法

  • 字符串实例的方法

    • includes()

      返回布尔值,表示是否找到了参数字符串

      var st = "lagou web";
      var b = st.includes("web");
      console.log(b); //true
      
    • startsWith()

      返回布尔值,表示参数字符串是否在原字符串的头部

      var st = "lagou web";
      var b = st.startsWith("la");
      console.log(b); //true
      
    • endsWith()

      返回布尔值,表示参数字符串是否在原字符串的尾部

      var st = "lagou web";
      var b = st.endsWith("web");
      console.log(b); //true
      

函数

参数默认值

  • ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

    function fn(a, b = " lagou ") {
      console.log(a + b);
    }
    fn("hello"); //hello lagou
    
  • 注意:

    • 参数变量是默认声明的,所以不能用letconst再次声明.
    • 使用参数默认值时,函数不能有同名参数
  • 参数默认值的位置

    • 通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。

      function f(x = 1, y) {
        return [x, y];
      }
      
      f() // [1, undefined]
      f(2) // [2, undefined]
      f(, 1) // 报错
      

rest 参数

  • ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

    function add(...values) {
      console.log(values);
    }
    add(2, 5, 3); // [2, 5, 3]
    
  • rest 参数和函数中的参数解构有什么区别

    • rest 参数是发生在函数的定义阶段,函数的额参数解构是发生在函数的调用阶段
    • 二者是一种互为逆运算
function add(...values) {
  //这是rest参数
  console.log(values);
}
add(2, 5, 3); // [2, 5, 3]
var arr = [1, 2, 3];
function fn(a, b, c) {
  console.log(a + b + c);
}
fn(...arr); //6  这是参数的解构

箭头函数

  • ES6 允许使用“箭头”(=>)定义函数。
var f = (v) => v;
// 等同于
var f = function (v) {
  return v;
};

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

var f = () => 5;
// 等同于
var f = function () {
  return 5;
};

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function (num1, num2) {
  return num1 + num2;
};

如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

var sum = (num1, num2) => {
  return num1 + num2;
};

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

let getItem = id => { id: id, name: "Temp" }; //报错

let getItem = id => ({ id: id, name: "Temp" });//不报错
  • 箭头函数有几个使用注意点
    • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。箭头函数外面的 this 是什么,箭头函数内的 this 还是什么。
    • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
    • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
var name="web"
var obj={
    name:"lagou",
    fn(){
        var t=setTimeout(function(){
            console.log(this.name)//web  this是window
        },1000)
    }
}
obj.fn()
-----------------------------------
var name="web"
var obj={
    name:"lagou",
    fn(){
        var t=setTimeout(()=>{
            console.log(this.name)//lagou this是obj
        },1000)
    }
}
obj.fn()

Object

Object.assign()

  • Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

    const target = {
      a: 123,
      b: 123,
    };
    const sourcel = {
      a: 456,
      c: 456,
    };
    const result = Object.assign(target, sourcel);
    console.log(target); //{a: 456, b: 123, c: 456}
    console.log(target === result); //true
    

    如果目标对象与源对象有同名属性,则后面的属性会覆盖前面的属性。且assign()的返回值就是第一个对象。

    如果有多个源对象有同名属性,依然是后面的会覆盖前面的属性

    const target = { a: 1, b: 1 };
    const source1 = { b: 2, c: 2 };
    const source2 = { c: 3 };
    Object.assign(target, source1, source2);
    target; // {a:1, b:2, c:3}
    
  • 利用Object.assign()复制一个对象,且其中一个对象的修改不会影响到另一个对象

    const sourcel = {
      a: 123,
    };
    var obj = Object.assign({}, sourcel);
    obj.a = 456;
    console.log(obj); //{a: 456}
    console.log(sourcel); //{a: 123}
    

Object.is()

  • Object.is就是用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

    ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。

    console.log(Object.is(+0, -0)); //false
    console.log(+0 === -0); //true
    console.log(Object.is(NaN, NaN)); //true
    console.log(NaN === NaN); //false
    

Proxy

概述

Proxy 可以理解成一个快递员,我们发快递还是接受快递,都需要这个快递员充当一个代理的作用。ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例,这个实例就是一个代理对象(快递员)。

var proxy = new Proxy(target, handler);

目标对象

这个代理对象有两个参数,一个是代理的目标对象,第二个也是一个对象,它是配置对象,用来定制代理的拦截行为。

const person = {
  name: "zce",
  age: 20,
};
const personProxy = new Proxy(person, {
  get(target, property) {
    console.log(target, property); //person{name:"zce",age:20}
    return 100;
  },
  set() {},
});

配置对象

  • 配置对象中一般有两个方法getset,get是用来拦截对目标对象属性的访问请求。get方法中有两个参数,第一个参数是目标对象,第二个参数是访问的那个属性。

    const person = {
      name: "zce",
      age: 20,
    };
    const personProxy = new Proxy(person, {
      get(target, property) {
        console.log(target, property);
        return 100;
      },
      set() {},
    });
    
    console.log(personProxy.name); //100
    

注意,这个get方法的返回值就是我们获取的这个属性的返回值。

  • 这个get方法中有三个参数,一个是代理的目标对象,一个是代理的处理对象,第三个参数是 proxy 实例本身,且第三个参数是可选参数。

    const person = {
      name: "zce",
      age: 20,
    };
    const personProxy = new Proxy(person, {
      get(target, property, o) {
        console.log(o); //proxy{name:"zec",age:20}
        return property in target ? target[property] : undefined;
      },
      set() {},
    });
    
    console.log(personProxy.age); //20
    
  • 这个set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选

    const person = {
      name: "zce",
      age: 20,
    };
    const personProxy = new Proxy(person, {
      get(target, property, o) {
        return property in target ? target[property] : undefined;
      },
      set(obj, pro, value, o) {
        console.log(obj, pro, value, o);
      },
    });
    
    console.log((personProxy.name = "zhang"));
    //{name: "zce", age: 20} "name" "zhang" Proxy {name: "zce", age: 20}
    

    可以去设置一些属性或修改

    const person = {
      name: "zce",
      age: 20,
    };
    const personProxy = new Proxy(person, {
      get(target, property, o) {
        return property in target ? target[property] : undefined;
      },
      set(target, pro, value, o) {
        //可以做一些内部校验
        target[pro] = value;
      },
    });
    console.log((personProxy.name = "lagou"));
    person; //{name:"lagou",age:20}