blog icon indicating copy to clipboard operation
blog copied to clipboard

ES6笔记

Open LiuL0703 opened this issue 7 years ago • 6 comments

let

因为JavaScript中没有块级作用域这一概念 而使用let 则可以达到这一效果 let允许把变量作用域限制在块级域中 与var不同的是:var申明要么是全局的 要么是函数级的 而 无法是块级作用域

let vs var

let作用域 是 块级 var 作用域是函数

for(var i = 0;i<5;i++){
    console.log(i); // 0, 1, 2, 3, 4 
}
console.log(i)       // 5
for(let i = 0; i< 5;i++){
    console.log(i); // 0, 1, 2, 3, 4 
}
console.log(i)      // i is not defined

关于let 声明提升

  1. let 的「创建」过程被提升了,但是初始化没有提升。
  2. var 的「创建」和「初始化」都被提升了。
  3. function 的「创建」「初始化」和「赋值」都被提升了。

其他: let 声明会提升到块顶部 ,从块顶部到该变量的初始化语句,这块区域叫做 TDZ(临时死区) 如果你在 TDZ 内使用该变量,JS 就会报错

块级作用域

很多语言中都有块级作用域 JavaScript中使用var 声明变量 以function来划分作用域 用大括号 "{}" 无法限制var的作用域 使用var声明的变量据欧变量提升(declaration hoisting)的效果 ES6中增加的let 在 {} ,if for 里声明 用法与var 相同 但是作用域限定在块级 let声明变量不存在变量提升

'use strict';
function f1(){
    var a = 1;
    let n = 2;
    if(true){
      var a = 20;
      let n = 10;
    }
    console.log(n);       //  2
    console.log(a);       //  20
}
f1();

const

const 用来声明和创建一个常量

  • 常量可以是全局或者局部的函数声明
  • 常量遵循与变量相同的作用域规则
  • 常量不能重新赋值 和 重复声明 所以可以在声明一个常量时 不进行初始化 这个常量将永远保持为undefined
  • 常量不能和他所在的作用域内的其他变量或者函数拥有相同的名称
  • const 也具有块级作用域属性
  • const不能变量提升(必须先声明后使用)
  • const指定变量所在地址 对该变量进行属性设置是可以的 如果完全不想让其变化可以冻结(Object.freeze()) 例子:
const num = 10;
num = 20;
console.log(num);     //10
const num = 10;
var num = 20;
console.log(num);    // 'num' has already been declared
const C = {};
C.a = 1;
console.log(C.a)  // 1
const D = Object.freeze({});
D.a = 2;  // Error
console.log(D.a)   // undefined

类声明和类表达式

类声明

类声明式定义类的一种方式 使用class关键字后跟一个类名就可以定义一个类

'use strict';
class Polygon{
  constructor(height,width){
    this.height = height;
    this.width = width;
  }
}

变量提升

类声明与函数声明不同的一点是 函数声明存在变量提升现象 而类声明不存在 也就是说 必须先声明类 才能使用 否则会抛出异常

var p = new Polygon();    // ReferenceError
class Polygon{}
"use strict"
class Person{
  constructor(name){
    this.name = name;
  }
  sayName(){
        console.log("My Name is "+this.name);
    }
}
var l= new Person("Lily");
l.sayName();   // "My Name is Lily"

类表达式

类表达式是另外一种方式 就像函数表达式一样 在类表达式中 类名是可有可无的 如果定义了类名 则该类名只有在类体中才可以访问到

"use strict";

// 匿名函数表达式
var Polygon = class {
    constructor(height,width){
        this.height = height;
        this.width = width;
    }
};

// 命名函数
var Polygon = class Polygon{
    constructor(height,width){
        this.height = height;
        this.width = width;
    }
};

构造函数

类成员包括类构造器和类方法(包括静态方法和实例方法) class 根据constructor方法来创建和初始化对象 constructor方法是类的默认方法 通过new 命令生成对象实例时 自动调用该方法 一个类只能有一个constructor方法 如果没有显示定义就会被隐式添加

constructor(){}

constructor默认返回实例对象(即this)完全可以指定返回另一个对象

"use strict"

class Foo{
    constructor(){
        return Object.create(null);
    }
}

new Foo() instanceof Foo     // false

constructor 方法是一个特殊的类方法 它既不是静态方法也不是实例方法 它仅会在实例化一个类的时候被调用 一个类只能有一个名为constructor属性的方法 否则 会抛出SyntaxError异常

