0
点赞
收藏
分享

微信扫一扫

ECMAScript

倪雅各 2022-05-01 阅读 88

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);
}

强类型与弱类型

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

静态类型与动态类型

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

相关推荐

0 条评论