在学ES6

ES6 get() set() bind()

在TS中有的就不在提及
TS的那篇

  • const命令
    禁止变量指向别的对象

  • 字符串的几个方法

    • string.includes(“mystr”,num)返回bool值,表示从string下标为num的位置开始找,是否找到了mystr
    • string.startsWith(“mystr”,num)返回bool值,表示从string下标为num的位置开始往后的字符串,是否在mystr为首部
    • string.endsWith(“mystr”,num)返回bool值,表示在string的前num个元素中,是否mystr在尾部
    • string.repeat(n)返回string的n此重复
    • string.padStart/End(length,mystr)用mystr头部/尾部讲补全string至length的长度
    • string.replace(a,b)用b置换第一个找到的a,全部置换则是string.replace(/a/g,b)
  • 二进制和八进制
    二进制前缀0b,八进制前缀0o

  • number的几个方法
    isFinite(),isNaN(),parseInt(),parseFloat(),isInteger(),isSafeInteger()

  • 指数运算符**,例 2**3=8

  • 箭头表达式需要注意的点

    • 箭头表达式函数内的this,指的是所在对象的this,不指向使用时的this
      class mycla{
          name;
          constructor(){
              document.write(this.name);
          }
      }

    这里的this指的是mycla,不是document

    • 不能用argument
    • 不能作为generator函数
  • 双冒号运算符

    foo::bar();

    讲bar中的this指定为foo

  • 尾调用/递归
    意思是,在函数最后一步调用其他函数(两者只能在严格模式下才能起作用)
    调用/递归的时候因为要返回上一个函数,栈要很深,占位置。使用尾调用/递归,因为不需要再返回上一个函数,栈的深度永远只有1

    function factorial(n, total = 1) {
        if (n === 1) return total;
        return factorial(n - 1, n * total);
    }

    阶乘的尾递归写法

  • 有关数组

    • …的运用
      1. 将一个数组分为若干个单参数
        function add(a,b){
            return a+b;
        }
        let numbers=[1,2];
        add(...numbers);

    numbers被拆成1和2传入add

    1. 复制数组
      const a1=[1,2];
      const a2=a1;
      const a3=[...a1];
      /*const[...a3]=a1*/

    a2是a1的引用,改变a1,a2也会变,a3才是a1的克隆(a3的两种复制写法都可以)

    1. 合并数组

      const a1=[1,2];
      const a2=[3,4];
      const a3=[...a1,...a2];
    2. 将字符串转化成真正的数组

      const a1=[...`hello`];//a1为["h","e","l","l","o"]

    实际上,任何 Iterator 接口的对象,都可以用…运算符转为真正的数组
    总的来说,…就是可以将数组/类似数组的对象拆分成单个元素,因此可以实现复制和合并数组的功能

    • Array.from()可以将所有具有length的对象转化成数组。同时,Array.from()还可以接受第二个参数,用来对每个元素处理
      let a1=Array.from([1,2,3],x=>x**2);
      /*a1=[1,4,9]*/

    简单来说,数组的每个元素会被传入这个函数,然后等于这个函数的返回值

    • Array.of(),返回参数值组成的数组

      let a1=Array.of(1,2,3);
      /*a1=[1,2,3]*/
    • copyWithin数组内部复制(会影响原数组)
      copyWithin(target, start = 0, end = this.length)
      target 从这个位置开始覆盖数据
      start 从这个位置开始读取数据
      end 到这个位置结束读取数据(不读取这个位置的数据)

      [1, 2, 3, 4, 5].copyWithin(0, 3);
      // [4, 5, 3, 4, 5]
    • find和findIndex函数 找出并返回数组里第一个符合条件的元素

      let num=[1, 5, 10, 15].find(value => value > 9);
      /*num=10*/

    find()的参数为判断函数,判断函数返回一个布尔值,为true时就停止回调,回调完都找不到就返回undefine
    findIndex()函数差不多,就是如果找不到符合条件的元素就返回-1
    它们都可以接受第二个参数,如果有第二个参数,它们里面的this指针指向第二个参数的对象

    • fill()数组填充
      ['a', 'b', 'c'].fill(7, 1, 2);
      /*['a',7,'c']*/

    代码意思:讲这个数组用7填充,从下标为1开始,到下标为2结束填充(不包括2)

  • entries(),keys(),values()用于遍历数组或对象

    for (let index of ['a', 'b'].keys()) {
    console.log(index);
    }
    // 0
    // 1
    
    for (let elem of ['a', 'b'].values()) {
    console.log(elem);
    }
    // 'a'
    // 'b'
    
    for (let [index, elem] of ['a', 'b'].entries()) {
    console.log(index, elem);
    }
    // 0 "a"
    // 1 "b"

    keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历

  • 合并对象Object.assign(obj1,obj2)

    1. 可以用来复制对象
      但这是浅拷贝,而且会有同名属性替换的特性
      也可以利用这个特性,为对象提供默认值

      function processContent(options) {
       options = Object.assign({}, DEFAULTS, options);
      console.log(options);
      }
      /*如果传入的options和DEFAULTS有相同属性,就会替换,否则options就会采用DEFAULTS的属性值*/
      
    2. 为对象添加/修改属性或方法
      也可以对数组进行操作,数组是特殊对象,属性为0,1,2···n-1

  • reduce函数

    function func3(val){
        let func1=x=>x+1;
        let func2=x=>x*2;
        let funcs=[func1,func2];
        funcs.reduce((a,b)=>b(a),val);
    }
    func3(5);//12
    /*这里的val代表起始值,reduce里的a代表当前总的值,reduce会把funcs的值依次放入b,通过某些处理后返回一个值给a,作为新的当前总值*/
    //val可以不写,默认0


