JavaScript es6-学习笔记-(自用)

es6

ECMA介绍,名称

let const

1. let 命令

  1. let声明的变量只在所处的块级有效,var不具有这个特点

for循环:循环变量是一个父作用域,而循环体内部是一个单独的子作用域

  1. 没有变量提升
  2. 暂时性死区
    只要块级作用域内存在let命令,不再受外部的影响。
    即使用let命令声明变量之前,该变量都是不可用的
var tmp = 123;

if (true) {
     
  tmp = 'abc'; // ReferenceError
  let tmp;
}

规定暂时性死区和letconst语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为

  1. 不能重复定义

2.块级作用域与函数声明

  1. 为什么要用块级作用域
  • 内层变量可能会覆盖外层变量
  • 用来计数的循环变量泄露为全局变量。
  1. ES6 的块级作用域
  • ES6 允许块级作用域的任意嵌套。
  • 内层作用域可以定义外层作用域的同名变量。
  • 块级作用域的出现,匿名立即执行函数表达式不再必要了。
  1. 块级作用域与函数声明

ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用
避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。

// 块级作用域内部的函数声明语句,建议不要使用
{
     
  let a = 'secret';
  function f() {
     
    return a;
  }
}

// 块级作用域内部,优先使用函数表达式
{
     
  let a = 'secret';
  let f = function () {
     
    return a;
  };
}

ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。

3.const命令

基本用法:

const声明一个只读的常量。一旦声明,常量的值就不能改变。

注意

  1. const声明的变量不得改变值,const一旦声明变量,就必须立即初始化,不能留到以后赋值
  2. 只在声明所在的块级作用域内有效。
  3. 不能提升
  4. 不可重复声明
本质:
  1. 简单类型:变量指向的内存地址,值保存在内存地址
  2. 复合类型的数据:变量指向的内存地址,保存的只是一个指向实际数据的指针。
    所以它指向的数据结构是不是可变的,就完全不能控制了。
    但是可以将对象冻结,使用Object.freeze方法
//将对象彻底冻结的函数
var constantize = (obj) => {
     
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, i) => {
     
    if ( typeof obj[key] === 'object' ) {
     
      constantize( obj[key] );
    }
  });
};
ES6 声明变量的六种方法

var function let const import class

4. 顶层对象属性

var命令和function命令声明的全局变量,依旧是顶层对象的属性;
let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。

5. globalThis 对象

在各种环境下取到顶层对象

(typeof window !== 'undefined'
   ? window
   : (typeof process === 'object' &&
      typeof require === 'function' &&
      typeof global === 'object')
     ? global
     : this);

// 方法二
var getGlobal = function () {
     
  if (typeof self !== 'undefined') {
      return self; }
  if (typeof window !== 'undefined') {
      return window; }
  if (typeof global !== 'undefined') {
      return global; }
  throw new Error('unable to locate global object');
};

结构赋值

数组的解构赋值

1. 基本用法
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
...展开运算符,剩余运算符

let [a, b, c] = [1, 2, 3];

如果解构不成功,变量的值就等于undefined
对于 Set 结构,也可以使用数组的解构赋值。

let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"

事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。

2.默认值
注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。
默认值可以引用解构赋值的其他变量,但该变量必须已经声明。

let [x = 1, y = x] = [];     // x=1; y=1

对象的解构赋值

1.简介
如果变量名与属性名不一致,必须写成下面这样。

let {
      foo: baz } = {
      foo: 'aaa', bar: 'bbb' };
baz // "aaa"

对象的解构赋值是下面形式的简写,也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

let {
      foo: foo, bar: bar } = {
      foo: 'aaa', bar: 'bbb' };

上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。

注意,对象的解构赋值可以取到继承的属性。

const obj1 = {
     };
const obj2 = {
      foo: 'bar' };
Object.setPrototypeOf(obj1, obj2);

const {
      foo } = obj1;
foo // "bar"

Object.setPrototypeOf() 方法设置一个指定的对象的原型到另一个对象或null。
2. 默认值
默认值生效的条件是,对象的属性值严格等于undefined。
解构赋值允许等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式。

({
     } = [true, false]);
({
     } = 'abc');
({
     } = []);

3.由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。

