【问题标题】:Is it possible to know the 'this' object before calling the constructor?在调用构造函数之前是否可以知道“this”对象?
【发布时间】:2018-04-25 13:58:21
【问题描述】:

在 ES6 类之前,函数可以用作构造函数:

function MyClass(a, b) {
}

那么,下面的代码就相当于一个经典的实例化(如let thisObj = new MyClass("A", "B")):

let thisObj = Object.create(MyClass.prototype)
// Here we know the `this` object before to call the constructor.
// Then, the constructor is called manually:
MyClass.call(thisObj, "A", "B")

... 这种技术是一种在调用构造函数之前了解this 对象的方法。但是Function.prototype.call() 不适用于 ES6 类构造函数。

对于 ES6,我们有 Reflect.construct():

let thisObj = Reflect.construct(MyClass, "A", "B");

但它没有提供在this对象创建后调用构造函数的方法。

使用 ES6 类仍然可以做到这一点吗?

我的用例

我需要将这个特性从 ES5 保留到 ES6 的框架。该框架负责实例化组件(即 ES6 类)。组件可以从其构造函数创建子组件(在组件树中,这里没有继承)。然后,子组件可以查询框架以从其自己的构造函数中获取其父组件。在这种情况下,我们有一个技术限制,因为框架仍然没有父组件构造函数的返回值。这是与 ES5 相比的回归。

【问题讨论】:

  • 出于兴趣,您会将这些知识用于什么目的?
  • @deceze 对于一个框架。该框架负责实例化组件(即 ES6 类)。组件可以从其构造函数创建子组件。然后,子组件可以查询框架以从其自己的构造函数中获取其父组件。在这种情况下,我们有一个技术限制,因为框架仍然没有父组件构造函数的返回值。
  • 在调用构造函数之前你对thisObj做了什么?似乎您的选择将非常有限,因为可以在构造函数中完成很多事情,但它还没有运行。也许您可以考虑让您的框架有一个基类,在子类运行之前初始化this 上的所有内容?归根结底,构造函数本身负责决定this 是什么,所以如果你还没有调用构造函数,this 可以是任何东西。
  • 听起来像是一个粗制滥造的框架。如果组件构造函数需要访问父组件,则父组件应作为构造函数参数传递。

标签: javascript class ecmascript-6 constructor this


【解决方案1】:

使用 ES6 类是不可能做到这一点的。 ES6 类应该只用newReflect.construct 实例化。

目前禁止调用函数类。这样做是为了为未来保留选项,最终添加一种通过类处理函数调用的方法。 [来源:exploringjs]

另见:

为什么这种模式不可行

构造函数中出现的this对象不需要类实例,因为ES6类可以从构造函数返回一个值,这被认为是类实例:

class Foo {
  constructor {
    // `this` is never used
    return {};
  }
}

组件可以从其构造函数创建子组件。然后,子组件可以查询框架以从自己的构造函数中获取其父组件

这种模式在 ES6 类中是不可行的。该限制限制this 出现在super 之前。

为了达到层次结构中的特定类,装饰器模式可用于在定义类时对其进行装饰。这允许在定义时修改其原型或在父子之间放置一个中间类。这种方法解决了许多特定于框架的任务,如依赖注入,并用于现代框架(Angular 等)。

一个方便的方法是 ECMAScript Next/TypeScript 装饰器。下面是一个例子,说明装饰器允许动态拦截子构造函数并增加子原型:

let BAZ;

class Foo {
  constructor(foo) {
    console.log(foo);    
  }
}

function bazDecorator(Class) {
  return class extends Class {
    constructor(foo) {
      super(BAZ || foo);
    }

    get bar() {
      return BAZ || super.bar;
    }
  }
}

@bazDecorator
class Bar extends Foo {
  constructor(foo) {
    super(foo);

    console.log(this.bar);
  }

  get bar() {
    return 'bar';
  }
}

// original behaviour
new Bar('foo'); // outputs 'foo', 'bar'

// decorated behaviour
BAZ = 'baz';
new Bar('foo'); outputs 'baz', 'baz'

ES.next 装饰器基本上是一个辅助函数。即使没有语法糖,它仍然适用于 ES6,但语法略有不同:

const Bar = bazDecorator(
    class Bar extends Foo { ... }
);

【讨论】:

  • 谢谢。 “这种模式在 ES6 类中不可行。限制限制 this 出现在 super 之前。” 抱歉,我不清楚。子组件实际上是组件树中的 child 组件。这里没有继承。
  • 我明白了。是的,情况不同,但答案是一样的。这不能在 ES6 类中完成,Object.create 配方不再相关。尽管如此,装饰器可以在定义组件的层次结构时提供某些好处。您所描述的事情通常由框架生命周期处理。例如。在 Angular 中有 AfterViewInit 生命周期钩子 - 子组件在组件构造函数中不可用,但在 ngAfterViewInit 组件方法中可用,当子组件准备好时由框架调用。这就是通常的做法。希望这会有所帮助。
  • 我编辑了您的答案,为事实答案添加了引用。并将其他考虑因素分开。然后我接受了。欢迎再次更新。
  • 关于您的评论的通知:生命周期主要与模板引擎相关。
  • 当然不仅仅是模板引擎,但是,Angular 和 React 等前端框架有生命周期是有充分理由的。
猜你喜欢
  • 1970-01-01
  • 2021-09-14
  • 2023-03-31
  • 1970-01-01
  • 2016-05-26
  • 1970-01-01
  • 2020-04-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多