ES6

文章目录

es6一些语法

ES6

块状作用域的绑定

使用var声明的变量都会被当成当前作用域顶部声明的变量,这个就是变量提升(Hoisting)机制

  • let 声明:let声明的变量不会提升,而且其作用域限制在函数内部以及块中(两个花括号之间)
  • const 声明:同let,但是const声明的变量不能改变其值,故用const声明的同时要初始化
    注意事项:
  • 被let或者const声明的变量,在同一作用域不能再被声明,否则会出现错误
  • 使用let或者const声明的变量必须在声明之后才能使用(用typeof 操作符也不能),否则会出现错误
  • 使用let或者const在全局作用域创建变量,不会添加为全局对象的属性,它只会遮蔽全局对象属性而不会覆盖
  • let或const声明的变量在循环中的行为是每次迭代创建新的绑定

建议:默认使用const,实在需要改变值时使用let

字符串和正则

子字符串的识别

  • include(),如果字符串中检查到指定文本则返回true,否则返回false
  • startsWith(),如果字符串的起始部分检测到指定文本就返回true,否则返回false
  • endsWith(),如果字符串的结束部分检测到指定文本则返回true,否则返回false

他们都支持第二参数(可选):指定开始搜索的索引值

字符串的重复

repeat()方法,接收一个number参数,表示重复的次数,返回重复之后的新字符串

正则表达式的复制

1
2
3
4
let re1 = /ab/i;
re2 = new RegExp(re1,"g"); //支持通过第二参数修改修饰符

console.log(re2.toString()); //"/ab/g"

获取修饰符

1
2
3
4
let re = /ab/g;

console.log(re.source); //ab
console.log(re.flags); //g

模板字面量

模板字面量语法支持创建领域专用语法(DSL)

  • 多行字符串
  • 基本的字符串格式化
  • HTML转义

    多行字符串

    1
    2
    let message = `Multiline
    string`;

反撇号中所有的空白符都属于子字符串的一部分,要注意缩进
可以这样写:

1
2
3
4
5
let html = `
<div>
<h1></h1>
</div>`;
html.trim();

可以在模板字面量显式使用\n来指明换行符

字符串占位符

在模板字面量中,可以把任何合法的Javascript表达式嵌入到占位符中并将其作为字符串的一部分输出到结果中。
占位符构成:${表达式}
如:

1
2
3
let name = "Nicholas",
message = `hello,${name}!`;
console.log(message); //hello,Nicholas!

可以把一个模板字面量通过${}嵌入到另一个模板字面量中

标签模板

1
let message = tag`hello,world`;

tag是一个函数,执行模板字面量上的转换并返回最终的字符串值

标签函数使用不定参数来定义占位符,从而简化数据处理的过程

1
2
3
function tag(literlas,...substitutions){
//返回一个字符串
}

literals包含一个额外属性raw,是包含每一个字面值的原生等价信息的数组,如:literlas.raw[1]
literlas.length - 1 === substitution.length

使用原始值

使用String.raw()作为tag

1
2
let message = String.raw`Multiline\nstring`;
console.log(message);//"Multiline\\nstring"

函数

函数默认参数值

声明函数时,可以为任意参数指定默认值,只有不为参数传入值或者主动传入undefined时才会使用默认值。

1
2
3
function makeRequest(url,timeout = 2000,callback = function(){}){
//do something
}

null是一个合法值,故传入null时不会使用默认值
还可以使用函数来得到默认的参数值

1
2
3
4
5
6
7
let value = 1;
function getValue(){
return value++;
}
function add(a,b = getValue()){
//do something
}

也可以引用前面参数的值

1
2
3
function add(a,b = a){
//do something
}

arguments对象

在ES6下,arguments对象将不随着形式参数的变化而变化。

不定参数

在函数命名参数前添加三个点(…)就表明这是一个不定参数,该参数是一个数组,包含着自它之后传入的所有参数

1
2
3
function add(a,...nums){
//do something
}

每一个函数最多只能声明一个不定参数,并且要放在所有参数的末尾

展开运算符

