ES6 (ES2015)总览

一、let 与 const

1.let与var的区别:

  1. let 是在代码块内有效,var 是在全局范围内有效
  2. let 只能声明一次 var 可以声明多次
  3. let 不存在变量提升,var 会变量提升

注:for 循环计数器很适合用 let

for (var i = 0; i < 10; i++) {
  setTimeout(function(){
    console.log(i);
  })
}
// 输出十个 10
for (let j = 0; j < 10; j++) {
  setTimeout(function(){
    console.log(j);
  })
}
// 输出 0123456789

变量 i 是用 var 声明的,在全局范围内有效,所以全局中只有一个变量 i, 每次循环时,setTimeout 定时器里面的 i 指的是全局变量 i ,而循环里的十个 setTimeout 是在循环结束后才执行,所以此时的 i 都是 10。

**变量 j 是用 let 声明的,当前的 j 只在本轮循环中有效,每次循环的 j 其实都是一个新的变量,**所以 setTimeout 定时器里面的 j 其实是不同的变量,即最后输出12345。(若每次循环的变量 j 都是重新声明的,如何知道前一个循环的值?这是因为 JavaScript 引擎内部会记住前一个循环的值)。

**2.const:声明之后不允许改变的变量 **
const 声明一个只读变量,声明之后不允许改变。意味着,一旦声明必须初始化,否则会报错。

3.let和const暂时性死区:

var PI = "a";
if(true){
  console.log(PI);  // ReferenceError: PI is not defined
  const PI = "3.1415926";
}

ES6 明确规定,代码块内如果存在 let 或者 const,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。代码块内,在声明变量 PI 之前使用它会报错。

注意点:const 如何做到变量在声明初始化之后不允许改变的?其实 const 其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不允许改动。此时,你可能已经想到,简单类型和复合类型保存值的方式是不同的。是的,对于简单类型(数值 number、字符串 string 、布尔值 boolean),值就保存在变量指向的那个内存地址,因此 const 声明的简单类型变量等同于常量。而复杂类型(对象 object,数组 array,函数 function),变量指向的内存地址其实是保存了一个指向实际数据的指针,所以 const 只能保证指针是固定的,至于指针指向的数据结构变不变就无法控制了,所以使用 const 声明复杂类型对象时要慎重。




二、解构赋值

可嵌套、可忽略、不完全解构、剩余运算符、解构默认值、

在数组的解构中,解构的目标若为可遍历对象和对象,皆可进行解构赋值。可遍历对象即实现 Iterator 接口的数据。es6中有三类结构生来就具有Iterator接口:数组、类数组对象、Map和Set结构。 至于对象没有布置iterator接口的原因,不知道最近大家有没有看根据《你一生的故事》拍成的电影“降临",片中出现的外星语言是一门非线性的语言。而我们说的数组,Map等结构中的成员都是有顺序的,即都是线性的结构,而对象,各成员并没有一个确定的顺序,所以遍历时先遍历谁后遍历谁并不确定。所以,给一个对象部署iterator接口,其实就是对该对象做一种线性转换。如果你有这种需要,就需要手动给你的对象部署iterator接口咯




三、Symbol

用途:唯一的属性名

ES6 数据类型除了 Number 、 String 、 Boolean 、 Objec t、 null 和 undefined ,还新增了 Symbol
ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名

用法:
Symbol 函数栈不能用 new 命令,因为 Symbol 是原始数据类型,不是对象。可以接受一个字符串作为参数,为新创建的 Symbol 提供描述用来显示在控制台或者作为字符串的时候使用便于区分

let sy = Symbol("KK");
console.log(sy);   // Symbol(KK)
typeof(sy);        // "symbol"
 
// 相同参数 Symbol() 返回的值不相等
let sy1 = Symbol("KK"); 
sy === sy1;       // false

Symbol 作为对象属性名时不能用 . 点运算符,要用方括号。因为.运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性。

let syObject = {};
syObject[sy] = "kk";
 
syObject[sy];  // "kk"
syObject.sy;   // undefined

