ES6的梳理

前言

ES6 的教程非常多用的时候只需稍稍百度即可,所以很多时候教主都是高不成低不就的,所以稍微记录一下对教主来说有些小纠结的地方其实本身也无可厚非吧,至于像letconst、作用域、解构赋值、默认参数不定参数、箭头函数等等的就直接查一下就行了不记录了。

INFO

其中很多部分都来自菜鸟教程ECMAScript 6 入门以及一些很著名的网站,主要目的是记录更经典的例子。

参考:



Symbol 类型

定义

Symbol是 ES6 新增的一个原始数据类型,表示独一无二的值,最大的用法就是用来定义对象的唯一属性名。

  • 因为 Symbol 是原始数据类型,所以不能用 new 创建。
  • 因为 Symbol 可以表示独一无二的值,所以可以用于:作为常量值的类型、作为属性名的类型。

作为常量值的类型

作为常量值的类型好理解,直接将常量值换为Symbol类型的就行了:

// String 类型
const A_RED = 'RED';	
const B_RED = 'RED';

console.log(A_RED === B_RED);	// true
// Symbol 类型
const A_RED = Symbol('RED');
const B_RED = Symbol('RED');

console.log(A_RED === B_RED);	// false

PS

咋一看感觉String类型的没毛病啊,那不就应该是这样的嘛,那的确应该是这样的啊???emmmmmm对于常量来说我们需要的是:常量和常量值都应该是一一对应的关系。

作为属性名的类型

那表示属性呢?教主也一直很疑问:如果说表示常量是为了严谨,那表示属性名是为了啥,难不成属性名还会重名不成?

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

举例:

// Symbol 作为对象的属性名的类型
let obj = { };						
obj[Symbol('k1')] = 'v1';
obj.k2 = 'v2';

// let obj = {[Symbol("k1")]: "v1", k2: "v2"};

console.log(obj);	// {k2: "v2", Symbol(k1): "v1"}

关于中括号运算符(obj[key])和点运算符(obj.key):

  • 中括号运算符总是能代替点运算符。但点运算符却不一定能全部代替中括号运算符。
  • 中括号运算符可以用字符串变量的内容作为属性名。点运算符不能。
  • 中括号运算符可以用纯数字为属性名。点运算符不能。
  • 中括号运算符可以用js的关键字和保留字作为属性名。点运算符不能。

这一点怎么理解呢:

  • 首先可以联想到C中结构体和数组的汇编是一样的;
  • 其次JS中基于原型链的继承方式,大道归一Object.prototype
  • 最后如果从解析代码为语法树来说,点运算符没办法解析动态的属性,而中括号运算符只需要满足括号内的表达式能被计算(转换)成一个字符串就行了,比如说obj['key' + i]
  • 那么是否可以理解成:点运算符是不是只是为了更好的描述JS面向对象的特征呢。

至此还是没得到问题的答案。又或者说“为什么不同的库可能会向同一个对象添加可能重名的字段?”这属于语言的哲学问题是另一个范畴,毕竟欲戴王冠必承其重,因为自由所以可能。

那么还可以从另一些特性想:

  • Symbol类型的属性不会被轻易迭代,也不会被自动转换为字符串(可以使用toString()方法获得获得),所以可以“定制化”的JSON.stringfy()成一个JSON。
  • Symbol类型的属性无法跨模块访问,所以在模块化中,用于私有化对象大的字段或方法是个很不错的选择。

PS

关于Symbol在JS内部的很多应用,还有待了解。



Proxy 对象

简单来说就是代理模式,这里记录一下最简单用法:

// 目标对象
let girl = { name: "xxx", sex: 2, single: true };

// 拦截方法
let way = {
    get: function (target, property) {
        if (property === 'name') return target.name;
        else if (property === 'sex') return 4;
        else if (property === 'single') return false;
        else return 'unkown';
    }
}

// 代理对象
let bestie = new Proxy(girl, way);

QUOTE 《GB/T 2261.1-2003 性别代码》

  • 0 = 未知项
  • 1 = 男性
  • 2 = 女性
  • 3 = 女改男,男性
  • 4 = 男改女,女性
  • 9 = 未指明
// 访问者
let boy = {
    name: "yyy", sex: 1, single: true, askabout: function (proxy) {
        return (proxy.sex === 2 && proxy.single === true);
    }
}

let result = boy.askabout(bestie);	// 细思恐极
console.log(result);				// false



Reflect 对象

定义