静态方法

static关键字定义两个类的静态方法 静态方法被称为无需实例化类也可以当类被实例化 静态方法通常用于为应用程序创建实用函数

"use strict";
class Point{
    constructor(x,y){
        this.x = x;
        this.y = y;
    }
    static distance(a,b){
        const dx = a.x - b.x;
        const dy = a.y - b.y;
        return Math.sqrt(dx*dx+ddy*dy);
    }
}

const p1 = new Point(5,5);
const p2 = new Point(10,10);

console.log(Point.distance(p1,p2));

extend 关键字 创建子类

extends 关键字可以用来创建继承于某个类的子类

栗子:根据Animal 类创建一个Dog类

class Animal{
    constructor(name){
        this.name = name;
    }
    speak(){
        console.log(this.name+" makes a noise");
    }
}
class Dog extends Animal{
    speak(){
        console.log(this.name +" barks");
    }
}

var dog = new Dog("Mini");
dog.speak();       // "Mini barks"

Map

map对象是一个简单的键值 任何值(包括对象和原始值)都可以作为一个键或者一个值

var m  = new Map();
var o = {p:"Hello World"};
m.set(o,"content");
m.get(o)                 // "content"
console.log(m)      //  Map { { p: 'Hwll' } => 'content' }

上面代码使用set方法 将对象o作为m的一个键 Map也接受一个数组作为参数 该数组的成员是一个个表示键值对的数组

var map = new Map([["name","zhangsan"],["title","Author"]]);
map.size  // 2
map.get("name")   // "zhangsan"
map.get("title")     // "Author"

注意Map的键实际上是和内存地址绑定的 知道内存地址不一样就视为两个键

  • 如果使用对象作为键名 就不用担心自己的属性与原对象的属性名同名
  • 如果Map键是简单类型(数字,字符串 布尔)则 主要两个值严格相等 Map将其视为一个键 包括0 和-0
  • NaN不严格的等于自身 但是 Map将其视为同一个键
var myMap = new Map();
myMap.set(NaN, "not a number");

myMap.get(NaN);                 // "not a number"

var otherNaN = Number("foo");
myMap.get(otherNaN);        // "not a number"

两个NaN作为Map的键来说是没有区别的

实例的属性和操作方法

size属性返回Map结构的成员总数 即返回映射对象中的键/值对数目 **set(key,val)**属性设置key对应的键值 然后返回整个Map结构 如果key已有值 则覆盖原有值 否则就生成这个键值

var m = new Map();
m.set("edition",6);
m.set(272,"ozil");
m.set(undefined,"nah");

set方法返回的是Map本身 因此可以采用链式写法

  • get(key) 方法读取key对应的键值 找不到就返回 undefined
  • has(key) 方法返回一个布尔值 表示某个键是否存在于Map中
  • delete(key) 方法删除某个键 返回true 如果删除失败 返回false
  • clear() 清除所有成员 没有返回值
var m = new Map();
m.set("a",1);
m.set("b",2);
m.set("keys",3);
m.has("keys")           //  true
m.delete("a")            
console.log(m)         // Map(2) {"b" => 2, "keys" => 3}
m.clear()                   // {}

map 遍历方法

  • keys():返回键名的遍历器
  • values(): 返回键值得遍历器
  • entries(): 返回所有成员的遍历器
  • forEach(): 返回Map的所有成员
var myMap  = new Map();
myMap.set(0,"zero");
myMap.set(1,"one");
maMap.set(2,"two");

for(var key of myMap.keys()){
    console.log(key); 
}
//  0  1  2

for(var item of myMap.entries()){
    console.log(item);
}

//  [0, "zero"]  [1, "one"]  [2, "two"]

myMap.forEach(function(val,key){
    console.log(key+ "="+val);
},myMap)

//  0=zero  1=one 2=two

WeakMap

WeakMap结构与Map结构基本类似 唯一的区别是它只接受对象作为键名(null除外)不接受其他类型的值作为键名 而且键名所指向的对象 不计入垃圾回收机制

var map = new WeakMap();
map.set(1,2);
// TypeError: 1 is not an object 
map.set(Symbol(),2);
// TypeError:Invalid value used as weak map key