let arr = [1, 2, 3];
let {
     0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3

字符串的解构赋值

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

const [a, b, c, d, e] = 'hello';

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

let {
     length : len} = 'hello';
len // 5

数值和布尔值的解构赋值

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

let {
     toString: s} = 123;
s === Number.prototype.toString // true

let {
     toString: s} = true;
s === Boolean.prototype.toString // true

上面代码中,数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象
由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。

let {
      prop: x } = undefined; // TypeError
let {
      prop: y } = null; // TypeError

函数参数的解构赋值

函数的参数也可以使用解构赋值。

function add([x, y]){
     
  return x + y;
}

add([1, 2]); // 3

函数参数的解构也可以使用默认值。

圆括号问题

1.不能使用圆括号的情况
(1)变量声明语句
(2)函数参数
(3)赋值语句模式
2.可以使用圆括号的情况
可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号。

用途

(1)交换变量的值
(2)从函数返回多个值
(3)函数参数的定义、
(4)提取 JSON 数据
(5)函数参数的默认值

jQuery.ajax = function (url, {
     
  async = true,
  beforeSend = function () {
     },
  cache = true,
  complete = function () {
     },
  crossDomain = false,
  global = true,
  // ... more config
} = {
     }) {
     
  // ... do stuff
};

(6)遍历 Map 结构
(7)输入模块的指定方法
加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。

const {
      SourceMapConsumer, SourceNode } = require("source-map");

字符串的扩展

字符的 Unicode 表示法

允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码点。
这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示。
ES6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。

有了这种表示法之后,JavaScript 共有 6 种方法可以表示一个字符。

'\z' === 'z'  // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true

字符串的遍历器接口

ES6 为字符串添加了遍历器接口,使得字符串可以被for…of循环遍历。

for (let codePoint of 'foo') {
     
  console.log(codePoint)
}
// "f"
// "o"
// "o"

直接输入 U+2028 和 U+2029

ES2019 允许 JavaScript 字符串直接输入 U+2028(行分隔符)和 U+2029(段分隔符)。

JSON.stringify() 的改造

为了确保返回的是合法的 UTF-8 字符,ES2019 改变了JSON.stringify()的行为。
如果遇到0xD800到0xDFFF之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。

模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识。
它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
模板字符串中嵌入变量,需要将变量名写在${}之中。
大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
如果需要引用模板字符串本身,在需要时执行,可以写成函数。

let func = (name) => `Hello ${
       name}!`;
func('Jack') // "Hello Jack!"

实例:模板编译

标签模板

let name = 'zpp';
let sex = 'female';

function fn() {
     
    console.log(arguments);
    /*
    0: (3) ["名字是:", "性别", "", raw: Array(3)]
    1: "zpp"
    2: "female"
    */
}
// function fn(arr, ...args) {
     
//     console.log(args);//(2) ["zpp", "female"]
//     console.log(arr);//(3) ["名字是:", "性别", "", raw: Array(3)]
// }
let str = fn`名字是:${
       name}性别${
       sex}`;
//includes startWith endWith padStart padEnd //不够的话,在前后补零
console.log('djsk'.includes('dj'));
console.log("3".padStart(2, 0));

模板字符串的限制

字符串的新增方法

String.fromCodePoint()

ES6 提供了String.fromCodePoint()方法,可以识别大于0xFFFF的字符,用于从 Unicode 码点返回对应字符。
在作用上,正好与下面的codePointAt()方法相反。

String.fromCodePoint(0x20BB7)
// ""

String.raw()

该方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法。

String.raw`Hi\n${
       2+3}!`
// 实际返回 "Hi\\n5!",显示的是转义后的结果 "Hi\n5!"

实例方法:codePointAt()

codePointAt()方法会正确返回 32 位的 UTF-16 字符的码点。
对于那些两个字节储存的常规字符,它的返回结果与charCodeAt()方法相同。

实例方法:normalize()

实例方法:includes(), startsWith(), endsWith()

includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。

实例方法:repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。

实例方法:padStart(),padEnd()

如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。

实例方法:trimStart(),trimEnd()

trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

实例方法:matchAll()

matchAll()方法返回一个正则表达式在当前字符串的所有匹配。

函数的扩展

函数参数的默认值

1. 基本用法

function log(x, y = 'World') {
     
  console.log(x, y);
}

log('Hello') // Hello World

函数的参数默认已经定义了,不能再用let,const声明

 function show(a = 18) {
     
            let a = 101;// 错误
            console.log(a)
        }

使用参数默认值时,函数不能有同名参数。
参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。

2. 与解构赋值默认值结合使用

function foo({
     x, y = 5} = {
     }) {
     
  console.log(x, y);
}

foo() // undefined 5

上面代码指定,如果没有提供参数,函数foo的参数默认为一个空对象。

3. 参数默认值的位置
通常情况下,定义了默认值的参数,应该是函数的尾参数。
有默认值的参数都不是尾参数。这时,无法只省略该参数,而不省略它后面的参数,除非显式输入undefined。

function foo(x = 5, y = 6) {
     
  console.log(x, y);
}

foo(undefined, null)

4. 函数的 length 属性
length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。
如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。

(function (a = 0, b, c) {
     }).length // 0
(function (a, b = 1, c) {
     }).length // 1

5. 作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。
如果参数的默认值是一个函数,该函数的作用域也遵守这个规则。
6 .应用
利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。
另外,可以将参数默认值设为undefined,表明这个参数是可以省略的。

rest 参数

  1. 数组 展开
let arr = ['appele', 'banana', 'orange'];
console.log(...arr); //appele banana orange
  1. 传入的1,2,3,4 变为数组
  function show(...a) {
     
        console.log(a); //(5) [1, 2, 3, 9, 8]
        console.log(a.sort());//(5) [1, 2, 3, 8, 9]
  }
	show(1, 2, 3, 9, 8)
  1. 该变量将多余的参数放入数组中
        function show(a, ...b) {
     
            console.log(a); //1
            console.log(b); //(4) [2, 3, 4, 5]
        }
        show(1, 2, 3, 4, 5);
  1. 数组的copy

Array.from(arr): 把类数组对象(获取的一组对象,arguments…)转成数组

let arr = [1, 2, 3, 4, 5];
//1
let arr2 = [...arr];
//2
let arr2 = Array.from(arr);

严格模式

name 属性

如果将一个匿名函数赋值给一个变量,ES5 的name属性,会返回空字符串
而 ES6 的name属性会返回实际的函数名。

var f = function () {
     };

// ES5
f.name // ""

// ES6
f.name // "f"

箭头函数

1. 基本用法

左边是参数,右边是返回值
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
如果箭头函数只有一行语句,且不需要返回值,不用写大括号了。

()=>{
     

 }

2. 注意点
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

3. 不适用的场合
第一个场合是定义对象的方法,且该方法内部包括this。
第二个场合是需要动态this的时候,也不应使用箭头函数。

尾调用优化

函数参数的尾逗号

ES2017 允许函数的最后一个参数有尾逗号(trailing comma)。

Function.prototype.toString()

修改后的toString()方法,明确要求返回一模一样的原始代码。

catch 命令的参数省略

ES2019 做出了改变,允许catch语句省略参数。

数组的各种循环

1. for循环

 let arr = ['apple', 'banana', 'orange', 'tomato'];
        for (let i = 0; i < arr.length; i++) {
     
            console.log(arr[i]);
        }

2. arr.forEach循环
arr.forEach(回调函数,this指向谁)

   //代替普通的for循环,没有返回值
   arr.forEach(function (val, index, array) {
     

   })

3. arr.map循环
正常情况下需要配合return,没有return的相当于forEach
作用:重新整理数据结构

4. arr.filter()循环
过滤,过滤不合格的元素,如果回调函数返回true就保留,否则丢弃

 let arr = [
       {
      title: 'aaa1', read: 100, hot: true },
       {
      title: 'aaa2', read: 100, hot: true },
       {
      title: 'aaa3', read: 100, hot: false },
       {
      title: 'aaa4', read: 100, hot: true },
       {
      title: 'aaa5', read: 100, hot: false },
       {
      title: 'aaa6', read: 100, hot: true },

   ]
   let newArr = arr.filter((item, index, arr) => {
     
       return item.hot;
   })
   console.log(newArr)

5. arr.some()循环
类似查找,数组中里面的某一个元素符合条件,返回true

  let arr = ['apple', 'banana', 'orange', 'tomato'];
  let b = arr.some((val, index, arr) => {
     
      return val == 'banana';
  });
  console.log(b); //false

6. arr.every()循环

  // 类似查找,数组中里面的每一个元素都符合条件,返回true
  // arr.every()
  function findInArray(arr,item){
     
      return arr.some((val,index,arr)=>{
     
          return val == item;
      });
  }
  console.log(findInArray(arr,'orange2')); //数组中有没有orange2这个单词

7. arr.reduce()
做一个阶乘。。。
tip:2的3次方 Math.pow(2,3)或者2**3

let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// prev 上一次返回的结果 cur 当前的值
let res = arr.reduce((prev, cur, index, arr) => {
     
    return prev + cur;
})
console.log(res)

8. arr.reduceRight()
从右往左

9. for .. of 循环

arr.keys() 数组的下标
arr.entries() 数组的某一项

  let arr = ['apple', 'banana', 'orange', 'tomato'];
  for (let val of arr) {
     
      console.log(val);
  }
 /*
apple
banana
orange
tomato
*/
  for (let index of arr.keys()) {
     
      console.log(index);
  }
  /*
0
1
2
3
*/
  for (let item of arr.entries()) {
     
      console.log(item);
  }
  /*
(2) [0, "apple"]
(2) [1, "banana"]
(2) [2, "orange"]
(2) [3, "tomato"]
*/
  for (let [key, val] of arr.entries()) {
     
      console.log(key, val);
  }

数组新增东西

1. Array.from(arr):
把类数组对象(获取的一组对象,arguments…)转成数组
2. Array.of()
把一组值转换为数组 Array.of('aaa','bbb','ccc')
3. arr.find():查找
找到第一个符合条件的数组成员,如果没找到返回undefined
4. arr.findIndex():
找得是位置,没有找到返回-1
5. arr.fill():填充
arr.fill(填充的东西,开始的位置,结束的位置)

let arr = new Array(10);
        arr.fill('默认值', 1, 3)
        console.log(arr);

6. arr.include
数组中是否包含某一个成员

let arr = ['apple','banana','tomato'];
let b = arr.include('orange2');
console.log(b); //false

对象简洁语法以及对象新增

对象有简洁的语法

不要使用箭头函数,因为对象的大括号并不是一个块级的作用域,this会指向window

let name = 'zpp';
  let age = 18;
  let json = {
     
      name, //name :name
      age,   //age:age
      // showA:function(){
     
      //     return this.name;
      // },
      showA(){
     
          return this.name;
      }
  };
  console.log(json)
  1. Object.is():用来比较两个值是否相等
    Object.is(NaN,NaN)

  2. Object.assign(): 用来合并对象
    作用:
    (1)合并参数:多用于ajax接受用户的参数,用户没有写参数时,用默认的,如果写的话,就覆盖默认值
    (2)复制一个对象

let json = {
      a: 1 };
let json2 = {
      b: 2, a: 2 };
let json3 = {
      c: 2 };
let obj = Object.assign({
     }, json, json2, json3);
console.log(obj);
//{a: 2, b: 2, c: 2} 后面的胡覆盖前面

Object.keys()
Object.entries();
Object.values();

  		let {
      keys, values, entries } = Object;
  		//解构
        let json = {
     
            a: 1,
            b: 2,
            c: 3
        }
        for (let key of keys(json)) {
     
            console.log(key);
        }

        for (let value of values(json)) {
     
            console.log(value);
        }

        for (let item of entries(json)) {
     
            console.log(item);
        }

        for (let [key, val] of entries(json)) {
     
            console.log(key, val);
        }
  1. 对象上的扩展函数
let json = {
     a:3,b:4};
let json2 = {
     ...json}

promise

Promise 的含义

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

基本用法

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

const promise = new Promise(function(resolve, reject) {
     
  // ... some code

  if (/* 异步操作成功 */){
     
    resolve(value);
  } else {
     
    reject(error);
  }
});

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。

promise.then(function(value) {
     
  // success
}, function(error) {
     
  // failure
});

补充
setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式。

setTimeout(code, milliseconds, param1, param2, ...)
setTimeout(function, milliseconds, param1, param2, ...)

下面是一个用Promise对象实现的 Ajax 操作的例子。

const getJSON = function(url) {
     
  const promise = new Promise(function(resolve, reject){
     
    const handler = function() {
     
      if (this.readyState !== 4) {
     
        return;
      }
      if (this.status === 200) {
     
        resolve(this.response);
      } else {
     
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

  });

  return promise;
};

getJSON("/posts.json").then(function(json) {
     
  console.log('Contents: ' + json);
}, function(error) {
     
  console.error('出错了', error);
});

执行的顺序
Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。

let promise = new Promise(function(resolve, reject) {
     
  console.log('Promise');
  resolve();
});

promise.then(function() {
     
  console.log('resolved.');
});

console.log('Hi!');

// Promise
// Hi!
// resolved

立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务

new Promise((resolve, reject) => {
     
  resolve(1);
  console.log(2);
}).then(r => {
     
  console.log(r);
});
// 2
// 1

Promise.prototype.then()

它的作用是为 Promise 实例添加状态改变时的回调函数。

Promise.prototype.catch()

Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

getJSON('/posts.json').then(function(posts) {
     
  // ...
}).catch(function(error) {
     
  // 处理 getJSON 和 前一个回调函数运行时发生的错误
  console.log('发生错误!', error);
});

Promise.prototype.finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

promise
.then(result => {
     ···})
.catch(error => {
     ···})
.finally(() => {
     ···});

Promise.all()

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.all([p1, p2, p3]);

必须确保,所有的promise对象都是resolve状态,都是成功状态

Promise.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.race([p1, p2, p3]);

只要有一个成功就可以

Promise.allSettled()

Promise.any()

Promise.resolve()

将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

Promise.reject()

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
     
  console.log(s)
});
// 出错了

应用

加载图片

const preloadImage = function (path) {
     
  return new Promise(function (resolve, reject) {
     
    const image = new Image();
    image.onload  = resolve;
    image.onerror = reject;
    image.src = path;
  });
};
function loadImageAsync(url) {
     
  return new Promise(function(resolve, reject) {
     
    const image = new Image();

    image.onload = function() {
     
      resolve(image);
    };

    image.onerror = function() {
     
      reject(new Error('Could not load image at ' + url));
    };

    image.src = url;
  });
}

Promise.try()

模块化

概述

  • 模块化:
    注意:需要放到服务器环境
    a).如何定义模块?
    export东西
    export const =122
    b).如何使用?
    import
    import ‘./modules/1.js’;
  • 使用模块:
  • import:特点
    a). import可以是相对路径,也可以是绝对路径
    import https://code.jquery .com/jgquery-3.3.1js;
    b). import模块只会导入- -次, 无论你引入多少次
    c). import './modules/1js';如果这么用,相当于引入文件
    import 有提升功能,会自动提升到顶部执行
    import() 类似于node的require,可以动态引入,而默认的import语法不能写在if中,返回值是一个promise对象
    优点:可以按需加载,可以写在if中,引入的路径可以是动态的