可以指定一个数组,将他们打散之后作为各自独立的参数传入函数

1
2
let values = [25,50,75,100];
console.log(Math.max(...values));

new.target

当通过new调用函数时,即调用函数的[[Construct]]方法,函数体内的new.target被赋值为new操作符的操作目标;当调用[[Call]]方法时,new.target的值为undefined。

块状函数

在块状作用域中声明的函数会被视为块状函数,但是使用function 操作符仍然会变量提升

箭头函数

  • 箭头函数中this、super、arguments及new.target这些值由最近一层非箭头函数决定。
  • 不能通过关键字new调用(没有[[Construct]]方法)
  • 没有原型
  • 不可以改变this值的绑定
  • 不支持arguments对象
  • 不支持重复的命名参数
    1
    2
    3
    4
    5
    6
    let getName = ()=>"name";
    let sum = (a,b) => a+b;
    let sum2 = (a,b) => {return a+b;};
    let reflect = value => value;
    let doNothing = () => {};
    ((name) => {console.log(name)})("name");//创建并立即调用

箭头函数this的值取决于最近一层非箭头函数的this;否则this值会被设置为undefined

尾调用优化

  • 尾调用布冯问当前栈的变量(也就是说函数不是一个闭包)
  • 在函数内部,尾调用时最后一条语句
  • 尾调用的结果作为函数值返回
    常见递归优化:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const factorial = function(n){
    if(n <= 1){
    return 1;
    } else{
    return n*factorial(n-1); //无法优化
    }
    }
    const factorial = function(n,res = 1){
    if(n <= 1){
    return 1*res;
    } else{
    res *= n;
    return factorial(n-1,res);//可以优化
    }
    }

对象

对象类别

  • 普通对象(Ordinary):具有JavaScript所有的默认内部行为对象
  • 特异对象(Exotic):具有某些与默认行为不符的对象
  • 标准对象(Standard):ES6中定义的对象,如Array,Date等。其既可以是普通对象,也可以是特异对象
  • 内建对象:脚本执行时就存在于JavaScript执行环境中的对象,所有的标准对象都是内建对象

    对象初始值的简写

    当一个对象的属性与本地变量同名时,不必再写冒号和值,简单地只写属性名即可。
    1
    2
    3
    4
    5
    6
    7
    8
    const Person = function(name,age){
    return {
    name,
    age
    };
    };
    let person1 = new Person("dengyf",22);
    console.log(person1.name);//dengyf

    对象方法的简写

    1
    2
    3
    4
    5
    6
    let person = {
    name: "Micholas",
    sayname(){
    console.log(this.name);
    }
    }

简写方法可以使用super关键字

可计算属性

1
2
3
4
5
6
7
let suffix = "name";
let person = {
["first" + suffix]: "Nicholas",
[`last${suffix}`]: "Zakas"
};
console.log(person["lastname"]);//"Zakas"
console.log(person.firstname);//"Nicholas"

新增方法

Object.is()

1
2
3
4
Object.is(NaN,NaN);//true
NaN === NaN; //false
Object.is(+0,-0);//false
+0 === -0; //true

Object.assign()

接收任意对象的源对象,并按指定顺序将属性复制到接收对象中。如果多个源对象具有同名属性,则排位靠后的源对象会覆盖排位靠前的。

1
2
3
4
5
6
7
8
9
10
let receiver = {};
Object.assign(receiver,{
name: "Nicolas",
age:23
},
{
firstName: "sed",
age:11
});
console.log(receiver.age)//11

Object.setPrototypeOf()

接收两个参数,第一个是要改变原型的对象,第二个为替代第一个参数的对象

1
2
3
4
5
6
7
8
9
10
11
 let human = {
getName(){
return this.name;
}
};
let person = {
name: "Nicholas",
age:22
};
Object.setPrototypeOf(person,human);
console.log(human.isPrototypeOf(person));//true

super

