与“ this”相比,“ super”的价值如何确定?

如何解决与“ this”相比,“ super”的价值如何确定?

this由执行上下文决定

我已经习惯了JavaScript中this的特性。在以下示例中,this由执行上下文确定。即使在getProtoPropViaThis上定义了x函数,this的值也由该函数的调用方式确定:

const xProto = {
  protoProp: "x",};

const x = {
  getProtoPropViaThis() {
    return this.protoProp;
  },}
Object.setPrototypeOf(x,xProto);

const yProto = {
  protoProp: "y",};

const y = {
  getProtoPropViaThis: x.getProtoPropViaThis,}
Object.setPrototypeOf(y,yProto);

console.log( x.getProtoPropViaThis() ); // Output: x
console.log( y.getProtoPropViaThis() ); // Output: y

super是否不受执行上下文的影响?

我已经使用super已有一段时间了,但始终在类的上下文中使用。因此,最近当我阅读an article并切向地证明super似乎没有遵循与this相同的规则时,我感到很惊讶。以某种我不完全理解的方式(尽管多次读过ECMAScript 2021 language docs),super设法保留了其原始参考:

const xProto = {
  protoProp: "x",};

const x = {
  getProtoPropViaSuper() {
    return super.protoProp;
  },};

const y = {
  getProtoPropViaSuper: x.getProtoPropViaSuper,yProto);

console.log( x.getProtoPropViaSuper() ); // Output: x
console.log( y.getProtoPropViaSuper() ); // Output: x

请注意,y在其原型链中的任何地方都没有xxProto,但是调用{{ 1}}

xProto明显的差异

对于另一个示例,以下内容根本不起作用:

y.getProtoPropViaSuper()

Object.assign的值是Object的原型,而不是const xProto = { protoProp: "x",}; const x = Object.assign(Object.create(xProto),{ getProtoPropViaSuper() { return super.protoProp; },}); console.log(x.getProtoPropViaSuper());的原型,因此上面的示例仅打印super

关注文档

12.3.7.1 Runtime Semantics: Evaluation

SuperProperty :super。 IdentifierName

  1. 让我们成为GetThisEnvironment()。
  2. 实际上是这样吗? env.GetThisBinding()。
  3. 让propertyKey为IdentifierName的StringValue。
  4. 如果与此SuperProperty匹配的代码是严格模式代码,请使strict为true;否则,为true。否则,请让strict为假。
  5. 返回? MakeSuperPropertyReference(actualThis,propertyKey,严格)。

12.3.7.3 MakeSuperPropertyReference ( actualThis,propertyKey,strict )

抽象操作MakeSuperPropertyReference接受参数ActualThis,propertyKey和strict。调用时,它将执行以下步骤:

  1. 让我们成为GetThisEnvironment()。
  2. 声明:env.HasSuperBinding()为真。
  3. 让baseValue为? env。GetSuperBase()。
  4. 让bv成为? RequireObjectCoercible(baseValue)。
  5. 返回类型为Reference的值,该类型是超级引用,其基本值成分为bv,其引用名称成分为propertyKey,其thisValue成分为ActualThis,并且其严格引用标志为strict。

8.1.1.3.5 GetSuperBase ( )

  1. 让envRec为为其调用方法的环境记录功能。
  2. 让我们成为envRec。[[HomeObject]]。
  3. 如果home的值不确定,则返回undefined。
  4. 声明:Type(家庭)是对象。
  5. 返回?主页。[GetPrototypeOf]。

最后:

8.1.1.3 Function Environment RecordsTable 17

x:如果关联的函数具有超级属性访问权限,并且不是ArrowFunction,则[[HomeObject]]是该函数作为方法绑定到的对象。 [[HomeObject]]的默认值是不确定的。

在遵循本文档之后,似乎undefined应该作为一种方法绑定到[[HomeObject]],但是也许在创建getProtoPropViaSuper对象并保留时仍然以某种方式存储该绑定。该功能已分配给y。但是,我无法从这些文档中分析发生的时间或地点。

如果有人能用简单的语言解释这种行为,我将非常感激。 x如何确定其价值?它看起来如何保持其原始的y上下文?如果它保留原始对象,则可能会导致内存泄漏,因为原始对象无法被垃圾回收。但是也许super引用是在准编译时确定的? (我之所以说“准”,是因为引用仍然可以通过super进行更改)