WeakMap的设计目的在于,键名是对象的弱引用(垃圾回收机制不将该引用考虑在内),所以其所对应的对象可能会被自动回收。当对象被回收后,WeakMap自动移除对应的键值对。   典型应用是,一个对应DOM元素的WeakMap结构,当某个DOM元素被清除,其所对应的WeakMap记录就会自动被移除。基本上,WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。   WeakMap与Map在API上的区别主要是两个,一是没有遍历操作(即没有key()、values()和entries()方法),也没有size属性;二是无法清空,即不支持clear方法。这与WeakMap的键不被计入引用、被垃圾回收机制忽略有关。

LiuL0703 avatar Jan 02 '18 11:01 LiuL0703

Arrow Funtion

箭头函数使用注意点

  • 函数体内的this对象 绑定定义时所在的对象 而不是使用时所在的对象 也就是说箭头函数中使用this会发生this丢失 箭头函数中this是固定的 他没有自己的this 所以不能调用call() apply() bind()这些方法改变this指向
  • 不可以当做构造函数 即不可以使用new 命令 否则会抛出错误
  • 不可以使用arguments对象 该对象在函数体内不存在 如果要用可以用Rest参数代替
  • 不可以用yield命令 因此箭头函数不能做Generator函数 this对象的指向是可变的 但在箭头函数中 它是固定的
function Timer(){
    this.seconds = 0;
    setInterval(()=>this.seconds++,1000)
}
var timer = new Timer();
setTimeout(()=>console.log(timer.seconds),3000)
//  3

上面代码中,Timer函数内部的setInterval调用了this.seconds属性,通过箭头函数将_this绑定在Timer的实例对象。否则,输出结果是0,而不是3。

function Timer () {
  this.seconds = 0
  setInterval(function(){ this.seconds++}, 1000)
}
var timer = new Timer()
setTimeout(function(){ console.log(timer.seconds)}, 3000)
// 0

LiuL0703 avatar Jan 02 '18 11:01 LiuL0703

字符

includes(), startsWith(), endsWith()

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

var s = "Hello World!";

s.includes("l");        // true
s.startsWith("Hello")    //true
s.endsWith("!")      // true

同时这三个方法都支持第二个参数 表示搜索位置

var s = 'Hello world!';
 
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false

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

repeat()

返回一个新的字符串表示原字符串重复n次

x.repeat(3)    // "xxx"
"Hello".repeat(2)  //"HellloHello"
"na".repeat(0)    // ""

参数为小数则会被向下取整 如果repeat的参数是负数或者Infinity,会报错。 但是,如果参数是0到-1之间的小数,则等同于0,这是因为会先进行取整运算。0到-1之间的小数,取整以后等于-0,repeat视同为0。 如果repeat的参数是字符串,则会先转换成数字。

"n".repeat(2.9)  // "nn"

'na'.repeat(Infinity)
// RangeError
'na'.repeat(-1)
// RangeError

'na'.repeat(-0.9) // ""
'na'.repeat(NaN) // ""

'na'.repeat('na') // ""
'na'.repeat('3') // "nanana"

LiuL0703 avatar Jan 02 '18 11:01 LiuL0703

Math

Math.trunc()::去除一个数的小数部分 返回正数部分 (对空值和无法截取的值 返回NaN)

Math.tranc(1.1)    // 1
Math.tranc(-1.1)   // 1

Math.sign: 判断一个数到底是正数 复数 还是0 正数返回1 负数返回-1 为0 返回0 为-0 返回-0 其他值返回 NaN

Math.sign(-5) // -1
Math.sign(5) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign('hubwiz'); // NaN

Math.cbrt:计算一个数的立方根。

Math.cbrt(-1); // -1
Math.cbrt(0);  // 0
Math.cbrt(2);  // 1.2599210498948732

Math.fround:返回一个数的单精度浮点数形式。

Math.fround(0);     // 0
Math.fround(1.337); // 1.3370000123977661
Math.fround(NaN);   // NaN

Math.hypot:返回所有参数的平方和的平方根。

Math.hypot(3, 4);        // 5
Math.hypot(3, 4, 5);     // 7.0710678118654755
Math.hypot();            // 0
Math.hypot(NaN);         // NaN
Math.hypot(3, 4, 'foo'); // NaN
Math.hypot(3, 4, '5');   // 7.0710678118654755
Math.hypot(-3);          // 3

如果参数不是数值,Math.hypot方法会将其转为数值。只要有一个参数无法转为数值,就会返回NaN。

LiuL0703 avatar Jan 02 '18 11:01 LiuL0703

Symbol