注意:
Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问。但是不会出现在 for…in 、 for…of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols()Reflect.ownKeys() 取到。

使用 Symbol 定义常量,这样就可以保证这一组常量的值都不相等。

Symbol.for()
Symbol.for() 类似单例模式,首先会在全局搜索被登记的 Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值,若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索。

let yellow = Symbol("Yellow");
let yellow1 = Symbol.for("Yellow");//创建一个以Yellow为key的Symbol值,并登记在全局环境中
yellow === yellow1;      // false
 
let yellow2 = Symbol.for("Yellow");//查找到全局环境中的Symbol,并返回使用
yellow1 === yellow2;     // true

Symbol.keyFor()
Symbol.keyFor() 返回一个已登记的 Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被登记。

let yellow1 = Symbol.for("Yellow");
Symbol.keyFor(yellow1);    // "Yellow"



四、Map 与 Set

Map对象
任何值(对象(包括函数)或者原始值(NaN为number类型)) 都可以作为一个键或一个值的对象。相当于Object的扩展

注意:虽然 NaN 和任何值甚至和自己都不相等(NaN !== NaN 返回true),NaN作为Map的键来说是没有区别的。

Maps 和 Objects 的区别:

  1. 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。
  2. Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。
  3. Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
  4. Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。

Map 与 Array的转换:
Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象。
使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组。

var kvArray = [["key1", "value1"], ["key2", "value2"]];
 
// Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象
var myMap = new Map(kvArray);
 
// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组
var outArray = Array.from(myMap);

Set 对象
Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。(不重复地存储任意类型)

注意:

  1. +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复;
  2. undefined 与 undefined 是恒等的,所以不重复;
  3. NaN 与 NaN 是不恒等的,但是在 Set 中只能存一个,不重复。
  4. 对象之间引用不同不恒等,即使值相同,Set 也能存储

类型转换:

// Array 转 Set
var mySet = new Set(["value1", "value2", "value3"]);
// 用...操作符,将 Set 转 Array
var myArray = [...mySet];
String
// String 转 Set
var mySet = new Set('hello');  // Set(4) {"h", "e", "l", "o"}
// 注:Set 中 toString 方法是不能将 Set 转换成 String

Set 对象作用
数组去重

var mySet = new Set([1, 2, 3, 4, 4]);
[...mySet]; // [1, 2, 3, 4]

并集

var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var union = new Set([...a, ...b]); // {1, 2, 3, 4}

交集

var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var intersect = new Set([...a].filter(x => b.has(x))); // {2, 3}

差集

var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var difference = new Set([...a].filter(x => !b.has(x))); // {1}



五、Proxy与 Reflect

Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。

Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。

Proxy:
一个 Proxy 对象由两个部分组成: target 、 handler 。在通过 Proxy 构造函数生成实例对象时,需要提供这两个参数。 target 即目标对象, handler 是一个对象,声明了代理 target 的指定行为。通过构造函数新建实例时其实是对目标对象进行了浅拷贝,因此目标对象与代理对象会互相影响
注意:严格模式下,set代理如果没有返回true,就会报错。

let target = {
    name: 'Tom',
    age: 24
}
let handler = {
    get: function(target, key) {   //拦截读取
        console.log('getting '+key);
        return target[key]; // 不是target.key
    },
    set: function(target, key, value) { //拦截赋值,如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用。
        console.log('setting '+key);
        target[key] = value;
    }
}
let proxy = new Proxy(target, handler)
proxy.name     // 实际执行 handler.get
proxy.age = 25 // 实际执行 handler.set
// getting name
// setting age
// 25

