hax.github.com icon indicating copy to clipboard operation
hax.github.com copied to clipboard

How you interpret this code? -- test of a imaginary JavaScript code

Open hax opened this issue 6 years ago • 24 comments

Note, this is NOT a valid code as current JavaScript syntax. So the choices below are not correct or wrong, please just choose what your intuition tell.

注意,下面的代码目前并不是合法的代码。所以后面的选择没有对错之分,请按照你的直觉选择。

var foo = 0;

class Whatever {
   var bar;

   constructor(v) {
      bar = v;
   }
   
   test() {
      ++foo;
      ++bar;
      return `${foo}:${bar}`;
   }
}

var x = new Whatever(10);
x.test(); // output 1:11
var y = new Whatever(20);
y.test(); // output 2:21

x.test(); // output?
y.test(); // output?

Please use 👍 or 👎 to vote. Use 👍 to choose what match your intuition, you could also use 👎 to denote you will never interpret the code like that. If you have other answer, please add comment. Thank you.

请用表情符号投票。👍 表示跟你的直觉相符,你也可以用 👎 表示完全不会这么想。如果你觉得有其他结果,可以留言。谢谢。

hax avatar Mar 19 '18 14:03 hax

Choice A: bar is an instance variable which each instance of Whatever have a individual bar value 选择A:bar是一个实例变量,每个Whatever的实例都有各自的bar

3:12 4:22

hax avatar Mar 19 '18 14:03 hax

Choice B: bar is a bit like foo which shared by all the instances 选择B:barfoo差不多,所有实例访问的是同一个

3:22 4:23

hax avatar Mar 19 '18 14:03 hax

I think B is better.it's confirm to the js's grammer

BigGress avatar Mar 20 '18 01:03 BigGress

For choice B, is it drop support for instance variable? or just not shown in the code? 选项 B 是不支持创建实例变量吗?还是只是目前的代码里没有示例?

amio avatar Mar 20 '18 03:03 amio

@amio Just not demo-ed IMO 应该只是没有示例

SilentDepth avatar Mar 20 '18 03:03 SilentDepth

@SmallGress This is not which is better question. Don't think too much about it. Just use your intuition. 这不是一个“哪一个更好”的问题。不要想得太多,直接按你的第一直觉来就好了。

@amio This is not a poll for which is the better syntax of instance variable. In fact I don't think poll is a good way to choose syntax of new feature because it's too complex for most programmers to see all consequences of a syntax. But we can use a poll to test a much simple, much specific issue. So this is just a test to see whether programmers would be confused if we use var to declare instance var in the class.

hax avatar Mar 20 '18 06:03 hax

It will not ambiguous if we write: 写成这样就没歧义了吧:

var foo = 0;

class Whatever {
   var bar; // private instance field, use 'this->bar' for reference

   constructor(v) {
      this->bar = v;
   }
   
   test() {
      ++foo;
      this->bar++;
      return `${foo}:${this->bar}`;
   }
}

zheeeng avatar Mar 20 '18 09:03 zheeeng

@zheeeng It's may much clear bar is instance var if we using class1.1 full syntax this->bar to differentiate foo, but it also throw another question, how programmers see var bar in the class as a declaration for this->bar. Anyway, this poll is try to investigate the intuitions of the shortcut form. Because if class1.1 proposal support bar as a shortcut form of this->bar, then I guess programmers will mostly use bar in the code, only use this->bar or other->bar in rare case.

使用class1.1提案的 this->bar 语法以区分 foo 当然会让大家更清楚 bar 实际上是实例变量,不过有另外一个问题,程序员是否第一眼就认为class里的var bar会对应this->bar呢?但这些不是本测试的目的,本测试是为了看看直接写成bar的这个方式是否符合大家的直觉。因为如果class1.1提案支持把this->bar简写为bar,那么估计我们大部分时候在代码里都是直接写bar,只有少数时候会使用this->bar(函数里有bar同名变量)或other->barother是外面传进来的另一个实例)。

hax avatar Mar 20 '18 09:03 hax

My intuition tells me that just treat Class as a function and then translate the code like:

var foo = 0;

function Whatever(v) {
  var bar;

  bar = v;

  return {
    test: function () {
      ++foo;
      ++bar;
      return foo + ":" + bar;
    }
  }
}

var x = new Whatever(10);
x.test(); // 1:11
var y = new Whatever(20);
y.test(); // 2:21

x.test(); // 3:12
y.test(); // 4:22

Of course, they are not equivalent. The point I want to express is that my intuition treated bar as a local variable, rather than an instance variable. x.test and y.test are both closures, they share the same function body definition but store different lexical environments.

That's why I choose A.

CodeDaraW avatar Mar 20 '18 10:03 CodeDaraW

@hax 切记class只是语法糖啊!Choice A是搞哪一出? 截至ES2018,Whatever.prototype.a=function(){}能写成class形式(像这样:class Whatever{a(){}}),Whatever.prototype.b=1却写不成class形式。想问这种不一致是有意的吗?