export 命令

模块功能主要由两个命令构成:export和import。
export命令用于规定模块的对外接口
import命令用于输入其他模块提供的功能。

使用:

  1. 使用export命令输出变量。
  2. 可以单独输出也可以一起输出
  3. export命令除了输出变量,还可以输出函数或类(class)。
  4. 可以使用as关键字重命名
  5. export语句输出的接口,与其对应的值是动态绑定关系,当模块中的值变化时,会随着改变
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export {
      firstName, lastName, year };
function v1() {
      ... }
function v2() {
      ... }

export {
     
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};

import 命令

  1. import命令要使用as关键字,将输入的变量重命名。
  2. import后面的from指定模块文件的位置,可以是相对路径,也可以是绝对路径,.js后缀可以省略。
  3. import命令具有提升效果,会提升到整个模块的头部,首先执行。import命令是编译阶段执行的,在代码运行之前。
  4. import是静态执行,所以不能使用表达式和变量。
  5. 如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次。
import {
      lastName as surname } from './profile.js';

模块的整体加载

使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。

import * as circle from './circle';

export default 命令

为模块指定默认输出。
export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。

// export-default.js
export default function () {
     
  console.log('foo');
}

其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。

// import-default.js
import customName from './export-default';
customName(); // 'foo'

本质上,export default就是输出一个叫做default的变量或方法,所以它后面不能跟变量声明语句。