一般当对象中一些方法与原型中方法重名时,调用原型中方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let person = {
name:"person",
getName(){
return this.name;
}
};
let dog = {
name:"dog",
getName(){
return this.name;
}
}
let friend = {
name:"friend",
getName(){
return Object.getPrototypeOf(this).getName.call(this) + this.name; //if don't add call(this),will return "perosn"
}
}
Object.setPrototypeOf(friend,person);
friend.getName();"friendfriend"

而super就是简化Object.getPrototypeOf(this).getName.call(this)
源代码变成:

1
2
3
4
5
6
let friend = {
name:"friend",
getName(){
return super.getName() + this.name;
}
};

super.getName()始终指向perosn.getName(),其引用不是动态变化的
ES6将一个方法定义为一个函数,它有一个内部的[[HomeObject]]属性来容纳这个方法从属的对象
super的引用第一步在其函数的[[HomeObject]]属性上调用Object.getPrototypeOf()方法检索原型,然后搜索原型找到同名函数,最后设置this绑定并且调用相应的方法。

解构

解构是一种打破数据结构,将其拆分成更小部分的过程。

对象解构

1
2
3
4
5
6
7
let node = {
type: "Identifier",
name: "foo"
};
let {type,name} = node;
console.log(type);//"Identifier"
console.log(name);//"foo"

使用let、const、var解构声明变量,必须提供初始化程序(等号右侧的值)

解构赋值

1
({type,name} = node)

代码块语句不允许出现在赋值语句左侧,所以上面的括号是不可缺少的

设定默认值

1
2
3
4
5
6
7
8
let node = {
type: "Identifier",
name: "foo"
};
let {type,name,value = true} = node;
console.log(type);//"Identifier"
console.log(name);//"foo"
console.log(value);//true

非同名变量赋值

1
2
3
4
5
6
7
let node = {
type: "Identifier",
name: "foo"
};
let {type:localType,name:localName = "bar"} = node;
console.log(localType);//"Identifier"
console.log(localName);//"foo"

嵌套对象解构

1
2
3
4
5
6
7
8
9
10
11
let node = {
type: {
start:"css",
end:"js"
},
name: "foo"
};
let {type:{start:localStart}} = node;
let {end} = node.type;
console.log(localStart);//css
console.log(end);//js

数组解构

1
2
3
4
5
6
7
let colors = ["green",["red","light red"],"blue"],
black = "black";
let [firstColor = "pink",[,lightRed],thirdColor] = colors;
[,,black] = colors;
console.log(lightRed);//"light red"
console.log(firstColor);//"green"
console.log(black);//"blue"

不定元素

1
2
3
let colors = ["green",["red","light red"],"blue"];
let [firstColor,...restColor] = colors;
console.log(restColor.length);//2

高级应用

  • 交换两个数值
    1
    2
    3
    4
    5
    let a = "a",
    b = "b";
    [a,b] = [b,a];
    console.log(a);//"b"
    console.log(b);//"a"
  • 复制数组
    1
    2
    let colors = ["red","green"];
    let [...colorsCopy] = colors;

    解构参数

    1
    2
    3
    function setCookie(name,value,{secure,path,domain,expires} = {}){ //define default
    //do something
    }
    设置默认值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    const cookieDefault = {
    secure:false,
    path:"/",
    domain:"example.com",
    expires:new Date()(Date.now() + 36000000)
    };
    function setCookie(name,value,{secure = cookieDefault.secure,path = cookieDefault.path,domain = cookieDefault.domain,expires = cookieDefault.expires} = cookieDefault){
    //do something
    }

    Symbol

    ES6引入的第六种原始类型:symbol->创建非字符串名称。

    Symbol的使用

    1
    2
    3
    4
    5
    6
     let firstName = Symbol("first name"); //接收一个可选参数,添加一段文本描述
    let person = {
    [firstName] : "Nicholas"
    };
    console.log(person[firstName]);//"Nicholas"
    console.log(firstName.toString());//"Symbol(first name)"
    symbol还可以用在Object.defineProperty()Object.defineProperties()方法

    Symbol共享体系

    1
    2
    3
    4
    5
    let uid = Symbol.for("uid");
    let o = {};
    o[uid] = "123";
    console.log(o[uid]);//"123"
    console.log(uid);//"Symbol(uid)"
    Symbol.for()方法首先在全局Symbol注册表中搜索键为”iud”的Symbol是否存在,如果存在,直接返回已有的Symbol;否则,创建一个新的Symbol并使用这个键在Symbol全局注册表中注册,然后返回一个新创建的Symbol