ES6引入的一种新的原始数据类型 表示独一无二的值 是JavaScript的第七种数据结构 (undefined, null,Boolean,String,Number,Object,Symbol) Symbol通过Symbol函数生成 凡是属性名属于Symbol类型 就是独一无二的 可以保证不与其他属性名冲突

let s = Symbol();
typeof s
// "symbol"
var s1 = Symbol("foo");
var s2 = Symbol("foo");
console.log(s1===s2);  // false;

做属性名的Symbol三种写法

var mySymbol = Symbol();
//First
var a = {};
a[mySymbol] = "Hello";
// Second
var a = {
    [mySymbol]:"Hello"
};
// Third
var a = {};
Object.defineProperty(a,mySymbol,{value:"Hello"})

// 上面所以写法都会得到相同的结果
console.log(a[mySymbol]);    // "Hello"

注意:

  • Symbol值作为对象属性名时,不能用点运算符。
  • 在对象的内部,使用Symbol值定义属性时,Symbol值必须放在方括号之中。
  • Symbol值作为属性名时,该属性还是公开属性,不是私有属性。
  • Symbol类型还可以用于定义一组常量,保证这组常量的值都是不相等的。

属性名遍历

Symbol作为属性名,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有Symbol属性名。 Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的Symbol值。

var obj = {};
var a = Symbol("a");
var b = Symbol("b");
obj[a] = "Hello";
obj[b] = "World";
obj["c"] = "Wow";
var objectSymbols = Object.getOwnPropertySymbols(obj);
console.log(objectSymbols);    //  [Symbol(a),Symbol(b)]

Symbol.for()

使用给定的key搜索现有符号 如果有找到则返回 否则新创建一个 有时,我们希望重新使用同一个Symbol值,Symbol.for方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值。 Symbol.for()与Symbol()这两种写法,都会生成新的Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的Symbol类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for("cat") 30次,每次都会返回同一个Symbol值,但是调用Symbol("cat") 30次,会返回30个不同的Symbol值。

Symbol.for("bar")===Symbol.for("bar"); 
// true;
Symbol("bar")===Symbol("bar")   
// false;

Symbol.keyFor()

为给定的符号从全局符号注册表中索检一个共享符号键 Symbol.keyFor方法返回一个已经登记的Symbol类型的值key

var s1 = Symbol.for("foo");
Symbol.For(s1);   // "foo"

var s2 = Symbol("bar");
Symbol.For(s2);   // undefined 

LiuL0703 avatar Jan 02 '18 11:01 LiuL0703

Promise

Promise 对象用于异步计算。一个 Promise 表示一个现在或将来可用,亦或永远不可用的值。 Promise 对象 两个特点

  • 对象状态不受外界影响 Promise对象代表一个异步操作 有三种状态 Pending( 初始状态,未履行或拒绝。) Resolve(已完成又称Fullfilled 意味着操作成功完成。)和Rejected(意味着操作失败)
  • 一旦状态改变 就不会再次发生变化

基本用法

var promise = new Promise(function(resolve,reject){
    // some code 
    if(/* 异步操作成功*/){
        resolve(value);
    }else{
        reject(error);
    }
});

Promise构造函数接受一个函数作为参数 该函数 提供两个参数分别是resolve和reject 这两个函数又JavaScript引擎提供 不用自己实现 其中resolve函数的作用是将Promise对象的状态从"未完成"=>"成功"(即从Pending=>Resolved)在异步操作成功时调用 并将异步操作结果 作为参数传递出去; reject函数则用来将Promise对象的状态从Pending=>"Rejected"在异步操作失败时调用 将一步操作报出的错误作为参数 传递出去 Promise实例生成后可以用then 方法分别指定Resolved状态和Reject状态的回调函数

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

then 方法可以接受两个回调函数作为参数第一个回调函数是Promise对象的状态变为Resolved时调用 第二个回调函数是Promise对象状态变为Reject时调用 其中第二个参数可选 这两个函数都接受Promise对象传出的值作为参数

Promise.prototype.then()与catch()

then() 方法返回一个Promise 有两个参数 分别为Promise在 success和failure情况下的回调函数

p.then(onFulfilled,onRejected);
p.then(function(value){
    // success
},function(reason){
    // failure
})
var p2 = new Promise(function(resolve,reject){
    resolve(1);
});
p2.then(function(value){
    console.log(value);   // 1
    return value+1; 
}).then(function(value){
    console.log(value);   // 2
});