// 正确
export var a = 1;

// 正确
var a = 1;
export default a;

// 错误
export default var a = 1;

如果想在一条import语句中,同时输入默认方法和其他接口,可以写成下面这样。

import _, {
      each, forEach } from 'lodash';

export 与 import 的复合写法

如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。

export {
      foo, bar } from 'my_module';

// 可以简单理解为
import {
      foo, bar } from 'my_module';
export {
      foo, bar };
// 接口改名
export {
      foo as myFoo } from 'my_module';

// 整体输出
export * from 'my_module';

模块的继承

跨模块常量

import()

import()函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块。
另外,import()函数与所加载的模块没有静态连接关系,这点也是与import语句不相同。
import()类似于 Node 的require方法,区别主要是前者是异步加载,后者是同步加载。

  • 适用场合
    • 按需加载
    • 条件加载
    • 动态的模块路径
  • 注意
  1. import()加载模块成功以后,这个模块会作为一个对象,当作then方法的参数。因此,可以使用对象解构赋值的语法,获取输出接口。
import('./myModule.js')
.then(({
     export1, export2}) => {
     
  // ...·
});
  1. 如果想同时加载多个模块,可以采用下面的写法
Promise.all([
  import('./module1.js'),
  import('./module2.js'),
  import('./module3.js'),
])
.then(([module1, module2, module3]) => {
     
   ···
});
  1. import()也可以用在 async 函数之中。