Set与Map集合

  • Set集合是一种无重复元素的列表
  • Map集合内含多组键值对

    Set集合

    1
    2
    3
    4
    5
    6
    let set = new Set();
    set.add(5);
    set.add("5");
    console.log(set.size);//2
    let set1 = new Set([1,2]);
    console.log(set.size);//2

Set集合不会对所存值进行强制的类型转换
如果后续传入的值已经存在,则此次操作会被忽略
通过has()方法检测Set集合是否存在某个值 —>使用Object.is()判断是否相等

1
console.log(set.has(5));//true

通过delete()方法移除Set集合中某一个元素,调用clear()方法移除集合中所有元素

1
2
3
4
5
6
7
let set = new Set();
set.add(5);
set.add("5");
set.delete(5);
console.log(set.has(5));//false
set.clear();
console.log(set.size);//0

forEach()

forEach()方法回调函数接收三个参数:

  • Set集合中下一个索引位置
  • 与第一个参数一样的值
  • 被遍历的Set集合本身
    Set中第一个和第二个参数完全一样

    Set和数组互相转化

    Set —> Array
    1
    let set = new Set([1,2,3,4,5]);
    Array —> Set
    1
    let array = [...set];

    Weak Set

    Weak Set只存储对象的弱引用,并且不可以存储原始值;集合中的弱引用如果是对象唯一的引用,则会被回收并释放相应内存。
  • WeakSet只有add、delete、has方法,不支持forEach、size、clear
  • WeakSet不暴露任何迭代器(例如keys()、value()方法),所以无法通过程序检测里面的内容
  • 通过add、delete、has传入非对象参数都会导致程序报错
  • 不能用于for-of循环

    Map集合

    1
    2
    3
    4
    5
    6
    7
    let map = new Map();
    map.set("title","ECMAScript 6");
    console.log(map.get("title"));//"ECMAScript 6" 若不存在返回undefined
    console.log(map.has("title"));//true
    console.log(map.size);//1
    map.delete("title");
    map.clear();

    Map 与 Array

    1
    let map = new Map(["name","Nicholas"],["age",23]);

    forEach()

    回调函数接收三个参数
  • Map集合中下一次索引位置 value
  • 值对应的键名 key
  • Map集合本身

    WeakMap

    列表的键名必须是非null对象,键名对应的值可以是任何类型
    通过set、get、has、delete操作,不支持clear和forEach

Iterator and Generator

ES5创建一个迭代器方法

1
2
3
4
5
6
7
8
9
10
11
12
13
let createIterator = function(items){
let i = 0;
return {
next(){
let done = (i >= items.length);
let value = done? undefined : i++;
return{
done,
value
};
}
}
}

ES6通过生成器创建迭代器

1
2
3
4
5
6
const createIterator = function *(items){
for(let i = 0; i < items.length; i++){
yield items[i];
}
};
let iterator = createIterator([1,2,3]);

也可以这样:

1
2
3
function *createIterator(){
//do something
}

或者