catch() 方法只处理Promise被拒绝的情况 并返回一个Promise 该方法的行为和调用Promise.prototype.then(undefined,onRejected)相同

p.catch(onRejected);
p.catch(function(reason){
    // rejected
})

示例:

var p1 = new Promise(function(value){
    resolve("Success");
});
p1.then(function(value){
    console.log(value);   // "Success"
    throw "Wrong";
}).catch(function(e){
    console.log(e);    // "Wrong"
});

.catch()方法主要用于处理promise组合

Promise.all()

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

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

Promise.all()接受一个数组作为参数 p1,p2,p3都是Promise 对象的实例 如果不是就会调用Promise。resolve方法 将参数转化为Promise实例在处理 p的状态由p1 p2 p3 决定 只有p1,p2,p3状态都为fullfilled p的状态才会是fullfilled 只要其中之一是rejected p 的状态就变为rejected 同时返回第一个被rejected的实例

var p = Promise.resolve(3);
Promise.all([true,p,]).then(value=>{
    console.log(value)  // [true,3]
})

Promise.race()

race函数返回一个Promise 这个Promise根据传入的Promis中的第一个状态确定

var p1 = new Promise(function(resolve,reject){
    setTimeout(resolve,500,"one");
})
var p2 = new Promise(function(resolve,reject){
    setTimeout(resolve,100,"two");
})

Promise.race([p1,p2]).then(value=>{
    console.log(value);    // "two"
})

Promise.reject()

Promise.reject()方法会返回一个新的实例 该实例状态为rejected

Promise.reject("Testing Static reject").then(function(reason){
    // 未被调用
},function(reason){
    console.log(reason);   // "Testing Static reject"
});

Promise.reject(new Error("faill")).then(function(error){
    // 未被调用
},function(err){
    console.log(err);  // [Error:fail]
})

Promise.resolve(value)

该方法返回一以给定值resolve掉的Promise对象 如果这个值是thenable 返回的Promise会继续这个thenable的对象接收到它的最终状态 否则这个被返回的promise对象会以这个值被fullfilled

Promise.resolve(value);
Promise.resolve(promise);
Promise.resolve(thenable);

value用来resolve待返回的promise对象的参数。既可以是一个Promise对象也可以是一个thenable。 使用静态方法Promise.resolve

Promise.resolve("Success").then(function(val){
    console.log(value);    // "Success"
},function(val){
   // not found
});

以数组resolve

varv p = new Promise.resolve([1,2,3]);
p.then(function(v){
    console.log(v[0]);   // 1
});

resolve另一个Promise对象

var o = Promise.resolve(true);
varv cast = Promise.resolve(o);
cast.then(v=>{
    console.log(v)  //true
})

LiuL0703 avatar Jan 02 '18 11:01 LiuL0703

Generator

生成器对象是由一个 generator function 返回的,并且它符合可迭代协议和迭代器协议。从语法上可以理解为Generator函数是一个状态机 封装多个内部状态 执行Generator函数返回一个遍历对象 可以依次遍历Generator函数内部的每一个状态 Generator函数有两个特征

  • function命令与函数名之间有一个*
  • 函数内部使用yield语句 定义不同的内部状态
function* helloWorldGenerator(){
    yield"hello";
    yield"world";
    return "ending";
}

var hw = helloWorldGenerator();

next方法

调用next方法 使指针移向下一个状态 也就是说 每次调用next方法 内部指针就从函数头部或上一次停下来的地方开始执行 直到遇到下一个yield语句 为止 换言之 Generator函数是分段执行的 yield语句是暂停执行的标记 而next方法可以恢复执行

hw.next();
//   { value: 'hello', done: false }
hw.next();
//  { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }

调用Generator函数,返回一个遍历器对象,代表Generator函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield语句后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。

yield* 语句

用来在一个Generator函数里面执行另一个Generator函数 如果yield命令后面跟的是一个遍历器对象 需要在以yield命令后加* 号 表明返回的是一个遍历器对象

function* anotherGeneator(i){
    yield i+1;
    yield i+2;
    yield i+3;
}

function *generator(i){
    yield i;
    yield* anotherGenerator();
    yield i+10;
}

var gen = generator(10);
console.log(gen.next().value);  // 10
console.log(gen.next().value);  // 11
console.log(gen.next().value);  // 12
console.log(gen.next().value);  // 13
console.log(gen.next().value);  // 20

LiuL0703 avatar Jan 02 '18 11:01 LiuL0703