Keep in mind that class is just syntactic sugar! What is Choice A doing? In ES2018, Whatever.prototype.a=function(){} can be rewriten in class-style like class Whatever{a(){}} while Whatever.prototype.b=1 cannot. Tell whether the inconsistency is deliberate.

dou4cc avatar Mar 21 '18 04:03 dou4cc

For Choice A, consider:

Whatever.prototype.test.call(x); // ????
y.test.call(x); // ????
Whatever.prototype.test.call(class{var bar = 0}); // ????
Whatever.prototype.test.call(class{}); // ????
Whatever.prototype.test.call(class{var foo = 0}); // ????
(function(){return foo}).call(class{var foo = 0}); // ????
(() => foo).call(class{var foo = 0}); // ????

dou4cc avatar Mar 21 '18 05:03 dou4cc

@dou4cc

这个测试只是用来看看大家对 class 级别的 var 直觉上的反应。不用想得太深。 当然,如果你对这种用法感到很抵触,可以通过投票表示。

This is only a test to investigate the intuition of the programmers if they see class-level var usage. Don't think too much. But you of coz can vote against it if you think it's a "wrong" thing.

我个人建议不要把class当做语法糖来看待,而是把prototype看成是class的内部细节。当然基于prototype必然对ES6+ class的设计产生影响,你说的只有“方法”没有“属性/数据字段”就是后果之一。这里有个有趣的事情是,如果一定要说语法糖的话,你觉得class-level的var应该是每实例上还是原型上的属性呢?还是其他?

I will recommend that don't treat class as syntactic sugar, but treat prototype as the inner details of class. Of coz, the fact that ES6+ class is based on prototype cause consequence to the design, one of it is only "methods" but no "properties/data fields". I doubt how you think class-level var in the view of syntactic sugar? Property per-instance, or property on the prototype? or other?

至于后面你写的那些例子,这体现了你在考虑this动态绑定的语义问题。当然这是一个重要的问题,提案必须要给出精确的语义。但是这不代表这对于实际代码一定是特别重要的问题。可以做某件事(比如把 class 上的方法扒下来绑一个其他 context 调用),不代表你真的有需求在代码里这么用。我希望咱们能聊一些更为切实的 use case,看看会有什么问题。谢谢。

About the code examples of call usage, I believe you are considering the semantic of this binding. It's a important issue for the spec, the proposal should give precise semantic for every possible usage. But I don't think it's a important issue for real coding. We have the ability to do something (like extract the method from a instance of a class and call it with other context), don't necessarily mean you have a real use case in the daily coding. I hope we can focus on some much real use cases, and figure out if there is any problem. Thank you.

hax avatar Mar 21 '18 13:03 hax

@hax

我个人建议不要把class当做语法糖来看待,而是把prototype看成是class的内部细节。当然基于prototype必然对ES6+ class的设计产生影响,你说的只有“方法”没有“属性/数据字段”就是后果之一。这里有个有趣的事情是,如果一定要说语法糖的话,你觉得class-level的var应该是每实例上还是原型上的属性呢?还是其他?

I choosed B.

In ES2018, Whatever.prototype.a=function(){} can be rewriten in class-style like class Whatever{a(){}} while Whatever.prototype.b=1 cannot. Tell whether the inconsistency is deliberate.


至于后面你写的那些例子,这体现了你在考虑this动态绑定的语义问题。当然这是一个重要的问题,提案必须要给出精确的语义。但是这不代表这对于实际代码一定是特别重要的问题。可以做某件事(比如把 class 上的方法扒下来绑一个其他 context 调用),不代表你真的有需求在代码里这么用。我希望咱们能聊一些更为切实的 use case,看看会有什么问题。谢谢。

You are against not only prototype but also implementation of existing native methods.

Carefully review the following usual code:

[].map.call(["1", "2", "3"], a => +a); // [1, 2, 3]
[].map.call("123", a => +a); // [1, 2, 3]
[].map.call(null, a => +a); // TypeError

When binding context is needed, https://github.com/hax/hax.github.com/issues/44#issuecomment-374545055 is just ok. To support switching to different context, what we need is not syntax.

dou4cc avatar Mar 22 '18 12:03 dou4cc

@dou4cc 我不是很明白你的意思。你能不能先明确的说一下你直觉上class-level的var应该是每实例上还是原型上的属性呢?还是其他?

You are against not only prototype but also implementation of existing native methods.

完全不明白你在说什么。也许你用中文直接写会比较容易点?

我再解释一次,这个测试的目的只是为了看大家对于这个代码的直觉。当然我们可以在某项前提下讨论具体的语义(比如call)。

hax avatar Mar 22 '18 14:03 hax

@hax I choosed B.

dou4cc avatar Mar 22 '18 14:03 dou4cc

@dou4cc 我知道你选B。我不清楚的是你的其他意思。实例变量跟原型没有关系。

hax avatar Mar 22 '18 14:03 hax

@hax 具体而言我希望var是原型上的属性。我一直好奇你们为什么不提供这样的class风格的写法。

dou4cc avatar Mar 22 '18 14:03 dou4cc