解决方法

即使在x对象被创建和保留时,绑定也可能以某种方式存储,即使该功能已分配给y

是的,正是这种情况。 getProtoPropViaSuper方法基本上会关闭定义该对象的对象。该方法存储在函数本身的内部 [[HomeObject]] 插槽中,这就是为什么在分配时将其保留的原因方法转移到另一个对象,或者-更重要的是-在另一个对象 1 上继承。对于对象常量中的方法定义,它是由常量创建的对象。对于class es中的方法定义,它是类的.prototype对象。

1:有关为什么它需要是静态引用而不是诸如Object.getPrototypeOf(this)之类的依赖于调用的内容的原因,请参见here

如果保留原始对象,则可能会导致内存泄漏,因为原始对象无法被垃圾回收。

否,它不会比其他闭包引起更多的内存泄漏。当然,该方法可以防止对其原始对象进行垃圾回收,但是考虑到该原始对象是原型对象(至少在正常情况下是这样),在通常调用该方法的对象的原型链中也引用了该原型对象,因此不是问题。

如果有人能用简单的语言解释这种行为,我将非常感激。 super如何确定其价值?

它获取其绑定的原始对象的原型,该对象是定义方法的对象。但是请注意,访问super上的属性不会返回对该对象属性的普通引用,但是有一个特殊的引用,该引用在被调用(一个方法)时会将当前作用域的this值用作方法调用的this参数,而不是原型对象。简而言之,

const x = {
  method() {
    super.something(a,b);
  }
}

对...的厌恶

const x = {
  method() {
    Object.getPrototypeOf(x).something.call(this,a,b);
  }
}

class X {
  method() {
    super.something(a,b);
  }
}

对...的厌恶

class X {
  method() {
    Object.getPrototypeOf(X.prototype).something.call(this,b);
  }
}
,

注意: TL; DR参见super mixin code 此答案末尾的解决方案;包括干净的方法[[Home_Object]]

作为Smalltalk,JavaScript和其他动态语言的(引擎/运行时)实现者,其引擎和框架[QKS,Microsoft,Smallscript] 1991年至今。重要的(引擎/运行时)实现者规则之一是,为了获得性能(功能,速度,资源消耗等)而在任何需要的地方高效欺骗实现者;但绝不要以用户层语义层所能捕捉到的方式来实现。

super的ES6定义和实现表现出静态语言语义。不是预期的声明式/命令式JavaScript动态原型语义。因此,这可能导致对POLS的混乱理解,即使看起来不仅仅是错误的语义。

如果您不同意,请参见下面在支持 mixins 中说明的问题(以及间接引用inst-field-enumeration的问题,这些问题不需要构造实例等)。 [POLS-最不惊奇的原则]。

就目前而言,ES6 classsuper的设计使构建自然的JS合成器和mixins具有挑战性。实际上,ES6设计迫使引擎开发人员违反“欺诈但不要被抓住”实施者规则。

我在ES6的class实现设计中的许多重要细节中观察到类似的实现约束决定。

正确地实施super作为一种“有效的” Smalltalk /动态语言绑定动作,实际上是一项挑战。

超级绑定要求绑定机制找到使用{{1}引用fn-container的{​​{1}} this-call的{​​{1}}(方法词典) }原型链(方法词典链)。

已重定:super-binding-ctx function的确定在逻辑上遍历了super原型链,以识别哪个this包含引用{{1}的[[Home_Object]] }。该this成为原始“锚点”,活页夹高速缓存应从该原始“锚点”尝试绑定object请求。这种查找需要function 谓词绑定器设计。

步骤:

  1. 确定正在使用super的当前fn-container.__proto__
  2. 确定哪个super贡献了#1的绑定力
  3. 从#2的perspective-type开始搜索function(锚定)键。

“欺骗”是他们选择不做#1和#2。

在分类时(当superthis...fn-container时)将#2 硬烘焙到包含super引用的__proto__中。
给出class-function,大概是ES6实现将new-ed设置为function