async function main() {
     
  const myModule = await import('./myModule.js');
  const {
     export1, export2} = await import('./myModule.js');
  const [module1, module2, module3] =
    await Promise.all([
      import('./module1.js'),
      import('./module2.js'),
      import('./module3.js'),
    ]);
}
main();

class的基本用法

es5中用function来写一个class

function Person(name, age) {
     
            this.name = name;
            this.age = age;

        }
        var person = new Person('Strive', 18);
        Person.prototype.showName = function () {
     
            console.log(this.name);
        }
        person.showName();

es6 中的class

class Person {
     
            constructor(name, age) {
      //构造方法,只要调用了new就会执行
                //console.log(`构造函数执行了,${name},${age}`);
                this.name = name;
                this.age = age;
            }
            showName() {
     
                return `名字为:${
       this.name}`;
            }
            showAge() {
     
                return `年龄为:${
       this.age}`;
            } //不需要加,
        }
        let p1 = new Person('zpp', 18);
        console.log(p1.showName(), p1.showAge());

注意:es6中class没有提升功能,es5用函数模拟是可以提升的

class里面的取值(get)和存值函数(set)

静态的方法:就是类上的方法

static aaa() {
     
    return `静态的方法`
}
父类.aaa();

继承:

class Student extends Person{
     
            
        }

简介

class Point {
     
  constructor(x, y) {
     
    this.x = x;
    this.y = y;
  }

  toString() {
     
    return '(' + this.x + ', ' + this.y + ')';
  }
}

注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。

class B {
     }
let b = new B();

b.constructor === B.prototype.constructor // true

上面代码中,b是B类的实例,它的constructor方法就是B类原型的constructor方法。

Point.prototype.constructor === Point // true

prototype对象的constructor属性,直接指向“类”的本身,这与 ES5 的行为是一致的

类的内部所有定义的方法,都是不可枚举的(non-enumerable)。

class Point {
     
  constructor(x, y) {
     
    // ...
  }

  toString() {
     
    // ...
  }
}

Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]

2. constructor方法
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
3. 类的实例
生成类的实例的写法,与 ES5 完全一样,也是使用new命令。
4. 取值和存值函数
与 ES5 一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