Reflect是 ES6 为了操作对象而提供的新 API。Reflect对象的设计目的有这样几个。

  • Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。
  • 修改某些Object方法的返回结果,让其变得更合理。
  • Object操作都变成函数行为。
  • Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。

PS

简单来说,Reflect是在JS的设计哲学上做改动以使其更加合理,与字面值“反射”有异曲同工之妙

静态方法

Reflect对象有13个静态方法,教主还有待熟悉。

Reflect.apply(func, thisArg, args)

Reflect.construct(target, args)

Reflect.get(target, name, receiver)

Reflect.set(target, name, value, receiver)

Reflect.defineProperty(target, name, desc)

Reflect.deleteProperty(target, name)

Reflect.has(target, name)

Reflect.ownKeys(target)

Reflect.isExtensible(target)

Reflect.preventExtensions(target)

Reflect.getOwnPropertyDescriptor(target, name)

Reflect.getPrototypeOf(target)

Reflect.setPrototypeOf(target, prototype)

其中大部分方法其实都可以从字面值理解,唯独这个Reflect.apply(func, thisArg, args)方法不知所谓,请教群友后结论:暂时将函数绑定在一个对象上来调用:

// 假设有某一函数
let func = function (param1, param2) {
    console.log(this);
    console.log(`${param1} ${param2}`);
}

// 假设有某一对象
let obj = { name: "xxx", sex: 1, single: true };
// 暂时以 obj.func() 的形式调用, 将参数放在数组中传入
Reflect.apply(func, obj, ['param1', 'param2', 3]);	// { name: 'xxx', sex: 1, single: true } 
													// param1 param2

PS

比较纠结的是:为什么要暂时暂时绑定,永久绑定不好嘛!

同学说:为什么要永久绑定,用完就可以销毁了呀!

教主:好吧~



Promise 对象

定义

Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

PS

简单来说,Promise就是把异步的操作用同步的直觉来写。起码少了嵌套回调,回调的······的那种回调。

简单用法

const p = new Promise(function (resolve, reject) {
    // 2 秒后回调 resolve()
    setTimeout(resolve, 2000, 'done');
});

p.then(value => console.log(value));    // done

AJAX 可能是最好的模拟异步的操作了。假设某监听 80 端口的服务器目录下:

/					# localhost
|--- promise.html	
|--- data/
|    |--- 1.json	# order: 1
|    |--- 2.json	# order: 2
|    |--- 3.json	# order: 3

如果既要异步请求还要保证请求顺序,可能会:

<!DOCTYPE html>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    axios.get('data/1.json').then((response) => {
        console.log(response.data);
        axios.get('data/2.json').then((response) => {
            console.log(response.data);
            axios.get('data/3.json').then((response) => {
                console.log(response.data);
            });
        });
    })
</script>

但是像JQ、axios等等的库,它们的AJAX都会返回一个Promise对象,所以才可以.then(succeedFunc)回调如果成功时的函数,或者.catch(faildFunc)回调如果失败的函数。而刚好有一个Promise.all()的静态方法,传入一个Promise数组,然后统一去在.then()里回调:

<!DOCTYPE html>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    Promise.all([
        axios.get('data/1.json'),
        axios.get('data/2.json'),
        axios.get('data/3.json')
    ]).then((results) => {
        let [r1, r2, r3] = results;
        console.log(r1.data);
        console.log(r2.data);
        console.log(r3.data);
    });
</script>



async 函数

定义

async函数是Generator函数的语法糖,语义化更好,而且会返回一个Promise对象。

  • async函数会立即返回一个Promise对象。
  • await后等待的表达式为Promise对象时会等待执行并返回resolve()函数的结果;等待的表达式为其他类型则该是什么就是什么。
  • async函数的return的值会传给.then()时的回调函数;错误会传给.catch()时的回调函数。

简单使用

正常返回:

let func = async function () {
    let a = await (10 ** 3);
    console.log(a);                           // 1000
    let b = await new Promise((resolve, reject) => resolve('resolve'));
    console.log(b);                           // resolve
    return 'success';
}

let promise = func();
promise.then(success => console.log(success)) // success
       .catch(error => console.log(error));	 

抛出异常:

let func = async function () {
    throw new Error('error');
}

let promise = func();
promise.then(success => console.log(success))     
       .catch(error => console.log(error));   // Error: error

然后呢貌似比较简单的就和Promise.all()那样需要按顺序读取的效果差不多,但是控制起来就相对而言比较i方便了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章