apply(target, ctx, args)用于拦截函数的调用、call 和 reply 操作。target 表示目标对象,ctx 表示目标对象上下文,args 表示目标对象的参数数组。
has(target, propKey)用于拦截 HasProperty 操作,即在判断 target 对象是否存在 propKey 属性时,会被这个方法拦截。此方法不判断一个属性是对象自身的属性,还是继承的属性。(注意:此方法不拦截 for … in 循环。)
construct(target, args)用于拦截 new 命令。返回值必须为对象。
等等。(更多见 菜鸟教程https://www.runoob.com/w3cnote/es6-reflect-proxy.html

Reflect

  • ES6 中将 Object 的一些明显属于语言内部的方法移植到了 Reflect 对象上(当前某些方法会同时存在于 Object 和 Reflect 对象上),未来的新方法会只部署在 Reflect 对象上。
  • Reflect 对象对某些方法的返回结果进行了修改,使其更合理。
  • Reflect 对象使用函数的方式实现了 Object 的命令式操作。

Reflect.get(target, name, receiver) 查找并返回 target 对象的 name 属性。
Reflect.set(target, name, value, receiver) 将 target 的 name 属性设置为 value。返回值为 boolean ,true 表示修改成功,false 表示失败。当 target 为不存在的对象时,会报错。
Reflect.has(obj, name)是 name in obj 指令的函数化,用于查找 name 属性在 obj 对象中是否存在。返回值为 boolean。如果 obj 不是对象则会报错 TypeError。

Proxy 与 Reflect 的组合使用
Reflect 对象的方法与 Proxy 对象的方法是一一对应的。所以 Proxy 对象的方法可以通过调用 Reflect 对象的方法获取默认行为,然后进行额外操作。

(拓展)组合使用实现观察者模式:

// 定义 Set 集合
const queuedObservers = new Set();
// 把观察者函数都放入 Set 集合中
const observe = fn => queuedObservers.add(fn);
// observable 返回原始对象的代理,拦截赋值操作
const observable = obj => new Proxy(obj, {set});
function set(target, key, value, receiver) {
  // 获取对象的赋值操作
  const result = Reflect.set(target, key, value, receiver);
  // 执行所有观察者
  queuedObservers.forEach(observer => observer());
  // 执行赋值操作
  return result;
}



六、字符串

子串的识别

let string = "apple,banana,orange";
string.includes("banana");     // true
string.startsWith("apple");    // true
string.endsWith("apple");      // false
string.startsWith("banana",6)  // true从索引(下标)为6的地方开始

这三个方法只返回布尔值,如果需要知道子串的位置,还是得用 indexOf 和 lastIndexOf 。
这三个方法如果传入了正则表达式而不是字符串,会抛出错误。而 indexOf 和 lastIndexOf 这两个方法,它们会将正则表达式转换为字符串并搜索它

字符串重复
repeat():返回新的字符串,表示将字符串重复指定次数返回。如果参数是小数,向下取整

字符串补全
padStart:返回新的字符串,表示用参数字符串从头部(左侧)补全原字符串。
padEnd:返回新的字符串,表示用参数字符串从尾部(右侧)补全原字符串。

如果原字符串加上补全字符串长度大于指定长度,则截去超出位数的补全字符串

模板字符串
模板字符串相当于加强版的字符串,用反引号 `,除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式(可以调用函数)。

标签模板
标签模板,是一个函数的调用,其中调用的参数是模板字符串。

alert`Hello world!`;
// 等价于
alert('Hello world!');

当模板字符串中带有变量,会将模板字符串参数处理成多个参数。

function f(stringArr,...values){
 let result = "";
 for(let i=0;i<stringArr.length;i++){
  result += stringArr[i];
  if(values[i]){
   result += values[i];
        }
    }
 return result;
}
let name = 'Mike';
let age = 27;
f`My Name is ${name},I am ${age+1} years old next year.`;
// "My Name is Mike,I am 28 years old next year."
 
//f`My Name is ${name},I am ${age+1} years old next year.`;
// 等价于
//f(['My Name is',',I am ',' years old next year.'],'Mike',28);



七、数值

数值的表示
二进制表示法新写法: 前缀 0b 或 0B
八进制表示法新写法: 前缀 0o 或 0O 。

console.log(0b11 === 3); // true
console.log(0B11 === 3); // true
console.log(0o11 === 9); // true
console.log(0O11 === 9); // true

常量Number.EPSILON
Number.EPSILON 属性表示 1 与大于 1 的最小浮点数之间的差。

它的值接近于 2.2204460492503130808472633361816E-16,或者 2-52。

测试数值是否在误差范围内:

0.1 + 0.2 === 0.3; // false
// 在误差范围内即视为相等
equal = (Math.abs(0.1 - 0.3 + 0.2) < Number.EPSILON); // true

最大/最小安全整数
安全整数表示在 JavaScript 中能够精确表示的整数,安全整数的范围在 2 的 -53 次方(Number.MIN_SAFE_INTEGER)到 2 的 53 次方(Number.MAX_SAFE_INTEGER)之间(不包括两个端点),超过这个范围的整数无法精确表示。

方法
Number.isFinite()用于检查一个数值是否为有限的( finite ),即不是 Infinity

// NaN 不是有限的
// Number.isFinate 没有隐式的 Number() 类型转换,所有非数值都返回 false

Number.isNaN()用于检查一个值是否为 NaN

// 在全局的 isNaN() 中,以下皆返回 true,因为在判断前会将非数值向数值转换
// 而 Number.isNaN() 不存在隐式的 Number() 类型转换,非 NaN 全部返回 false
Number.isNaN("NaN");      // false
Number.isNaN(undefined);  // false
Number.isNaN({});         // false
Number.isNaN("true");     // false

Number.parseInt()用于将给定字符串转化为指定进制的整数。是从全局移植到 Number 对象的方法,之所以逐步减少全局方法,是用于全局变量的模块化。方法的行为没有发生改变。

// 不指定进制时默认为 10 进制
// 指定进制
Number.parseInt('0011',2); // 3
// 与全局的 parseInt() 函数是同一个函数

Number.parseFloat()用于把一个字符串解析成浮点数。
Number.isInteger()用于判断给定的参数是否为整数。

// JavaScript 内部,整数和浮点数采用的是同样的储存方法,因此 1 与 1.0 被视为相同的值
// 数值的精度超过 53 个二进制位时,由于第 54 位及后面的位被丢弃,会产生误判
// 一个数值的绝对值小于 Number.MIN_VALUE(5E-324),即小于 JavaScript 能够分辨
// 的最小值,会被自动转为 0,也会产生误判

Number.isSafeInteger()用于判断数值是否在安全范围内。

Math 对象的扩展
ES6 在 Math 对象上新增了 17 个数学相关的静态方法,这些方法只能在 Math 中调用。
Math.cbrt用于计算一个数的立方根。
Math.trunc用于返回数字的整数部分。
Math.sign判断数字的符号(正、负、0)
Math.log10(x)用于计算以 10 为底的 x 的对数。
Math.log2()用于计算 2 为底的 x 的对数。
等。。。。。。
指数运算符

1 ** 2; // 1
// 右结合,从右至左计算
2 ** 2 ** 3; // 256
// **=
let exam = 2;
exam ** = 2; // 4



八、对象

对象字面量

  1. 属性的简洁表示法:
    ES6允许对象的属性直接写变量,这时候属性名是变量名,属性值是变量值。
  2. 方法名也可以简写:
    方法名可以去掉:function,如果是Generator 函数,则要在前面加一个星号,例如:
    const obj = {
      * myGenerator() {
        yield 'hello world';
      }
    };
    
  3. 属性名表达式:
    ES6允许用表达式作为属性名,但是一定要将表达式放在方括号内。注意点:属性的简洁表示法和属性名表达式不能同时使用,否则会报错。

对象的拓展运算符

拓展运算符(…)用于取出参数对象所有可遍历属性然后拷贝到当前对象。
注意:有相同的属性的时候,后面的属性会覆盖前面的属性;拓展运算符后面紧跟的是null或者undefined,没有效果也不会报错。

对象的新方法
Object.assign(target, source_1, ···)用于将源对象的所有可枚举属性复制到目标对象中。
注意点:

  1. 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回。
  2. 因为 null 和 undefined 不能转化为对象,所以会报错。
  3. 当参数不止一个时,null 和 undefined 不放第一个,即不为目标对象时,会跳过 null 和 undefined ,不报错
  4. assign 的属性拷贝是浅拷贝(数据复制只是复制了一个引用地址)
  5. 会将数组处理成对象,然后再进行属性复制。

Object.is(value1, value2)用来比较两个值是否严格相等(复杂类型引用的内存地址需相同),与(===)基本类似
基本用法

Object.is("q","q");      // true
Object.is(1,1);          // true
Object.is([1],[1]);      // false,复杂类型需要指向的内存地址相同才能相等,
                         //判断两个数组或对象是否相同可以使用JSON.stringify函数
                         //(但要保证属性顺序相同才有可能相等)
Object.is({q:1},{q:1});  // false

与(===)的区别

//一是+0不等于-0
Object.is(+0,-0);  //false
+0 === -0  //true
//二是NaN等于本身
Object.is(NaN,NaN); //true
NaN === NaN  //false



九、数组

方法:

  1. Array.of()将参数中所有值作为元素形成数组。参数不可含空位,会报错。

  2. Array.from()类数组对象可迭代对象转化为数组。参数可含空位。一个类数组对象必须含有 length
    属性,且元素属性名必须是数值或者可转换为数值的字符。可迭代对象指:map对象、set对象、字符串等。

  3. find()查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素。// 数组空位处理为 undefined

  4. findIndex()查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。// 数组空位处理为 undefined

  5. fill()将一定范围索引的数组元素内容填充为单个指定的值。

  6. copyWithin()将一定范围索引的数组元素修改为此数组另一指定范围索引的元素。

  7. entries()遍历键值对。

  8. keys()遍历键名。

  9. values()遍历键值。

  10. includes()数组是否包含指定值。

  11. flat() 嵌套数组转一维数组;flatMap()先对数组中每个元素进行了的处理,再对数组执行 flat() 方法。

数组缓冲区
数组缓冲区是内存中的一段地址。
定型数组的基础。
实际字节数在创建时确定,之后只可修改其中的数据,不可修改大小。
视图
视图是用来操作内存的接口。
视图可以操作数组缓冲区或缓冲区字节的子集,并按照其中一种数值数据类型来读取和写入数据。
DataView 类型是一种通用的数组缓冲区视图,其支持所有8种数值型数据类型。

// 默认 DataView 可操作数组缓冲区全部内容
let buffer = new ArrayBuffer(10);
    dataView = new DataView(buffer); 
dataView.setInt8(0,1);
console.log(dataView.getInt8(0)); // 1
 
// 通过设定偏移量(参数2)与长度(参数3)指定 DataView 可操作的字节范围
let buffer1 = new ArrayBuffer(10);
    dataView1 = new DataView(buffer1, 0, 3);
dataView1.setInt8(5,1); // RangeError

定型数组
一种视图。
数组缓冲区的特定类型的视图。
可以强制使用特定的数据类型,而不是使用通用的 DataView 对象来操作数组缓冲区。
注意:

  1. length 属性不可写,如果尝试修改这个值,在非严格模式下会直接忽略该操作,在严格模式下会抛出错误。
  2. 定型数组可使用 entries()、keys()、values()进行迭代。
  3. find() 等方法也可用于定型数组,但是定型数组中的方法会额外检查数值类型是否安全,也会通过 Symbol.species 确认方法的返回值是定型数组而非普通数组。
  4. concat() 方法由于两个定型数组合并结果不确定,故不能用于定型数组;
  5. 另外,由于定型数组的尺寸不可更改,可以改变数组的尺寸的方法,例如 splice() ,不适用于定型数组。
  6. 所有定型数组都含有静态 of() 方法和 from() 方法,运行效果分别与 Array.of() 方法和 Array.from() 方法相似,区别是定型数组的方法返回定型数组,而普通数组的方法返回普通数组。
  7. 定型数组不是普通数组,不继承自 Array 。
  8. 定型数组中增加了 set() 与 subarray() 方法。 set() 方法用于将其他数组复制到已有定型数组, subarray() 用于提取已有定型数组的一部分形成新的定型数组。

扩展运算符
复制数组:数组中的空位,扩展后会变成undefined




十、函数

函数参数的扩展

默认参数:

  1. 使用函数默认参数时,不允许有同名参数。

  2. 只有在未传递参数,或者参数为 undefined 时,才会使用默认参数,null 值被认为是有效的值传递。

  3. 函数参数默认值存在暂时性死区,在函数参数默认值表达式中,还未初始化赋值的参数值无法作为其他参数的默认值。

    function f(x,y=x){
        console.log(x,y);
    }
    f(1);  // 1 1
     
    function f(x=y){
        console.log(x);
    }
    f();  // ReferenceError: y is not defined
    

不定参数:
不定参数用来表示不确定参数个数,形如,…变量名,由…加上一个具名参数标识符组成。有且只有一个不定参数。

箭头函数
箭头函数提供了一种更加简洁的函数书写方式

  1. 当箭头函数没有参数或者有多个参数,要用 () 括起来。

  2. 当箭头函数函数体有多行语句,用 {} 包裹起来,表示代码块,当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。

  3. 当箭头函数要返回对象的时候,为了区分于代码块,要用 () 将对象包裹起来

  4. == 没有 this、super、arguments 和 new.target 绑定 ==
    箭头函数里面没有 this 对象,
    此时的 this 是外层的 this 对象。
    箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象。
    总结:箭头函数的this是定义时候的外层this对象

  5. 不可以作为构造函数,也就是不能使用 new 命令,否则会报错

  6. 适合使用的场景:将外部 this 传递到回调函数中

  7. 不适合使用的场景:定义函数的方法,且该方法中包含 this;以及需要使用动态 this 的时候




十一、迭代器Iterator

Iterator 是 ES6 引入的一种新的遍历机制,迭代器有两个核心概念:

  1. 迭代器是一个统一的接口,它的作用是使各种数据结构可被便捷的访问,它是通过一个键为Symbol.iterator 的方法来实现。
  2. 迭代器是用于遍历数据结构元素的指针(如数据库中的游标)。

可迭代的数据结构

  • Array
  • String
  • Map
  • Set
  • Dom元素(正在进行中)

普通对象不可迭代,如果数据结构类似于数组 (类数组) 的形式,则可以借助 Array.from() 方法进行转换迭代。




十二、Class 类

  • 在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。
  • class 的本质是 function。
  • 它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。

注意:

  1. 类定义不会被提升,这意味着,必须在访问前对类进行定义,否则就会报错。
  2. 类中方法不需要 function 关键字。
  3. 方法间不能加分号。
  4. ES6 中,prototype 仍旧存在,虽然可以直接自类中定义方法,但是其实方法还是定义在 prototype 上的。

静态属性
静态属性:class 本身的属性,即直接定义在类内部的属性( Class.propname ),不需要实例化。 ES6 中规定,Class 内部只有静态方法,没有静态属性。

class Example {
// 新提案
    static a = 2;
}
// 目前可行写法
Example.b = 2;

公共属性

class Example{}
Example.prototype.a = 2;

实例属性
实例属性:定义在实例对象( this )上的属性。

class Example {
    a = 2;
    constructor () {
        console.log(this.a);
    }
}

name 属性
返回跟在 class 后的类名(存在时)。

let Example=class Exam {
    constructor(a) {
        this.a = a;
    }
}
console.log(Example.name); // Exam
 
let Example=class {
    constructor(a) {
        this.a = a;
    }
}
console.log(Example.name); // Example

方法:
constructor() 方法是类的默认方法,创建类的实例化对象时被调用。
static()静态方法,可通过.来调用
原型方法,可通过.来调用
实例方法,创建类的实例化对象时被调用。

类的实例化

  • class 的实例化必须通过 new 关键字。
  • 实例化的对象共享原型对象

decorator
decorator 是一个函数,用来修改类的行为,在代码编译时产生作用。

  1. 类修饰:
    只能有一个参数,第一个参数 target,指向类本身。

      function testable(target) {
    	        target.isTestable = true; 
    	        } 
      @testable class Example {} 
      Example.isTestable; // true 
    

    多个参数——嵌套实现在这里插入代码片

        function testable(isTestable) {
            return function(target) {
                target.isTestable=isTestable;
            }
         } 
        @testable(true) class Example {} 
        Example.isTestable; // true 实例属性
    

    上面两个例子添加的是静态属性,若要添加实例属性,在类的 prototype 上操作即可。

  2. 方法修饰
    3个参数:target(类的原型对象)、name(修饰的属性名)、descriptor(该属性的描述对象)。

     class Example {
            @writable
            sum(a, b) {
                return a + b;
            } 
        } 
      function writable(target, name, descriptor) {
            descriptor.writable = false;
            return descriptor; // 必须返回 
            } 
    

    修饰器执行顺序: 由外向内进入,由内向外执行。

     class Example {
        @logMethod(1)
        @logMthod(2)
        sum(a, b){
            return a + b;
        }
    }
    function logMethod(id) {
        console.log('evaluated logMethod'+id);
        return (target, name, desctiptor) => console.log('excuted         logMethod '+id);
    }
    // evaluated logMethod 1
    // evaluated logMethod 2
    // excuted logMethod 2
    // excuted logMethod 1
    

封装与继承

 set a(a){
        console.log('setter');
        this.a = a; // 自身递归调用
    }
  • set方法中的赋值语句中的变量前面添加一个下滑线来避免自身调用的死循环。

  • getter (get方法)不可单独出现,且出现的时候应当同级

  • 通过 extends 实现类的继承。子类 constructor 方法中必须有 super ,且必须出现在 this 之前。(也就是:调用父类构造函数,只能出现在子类的构造函数。)

  • 调用父类方法, super 作为对象,在普通方法中,指向父类的原型对象,在静态方法中,指向父类

    class Father {
        test(){
            return 0;
        }
        static test1(){
            return 1;
        }
    }
    class Child2 extends Father {
        constructor(){
            super();
            // 调用父类普通方法
            console.log(super.test()); // 0
        }
        static test3(){
            // 调用父类静态方法
            return super.test1+2;
        }
    }
    Child2.test3(); // 3
    
  • 不可继承常规对象。 解决方案:Object.setPrototypeOf(Child.prototype, Father);




十三、模块

ES6 的模块自动开启严格模式,不管你有没有在模块头部加上 use strict;
每个模块都有自己的上下文,每一个模块内声明的变量都是局部变量,不会污染全局作用域。
export 与 import

  1. import 命令会提升到整个模块的头部,首先执行。
  2. 可以改写 import 变量类型为对象的属性值,不能改写 import 变量类型为基本类型的值。
  3. 单例模式:多次重复执行同一句 import 语句,那么只会执行一次,而不会执行多次。
  4. import 同一模块,声明不同接口引用,会声明对应变量,但只执行一次 import 。
  5. 静态执行特性:import 是静态执行,所以不能使用表达式和变量。
  6. export 与 import 可以在同一模块使用
  7. 在一个文件或模块中,export、import 可以有多个,export default 仅有一个。
  8. export default 中的 default 是对应的导出接口变量。
  9. 通过 export 方式导出,在导入时要加{ },export default 则不需要。
  10. export default 向外暴露的成员,可以使用任意变量来接收。
  11. 不同模块导出接口名称命名重复, 使用 as 重新定义变量名。



十四、Promise 对象

用途:处理异步编程
从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

Promise 状态
Promise 异步操作有三种状态:

  1. pending(进行中)
  2. fulfilled(已成功)
  3. rejected(已失败)

除了异步操作的结果,任何其他操作都无法改变这个状态。

Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变。只要处于 fulfilled 和 rejected ,状态就不会再变了即 resolved(已定型)。

Promise 创建
要想创建一个 promise 对象、可以使用 new 来调用 Promise 的构造器来进行实例化。下面是创建 promise 的步骤:

var promise = new Promise(function(resolve, reject) {
    // 异步处理
    // 处理结束后、调用resolve 或 reject
});

Promise 构造函数包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject。

then 方法
对于已经实例化过的 promise 对象可以调用 promise.then() 方法,传递 resolve 和 reject 方法作为回调。

promise.then(onFulfilled, onRejected)

promise简化了对error的处理,上面的代码我们也可以这样写:

promise.then(onFulfilled).catch(onRejected)

then 方法接收两个函数作为参数,第一个参数是 Promise 执行成功时的回调,第二个参数是 Promise 执行失败时的回调,两个函数只会有一个被调用。通过 .then 形式添加的回调函数,不论什么时候,都会被调用。
then 方法将返回一个 resolved 或 rejected 状态的 Promise 对象用于链式调用,且 Promise 对象的值就是这个返回值。

catch方法
Promise.prototype.catch 方法是 Promise.prototype.then(null, rejection) 的别名,用于指定发生错误时的回调函数。
Promise 对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获。

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

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

上面代码中,Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)

p 的状态由 p1、p2、p3 决定,分成两种情况:

  1. 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  2. 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

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

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

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值。
如果Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调Promise.resolve方法,将参数转为Promise实例,再进一步处理。

resolve 方法
将现有对象转为Promise对象

var jsPromise = Promise.resolve($.ajax('/whatever.json'));

上面代码将 jQuery 生成 deferred 对象,转为一个新的 ES6 的 Promise 对象。
如果 Promise.resolve 方法的参数,不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为fulfilled。
Promise.resolve方法的参数就是回调函数的参数。

reject方法
Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递给实例的回调函数。




十五、Generator 函数

ES6 新引入了 Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。

Generator 有两个区分于普通函数的部分:

  1. 一是在 function 后面,函数名之前有个 * ;
  2. 函数内部有 yield 表达式。

其中 * 用来表示函数为 Generator 函数,yield 用来定义函数内部的状态。

使用场景
实现 Iterator:为不具备 Iterator 接口的对象提供遍历方法。
比如对象实现遍历:

function* objectEntries(obj) {
    const propKeys = Reflect.ownKeys(obj);
    for (const propKey of propKeys) {
        yield [propKey, obj[propKey]];
    }
}
 
const jane = { first: 'Jane', last: 'Doe' };
for (const [key,value] of objectEntries(jane)) {
    console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe

jane 原生是不具备 Iterator 接口无法通过 for… of遍历。这边用了 Generator 函数加上了 Iterator 接口,所以就可以遍历 jane 对象了。

Generator 函数不会像普通函数一样立即执行,而是返回一个指向内部状态对象的指针,所以要调用遍历器对象Iterator 的 next 方法,指针就会从函数头部或者上一次停下来的地方开始执行。

函数返回的遍历器对象的方法:

  1. next 方法:
    一般情况下,next 方法不传入参数的时候,yield 表达式的返回值是 undefined 。当 next 传入参数的时候,该参数会作为上一步yield的返回值
  2. return 方法:
    return 方法返回给定值,并结束遍历 Generator 函数。
    return 方法提供参数时,返回该参数;不提供参数时,返回 undefined 。
  3. throw 方法:
    throw 方法可以再 Generator 函数体外面抛出异常,再函数体内部捕获。
  4. yield* 表达式
    yield* 表达式表示 yield 返回一个遍历器对象,用于在 Generator 函数内部,调用另一个 Generator 函数。



附:ES7中的async

用途:处理需要等待的异步操作

async
async 是 ES7 才有的与异步操作有关的关键字,和 Promise , Generator 有很大关联。

async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。

await
async 函数中可能会有 await 表达式,async 函数执行时,如果遇到 await 就会先暂停执行 ,等到触发的异步操作完成后,恢复 async 函数的执行并返回解析值。
await 关键字仅在 async function 中有效。如果在 async function 函数体外使用 await ,你只会得到一个语法错误。
await 操作符用于等待一个 Promise 对象或者任何要等待的值。

返回值:
Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。
如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值