class MyClass {
     
  constructor() {
     
    // ...
  }
  get prop() {
     
    return 'getter';
  }
  set prop(value) {
     
    console.log('setter: '+value);
  }
}

let inst = new MyClass();

inst.prop = 123;
// setter: 123

inst.prop
// 'getter'

5.属性表达式
类的属性名,可以采用表达式。

let methodName = 'getArea';

class Square {
     
  constructor(length) {
     
    // ...
  }

  [methodName]() {
     
    // ...
  }
}

上面代码中,Square类的方法名getArea,是从表达式得到的。

6. class表达式
与函数一样,类也可以使用表达式的形式定义。

const MyClass = class Me {
     
  getClassName() {
     
    return Me.name;
  }
};

如果类的内部没用到的话,可以省略类的名称,也就是可以写成下面的形式。
采用 Class 表达式,可以写出立即执行的 Class。
7. 注意点
(1)严格模式
(2)不存在变量提升
(3)name属性
(4)Generator 方法
如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数。
(5)this 的指向
类的方法内部如果含有this,它默认指向类的实例。

class Logger {
     
  printName(name = 'there') {
     
    this.print(`Hello ${
       name}`);
  }

  print(text) {
     
    console.log(text);
  }
}

const logger = new Logger();
const {
      printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined

上面代码中,printName方法中的this,默认指向Logger类的实例。但是,如果将这个方法提取出来单独使用,this会指向该方法运行时所在的环境(由于 class 内部是严格模式,所以 this 实际指向的是undefined),从而导致找不到print方法而报错。

一个比较简单的解决方法是,在构造方法中绑定this,这样就不会找不到print方法了。

class Logger {
     
  constructor() {
     
    this.printName = this.printName.bind(this);
  }

  // ...
}

静态方法

如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

注意,如果静态方法包含this关键字,这个this指的是类,而不是实例。

class Foo {
     
  static bar() {
     
    this.baz();
  }
  static baz() {
     
    console.log('hello');
  }
  baz() {
     
    console.log('world');
  }
}

Foo.bar() // hello

父类的静态方法,可以被子类继承。
静态方法也是可以从super对象上调用的。

实例属性的新写法

实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。

class IncreasingCounter {
     
  _count = 0;
  get value() {
     
    console.log('Getting the current value!');
    return this._count;
  }
  increment() {
     
    this._count++;
  }
}

静态属性

静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。

class Foo {
     
}

Foo.prop = 1;
Foo.prop // 1

私有方法和私有属性

new.target 属性

new是从构造函数生成实例对象的命令。ES6 为new命令引入了一个new.target属性,该属性一般用在构造函数之中,返回new命令作用于的那个构造函数。如果构造函数不是通过new命令或Reflect.construct()调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的

Class 内部调用new.target,返回当前 Class。

class Rectangle {
     
  constructor(length, width) {
     
    console.log(new.target === Rectangle);
    this.length = length;
    this.width = width;
  }
}

var obj = new Rectangle(3, 4); // 输出 true

Symbol & generator

symbol

数据类型: number,string,boolean,Object,undefined,function

用typeof检测出来的数据类型是:symbol


定义

let syml = Symbol('aaa');

注意:

  1. Symbol 不能new
  2. Symbol() 返回一个唯一值
    可以做一个可以,定义一些唯一的的或私有一些东西
  3. sysmbol 是一个单独的数据类型,就叫symbol,基本类型
  4. 如果symbol作为key,用for in循环不出来

箭头函数
()=>{}

generator函数

  • 生成器
  • 解决异步问题,深度嵌套问题,async
  • 语法
function * show(){
	yield;
}

定义:

function* gen() {
     
            yield 'welcom';
            yield 'zpp';
            return 'haha'
        }

调用:

let g1 = gen();
console.log(g1.next());
 //{value: "welcom", done: false} false 指的是还没有完成,后面还有任务
 console.log(g1.next()); 
 //{value: "zpp", done: false}
 console.log(g1.next());
 //{value: "haha", done: true}
 console.log(g1.next()); 
 //{value: undefined, done: true}

上述调用太过复杂
可以通过for … of自动遍历generation(return的东西不会遍历出)

for (let val of g1) {
     
	console.log(val); //return的东西不会遍历
	//welcom
	//zpp
	}

还可以

  1. 结构赋值
let [a, b] = gen();
console.log(a, b);
//welcom zpp
  1. 扩展运算符
console.log(...gen());
//welcom zpp
  1. Array.from()
console.log(Array.from(gen()));
//(2) ["welcom", "zpp"]

应用:generation配合axios来进行简单的数据请求
https://github.com/axios/axios
https://api.github.com/users

异步的generator一般要配合promise来使用

function* gen() {
     
	let val = yield '2763899039zpp';
   	yield axios.get(`https://api.github.com/users/${
       val}`);
}
let g1 = gen();
let username = g1.next().value;
g1.next(username).value.then(res => {
     
   	console.log(res.data);
})

异步:不连续,上一个操作没有完成,下一个曹所照样开始
同步:连续,上一个操作没有完成,下一个没法开始

关于异步,解决

  • 回调函数
  • 事件监听
  • 发布/订阅
  • promise对象

async、await

async function fn(){
      //表示异步,这个函数里面有异步的任务
	let result = await xxx //表示后面的结果需要等待
}

async特点
1.await只能放到async函数中
2.相比generator语义化更强
3.await后面可以是promise对象,也可以是其他类型
4.async函数返回的是一个promise对象
5.只要await语句后面promise状态变成了reject,那么整个async函数留会中断了(可以使用try…catch)

  • 如何解决async中断问题:
    可以使用try…catch

JavaScript es6-学习笔记-(自用)_第1张图片

Set和WeakSet

数据结构
- 数组
- json,二叉树

set数据结构:类似数组,但是里面没有重复值
let arr = [‘a’,‘a’,‘b’]
let arr = new Array();

set用法:
1.new Set(['a','b','c']);
2.setArr.add('e'):添加
3.setArr.delete('b')):删除一箱
4.setArr.has('a'):判断是否存在‘a’这一项

