ES6入门教程

225 阅读8分钟

第1章 声明方式

一.声明变量let

  1. 没有预解析,不存在变量提升
  2. 一定要先定义再使用。(在代码块内,只要用let定义变量,在let定义之前使用都会报错)
  3. 同一个作用域内不能重复定义变量
  4. for循环里面是父级作用域,里面又是一个

二.块级作用域

  • { //一个大括号包裹就是一个块级作用域 }
  • {{{let a = 12; }}}
  • if(){xxx}
  • for(){}
  • while(){}

三.声明常量const

  1. 定义好了不能改变
  2. const定义完变量必须有值,不能定义后再赋值,不能修改
  3. Object.freeze(对象);可以冻结对象,防止对象被修改
  4. 下面这是一个对象,对象是引用的关系可以被修改
      const config = {
          host:
          username:
          password:
          version:
      }
  • eg.引用不可改的模块 const http = require('http');

第2章 解构赋值

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

做数据交互是非常有用

一.数组解构

  1. 左右两边结构格式要保持一致 let [a,b,c] = [12,5,6];

二.对象解构

let {name,age,job} = {
    name:'Strive',
    age:18,
    job:'teacher'
}
  1. 解构可以设置别名 let {name:n,age:g,job:a} = jsonData;
  2. 解构时可以设置默认值 let [a,b,c="暂无数据"] = [1,2];
  • eg.获取模块的指定方法?
  • eg.交互变量的值
      let x = 1,y = 2;
      [x,y]=[y,x];
  • eg.从函数中返回多个值?
      function fn(){ return [1,2,3] }
      let [a,b,c] = fn();
  • eg.undefined和null的区别?
  • undefined相当于什么都没有,null相当于有值,但值为null。
      let [a,b="JSPang"]=['技术胖',undefined];
      let [c,d="JSPang"]=['技术胖',null];
      console.log(a,b,c,d);//技术胖 JSPang 技术胖 null

三.字符串解构

字符串也可以解构,因为此时字符串被转换成了一个类似数组的对象。

      const [a,b,c,d,e,f]="JSPang";
      console.log(a)//J

四.函数参数解构

[[1, 2], [3, 4]].map(([a, b]) => a + b);// [ 3, 7 ]

五.圆括号的使用

如果解构之前就定义了变量,这时再解构就会报错。

解决方法:在解构的{}外边加一个圆括号

      let foo;
      ({foo} ={foo:'JSPang'});
      console.log(foo); //JSPang

第3章 字符串的扩展

一.字符串模板

优点是可以随意换行,添加变量${变量名字}

字符串模板可以嵌套

二.标签模板

  • 模板字符串可以紧跟在一个函数名后面,该函数将被用来处理这个模板字符串。
	alert `hello`==>等同于alert(["hello"])
  • eg.多语言转换(国际化处理)
      i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!`
      // "欢迎访问xxx,您是第xxxx位访问者!"
  • eg.过滤 HTML 字符串,防止用户输入恶意内容
      let sender = '<script>alert("abc")</script>'; // 恶意代码
      let message = SaferHTML`<p>${sender} has sent you a message.</p>`;
      function SaferHTML(templateData) {
          let s = templateData[0];
          for (let i = 1; i < arguments.length; i++) {
              let arg = String(arguments[i]);
              //在替换中转义变量的特殊字符
              s += arg.replace(/&/g, "&amp;")
                              .replace(/</g, "&lt;")
                              .replace(/>/g, "&gt;");
              s += templateData[i];
          }
          return s;
      }
      console.log(message);
      //'<p>&lt;script&gt;alert("abc")&lt;/script&gt; has sent you a message.</p>'

三.字符串方法

  1. 字符串查找
    • str.indexOf(要找的字符) 返回索引位置,没找到返回-1
    • str.includes(要找的字符) 返回true/farse
    • eg.判断浏览器 if(navigator.userAgent.includes('Chrome')){}
  2. 字符串是否以谁开头
    • str.startWith(检测字符)
  3. 字符串是否以谁结尾
    • str.endsWith(检测字符)
  4. 重复字符串
    • str.repeat(重复次数)
  5. 填充字符串
    • str.padStart(整个字符串长度,填充字符) 往前填充字符串
    • str.padEnd(整个字符串长度,填充字符) 往后填充字符串
    • eg."x".padStart(5,"a") //"aaaax"
    • eg.str01.padStart(str01.length+str02.length,str02)
  6. 消除空格
    • str.trim() 消除左右空格
    • str.trimStart() 消除字符串头部空格
    • str.trimEnd() 消除字符串尾部的空格

第4章 函数的扩展

一.函数参数默认值

      function fn({x=0,y=0}={}){
          alert(x,y);
      }
      fn();
  1. 作用域
    • 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域。
    • 函数参数设置默认值就是已经定义了,不能再使用let,const声明
      function fn(a=10){
          let a = 101; //这是错误的 
      }
  1. 与解构赋值默认值结合使用
      function fn({x=0,y=0}){
          alert(x,y);
      }
      fn({x:1,y:2});
  1. 参数默认值的位置
    • 定义了默认值的参数应该是函数的尾参数。否则报错。
  2. length属性
    • 含义是该函数预期传入的参数个数。
    • 指定默认值的参数和rest参数不计入length中
    • (function (a, b, c = 5) {}).length // 2
    • (function(...args) {}).length // 0
    • 如果设置了默认值的参数不是尾参数,那length属性也不再计入后面的参数
    • (function (a, b = 1, c,d) {}).length // 1

二.rest参数

rest参数(形式为...变量名)用于获取函数的多余参数,这样就不需要使用arguments对象了。

  1. 用来展开数组
    • [1,2,3] ——> ...[1,2,3] ——> 1,2,3
    • 1,2,3 ——> ...1,2,3 ——> [1,2,3]
  2. 用来获取剩余参数:必须放到最后
    • [a,b,...c] = [1,2,3,4,5] //==>c的值为3,4,5
  • eg.复制数组
    • let arr2 = [...arr1];或者let [...arr2] = arr1;
    • let arr2 = Array.from(arr1);
  • eg.合并数组(都是浅拷贝)
    • let arr1=[1],arr2[2],arr3=[3,4,5];
    • let merge = [...arr1,...arr2,...arr3] //==>[1,2,3,4,5]
    • ES5:let merge = arr1.concat(arr2,arr3);
  • eg.用push()追加数组
    • let arr1=[1,2,3],arr2=[4,5];
    • arr1.push(...arr2); //==>arr1的值 [1, 2, 3, 4, 5]
    • ES5:Array.prototype.push.apply(arr1,arr2); //==>arr1的值 [1, 2, 3, 4, 5]
  • eg.替代apply():求数组中最大值
    • Math.max(...[14,3,5]);或者Math.max(14,3,5);
    • ES5:Math.max.apply(null,[14,3,5])
  • eg.将字符串转换成字符串数组
    • alert( [...'hello']); //==>["h","e","l","l","o"]
  • eg.只有函数调用时,...才可以放到圆括号中否则会报错
    • let a = (...[1,2]) //==>报错

三.name属性

name属性是返回该函数的函数名。

  • const bar = function baz() {}; bar.name //"baz"
  • var f = function(){}; f.name //"f"

四.箭头函数=>

es6允许使用箭头(=>)来定义函数

  1. 如果箭头函数只有一个参数时
    • 使用格式 一个参数 => 只有一条执行语句;
    • eg.var f = v => v; //等同于 var f = function(v){ return v };
  2. 如果箭头函数没有参数或者有多个参数时,就用一个圆括号来代表参数部分。
    • 使用格式 (无参数或多个参数) => 只有一条执行语句;
    • var f = () => 5; //等同于 var f = function(){ return 5 };
    • var sum = (n1,n2) => n1 + n2; 
      //等同于
      var sum = function(n1,n2){
          return n1 + n2;
      };
      
  3. 如果箭头函数的代码块有多条语句时,就用大括号将代码块括起来,并使用return语句返回。
    使用格式 (无参数或多个参数) =>{
        执行语句
        return返回值
    }
  • 由于大括号被解释为代码块,所以箭头函数直接返回一个对象时,外边必须使用圆括号否则报错。
    • eg.let getItem = id => ({id:"9412",name:"lining"});
    • eg.箭头函数与变量解构结合使用
        let full = ({first,last}) => first+"是名字,姓氏为"+last;
        //等同于
        let full = function(person){
            return person.first+"是名字,姓氏为"+person.last;
        }
  • eg.用箭头函数来简化回调函数
        [1,2,3].map(x => x*x); 
        //等同于
        [1,2,3].map(function(x){return x*x});
        var result = values.sort((a,b) => a-b);
        //等同于
        var result = values.sort(function(a,b){
            return a-b;
        })
  • 函数体内的this对象,定义函数所在对象,不再是运行时所在的对象。
  • 不可以当作构造函数,也就是说,不可以使用new命令否则报错。
  • 不可以使用arguments对象,该对象在箭头函数体内不存在。如果要用,可以用reset参数代替。
  • 不可以使用yield命令,因此箭头函数不能当作Generator函数。
  1. 函数参数的尾逗号 ES6允许函数的最后一个参数有尾逗号。
      function clownEverywhere(
          param1,
          param2,
      ){ /*....*/}
  1. Function.prototype.toString()
    • 方法是返回函数本身
  2. catch命令的参数可以省略
      try{
          /*...*/
      }catch(err){
          //处理错误
      }
      // 现在允许写成
      try {
          // ...
      } catch {
          // ...
      }

第5章 数组的扩展

一.数组各种循环

  1. arr.forEach() 代替普通for循环
    • 在forEach中使用return无效它还是会循环全部数组
    • arr.forEach(function(val,index,arr){ //... })
  2. arr.map() 映射(更新)做数据交互非常有用
    • 正常情况下,需要配合return返回一个新数组
    • 只有用map循环一定要有return
    • eg.拼接DOM
    • eg.重新整理数据结构[{title:'andy'}] ——> [{t:'他叫andy'}]
        let newArr = arr.map((item,index,arr)=>{
            let json = {};
            json.t = `他叫${item.title}`;
            return json;
        })
        alert(newArr); //==>{t:'他叫andy'}
  1. arr.filter() 过滤
    • eg.过滤掉不合格的元素,如果回调函数返回true,元素就留下来
        let newArr = [2,3,6].filter((item)=>{
            return item>2 && item<5;
        })
        alet(newArr); //==>3
  1. arr.some() 类似循环查找
    • eg.查找数组里某个元素符合条件就返回true
        let result = [1,5,6].some((item,index)=>{
            return item.toString().indexOf(5) > -1
        })
        alert(result); //==>true
  1. arr.every() 类似循环查找
    • eg.查找数组里所有元素符合条件就返回true
  2. arr.reduce() 归并方法 从左往右遍历
  3. arr.reduceRight() 从右往左遍历

二.数组方法

  1. Array.from() 把类数组对象转成数组
    • 这个东西具备length,就可以使用from
    let str = 'Strive';
    let arr = str.split('');//==> ["S", "t", "r", "i", "v", "e"]
    let arr = Array.from(str); //==> ["S", "t", "r", "i", "v", "e"]
  1. Array.of() 类似于...扩展运算符,把一组值转成数组
    • eg. let arr = Array.of('a','b','c');
    • eg. alert(arr); //==> ['a','b','c']
  2. arr.find() 查找
    • eg.找出第一个符合条件的元素,没找到返回undefined
        let res = [1,5,6].find((val,index,arr)=>{
            return val>5;
        })
        alert(res); //==>6
  1. arr.findIndex() 查找
    • 找出第一个符合条件的元素位置,没找到返回-1
  2. arr.fill(填充内容,【开始位置,结束位置】) 填充
  3. arr.indexOf()
  4. arr.includes()

三.数组的扩展运算符

  1. 扩展运算符(spread)是三个点(...)。将一个数组转为用逗号分隔的参数序列。

    主要用于函数调用。

    function add(x,y,z){ 
    return x + y + z;
    }
    const num = [1,2,3];
    add(...num);
  1. 扩展运算符后面还可以放置表达式。
    const arr = [
        ...(x > 0 ? ['a'] : []),
        'b',
    ];
  1. 如果扩展运算符后面是一个空数组,则不产生任何效果。
    • [...[], 1] // [1]
    • 只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。
  2. 替代函数apply()方法
    • Math.max.apply(null, [14, 3, 77])// ES5 的写法
    • Math.max(...[14, 3, 77])// ES6 的写法
    • Math.max(14, 3, 77);// 等同于
  3. 通过push函数,将一个数组添加到另一个数组的尾部。
    • var arr1 = [0, 1, 2]; var arr2 = [3, 4, 5]; // ES5的 写法
    • Array.prototype.push.apply(arr1, arr2); // ES6 的写法 arr1.push(...arr2);
  4. 复制数组
    • const a1 = [1, 2];
    • const a2 = [...a1];// 写法一
    • const [...a2] = a1;// 写法二
  5. 合并数组
    • const arr1 = ['a', 'b'];
    • const arr2 = ['c'];
    • const arr3 = ['d', 'e']; // ES5 的合并数组
    • arr1.concat(arr2, arr3);// [ 'a', 'b', 'c', 'd', 'e' ] // ES6 的合并数组
    • [...arr1, ...arr2, ...arr3]// [ 'a', 'b', 'c', 'd', 'e' ]
  6. 与解构赋值结合
    • 扩展运算符可以与解构赋值结合起来,用于生成数组。
    • let list = [1,2,3,4,5]
    • let [a, ...rest] = list
    • console.log(rest);//[ 2, 3, 4, 5 ]
    • 如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
  7. 字符串
    • 扩展运算符还可以将字符串转为真正的数组。
    • [...'hello']// [ "h", "e", "l", "l", "o" ]
  8. entries(),keys() 和 values()
    • 都是用于遍历数组,它们都返回一个遍历器对象。
    • keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
    for (let index of ['a', 'b'].keys()) {
      console.log(index);
    }
    // 0
    // 1			
    for (let elem of ['a', 'b'].values()) {
      console.log(elem);
    }
    // 'a'
    // 'b'			
    for (let [index, elem] of ['a', 'b'].entries()) {
      console.log(index, elem);
    }
    // 0 "a"
    // 1 "b"			

四.Array.prototype.sort()

参数可选,是用来规定排序的顺序,但必须是函数。

  1. 默认按照字母顺序排序
    • var arr = ['tom','ani','love']; arr.sort()//["ani", "love", "tom"]
  2. 升序排列
    function sortNum(a,b){ 
        return a-b
    }
    var arr = [12,323,1000,50]
    arr.sort(sortNum)//[12, 50, 323, 1000]
  1. 降序排列
    function sortNum(a,b){ 
        return b-a
    }
    var arr = [12,323,1000,50]
    arr.sort(sortNum)//[1000, 323, 50, 12]
  1. 按照数组对象中某个属性值进行排序
    var arr = [
        {name:'aaa',age:34},
        {name:'sss',age:30},
        {name:'ddd',age:60}
    ]
    function compare(prop){
        return function(a,b){
            var value1 = a[prop];
            var value2 = b[prop];
            return value1 - value2;
        }
    }
    arr.sort(compare('age'))
  1. 根据参数来确定是升序还是降序
    function sortBy(attr,rev){
        if(rev == undefined){
            rev = 1;
        }else{
        rev = (rev) ? 1 : -1;
        }
        return function(a,b){
            a =a[attr],b = b[attr];
            if(a<b){ return rev * -1; }
            if(a>b){ return rev * 1; }
            return 0;
        }
    }
    arr.sort(sortBy('age'),true)//升序

第6章 对象的扩展

一.对象简洁写法

      let name = 'andy';
      let json ={
          name:name,
          showA:function(){
              return this.name;
          }
      }
      简写方法如下:
      let newJson = {
          name,	//name:name属性名就是变量名,属性值就是变量值
          showA(){ //一般不要用箭头函数
              return this.name;
          }
      }
      alert( newJson.showA() );
  • 简写的对象方法不能用作构造函数(new),会报错.

二.属性名表达式

用来定义对象的属性

  1. 直接用标识符作为属性名 obj.tip = true;
  2. 用表达式作为属性名 obj['a'+'bc'] = 123;
    let propKey = 'foo';
    let obj = {
        [propKey]: true,
        ['a' + 'bc']: 123
    };

三.方法的name属性

  • 函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。
  • bind方法创建的函数,name属性返回bound+原函数名
  • Function构造函数创建的函数,name属性返回anonymous

四.属性的可枚举性和遍历

  1. 可枚举性
  • 对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。
  • Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。
  • 描述对象的enumerable属性,称为“可枚举性”。
  • 总的来说,操作中引入继承的属性会让问题复杂化,大多数时候,我们只关心对象自身的属性。所以尽量不要用for...in循环,而用Object.keys()代替。
  1. 属性的遍历
  • ES6 一共有 5 种方法可以遍历对象的属性。
  • for...in
    • for...in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。
  • Object.keys(obj)
    • Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)的键名。
  • Object.getOwnPropertyNames(obj)
    • Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
  • Object.getOwnPropertySymbols(obj)
    • Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。
  • Reflect.ownKeys(obj)
    • Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
  1. super关键字
  • 我们知道,this关键字总是指向函数所在的当前对象。
  • ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象。
  • super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。
  eg.报错,super用在属性里面
      const obj={
          foo:super.foo
      }
  eg.报错,super用在一个函数里面再赋值给foo属性
      const obj={
          foo:()=>super.foo
      }
      const obj={
          foo:function(){
              return super.foo
          }
      }
      eg.正确,super用在对象的方法中
          const obj={
              find(){
                  return super.foo
              }
          }
  • JavaScript引擎内部,super.foo等同于Object.getPrototypeOf(this).foo(属性)
  • 或Object.getPrototypeOf(this).foo.call(this)(方法)。

五.对象的扩展运算符

  1. 解构赋值
          eg.
              let {x,y, ...z} ={x:1, y:2, a:3, b:4};		
              console.log(x,y, z); //==>1 2 {a: 3, b: 4}
          eg.
              new Vuex.Store({
                  state,
                  mutation,
                  types,
                  actions
              })
  • 对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。
    • 解构赋值要求等号右边是一个对象,如果等号右边是undefined或null,就会报错,因为它们无法转为对象。
      • let { ...z } = null; // 运行时错误
      • let { ...z } = undefined; // 运行时错误
    • 解构赋值必须是最后一个参数,否则会报错。
      • let { ...x, y, z } = someObject; // 句法错误
      • let { x, ...y, ...z } = someObject; // 句法错误
    • 解构赋值的拷贝是浅拷贝。
            let obj = { a: { b: 1 } };
            let { ...x } = obj;
            obj.a.b = 2;
            x.a.b // 2
    
    • 扩展运算符的解构赋值,不能复制继承自原型对象的属性。
            const o = Object.create({ x: 1, y: 2 });
            o.z = 3;
            let { x, ...newObj } = o;
            let { y, z } = newObj;
            x // 1
            y // undefined
            z // 3
    
    • 变量x是单纯的解构赋值,变量y和z是扩展运算符的解构赋值,只能读取对象o自身的属性,所以变量z可以赋值成功,变量y取不到值。
    • 变量声明语句时,如果使用解构赋值,扩展运算符后必须是一个变量名,而不能是一个解构赋值表达式,否则报错。 let { x, ...{ y, z } } = o; // SyntaxError: ... must be followed by an identifier in declaration contexts
    • 解构赋值的一个用处,是扩展某个函数的参数,引入其他操作。
        function baseFunction({ a, b }) {
          // ...
        }
        function wrapperFunction({ x, y, ...restConfig }) {
          // 使用 x 和 y 参数进行操作
          // 其余参数传给原始函数
          return baseFunction(restConfig);
        }
  1. 扩展运算符...
    • 对象的扩展运算符用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
      • let z = { a: 3, b: 4 };
      • let n = { ...z };
      • n // { a: 3, b: 4 }
    • 数组是特殊的对象,所以对象的扩展运算符也可以用于数组。
      • let foo = { ...['a', 'b', 'c'] };
      • foo // {0: "a", 1: "b", 2: "c"}
    • 扩展运算符后面是一个空对象,则没有任何效果。
      • {...{}, a: 1} // { a: 1 }
    • 扩展运算符后面不是对象,则会自动将其转为对象。
      • {...1} //等同于 {...Object(1)} 结果为{}
      • 扩展运算符后是整数1,会自动转为数值的包装对象Number{1}。
      • 由于该对象没有自身属性,所以返回一个空对象。`
      • {...true} //等同于 {...Object(true)}结果为{}
      • {...undefined} //等同于 {...Object(undefined)}结果为{}
      • {...null} //等同于 {...Object(null)}结果为{}
      • {...'hello'} //{0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}
      • 扩展运算符后面是字符串,它会自动转成一个类似数组的对象

六.链判断运算符

  • 编程时如果读取对象内的某个属性,往往需要先判断该对象是否存在。
  • eg.要读取message.body.user.firstName
    const firstName = (message
      && message.body
      && message.body.user
      && message.body.user.firstName) || 'default';
  • 或者使用三元运算符?:判断对象是否存在
    • const fooInput = myForm.querySelector('input[name=foo]')
    • const fooValue = fooInput ? fooInput.value : undefined;
  • 这样层层判断很麻烦,因此引入“链判断运算符”(?.)简化写法。
    • const firstName = messsage?.body?.user?.firstName || 'default';
    • const fooValue = myForm.querySelector('input[name=foo]')?.value;
  • 使用?.运算符,直接在链式调用的时候判断,左侧的对象是否为null或undefined。
  • 如果是的,就不再往下运算而是返回undefined。
  1. 链判断运算符的用法
    • obj?.prop //对象属性
    • obj?.[expr] //对象属性
    • func?.(...args) //函数或对象方法的调用
    • a?.b //等同于 a == null ? undefined : a.b
    • a?.[x] //等同于 a == null ? undefined : a[x]
    • a?.b() //等同于 a == null ? undefined : a.b()
    • a?.() //等同于 a == null ? undefined : a()
    • eg.判断对象方法是否存在,如果存在就立即执行。 iterator.return?.()
    • eg.对于可能没有实现的方法,这个运算符很有用。
      if (myForm.checkValidity?.() === false) {
        return;//表单校验失败
      }
    
    • 老式浏览器的表单可能没有checkValidity()方法,这时?.运算符就会返回undefined,
    • if语句就变成了undefined === false,所以就会跳过下面的代码。
  2. 短路机制
    • a?.[++x] //等同于 a == null ? undefined : a[++x]
    • 如果a为真,右侧表达式就不会进行递增运算。
  3. delete运算符
    • delete a?.b //等同于 a == null ? undefined : delete a.b
    • 如果a为null或undefined,会直接返回undefined不会进行delete运算。
  4. 括号的影响
    • (a?.b).c //等同于 (a == null ? undefined : a.b).c
    • 如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响。
    • 不管a对象是否存在,圆括号后面的.c总是会执行。
  5. 报错场合
    • 以下写法是禁止的,会报错。
    // 构造函数
    new a?.()
    new a?.b()			
    // 链判断运算符的右侧有模板字符串
    a?.`{b}`
    a?.b`{c}`
    // 链判断运算符的左侧是 super
    super?.()
    super?.foo
    // 链运算符用于赋值运算符左侧
    a?.b = c
  1. 右侧不得为十进制数值
    • 允许foo?.3:0被解析成foo ? .3 : 0也就是说,
    • 那个小数点会归属于后面的十进制数字,形成一个小数。

七.Null判断运算符

  • 设置属性默认值时有用到。
  • const animationDuration = response.settings.animationDuration || 300;
  • const showSplashScreen = response.settings.showSplashScreen || true;
  • 通过||设置默认值时,我们想要,只要属性值为null或undefined,默认值就会生效,
  • 但是属性的值如果为空字符串或false或0,默认值也会生效。
  • 为了避免这种情况,引入了Null判断运算符(??)。
  • 它的行为类似||,但只有运算符左侧的值为null或undefined时,才会返回右侧的值。
  • eg.判断函数参数是否赋值
    function Component(props){
        const enable = props.enabled ?? true;
        //等同于
        const {
            enabled: enable = true,
        } = props;
    }
  • 运算符优先级:和&&,||一起使用时必须用括号表明优先级,否则会报错。

八.对象的方法

  1. Object.is() 用来比较两个值是否严格相等,是(同值相等)算法等同于===
  2. Object.assign() 用来复制对象,合并对象参数
    • let 新对象 = Object.assign(目标对象,source1,source2,)
    • eg.合并对象参数的属性相同时会覆盖
        let json = {a:1},json2 = {b:2, a:2},json3 = {c:3};		
        let obj = Object.assign({}, json, json2,json3);		
        console.log(obj);//==>{a: 2, b: 2, c: 3}
    
    • eg.复制对象
        let arr = [1,3,5];
        let arr2 = Object.assign([], arr);
        * eg.为对象添加属性
        class Point {
          constructor(x, y) {
            Object.assign(this, {x, y});
          }
        }
    
    • eg.为对象添加方法
        Object.assign(SomeClass.prototype, {
          someMethod(arg1, arg2) {
            ···
          },
          anotherMethod() {
            ···
          }
        });
        // 等同于下面的写法
        SomeClass.prototype.someMethod = function (arg1, arg2) {
          ···
        };
        SomeClass.prototype.anotherMethod = function () {
          ···
        };
    
    • eg.克隆对象
        function clone(origin) {
          return Object.assign({}, origin);
        }
    
    • eg.合并多个对象
    • 将多个对象合并到某个对象。
    • const merge = (target, ...sources) => Object.assign(target, ...sources);
    • 如果希望合并后返回一个新对象,可以改写上面函数,对一个空对象合并。
    • const merge = (...sources) => Object.assign({}, ...sources);
    • eg.为属性指定默认值
        const DEFAULTS = {
          logLevel: 0,
          outputFormat: 'html'
        };
    
        function processContent(options) {
          options = Object.assign({}, DEFAULTS, options);
          console.log(options);
          // ...
        }
    
  3. 可以用for...of循环遍历数组,它们都返回一个遍历器对象
    • Object.keys() 是对键名的遍历,返回一个数组
    • Object.values() 是对键值的遍历,返回一个数组
    • Object.entries() 是对键值对的遍历,返回一个数组,
        for (let index of ['a', 'b'].keys()) {
            console.log(index);
        }// 0
        // 1
        for (let elem of ['a', 'b'].values()) {
            console.log(elem);
        }// 'a'
        // 'b'
        for (let [index, elem] of ['a', 'b'].entries()) {
            console.log(index, elem);
        }// 0 "a"
        // 1 "b"
        var obj1 = {"name":"lucas","age":22};
        console.log(Object.keys(obj1)) //["name", "age"]
    
  4. __proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()
    • __proto__属性 用来设置一个对象的prototype对象
    • Object.setPrototypeOf(object, prototype) 用于读取一个对象的原型对象。
    • Object.getPrototypeOf(obj);
  5. Object.fromEntries()
    • Object.entries()的逆操作,用于将一个键值对数组转为对象。
    • 适合将 Map 结构转为对象。
      const entries = new Map([
          ['foo', 'bar'],
          ['baz', 42]
      ]);
      Object.fromEntries(entries)// { foo: "bar", baz: 42 }
    

第7章 Symbol数据类型

  • mbol 意思是全局标记
  • 如何用Symbol构建对象的Key,并调用和赋值?
    var jspang = Symbol();
    var obj={    [jspang]:'技术胖'  }
    console.log(obj[jspang]);//==>技术胖
    obj[jspang]='web';
    console.log(obj[jspang]);//==>web
  • Symbol对象元素的保护作用?
  • 象中有多个属性值,但是循环输出时并不希望全部输出,那我们就可以使用Symbol进行保护。
  • 进行保护的写法:
    var obj={name:'jspang',skill:'web',age:18};
    for (let item in obj){
        console.log(obj[item]);;//==>jspang web 18
    }
  • 我不想别人知道我的年龄,这时候我就可以使用Symbol来进行循环保护。
    let obj={name:'jspang',skill:'web'};
    let age=Symbol();
    obj[age]=18;
    for (let item in obj){
        console.log(obj[item]);//==>jspang web
    } 
    console.log(obj);//==>{name: "jspang", skill: "web", Symbol(): 18}

第8章 Set和Map数据结构

一.Set & WeakSet数据结构

  • Set的数据结构是以数组的形式构建的。
    let setArr = new Set(['jspang','技术胖','web','jspang']);
    console.log(setArr);//==>Set {"jspang", "技术胖", "web"}
  • Set和Array的区别?
    • Set不允许内部有重复的值,如果有只显示一个相当于去重。
    • 虽然Set很像数组但它不是数组。
  • Set 追加用.add() setArr.add('前端职场');
  • Set 删除某一个用.delete()setArr.delete('前端职场');
  • Set 查找用.has()setArr.has('jspang');
  • Set 删除全部用.clear()setArr.clear();
  • Set 获取Set值的数量用size属性 setArr.size
  • forEach循环
left setArr = new Set(['jspang','技术胖','web','jspang']);
setArr.forEach( (value) => console.log(value) );
  • WeakSet的声明
    let weakObj=new WeakSet();
    let obj={a:'jspang',b:'技术胖'}
    weakObj.add(obj);
    console.log(weakObj);
  • 如果你直接在new 的时候就放入值将报错。
  • WeakSet里边的值也是不允许重复的。

二.Map数据结构

  • Map是一种灵活,简单的适合一对一查找的数据结构。
  • Map可以看成是一个特殊的键值对,但Map的key可以设成数组,值也可以设成字符串,让它不规律对应起来。
  • 当然也可key字符串,value是对象。我们调换一下位置,依然是符合map的数据结构规范的。
    let json = {
            name:'jspang',
            skill:'web'
    }
    console.log(json.name);//==>jspang
    var map=new Map();
    map.set(json,'iam');
    map.set('jspang',json);
    console.log(map);//==>输出结果如下
    Map(2) {{…} => "iam", "jspang" => {…}}
    [[Entries]]
    0: {Object => "iam"}
    key: {name: "jspang", skill: "web"}
    value: "iam"
    1: {"jspang" => Object}
    key: "jspang"
    value: {name: "jspang", skill: "web"}
    size: 2
    __proto__: Map
  • Map 取值用getmap.get(json);
  • Map 删除某一个用.delete()map.delete(json);
  • Map 查找用.has()map.has('jspang');
  • Map 删除全部用.clear()map.clear();
  • Map 获取Set值的数量用size属性console.log( map.size );

第9章 Proxy预处理

  1. Proxy是用来做什么的?
    • 钩子函数:在运行函数前初始化一些数据,在改变对象值后做一些善后处理。这些都算钩子函数。
    • Proxy的存在就可以让我们给函数加上这样的钩子函数。
    • 你也可以理解为在执行方法前预处理一些代码。
    • 你也可以理解为它是函数或者对象的生命周期。
  2. 声明Proxy
    • new Proxy({},{});
    • 是两个花括号,第一个花括号就相当于我们方法的主体。
    • 第二个花括号就是Proxy代理处理区域,相当于我们写钩子函数的地方。
  3. get属性
    • get属性是在你得到某对象属性值时预处理的方法,他接受三个参数
    • target:得到的目标值
    • key:目标的key值,相当于对象的属性
    • property:这个不太常用,用法还在研究中,还请大神指教。
      var pro = new Proxy({
          add: function (val) { return val + 10; },
          name: 'I am Jspang'
      }, {
          get:function(target,key,property){
               console.log('come in Get');//==>先输出,相当于在方法调用前的钩子函数。
               return target[key];
          }
      });
      console.log(pro.name)
  1. set属性
    • set属性是值你要改变Proxy属性值时,进行的预先处理。它接收四个参数。
    • target:目标值。
    • key:目标的Key值。
    • value:要改变的值。
    • receiver:改变前的原始值。
  2. 使用get,set
var pro = new Proxy({
    add: function (val) { return val + 10; },
    name: 'I am Jspang'
}, {
    get:function(target,key,property){
       console.log('come in Get');//==>第一个输出,相当于在方法调用前的钩子函数。
       return target[key];
    },
    set:function(target,key,value,receiver){
        console.log(`setting ${key} = ${value}`);//==>第三个输出 setting name = 技术胖
        return target[key] = value;
    }
});
console.log(pro.name);//==>第二个输出
pro.name='技术胖';
console.log(pro.name);//==>第四个输出
  1. apply的使用
    • apply的作用是调用内部的方法,它使用在方法体是一个匿名函数时。

第10章 Promise对象

Promise从语法上说就是一个对象,它可以获取异步操作的消息。用来解决异步回调问题。

ES6规定,Promise对象是一个构造函数,用来生成Promsie实例。

  1. 创建一个Promise实例
      let promise = new Promise(function(resolve,reject){
          if(/*异步调用成功*/){
              resolve(value)
          }else{
              reject(error)
          }
      })
  1. Promise实例生成后,指定成功和失败状态的回调函数
	写法一
		promise.then(res=>{
			//success成功的方法
		},err=>{
			//error失败的方法
		})
	写法二
		promise.then(res=>{
			//success成功的方法
		})
		promise.catch(err=>{
			//error失败的方法
		})
  1. Promise.resolve('a') 将现有的东西转成一个promise对象是成功状态
      等价于
      new Promise(resolve=>{
          resolve('aaa');
      })
  1. Promise.all([p1,p2,p3]) 把Promise打包扔到一个数组里面,打包完还是一个Promise对象
    • 必须确保所有的Promise对象都是resolve状态才可用all()
  2. Promise.race([p1,p2,p3]) 只要有一个成功,就返回
    • 同时处理没有关联的多个异步操作
      let p1 = Promise.resolve('aaaa');
      let p2 = Promise.resolve('bbbb');
      let p3 = Promise.resolve('cccc');
      Promise.all([p1,p2,p3]).then(res=>{
          //console.log(res);
          let [res1, res2, res3] = res;
          console.log(res1, res2, res3);
      })
同时处理有关联的多个异步操作
      let status = 1;
      let userLogin = (resolve,reject)=>{
          if(status == 1){
              resolve({data:'登录成功',msg:'xxx',token:'xxx'});
          }else{
              reject('失败了');
          }
      }
      let getUserInfo = (resolve,reject)=>{
          if(status == 1){
              resolve({data:'获取用户信息成功',msg:'xxx',token:'xxx'});
          }else{
              reject('失败了');
          }
      }
      new Promise(userLogin).then(res=>{
          console.log('登录成功');
          return new Promise(getUserInfo);
      }).then(res=>{
          console.log('获取用户信息成功');
      })

第11章 Class(类)的使用

  1. class是一个类也是一个函数
  2. class中不要用逗号和分号
  3. class没有预解析,没有提升功能,实例化一定要放在下面
      function Person(){ // ES5
          this.name = 'andy';
      }
      Person.prototype.showA = function(){
          return this.name;
      }
      var p1 = new Person();
      p1.showA();

      class Person(){ // ES6
          constructor(name){//构造函数,调用new会自动执行
              this.name = name;
          }
          showA(){
              return this.name;
          }
      }
      let p1 = new Person('andy');
      p1.showA();
  1. class表达式
      const Person = class{
          constructor(name){this.name=name;}
      }
  1. 属性表达式
      class Person(){
          constructor(){}
          [aaa+bbb](){
              return '使用属性表达式'
          }			
      }
      let aaa ='strive',let bbb ='method';
      let p1 = new Person();
      alert( pl[aaa+bbb]() ); //==>'使用属性表达式'
      alert( p1.strivemethod() ); //==>'使用属性表达式'
  1. 矫正this
    • fn.call(this指向谁,args1,args2...)
    • fn.apply(this指向谁,[args1,args2...])
    • fn.bind()
      class Person{
          constructor(){
              this.name = 'Strive';
              this.showName = this.showName.bind(this); //矫正this
          }
          showName(){
              console.log('this:', this);
              return `名字为: ${this.name}`;
          }
          showA(){ return 'showA'}
      }	
      let p1 = new Person();
      let {showName } = p1,{showA } = p1;
      console.log(showA(),showName());
      //==>showA
      //==>this: Person {name: "Strive", showName: ƒ}
      //==>名字为: Strive
  1. 静态方法:直接通过类来调用
      class Person(){
          constructor(){}
          showName(){}
          static aaa(){}
      }
      let p1 = new Person();
      alert( p1.showName() );
      alert( Person.aaa() );
  1. 继承
      function Person(name){ //ES5
          this.name = name;
      }
      Person.prototype.showName = function(){}
      function Student(name,skill){
          Person.call(this.name); //继承父类属性
          this.skill = skill; //定义子类属性
      }
      Student.prototype = new Person();//继承父类方法

      class Person(){ // ES6 //父类
          constructor(name){
              this.name = name
              this.age = age
          }
          showName(){}
      }
      class Student extends Person{//子类
          constructor(name,age,skill){
              super(name,age)
              this.skill = skill  //this要必须放到super的后面
          }
          showA(){
              super.showName() //执行父类的方法
          }
      }
      let stu = new Student('andy','teacher');

第12章 模块化操作

  1. 需要放到服务器环境
  2. as用来设置别名
  3. 如何定义模块?(都放到js文件中)
    • export导出东西需要加{}
    • export default导出东西不需要加
      let a = 1, b = 2,c = 3;
      export {a,b};
      export default c;
      export const d = 1;
      export {
          name as n, 
          showA  //showA是定义的方法
      }
  • 如何使用模块?<script type="module"></script>
  • 导入全部文件
    • import './1.js'; //(./)当前路径,不加就会报错
    • import * as login from './1.js';
  • 导入文件中的模块
    • import {name as n,age} from './1.js';
    • import c,{a,b} from './1.js';
  • import导入时要把export default默认的放前面,export的放后面
  • import有提升效果,会自动提升到顶部,首先执行
  • import无论你引入几次,模块只会导入一次
  • import支持相对路径和绝对路径
  • import类似node中的require,可以动态引入;import的返回值是一个Promise对象; 动态引入可以按需加载,可以写在if中,路径也可以是动态的;
      function config(sign){
          switch(sign){
              case 1:
                  return './modules/1.js';
                  break;
              case 2:
                  return './modules/2.js';
                  break;
          }
      }
      let sign = 1;
      import(config(sign)).then(res=>{
      });
  • 模块化默认就是严格模式'use strict'
  • 模块中顶层的this指向undefined,即不要在顶层代码中使用this