1
2
3
4
5
let o = {
*createIterator(){
//do something
}
}
  • yield关键字可以返回任何值或者表达式
  • yield只能用在生成器内部,不能用在函数外部或者内部的函数,否则会报错
  • 箭头函数不能使用yield关键字

    通过Symbol.iterator访问默认迭代器

    1
    2
    let values = [1,2,3,4];
    let iterator = values[Symbol.iterator]();
    也可以给自定义对象添加生成器
    1
    2
    3
    4
    5
    6
    7
    8
    let collection = {
    items = [],
    *[Symbol.iterator](){
    for(let item of this.items){
    yield item;
    }
    }
    };

    内建迭代器

  • entries() 返回一个迭代器,其值为多个键值对,以数组形式 –> Map集合默认迭代器
  • values() 返回一个迭代器,其值为集合的值 –> 数组和Set集合默认迭代器
  • keys() 返回一个迭代器,其值为集合中所有键名
    使用
    1
    2
    3
    4
    5
    6
    7
    let colors = ["red","green","blue"];
    for(let color of colors.entries()){
    console.log(color);
    }
    for(let key of colors.keys()){
    console.log(key);
    }
    可以使用解构语法
    1
    2
    3
    4
    5
    6
    7
    let data = new Map();
    data.set("name","Nicholas");
    data.set("age",34);

    for(let [key,value] of data){
    console.log(key + "=" + value);
    }

    高级迭代器功能

    给迭代器传递参数

    如果给迭代器的next()方法传递参数,则这个参数的值就会代替生成器内部上一条yield语句的返回值。

第一次调用next()方法时无论传入什么参数都会被丢弃

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function *createIterator(){
let first = yield 1;
let second = yield first + 2;
yield second + 3;
}
let iterator = createIterator();
console.log(iterator.next()); //首先调用yield 1
console.log(iterator.next(4));//将4作为 yield 1 的返回值,并赋值给first,调用yield first+2
console.log(iterator.next(5));//将5作为 yield first+2 的返回值,并赋值给second
console.log(iterator.next());//调用 yield second + 3
//Object {value: 1, done: false}
// Object {value: 6, done: false}
// Object {value: 8, done: false}
// Object {value: undefined, done: true}

在迭代器抛出错误

1
iterator.throw(new Error("Boom"));//错误抛出并阻止代码继续执行

生成器返回语句

在生成器中,return表示所有操作已经完成,done被设置为true。在return后面的语句不会运行。最后一次next()调用可以通过return返回一个特定值。
展开运算发与for-of循环会直接忽略指定的任意返回值,只要done变成true,就立即停止读取其他值

委托生成器

某些情况下,需要将两个迭代器合二为一,这时可以创建一个生成器,再给yield语句添加一个星号,可以将生成数据的过程委托给其他生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function *createNumberIterator(){
yield 1;
yield 2;
return 3;
}
function *createColorIterator(color){
yield "red";
yield color;
}
function *createCombinedIterator(){
yield *createNumberIterator(); //返回值3
yield *createColorIterator("green");
yield true;
}
let iterator = createCombinedIterator();
console.log(iterator.next());//1
console.log(iterator.next());//2
console.log(iterator.next());//"red"
console.log(iterator.next());//"green"
console.log(iterator.next());//true

**yield 可以直接用于字符串,此时使用字符串的默认迭代器。yield “hello”

异步任务执行

简单的任务执行器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function run(taskDef){
//创建一个迭代器
let task = taskDef();
//开始执行task
let result = task.next();
//循环执行task
function step(){
if(!result.done){
result = task.next(result.value);
step();
}
}
//开始迭代
step();
}
run(function *(){
let result = yield 3*8;
console.log(result);
});

JavaScript中的类

基本的类声明语法

1
2
3
4
5
6
7
8
9
10
11
class Person{
constructor(name){
this.name = name;
}
sayName(){
console.log(this.name);
}
}
let person = new Person("Nicholas");
person.sayName();
console.log(typeof Person);//function