let setArr = new Set(['a', 'b', 'c']);
console.log(setArr);
console.log(setArr.delete('b'));
console.log(setArr.has('a')); //true

5.属性:setArr.size元素个数
6.方法:setArr.clear()清空

循环:默认是values

let setArr = new Set(['a', 'b', 'c', 'd', 'e']);
for (let item of setArr) {
     
    console.log(item);
}
//a b c d e
console.log('-----------------------------')
for (let item of setArr.keys()) {
     
    console.log(item);
}
//a b c d e
console.log('-----------------------------')
for (let item of setArr.values()) {
     
    console.log(item);
}
//a b c d e
console.log('-----------------------------')
for (let item of setArr.entries()) {
     
    console.log(item);
}

JavaScript es6-学习笔记-(自用)_第2张图片
更方便的一种循环

setArr.forEach((v,k)=>{
     
    console.log(v,k)
})

add返回的是自己
setArr.add('e').add('f').add('g');

set的用处:数组去重

//数组去重
let arr = [1, 1, 2, 3, 4, 5, 5, 6, 7, 8, 7, 6, 2];
let newArr = [...new Set(arr)];
console.log(newArr);
 //(8) [1, 2, 3, 4, 5, 6, 7, 8]

注意点:
new Set([]) 存储数组
如果用add的方式添加对象的话是没有问题的,但是不能直接给Set中添加对象
new WeakSet({}) 存储json
初始往里面添加Json是不可以的,最好用add添加,没有size()属性

Map和WeakMap

map:

类似json,但是json的键(key)只能是字符串,map的值可以是任意的类型

使用:
1.let map = new Map();
2.map.set(key,value);设置一个值
3.map.get(key)获取一个值
4.map.delete(key) 删除一个值
5.map.has(key) 判断有没有这个值
6.map.clear(key) 清空

  let map = new Map();
        map.set('a', 'aaa');
        console.log(map);
        //Map(1) {"a" => "aaa"}
        console.log(map.get("a"));
        //aaa

循环:和set一样

WeakMap():

key只能是对象

总结:

  • set里面是数组,不重复,没有key,没有get方法
  • Map对json功能增强,key可以是任意类型值

数字变化和Math新增的东西

数字(数值)变化:

二进制:(Binary)
let a = 0b010101;
八进制:(Octal)
let a = 0o666
十六进制:
#ccc

Number(),paseInt,paseFloat()

Number.isNaN(NaN) ->true
Number.isFinite(a) 用来检查一个数值是否为有限的(finite)
Number.isInteger() 判断数字是不是整数
Number.paseInt();
Number.paseFloat();

安全整数Number.isSafeInteger(a);
[ -(2^ 53-1),(2^ 53-1)]
Number.MAX_SAFE_INTEGER;最大安全整数
Number.MIN_SAFE_INTEGER;最小安全整数

let a = 2 ** 53;
Number.isSafeInteger(a);
Math
方法 说明
Math.trunc 截取,只保留整数部分 方法用于去除一个数的小数部分,返回整数部分。
Math.sign 判断一个数是正数、负数、0
Math.cbrt() 方法用于计算一个数的立方根。