“抓到”的结果是(由于#2是硬烤),您无法在组成原型样式的情况下获得 dynamic-super-binding mixins或没有可用的super实现;但是在实现不存在时调用extends $class是错误/异常。

给出:(问题示例如下所示)

[[Home_Object]]

$class.prototype已设置为使用super
但是,当它运行时,其super将不会调用class M0 {a(){}} class M1 extends M0 {a(){super.a()} class X {a(){}} class Y extends X {} Y.prototype.a = M1.prototype.a const y = new Y y.a() ,它将INSTEAD调用(硬编码) y.a()

这不是预期的动态语言(或历史M1.prototype.a POLS或JS原始语言)行为。

也就是说,它不是Smalltalk中的super的动态语言语义(JS是从中衍生出来的)或其他祖先的动态语言实现设计。

因此,除其他问题外,使用X.prototype.aM0.prototype.a构建mixins 需要特别注意,并且在如何编写方面具有静态语言样式约束。这些限制“出现”,违背了 super在ES6中的灵活性。

话虽如此,我在下面提供了一个可行的实现,其中可以使用变通办法来实现许多所需的工作,并指出这是对真正的动态原型组合的一种折衷。

我还注意到,没有一种公开的方式要求类似super的东西然后执行类似classes的东西;实际上,superextends可以做到。

失望了...

根据我在语言标准组的经验以及在Microsoft和其他地方作为JavaScript架构师的参与,这真令人沮丧。经过4年的讨论,他们应该像我在这里所做的那样继续讨论它或提供代码示例。不这样做就释放它的潜力很强,它可以创建遗留代码,从而使将来很难甚至无法修复(这不是标准组织所希望的)。幕后的紧张局势之一是关于静态语言与动态语言技术的长期对立观点。与其认识到两者都需要按照精心设计的“命令式”和“声明式”语义在JavaScript中正确共存。

在我看来,ES6 this-fn设计应该既讨论了我在此处提出的问题,又提供了与我在此处和下文提供的代码示例相似的代码示例,以说明如何解决这些问题。

em>

“类”在很大程度上可能是语法糖,但由于它代表了不是糖的关键部分,因此对于JS动态原型组合和更新来说,它们是静态的“易碎”,似乎不适合“声明式”与“命令式” “ JS设计语义。

解决方法: 作为一种“声明式”解决方法(仍然不是动态组合),可以使用如下功能性“模板”模型来定义MIXIN:

this-fn.proto-container.__proto__.this-selector(...)

可以将其概括为以下模式:

super(...)

使用

super.a(...)

假设,我们可以将其重写为:

super

有了我们的 mixin-template-fn polyfill 支持,我们可以模拟 ESS(ess.dev)const Mx1 = super_type => class extends super_type {a(){super.a()} class Y extends Mx1(X) {} 的行为。让我们反思和询问 根据我们的 mixin-template-fn 构造的class Y extends X.mixin(Mx1,...) { } class MixinBase { static mixin(...mixinTemplateFns) { const compose = (super_type,mixinTemplateFn) => { const mixin = mixinTemplateFn(super_type) const mxNameDefn = Reflect.getOwnPropertyDescriptor(mixinTemplateFn,'name') if(mxNameDefn) { Reflect.deleteProperty(mixin,'name') Reflect.defineProperty(mixin,'name',mxNameDefn) // Polyfill support for: "ess.dev" `this-fn` and "qks.st/S#" // PerspectiveType "family" (protected) selector-namespace `super` Reflect.defineProperty(mixin,'$mixin_'+mxNameDefn.value,{value: mixin,writeable: false}) } return mixin // thanks "Bergi"; catching this line got elided } // thanks "Bergi" suggesting `reduceRight` to replace `reverse().reduce` return mixinTemplateFns.reduceRight(compose,this); } }

JS当前不允许任何功能(const M0 = $class => class extends $class {a(){}} // only for exemplar mixin(M1,M0) const M1 = $class => class extends $class {a(){super.a();...} class X extends MixinBase {a(){...}} // <= M1 injects here as expected and supports `super` as expected => class Y extends X.mixin(M1) {...} class Z extends X.mixin(M1,M0) {...} const y = new Y y.a() 除外) 只写this-fn。 即,它没有no-op语义,当没有 mixin_

它需要使用class捕获输入参数。所以,必须 对扩展变量进行硬编码,然后显式传递参数 声明为constructor隐式地做到这一点。

我们不能通用地引用super(...)

super[this-selector]

如果您想玩发现游戏并走spread-arg链,则可以使用以下内容(它与super(...)的{​​{1}} polyfill修复程序一起使用)。

this-selector

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-