不需要在类的各元素之间用逗号分隔,声明最后不需要分号

  • 类声明与let声明类似,不能被提升
  • 在类中,所有方法都是不可枚举
  • 每一个类都有[[Construct]]内部方法,通过关键字调用,用new调用不含[[Construct]]其他方法会抛出错误
  • 只能使用关键字new调用类,否则会抛出错误
  • 类中修改类名会抛出错误
    类表达式
    1
    2
    3
    4
    5
    let Person = class {
    constructor(name){
    this.name = name;
    }
    };

    类命名表达式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    let Person = class PersonType{ //Person可以在外部或者内部使用,可是PersonType只能在内部使用
    constructor(name){
    this.name = name;
    }
    sayName(){
    console.log(this.name);
    }
    };
    //等价于
    let Person = (function(){
    "use strict"
    //构造函数
    const PersonType = function(name){
    if(new.target === undefined){
    throw new Error("必须通过new关键字调用");
    }
    this.name = name;
    };
    Object.defineProperty(PersonType.prototype,"sayName",{
    value:function(){
    if(new.target !== undefined){
    throw new Error("不可通过new调用");
    }
    console.log(this.name);},
    enumerable:false,
    writable: true,
    configurable: true
    });
    return PersonType;
    })();
    let person = new Person("Nicholas");
    person.sayName();

    访问器属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class CustomHTMLElement{
    constructor(element){
    this.element = element;
    }
    get html(){
    return this.element.innerHTML + "hahaah";
    }
    set html(value){
    this.element.innerHTML = value;
    }
    }

    生成器方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class MyClass{
    *createIterator(){
    yield 1;
    yield 2;
    }
    }
    let instance = new MyClass(),
    iterator = instance.createIterator();
    console.log(iterator.next());//1
    console.log(iterator.next());//2

    静态成员

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Person{
    constructor(name){
    this.name = name;
    }
    sayName(){
    console.log(this.name);
    }
    static create(name){
    return new Person(name);
    }
    }
    let person = Person.create("Nicholas");
    person.sayName();//"Nicholas"
    console.log(typeof person.create);//undefined
  • 不能将static用在构造函数上
  • 不可在实例上访问静态成员

    继承与派生类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Rectangle{
    constructor(length,width){
    this.length = length;
    this.width = width;
    }
    getArea(){
    return this.length * this.width;
    }
    }
    class Square extends Rectangle{
    constructor(length){
    super(length,length);
    }
    }
    console.log(Rectangle.isPrototypeOf(Square));//true
    let square = new Square(4);
    console.log(square.getArea());//16
    console.log(square instanceof Square);//true
    console.log(square instanceof Rectangle);//true
    如果派生类不使用构造函数,则当创建新的实例的时候会自动调用super()并传入所有参数。
  • 使用extend的派生类构造函数一定要使用super,否则只能让构造函数返回一个对象。

    派生类中可以继承基类的静态方法

    派生自表达式的类

    只要表达式可以被解析为一个函数并具有[[Construct]]属性和原型,那么就可以用extend进行派生
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    function Rectangle(length,width){
    this.length = length;
    this.width = width;
    }
    Rectangle.prototype.getArea = function(){
    return this.length * this.width;
    }
    function getBase(){
    return Rectangle;
    }
    class Square extends getBase(){ //使用表达式
    constructor(length){
    super(length,length);
    }
    }
    let square = new Square(4);
    console.log(square.getArea());

    内建对象的继承

    1
    2
    3
    class MyArray extends Array{

    }

    Symbol.species属性

改进数组功能

新增方法:Array.of()Array.from()
Array.from接收三个参数,第一个为类数组,第二个为映射函数,第三个为映射函数this的值