ES2016 新增了一个指数运算符(**

ES2018(ES9)新增东西

命名捕获
  • 语法:?<名字>
    案例:拆封日期
    (1)不用命名捕获来拆分日期
let str = '2018-01-20';
let reg = /(\d{4})-(\d{2})-(\d{2})/
console.log(str.match(reg));
//(4) ["2018-01-20", "2018", "01", "20", index: 0, input: "2018-01-20", groups: undefined]
let dataArr = str.match(reg);
let year = dataArr[1];
let month = dataArr[2];
let day = dataArr[3];
console.log(year, month, day);
//2018 01 20

(2)用命名捕获来拆分日期,可以获得json格式

let reg1 = /(?\d{4})-(?\d{2})-(?\d{2})/
console.log(str.match(reg1).groups);
//{year: "2018", month: "01", day: "20"}
  • 反向引用命名捕获
    \k
let reg = /^(?welcome)-\k$/;
let str = 'a-a';
let str2 = 'strive-strive'
let str3 = 'welcome-welcome'
console.log(reg.test(str)); //false
console.log(reg.test(str2)); //false
console.log(reg.test(str3)); //true

\1\k都可以

let reg2 = /^(?welcome)-\k-\1$/;
let str4 = 'welcome-welcome-welcome';
console.log(reg2.test(str4)); //true
  • replace替换
    $
let str = '2018-01-20'
let reg = /(?\d{4})-(?\d{2})-(?\d{2})/
console.log(str.match(reg).groups);
//{year: "2018", month: "01", day: "20"}
console.log(str.replace(reg, '$/$/$'));
//2018/01/20

用函数来处理

let str1 = str.replace(reg, (...args) => {
     
    let {
      year, month, day } = args[args.length - 1];
    return `${
       day}/${
       month}/${
       year}`
})
console.log(str1);
//20/01/2018
dotAll模式 s

之前‘.’在正则里面表示匹配任意的东西,但是不包括\n

标签函数
funtion fn(){
}
fn() //这样调用是普通函数
fn `` //标签函数使用

Proxy的使用

  • proxy:代理·
    扩展(增强)对象的一些功能

  • proxp作用:预警
    上报、扩展功能、统计、增强对象
    proxy是设计模式的一种,代理模式

  • 语法:

new Proxy(target,handler);
let obj = new Proxy(被代理的对象,对代理的对象做什么操作);
handler:
{
get(){} //获取的时候干得事情
set(){} //设置的时候干得事情
deleteProperty(){} //删除的时候干得事情
has() //是否有这个东西
apply() //调用函数
}
  • 使用案例
    案例一:当属性不存在的时候不返回“undefined”,而是返回指定的值
let obj = {
     
    name: 'zpp',
}

let newObj = new Proxy(obj, {
     
    get(target, property) {
     
        if (property in target) {
       //如果该属性在这个对象上
            return target[property];
        }
        else {
     
            throw new ReferenceError(`${
       property}属性不在此对象上`)
        }
    }
})
console.log(newObj.age

案例二:创建元素

const DOM = new Proxy({
     }, {
     
    get(target, property) {
     
        return function (attr = {
     }, ...children) {
     
            const el = document.createElement(property);
            for (let key of Object.keys(attr)) {
     
                el.setAttribute(key, attr[key]);
            }
            for (let child of children) {
     
                if (typeof child == 'string') {
     
                    child = document.createTextNode(child);
                }
                el.appendChild(child);
            }
            return el;
        }
    }
})
//let oDiv = DOM.div({ id: 'div', class: 'aaa' }, '我是div', 'aaa')
//console.log(oDiv)
let oDiv = DOM.div(
    {
      id: 'div', class: 'aaa' }, '我是div', '哈哈',
    DOM.a({
      href: 'http://baidu.com' }, '访问百度'),
    DOM.ul({
     },
        DOM.li({
     }, '1111'),
        DOM.li({
     }, '2222'),
        DOM.li({
     }, '3333'),
        DOM.li({
     }, '4444'),
    )
)
window.onload = function () {
     
    document.body.appendChild(oDiv);
}

JavaScript es6-学习笔记-(自用)_第3张图片
案例三:set的使用

let obj = new Proxy({
     }, {
     
    set(target, prop, value) {
     
        if (prop == 'age') {
     
            if (!Number.isInteger(value)) {
     
                throw new TypeError(`年龄必须为整数`)
            }
            if (value > 200) {
     
                throw new RangeError('年龄超标了,必须小于200岁')
            }
        }
    }
})
obj.a = 123;
obj.name = 'zpp';
obj.age = 201;

案例四:hasdeleteProperty的使用

let json = {
     
    a: 1,
    b: 2
}
let newJson = new Proxy(json, {
     
    deleteProperty(target, property) {
     
        console.log(`你要删除${
       property}属性`);
        delete target[property];
    },
    has(target, property) {
     
        console.log(`判断是否调用has方法`);
        //todo
    }
})
'a' in newJson;
delete newJson.a;
console.log(newJson)

案例五:apply的使用

Reflect.apply()反射,可以将原来的函数返回出去

Reflect.apply(调用夫人函数,this指向,参数数组)

let res = Reflect.apply(Math.ceil, null, [9.8])
console.log(res);
//10

Reflect:反射
fn.call()
fn.apply() 类似

function show(...args) {
     
    console.log(this);
    console.log(args)
}
show(1, 2, 3, 4);
show.call('abc', 1232, 324);
Reflect.apply(show, 'aaaa', [1, 2, 3, 4]);

你可能感兴趣的:(JavaScript,javascript,es6)