【问题标题】:Comparison class constructor parameter between Typescript and Javascript ES6, ES.NEXTTypescript和Javascript ES6、ES.NEXT比较类构造函数参数
【发布时间】:2018-07-19 09:14:25
【问题描述】:

我一直在研究 TypeScript 作为 Angular2/5 的要求,我遇到了一些疑问。 几个月前,我还升级了我对 JS ES6 等的知识。 我很确定我没有错,但为了全面了解 TS,我还是会问你。

这是您可以在此处找到的代码:

https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties

{
    class Octopus {
       readonly numberOfLegs: number = 8;
       constructor(readonly classAttr: string) { ... } // name attribute 
    }
    console.log( (new Octopus('spicylemoned')).classAttr ); // It works
}

在最近的 JS 更新中,有没有办法像在 TS 中那样在 vanilla 的类构造函数中定义属性? (没有通过this 实例隐式分配它)

{
    class Test{
        constructor({ passedVar : classAttr } ) { ... };
    };
    console.log( (new Test({ passedVar : 'sweety' })).classAttr ); 
    //it doesnt work
}

【问题讨论】:

  • 我认为在 JavaScript 中没有办法做到这一点。你必须在构造函数中使用this

标签: javascript class typescript oop ecmascript-6


【解决方案1】:

ES6 类的语法禁止直接在类定义中的原型对象上分配属性。

你可以使用 getter 来达到同样的效果。仅定义 getter 也会使其成为只读。

然而,vanilla javascript 中没有隐式语法来处理类型检查,这意味着必须在函数中使用 typeof <variable> === '<primitiveType>'<variable> instanceof <Class> 手动完成。

使用 Octopus 类将产生以下结果:

class Octopus{
    constructor(classAttr){
        if (typeof classAttr !== 'string' && typeof classAttr !== 'object') { 
            throw new TypeError('classAttr is neither a string nor an object');
        }
        if (typeof classAttr === 'object') {
            Object.assign(this,classAttr);
        }
        else {
            this.classAttr = classAttr;
        }
    }

    //readonly, because there is no setter
    get numberOfLegs(){
        return 8;
    }
}

let octopus = new Octopus('bigEyed');
console.log(octopus.numberOfLegs);     // 8
console.log(octopus.classAttr);        // bigEyed

【讨论】:

    【解决方案2】:

    我想到了一种使用自定义Proxy 将对象初始化器传递给类的非常人为的方法。它可能看起来很冗长,但 Proxy 可以重复用于任意数量的类定义:

    // mem initializer list
    const list = (() => {
      let props = new WeakMap()
    
      // this: class instance
      // o: initializer object
      function initialize(o = {}) {
        if (props.has(this)) {
          return this[props.get(this)] = o;
        }
    
        return new Proxy(o, {
          get: (o, p) => (props.set(this, p), this[p] = o[p])
        });
      }
    
      // .call.bind() allows initialize(this, o)
      // to pass a context and one argument
      // instead of two arguments
      return initialize.call.bind(initialize);
    })();
    
    // example usage
    class Test {
      constructor (o, { classAttr = list(this, 'foo'), undefinedParameter = list(this, 'bar') } = list(this, o)) {
        /* ... */
      }
    }
    
    console.log(new Test({ classAttr: 'sweety', excessParameter: 'oops' }));
    console.log(new Test());

    请注意,undefinedParameter 始终使用其默认值 'bar' 进行初始化,而 excessParameter 从未添加到类实例中。

    注意:这绝不是一个高效的解决方案。这是一个纯粹的噱头,但表明可以隐式地初始化一个类实例。

    使用这种方法的一个缺点是无法拦截像这样的默认参数

    { classAttr = 'defaultValue' }
    

    所以你必须使用有点令人讨厌的语法

    { classAttr = list(this, 'defaultValue') }
    

    为了提供默认参数。

    如果您要扩展另一个类,则必须使用list(super(), o) 返回的Proxy 初始化实例:

    const list = (() => {
      let props = new WeakMap()
    
      function initialize(o = {}) {
        if (props.has(this)) {
          return this[props.get(this)] = o;
        }
    
        return new Proxy(o, {
          get: (o, p) => (props.set(this, p), this[p] = o[p])
        });
      }
    
      return initialize.call.bind(initialize);
    })();
    
    // example usage
    class Test extends Object {
      constructor (o, { classAttr = list(this, 'foo'), undefinedParameter = list(this, 'bar') } = list(super(), o)) {
        /* ... */
      }
    }
    
    console.log(new Test({ classAttr: 'sweety', excessParameter: 'oops' }));
    console.log(new Test());

    【讨论】:

      【解决方案3】:

      在 JS 中没有任何类似的语法。最简单的方法是使用Object.assign()

      class Test {
        constructor({ passedVar }) {
          Object.assign(this, { classAttr: passedVar });
        }
      }
      
      console.log(new Test({ passedVar: 'sweety' }).classAttr);

      如果你有很多属性,这种方式会更好;如果只有一个,你可以分配到thisthis.classAttr = passedVar

      【讨论】:

      • 更简单的方法是将输入参数与属性名称匹配:constructor ({ classAttr } = {}) { Object.assign(this, { classAttr }); } = {} 允许您调用不带参数的构造函数,默认情况下将所有内容初始化为 undefined。跨度>
      • 我知道您已按要求回答了问题。那张纸条是给 OP 的。我相信你已经知道解构和默认参数是如何工作的;)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-12
      • 2021-02-07
      • 2010-12-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多