* 新增的类型Symbol()
ts
let s1 = Symbol();
let s2 = Symbol();
s1 === s2 // false

每一个Symbol都是独特的,它可以接受一个参数,作为对这个变量的说明
Symbol变量可以通过String()或Boolean()转换成想相应的值
当它作为对象属性时,不能用点运算符(a.sym)提取,只能用,且在定义属性时,也必须把变量名放在[]中

* 返回对象所有键名Reflect.ownKeys(obj) 这是一个数组
* 希望使用同一个Symbol,可以用Symbol.for(`id`)
```ts
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1 === s2 // true
```
Symbol.for(`id`)会把这个id的Symbol登记起来,如果要创建一个已登记的Symbol,就返回登记过的那个Symbol,两个变量是一致的
*Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key*
  • Set结构
    set类似数组,区别是set里面没有重复元素

    const set = new Set([1, 2, 3, 4, 4]);
    [...set];
    // [1, 2, 3, 4]

    这意味着,Set函数可以用来剔除数组重复元素

    let a1=[1,2,3,3,4,4];
    a1=new Set(a1);
    /*a1=[1,2,3,4]*/

    它有以下常用四个函数
    add(value):添加某个值,返回 Set 结构本身。
    delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
    has(value):返回一个布尔值,表示该值是否为Set的成员。
    clear():清除所有成员,没有返回值。
    同时,它也可以通过Array.from()变成数组
    此外还有一个WeakSet,跟Set差不多,区别就是WeakSet是一个类,里面只可以加有iterable的对象

  • Map结构
    一般来说,对象的键只能是字符串,但是Map“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键

    • 两种构造方法
      const set = new Set([
        ['foo', 1],
       ['bar', 2]
      ]);
      const m1 = new Map(set);
      /*也可以直接
      const m1= new Map([['foo',1],['bar',2]]);
      */
      m1.get('foo') // 1
      
      const m2 = new Map([['baz', 3]]);
      const m3 = new Map(m2);
      m3.get('baz') // 3

    用键值对数组或者Map对象来创建新的Map对象

    • 通过set()和get()函数建立/获取键值对
      set(元素名,元素值):
      get(元素名);
      此外,它还有size,has,delete,clear等函数

    • 切换成数组[…Map]

    • Map 转为对象
      如果所有 Map 的键都是字符串,它可以无损地转为对象。

      function strMapToObj(strMap) {
      let obj = Object.create(null);
      for (let [k,v] of strMap) {
        obj[k] = v;
       }
      return obj;
      }
      
      const myMap = new Map()
       .set('yes', true)
       .set('no', false);
      strMapToObj(myMap)
      // { yes: true, no: false }

    如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。

    • 对象转为 Map
      function objToStrMap(obj) {
       let strMap = new Map();
      for (let k of Object.keys(obj)) {
        strMap.set(k, obj[k]);
       }
       return strMap;
      }
      
      objToStrMap({yes: true, no: false})
      // Map {"yes" => true, "no" => false}
  • 对象的遍历属性Symbol.iterator,作用是返回给next()或循环调用,Symbol.iterator()函数返回一个对象,这个对象里有一个可以获取下一个键值对的函数(在外部给一个对象设置新属性,可以用object.prototype.name=value)
    自带Symbol.iterator的一些对象:Array,Map,Set,String,TypedArray,函数的arguments对象,NodeList对象
    Symbol.iterator是一个Symbol属性的变量所以得这样定义

    let obj = {
    data: [ 'hello', 'world' ],
    [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return {
            value: self.data[index++],
            done: false/*done:false代表这个元素存在*/
          };
        } else {
          return { value: undefined, done: true };
         }
       }
     };
     }
    };

    有时候也可以直接调用已有的Symbol.iterator

    let iterable = {
     0: 'a',
     1: 'b',
     2: 'c',
     length: 3,
     [Symbol.iterator]: Array.prototype[Symbol.iterator]
    };
    for (let item of iterable) {
     console.log(item); // 'a', 'b', 'c'
    }

    如果要用数组的iterator,就得用这样的类似数组的对象(需要存在数值键名,即0,1,2之类的,且要有length属性)
    实际上[…str],就是调用了str的[Symbol.iterator]的next(),我们可以改写Symbol.iterator,来改变[…str]的值

    其实,也可以和generator结合起来

    let myIterable = {
     [Symbol.iterator]: function* () {
    yield 1;
    yield 2;
    yield 3;
     }
    }
    [...myIterable] // [1, 2, 3]

    这样写是最简便的

  • Generator函数的扩展

    • generator.next()可以带参数,意思是改变上一个yield的值
      function* foo(x) {
       var y = 2 * (yield (x + 1));
       var z = yield (y / 3);
       return (x + y + z);
      }
      
      var a = foo(5);
      a.next() /* Object{value:6, done:false}*/
      a.next() /* Object{value:NaN, done:false}*/
      a.next() /* Object{value:NaN, done:true}*/
      
      var b = foo(5);
      b.next() /* { value:6, done:false }*/
      b.next(12) /* { value:8, done:false }*/
      b.next(13) /* { value:42, done:true }*/

    函数中,y是不能正确等于2 * (yield (x + 1))的,函数的执行是,首先返回x+1,然后y=2*传入的参数,如果没有传入参数,yield所在的地方就会等于NaN

    • 利用generator写Symbol.iterator的高级用法

      function* objectEntries() {
      let propKeys = Object.keys(this);
      
       for (let propKey of propKeys) {
        yield [propKey, this[propKey]];
      }
      }
      
      let jane = { first: 'Jane', last: 'Doe' };
      
      jane[Symbol.iterator] = objectEntries;
      
      for (let [key, value] of jane) {
      console.log(`${key}: ${value}`);
      }
      // first: Jane
      // last: Doe
    • throw方法
      Generator 函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。

      var g = function* () {
      try {
      yield;
      } catch (e) {
      console.log('内部捕获', e);
      }
      };
      
      var i = g();
      i.next();
      
      try {
      i.throw('a');
      i.throw('b');
      } catch (e) {
      console.log('外部捕获', e);
      }
      // 内部捕获 a
      // 外部捕获 b

    几个要注意的地方

    1. 函数内部的catch只能catch一次,所以第二次被外部catch到了
    2. 另外,throw方法抛出的错误要被内部捕获,前提是必须至少执行过一次next方法
    3. 只要 Generator 函数内部部署了try…catch代码块,throw方法被捕获以后,会附带执行下一条yield表达式。也就是说,会附带执行一次next方法
    4. 函数中如果使用了throw new Error(‘generator broke!’)之类的命令,函数就只结束运行
    5. 除了我们手动调用throw方法或者throw一些内容,当函数自己运行出现错误的时候,也会自动throw错误

      • return方法
        可以直接终止函数运行,返回的value为return里面的参数(不写就是undefined),如果 Generator 函数内部有try…finally代码块,那么return方法会推迟到finally代码块执行完再执行。

        function* numbers () {
         yield 1;
         try {
        yield 2;
        yield 3;
        } finally {
        yield 4;
        yield 5;
         }
         yield 6;
        }
        var g = numbers();
        g.next() /* { value: 1, done: false }*/
        g.next() /* { value: 2, done: false }*/
        g.return(7) /* { value: 4, done: false }*/
        g.next() /* { value: 5, done: false }*/
        g.next() /* { value: 7, done: true }*/
      • 在Generator函数中不能直接调用别的Generator函数,要用yield*

        function* foo() {
         yield 'a';
         yield 'b';
        }
        
        function* bar() {
        yield 'x';
        yield* foo();
        yield 'y';
        /*x,a,b,y*/
        }

    yield* 的意思是遍历这个对象,也可以yield* [“a”, “b”, “c”], yield* ‘hello’

  • 绑定this指针
    obj.function.call(对象),这里的对象就是this指针指向的对象,当然也可以是函数或者其他

  • 类的getter和setter

    class MyClass {
    constructor() {
    
    }
    get prop() {
    return 'getter';
    }
    set prop(value) {
    console.log('setter: '+value);
    }
    }
    
    let inst = new MyClass();
    
    inst.prop = 123;
    // setter: 123
    
    inst.prop
    // 'getter'
  • 通过子类实例对象给父类添加属性或方法proto

    class B extends A {        
    }
    let B1=new B;
    B1._proto_._proto_.printName=()=>{console.log("Hi");};
    B1._proto_===B.prototype;/*true*/
    B.prototype.__proto__ === A.prototype;/*true*/

    因此,也可以用B.prototype.proto.printName()=>{console.log(“Hi”);}
    当然,更加可以直接用A.ptototype.printName()=>{console.log(“Hi”);}

  • 修饰器

    1. 类的修饰
      普通例子:
      @testable
      class MyTestableClass {
      
      }
      
      function testable(target) {/*传入的target就是MyTestableClass这个类*/
        target.isTestable = true;
      }
      
      MyTestableClass.isTestable // true

    高端一点的例子:

    /* mixins.js*/
    export function mixins(...list) {
    return function (target) {
    Object.assign(target.prototype, ...list)/*Object.assign(target,a,b,c,d...)函数能把后面若干个类的成员复制到target里面*/
      }
    }
    
    /* main.js*/
    import { mixins } from './mixins'
    
    const Foo = {
      foo() { console.log('foo') }
    };
    
    @mixins(Foo)
    class MyClass {}
    
    let obj = new MyClass();
    obj.foo() /* 'foo' 因为上面是用的target.prototype,因此这里可以用实例调用*/
    1. 类的方法的修饰
      class Person {
      @nonenumerable
      get kidCount() { return this.children.length; }
      }
      
      function nonenumerable(target, name, descriptor) {
      descriptor.enumerable = false;
      return descriptor;
      }

    修饰类方法的修饰器函数模板如上,三个参数

    1. 修饰器不能装饰函数
      因为有函数提升,如果真的要用修饰器,可以采用高阶函数的形式直接执行

    2. 第三方模块修饰器
      这里举个例子core-decorators.js
      在调用之前,我们需要先写这句

      import { 修饰器名 } from 'core-decorators';

    介绍三个比较有用的修饰器
    autobind修饰器使得方法中的this对象,绑定原始对象。
    readonly修饰器使得属性或方法不可写。
    override修饰器检查子类的方法,是否正确覆盖了父类的同名方法,如果不正确会报错。
    当然,我们也可以利用修饰器会先执行的特性,在修饰器中写一些事件。这样在调用类的方法时,就是先执行相应事件

    1. Trait
      我们除了可以像上面那样使用prototype给类加属性,也可以直接使用tarit,这里使用traits-decorator这个第三方模块作为例子
      import { traits } from 'traits-decorator';
      
      class TFoo {
      foo() { console.log('foo') }
      }
      
      const TBar = {
      bar() { console.log('bar') }
      };
      
      @traits(TFoo, TBar)
      class MyClass { }
      
      let obj = new MyClass();
      obj.foo() /* foo*/
      obj.bar() /* bar*/

    这里就直接给MyClass加入了TFoo和TBard类的方法
    但是,注意一点,tarit不允许加入同名方法。这里我们可以给方法起个别名

    import { traits, alias } from 'traits-decorator';
    
    class TFoo {
      foo() { console.log('foo') }
    }
    
    const TBar = {
      bar() { console.log('bar') },
      foo() { console.log('foo') }
    };
    
    @traits(TFoo, TBar::alias({foo: 'aliasFoo'}))
    class MyClass { }
    
    let obj = new MyClass();
    obj.foo() /* foo*/
    obj.aliasFoo() /* foo*/
    obj.bar() /* bar*/

    改函数名的一个函数alias,可以参考下(复习一下:双冒号可以用来绑定函数里面的this指针)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值