1
2
3
4
5
6
7
```fill()```接收三个参数:要填充的值、开始索引、结束索引(不包含)
```copyWithin()```接收两个参数,开始填充索引位置,开始复制索引
### 定型数组
#### 数组缓冲区
数组缓冲区时所有定型数组的根基,他是一段可以包含特定数量字节的内存地址。
```JavaScript
let buffer = new ArrayBuffer(10);//分配10字节

可以通过byteLength来查看缓冲区中的比特数量

1
console.log(buffer.byteLength);//10

通过slice可以返回一个新的ArrayBuffer实例:传入开始索引和结束索引(不包括结束索引)

1
2
let buffer2 = buffer.slice(4,6);
console.log(buffer2.byteLength);//2

通过视图操作数组缓冲区

DataView类型时一种通用的数组缓冲区视图,其支持所有8种数值数据类型

1
let view = new DataView(buffer,5,2);//选择索引5和索引6的字节 -->可选参数

获取视图信息

  • buffer 视图绑定的数组缓冲区
  • byteOffset DataView构造函数第二参数,默认0
  • byteLength DataView构造函数第三参数,默认缓冲区长度

    读写数据

    get方法接收两个参数:读取数据时偏移的字节数量;和一个可选的布尔值,表示是否按照小端序进行读取
    set方法接收三个参数,写入数据时偏移的字节数量,介入的值和一个布尔值,表示是否按照小端序格式存储
  • getInt8()
  • setUint8()
    1
    2
    3
    4
    5
    6
    let buffer = new ArrayBuffer(2);
    view = new DataView(buffer);
    view.setInt8(0,5);
    view.setInt8(1,-1);
    console.log(view.getInt8(0));//5
    console.log(view.getInt8(1))//1

    定型数组

    定型数组实际上是用于数组缓冲区的特定类型的视图,可以强制使用特定的数据类型,而不是使用通用的DataView对象来操作数组缓冲区
    构造函数(部分):
  • Int8Array()
  • Uint32Array()
  • Float64Array()
  • Uint8ClampedArray() –>强制转换:小于0 => 0;大于225 => 225

    创建定型数组

    1
    2
    3
    let buffer = new ArrayBuffer(10),
    view1 = new Int8Array(buffer,5,2);
    console.log(view1.byteLength);//2
    1
    2
    3
    4
    5
    6
    let int = new Int16Array(2),
    float = new Float32Array(5);
    console.log(int.byteLength);//4
    console.log(int.length);//2
    console.log(float.byteLength);//20
    console.log(float.length);//5
    可以将以下作为参数传入构造函数
  • 一个定型数组
  • 一个可迭代对象
  • 一个数组
  • 一个类数组对象
    1
    2
    3
    let int = new Int16Array([1,2,3]);
    console.log(int.length);//3
    console.log(int.byteLength);//6

定型数组不是Array实例
当给定型数组传入非法值时,会用0来代替

附加方法

```接收两个参数:一个是数组,一个是可选偏移量(默认0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
``` subarray() ```接收两个参数,可选开始参数和可选结束参数

## Promise与异步编程
### 背景
#### 单线程
JavaScript引擎是基于单线程事件循环的该概念构建的,同一时刻只允许一个代码块在执行。
JavaScript引擎需要跟踪即将运行的代码,那些代码被放在一个任务队列(job queue)中,每当一段代码准备执行时,都会被添加到任务队列。每当JavaScript引擎中一段代码结束执行,事件循环(event loop)会执行队列中的下一个任务,他是JavaScript引擎的一段程序,负责监控代码执行并管理任务队列。**队列中的任务会从第一个一直执行到最后一个**。
#### 事件模型
事件模型适合处理简单的交互
用户点击按钮或者按下键盘的按键会触发类似onclick这样的事件,它会向任务队列添加一个新任务来相应用户的操作。
#### 回调模式
```JavaScript
readFile("example.txt",function(err,content){
if(err){
throw err;
}
console.log(contents);
});
console.log("Hi");

readFile()函数立即执行,当读取磁盘上的文件时会停止执行。此时console.log(“Hi”)会立即执行并输出”Hi”;当readFile()结束执行时,会向任务队列的末尾添加一个新任务,该任务包含回调函数及相应的参数。

Promise

Promise相当于异步操作结果的占位符,它不会去订阅一个事件,也不会传递一个回调函数给目标函数,而是让函数返回一个Promise。

1
let promise = readFile("example.txt");

这段代码中,readFile()不会立即读取文件,函数会先返回一个表示异步读取操作的Promise对象,未来对这个对象的操作完全取决于Promise对象的声明周期。

Promise的生命周期

  • 进行中状态(pending),此时操作尚未完成,所以也是未处理(unsettled)
  • 异步操作执行结束,Promise变成已处理(settled)状态。包括以下两个状态:Fulfilled –> 操作完成 、 Rejected –> 操作未能完成
    Promise状态改变时,通过then()方法采取特定行动
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    另一个是当Promise状态变成rejected时调用的函数,与失败相关的数据都会传递到这个函数。
    ```catch()```相当于只传入rejected函数的then函数
    #### 创建未完成的Promise
    用Promise构造函数可以创建新的Promise,构造函数只接收一个参数:包含初始化Promise代码的执行器函数(executor)。执行器接收两个参数,分别是resolve()函数和reject()函数。执行器成功完成时调用resolve()函数,反之,失败时调用reject()函数
    ```JavaScript
    const fs = require("fs");

    function readFile(filename){
    return new Promise(function(resolve,reject){

    //触发异步操作
    fs.readFile(filename,"utf-8",function(err,content){
    //检查错误
    if(err){
    reject(err);
    return;
    }
    //成功读取
    resolve(content);
    });
    });
    }
    let promise = readFile("example.txt");
    //监听fulfilled和rejected状态
    promise.then(
    (content)=>{console.log(content);},
    (err)=>{console.error(err.message);}
    );
    创建Promise之后,执行器会立即执行,然后才执行后续代码

    创建已处理的Promise

    1
    2
    3
    ```JavaScript
    let promise = Promise(42);
    promise.then((value)=>{console.log(value);});//42
    1
    2
    3
    ```JavaScript
    let promise = Promise.reject(42);
    promise.catch((value)=>{console.log(value);});//42

如果向Promise.resolve或者Promise.reject函数传入一个promise,那么这个promise会被直接返回

执行器错误

如果执行器内部抛出一个错误,则Promise的拒绝处理程序就会被调用

1
2
3
4
let promise = new Promise(function(resolve,reject){
throw new Error("Explosion");
});
promise.catch((err)=>{console.log(err.message);});//Explosion

每一个执行器都隐含一个try catch块

全局的Promise拒绝处理

Node.js环境的拒绝处理
触发process对象上两个事件:

  • unhandledRejection 事件处理程序接收错误对象和Promise作为参数
  • rejectionHandled 事件处理程序接收被拒绝的Promise对象作为参数
    1
    2
    3
    4
    5
    6
    7
    8
    let rejected;

    process.on("unhandledRejection",function(err,promise){
    console.log(err.message);//"Explosion"
    console.log(rejected === promise);//true
    });

    rejected = Promise.reject(new Error("Explosion"));
    浏览器环境
    触发window对象两个事件,与Node环境完全等效
    浏览器不同的是,两个事件都可以使用拒绝值,即第一个参数

    串联Promise

    每次调用then()方法或者catch()方法时实际上创建并返回另一个Promise,只有当第一个Promise完成或被拒绝后,第二个才被解决
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    let p1 = new  Promise((resolve,reject)=>{
    resolve(42);
    });

    p1.then((value)=>{
    console.log(value);
    }).then(()=>{
    console.log("finished");
    });
    // output:
    // 42
    //finished

    捕获错误

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    let p1 = new  Promise((resolve,reject)=>{
    resolve(42);
    });

    p1.then((value)=>{
    console.log(value);
    throw new Error("explosion");
    }).catch((err)=>{
    console.log(err.message);
    });
    // output:
    // 42
    //explosion

    Promise链的返回值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let p1 = new Promise((resolve,reject)=>{
    resolve(42);
    });
    p1.then((value)=>{
    console.log(value);
    return value + 1;//42
    }).then((value)=>{
    console.log(value);//43
    })
    即使是拒绝处理程序中return也可以返回值到下一个promise完成处理程序

    在Promise链中返回Promise

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    let p1 = new Promise((resolve,reject)=>{
    resolve(42);
    });
    let p2 = new Promise((resolve,reject)=>{
    resolve(43);
    });
    p1.then((value)=>{
    console.log(value);//42
    return p2;
    }).then((value)=>{
    console.log(value);//43
    });
    如果p2被拒绝,就会调用拒绝程序,如果p2被完成,那么调用完成程序

    响应多个Promise

    Promise.all()
    Promise.race()
    都只接收一个参数并返回一个Promise,该参数是一个含有多个受监视Promise的可迭代对象。

    Promise继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class myPromise extends Promise{
    //use default Constructor
    success(resolve,reject){
    this.then(resolve,reject);
    }
    failure(reject){
    this.catch(reject);
    }
    }

代理(Proxy)和反射(Reflection)API

分享到: