ECMAScript

Posted coderlin_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ECMAScript相关的知识,希望对你有一定的参考价值。

ECMAScript

ECMAScirpt与js的关系

  • ECMAScript是一个脚本语言规范,通常看作是js的标准规范,但是js其实是ES的扩展语言。
  • 在ES钟,只是提供了最基本的语法,停留在语言层面。而js是实现了ES的标准,并且在基础之上实现了其他的功能。
  • 在浏览器中,js = ES + webApis(BOM,DOM)
  • 在node中,js = ES+nodeApis(fs,net,etc…)

模版函数

const name = 'test';

const myFuntag = (strings, name) => 
  console.log('strings', strings);
  console.log('name', name);
  return 123123;
;

const test1 = myFuntag`hello $name!`;
console.log('test1', test1);

//
strings [ 'hello ', '!' ]
name test
test1 123123

在模版字符串前面的函数,可以用来加强模版字符串的功能。
他接受参数个数都模版字符串中的变量个数决定。

  • 第一个参数,是以变量分割的,如上,name分割了hello和!,打印出来就是[hello !]。
  • 其他参数就是变量的参数,并且,他的返回值会作为模版字符串的返回值。

Proxy

对象属性读写使用Object.defineProperty进行属性的读取拦截。
proxy对整个对象做代理。

const person = 
  name: 'test',
  age: 20,
;

const personProxy = new Proxy(person, 
  get(target, property) 
    return target[property];
  ,
  set(target, property, value) 
    if (property === 'age') 
      target[property] = value > 20 ? 20 : value;
     else 
      target[property] = value;
    
  ,
);

console.log(personProxy.age);
personProxy.age = 19;
console.log(personProxy.age);
personProxy.age = 23;
console.log(personProxy.age);
Proxy vs Object.defineproperty
  • defineproperty只能监听对象属性的读写操作。
  • Proxy能够监视到更多对象操作
const personProxy = new Proxy(person, 
  get(target, property) 
    return target[property];
  ,
  set(target, property, value) 
    if (property === 'age') 
      target[property] = value > 20 ? 20 : value;
     else 
      target[property] = value;
    
  ,
  // 删除监听
  deleteProperty(target, property) 
    if (property === 'age') 
      console.log('age不能删除');
    
    delete target[property];
  ,
  has() , //监听in
  defineProperty() , // Object.defineProperty触发
  setPrototypeOf() , // Object.setPrototypeOf触发
  ...
);
delete personProxy.age; //age不能删除
  • proxy更好的支持数组对象的监视。
    像vue2监听数组的时候,比如往数组添加属性等等,采用的是重写数组的操作方法
const list = [];
const proxyList = new Proxy(list, 
  set(target, property, value) 
    console.log(target, property, value);
    target[property] = value;
    return true; //表示设置成功
  ,
);

proxyList.push(100); // 调用两次set,第一次是增加值,第二次是设置length为1
console.log(proxyList);
// 打印
[] 0 100
[ 100 ] length 1
[ 100 ]
  • Proxy以非侵入的方式监管整个对象的读写。一个定义好的对象不需要对对象做任何操作,就可以监视到他内部的读写。Object.defineProperty()就需要用特定的方式,单独去定义对象当中那些需要被监视的属性,对于一个已经存在的对象,需要对它做很多额外的操作

Reflect(静态类)

  • 统一的对象操作API
  • Reflect成员方法就是Proxy处理对象的默认实现。
const person = 
  name: 'test',
  age: 20,
;

const personProxy = new Proxy(person, );

const personProxy1 = new Proxy(person, 
  get(target, property) 
    return Reflect.get(target, property);
  ,
);

如上,new Proxy的默认就是调用了Relfect上面的方法。

为什么要有Reflect?
  • 统一提供一套用于操作对象的API
console.log('name' in person);
console.log(delete person.name);
console.log(proson['age']);

console.log(Reflect.get('age'));
console.log(Reflect.has('name'));
console.log(Reflect.deleteProperty('name'));

功能一样的,仔细比对,下面的明显合理一点。

Symbol

  • Symbol出现之前,对象的键只支持字符串,那么字符串就可能导致键一样从而改写了原来的内容
  • 对象的键支持Symbol,他是一个独一无二的值,使用Symbol之后不会出现重复现象。
  • 对象私有成员,外界若无法访问到Symbol的具体地址,那么将无法真正获取对象上该属性的值。如
const test = Symbol('test');
const person = 
  name: 'test',
  age: 20,
  [test]: 10,
;
console.log(person[Symbol('test')]); // undefined


const s1 = Symbol('foo');
const s2 = Symbol('foo');
console.log(s1 === s2); // false


const s1 = Symbol('foo');
const s2 = Symbol('foo');
console.log(s1 === s2); // true

Symbol.for维护了一个全局注册表
在Symbol类型当中还提供了很多内置的Symbol常量,用来作为内部方法的标识。这些标识符可以让自定义对象实现一些js当中内置的接口


console.log(Symbol.iterator)
console.log(Symbol.hasInstance)

如定义对象的toString

const obj = 
    //如果使用字符串标签去添加标识符,有可能会和内部的成员产生重复,ECMAScript要求我们使用Symbol去实现这个的一个接口
       [Symbol.toStringTag]:"xObject"
   
  console.log(obj.toString())//[object xObject] 
Symbol.Iterator接口
  • ES2015提出了一种Iterable接口,可迭代的。
  • 实现了Symbol.Iterator接口的对象,都可以被for of遍历

const obj = 
  a: 1,
  b: 2,
  c: 3,
  [Symbol.iterator]() 
    let index = 0;
    const arr = Object.keys(obj);
    return 
      next() 
        return  done: index > arr.length, value: obj[arr[index++]] ; // 迭代结果
      ,
    ;
  ,
;

for (let i of obj) 
  console.log('i-------', i);

// 打印结果
i------- 1
i------- 2
i------- 3
i------- undefined
迭代器模式

const todos = 
  life: ['吃饭', '睡觉'],
  learn: ['js', 'vue'],
  work: ['摸鱼'],
;

for (let a of todos.learn) 

for (let a of todos.life) 

for (let a of todos.work) 


想要遍历todos的所有内容必须事先知道它的属性方法,不够合理,
改造

const todos = 
  life: ['吃饭', '睡觉'],
  learn: ['js', 'vue'],
  work: ['摸鱼'],
  each(callback) 
    const all = [...this.learn, ...this.life, ...this.work];
    for (const item of all) 
      callback(item);
    
  ,
;

todos.each((item)=>)

对外暴露一个each方法,就可以遍历到所有属性。
使用迭代器模式:

const todos = 
  life: ['吃饭', '睡觉'],
  learn: ['js', 'vue'],
  work: ['摸鱼'],
  each(callback) 
    const all = [...this.learn, ...this.life, ...this.work];
    for (const item of all) 
      callback(item);
    
  ,
  [Symbol.iterator]() 
    const all = [...this.learn, ...this.life, ...this.work];
    let index = 0;
    return 
      next() 
        return 
          value: all[index++],
          done: index > all.length,
        ;
      ,
    ;
  ,
;
for (let i of todos) 
  console.log(i);

//结果
js
vue
吃饭
睡觉
摸鱼

外部使用的时候直接使用for of就行。迭代器模式的关键就是对外统一遍历的接口,让外部不用关心数据的结构。each方法只适用于这个对象,而Symbol.iterator是在语言层面上的,适用于所有数据结构。
使用生成器实现Symbol.iterator

//实现iterator
const todos1 = 
  life: ['吃饭', '睡觉'],
  learn: ['js', 'vue'],
  work: ['摸鱼'],
  [Symbol.iterator]: function* () 
    const all = [...this.learn, ...this.life, ...this.work];
    for (const item of all) 
      yield item;
    
  ,
;
for (const item of todos1) 
  console.log(item);

强类型与弱类型

  • 强类型语言不允许任意的隐士类型转换
  • 弱类型则允许。

静态类型与动态类型

  • 静态类型,一个变量声明的时候类型就已经确定了,并且之后不能修改。
  • 动态类型,一个变量只有运行的时候才知道是什么类型,并且之后可以任意更高。

以上是关于ECMAScript的主要内容,如果未能解决你的问题,请参考以下文章

20170329 隐士增强问题

全局变量的隐士声明

ECMAScript 5 新特性

九岭隐士传奇印书版

Scala笔记整理:类型参数(泛型)与隐士转换

ECMAScript学习笔记