@hax 更准确地说,我希望的是let和const,我不希望再有var了。let和const对应可写和只读。

dou4cc avatar Mar 22 '18 14:03 dou4cc

@dou4cc 我大概理解了,你是希望有语法针对原型上的属性?但是原型属性存在一些问题。所以ES6 class就没有加。你可以看我去年演讲的 slide :http://johnhax.net/2017/js-private/slide?qcon#43 。所以不管哪一份草案都不支持原型属性。

至于 let / const ,只是关键字的差别,跟原型没有关系。如果你关心为什么这份草案使用了 var 而不是 let/const,可以看 class1.1 里的 25 号 issue(我不贴链接了,避免产生引用)。

hax avatar Mar 22 '18 14:03 hax

我一直好奇你们为什么不提供这样的class风格的写法。

@dou4cc 我并不是TC39的成员。所以这个问题我不能做最权威的回答。不过基本原因就是我前面说的。

另外,我们每个人当然都有自由去相关proposal的讨论里发表意见。我觉得中国程序员去参与标准讨论是一件好事。不过标准提案的讨论的门槛是有点高的,而且又有英语的门槛。如果中国人都弄不懂你的意思,估计老外也看不懂。所以如果对一些问题不是特别清楚,欢迎先在这里提问,我会尽力解释。谢谢。

另外为了保护隐私起见,我把之前你留的联系方式删除了哦。

hax avatar Mar 22 '18 14:03 hax

作为一个 Jser,以前从未见过那样的语法,所以选 A 或选 B 都可能是对的 As a Jser, I have never seen such a grammar before, so choosing either A or B may be right

作为一个 Javaer,我们习惯于这么定义成员变量 As a Javaer, we are used to defining member variables like this

class Whatever {
   private int bar = 1;
}

如果去掉权限修饰符 (private) 和默认值 If we remove access modifiers (private) and default values

class Whatever {
   int bar;
}

再使用 var 代替 int And use var instead of int

class Whatever {
   var bar;
}

就跟上面一模一样了,所以我选 A Just like the above, so I choose A

xiedacon avatar Mar 27 '18 14:03 xiedacon

If

class foo {
    bar = 0;
}

means per-instance, then this should mean per-instance.

However, personally, I do think this is against javascript semantics. If class has a scope, then the declaration should be of the class scope, which means, "static", for both.

However, class does not mean a scope. How can you declare a variable without linking it to a scope ? That is wired, and "magic".

Besides, how should it work by your design, and how can this make the variables private ?

yw662 avatar Oct 21 '18 05:10 yw662

@yw662

This investigation is try to find what most javascript programmer's intuition on x is when they see class { var x } without any teaching. The vote result "145:21" has proved that most js programmers see x is per instance.

then the declaration should be of the class scope, which means, "static", for both.

On the other side, because most js programmers already know static so if they need a var which shared by all instances, they will just use static or outer var. If inner var just do the same thing, then the syntax is just useless. So they will think if this is a meaningful syntax, then it should mean per-instance.

how should it work by your design, and how can this make the variables private ?

The interesting part is, we do not need any magic like "this.#x" to make it "private". When you see

function C(v) {
  var x = v
  this.getX = function () { return x }
}
const c1 = new C(1)
const c2 = new C(2)

Would you think there is any way you can access x value for c1/c2 instances except calling getX()?

Actually, js programmers know there is no way to access it because x is the local var of the constructor.

The same intuition could apply to:

class C {
  var x
  constructor(v) { x = v }
  getX() { return x }
}
const c1 = new C(1)
const c2 = new C(2)

So you just know there is no way to access x of c1/c2 except calling c1.getX() or c2.getX().


@yw662 The classes 1.1 proposal is an alternative proposal for current field proposal. Even you think this proposal is not attractive, you should compare it to current this.#x proposal. If you must have to choose one, which one is better? Some members of TC39 just want to push this.#x to the standard, and some part of it already enabled in the Chrome canary! They can push it because they tell everyone there is no other better proposal.

I know you may don't want either. But unfortunately it already been you should choose the one you dislike the less. And recently, they just decide to refuse all alternatives. So that means you have no choice but just accept this.#x. I hope you already know the situation, and if you really don't want this.#x, you should tell TC39 that you think classes 1.1 is a better proposal so please do not use "there is no other alternative" as the excuse to push this.#x.

hax avatar Oct 21 '18 08:10 hax

The private here means, private but can be accessed by all members, which is semantically different from closure. If your implementation means no more than closure, it is not same with foo.#bar.

As a result, you do need magic to achieve this. You must allow foo.bar in member functions when bar is private, and deny it if foo.bar accessed outside of the class.

I personally love the closure implementation for private variables, and do think introducing a new private mechanism will definitely mess up the semantics. Actually I personally dislike all of the class things indeed. These are just hacks to semantics.

However,

foo.#bar feels like bad code, but is less magic. Your proposal seems not so bad, but is not javascript. So I would rather choose foo.#bar.

yw662 avatar Oct 22 '18 06:10 yw662