-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unqualified instance variable references #18
Comments
Similarly to the concern I raised in #17 (comment) , I would worry that programmers would misinterpret these variable references as being "lexically scoped" (and therefore, that the method is somehow bound to the instance), rather than based on |
So, if I understand correctly, the concern is that users might expect the following to work, because there's no class C {
var x;
m() { return x }
}
let { m } = new C();
m(); I think this is yet another case of JS wanting proper support for method extraction/binding. |
@zenparsing Yes, that's exactly the concern. Even if we add a method extraction/binding feature, I'm not sure if this would really be enough to change the intuition people might have about what lexical scoping means (though maybe it'd reduce the frequency of this coming up as an issue). |
I see what you're saying, but I'm tempted to disagree here. The lack of a method extraction feature is causing developers to reach for things that wouldn't make sense otherwise. Another example would be the anti-pattern of using public fields and arrow functions to create per-instance "methods". But I agree that a broader range of opinions would help us. |
As I think we've discussed in the context of an earlier proposal, this would seem to mean refactoring constructor() {
x = 0;
y = 0;
} into constructor(x, y) {
x = x;
y = x;
} becomes problematic. More generally it opens up the possibility for all sorts of unintended confusion between lexical variables and instance variables. For example, consider a method in a large class that makes a single use of some imported binding. Latter somebody (maybe the same dev) decides to add a new instance variable to that class and just happens to choose the same name as the imported binding because they are unaware (or don't remember) that single use buried in the method. More generally, this seems very unfriendly to people reading code. Reason for assigning to an instance variable is usually very different from the reason for assigning to an up-level lexical variable. When reading code, it is very helpful to be able to directly see this distinction rather than have to look somewhere else to see what kind of binding is being assigned to. I've argued in another thread that is it very important that we emphasis the difference between properties and instance variables and that using -> instead of . as the access operator is an important part of making that distinction. I think it is equally import to emphasis the distinction between block allocated lexical variables and instance variables. Making them indistinguishable at the point of use seems counterproductive from that perspective. This approach also seems counter to our minimizing complexity goals. It adds specification (and possibly runtime) complexity and it also seems to add complexity to the user's conceptual model. I think we a near a sweet spot with what we have currently spec'ed. It's good to consider this alternative but I don't think its a path we should take. |
You're correct that unqualified instance vars opens up the possibility of unintentional (or perhaps just annoying) lexical shadowing. But aren't these issues are familiar in any language that allows unqualified member access (e.g. C++, C#, and Java)? Furthermore, I think our choice of
I agree that, in some cases, readability is better when one always uses the It should be pointed out that, since unqualified instance variable references are statically resolvable, syntax highlighters can choose to use a different color for those identifiers.
I agree with all of this. We are currently at a sweet spot. But I think that this will be our only opportunity to give users the |
I agree. And I think that #17 is a better fit, even with the ASI hazard. |
@littledan It seems like over in #17 you reject unqualified instance vars based on "the problems of complicating lexical scope." Clearly it does complicate lexical scope, but is there an example or further arguments that might clarify the resulting problems? |
@zenparsing My main concern is #18 (comment) . This is why we removed shorthand support from the current class fields proposal. An unprefixed variable seems "even worse" since it's even harder to see the implicit |
Is that substantially different than the situation in Java, C++, or C#? |
JavaScript scoping is very different from those languages. They are more complicated in some ways, and simpler in other ways. JS has single-namespace lexical scoping, but then there is complexity from |
One reason the situation is different for (original) Java is that it lacked global variables, standalone functions, or really any common usage of deep lexical nesting. If you see a syntactic uncapitalized identifier you only have to check the immediately enclosing function to determine it is not a class field. Also see https://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.4 which places restrictions on some declarations that would shadow field names. |
Certainly the languages are different in how they handle scoping, and perhaps a lack of deep lexical nesting patterns in those languages is significant. I'm not sure yet. I do know that the closure-class pattern was popular before ES2015 classes. function OldSchoolClass() {
var a = 1, b = 2;
this.m = function() {
return a + b;
};
}
new OldSchoolClass().m(); And if users find that pattern intuitive (and in general they do), it seems to me that they will find this version intuitive as well: class NewSchoolClass {
var a, b;
constructor() {
a = 1;
b = 2;
}
m() {
return a + b;
}
} And so I'm still struggling to see the problem here. The main behavioral difference between |
@zenparsing The problem is, once they are so happy with the parallelism there, they may use |
@littledan And you think that users will continue to fall into that trap if we provide them with a suitable method extraction syntax? Let's say that we have: let fn = new NewSchoolClass()::m; If this becomes the canonical way to extract a method from a class instance, then does your objection still apply? |
@zenparsing It's definitely a mitigation, but I think people will continue to use the syntax |
Sorry, I'm still not quite understanding the strength of the objection. A typical class definition will contain many |
I'm not going to stand up in committee and veto prefix-less shorthand if everyone else likes the idea. However, I worked on the removal of this feature from the class fields proposal because the incorrect mental model seemed extremely prevalent (and most prevalent among the strongest supporters of the shorthand). |
@littledan Oh, I was more referring to the strength of the argument rather than level of personal objection, but that's good to know, nonetheless 😄 I think I understand now, though. It might be a matter of reaching out to more people (particularly those "strongest supporters") to find out their reactions. |
Catching up here (thanks for pointer in #17). I mostly agree with @zenparsing that we can separate concerns and address method extraction independently, and with no greater or lesser urgency (which is to say "not much" judging by TC39 sentiment!). Two things rankle here:
|
I toyed with suggesting constructor(->x, ->y) earlier and designed not to because of my general concern about keeping things "max-min". consider: |
Not sure if there's an issue, but I think we've chatted at some point about creating But it seemed to sort of confuse the distinction between destructuring bind and assignment in a way that seemed hard to generalize fully. For example, should we support |
Another syntactic option for shortening constructors might be something like: class Point {
var x, y;
constructor(var x = 0, var y = 0) {}
} Or some variation thereof. As @allenwb says.
@littledan asks:
I don't think so. Here is another instance where using the My feeling here is that we should leave off auto-assignment to instance vars from this proposal for the sake of minimalism. The lack of support for this kind of feature in C# and Java (please correct me if I'm wrong about that!) makes me feel confidant that programmers will get along without it just fine. |
@BrendanEich speculates:
How can we get a better understanding of the extent to which this will be a problem? It seems to me that linters should be able to detect unsafe shadowing of instance variables, as in the obvious case of For wrong- |
Closing in preparation for public review. Feel free to continue discussion here; we may choose to re-open at a later time. |
Unqualified access would actually have to be a part of this proposal, since it would be a backwards-incompatible change to add it later.
What do you think would lead them to "false conclusions": the choice of |
Well, I was referring to unqualified access (no matter what the keyword was), but with what @hax has written on several threads, it seems that at least they are coming to this false conclusion due to other things in this proposal; maybe the choice of |
@littledan By "false conclusions", do you mean the expectation of per-instance method binding? Or something more? FWIW, I think that @hax has a pretty good understanding both the proposal and the extension described in this thread. |
@zenparsing Yes, the expectation of per-instance method binding. |
@littledan What I try to express in all my comments is: When programmers use old closure private pattern, they do NOT expect per-instance method binding for its own purpose, this is why they will be trapped. So as the perspective of instance variable expectation which closure private pattern try to give, per-instance method binding is a bug in the |
After rethought of it, I believe even we do not support shortcut, we should throw SyntaxError for it. So it would still backwards-compatible change to add it later. let x = 0
// ... many lines of code
class A {
var x
constructor(x = 0) {
this->x = x // ok here
}
get x() {
return x // I really mean this->x here
}
set x(value) {
x = value // I really mean this->x here
}
}
const a1 = new A()
const a2 = new A()
a1.x // 0, ok!
a2.x // 0, ok!
++a1.x // 1, ok!
++a2.x // 2, WTF?? It's ok to teach programmers you need |
This is a very specific claim about programmers and their intuitions. I would've guessed the opposite--some group of programmers seem to always hope/assume that methods are bound (including from classes, where they never are); I'd expect that some of these are using the closure pattern and happy that they do, in fact, have these bound methods (with respect to closed-over values). If they use the closure desugaring as a mental model, they will be disappointed; @getify has pointed out that mental models that make incorrect predictions can be detrimental to learning. Maybe we should talk to a larger sample of JS programmers about what their intuitions would be. It seems like we have the two hypotheses pretty clearly laid out here; let's get some data. |
I have several concerns to share, related to the idea of instance variables (aka, IIUC, "private members"), and shorthand/ellision of the qualifier. I'll try to organize these concerns into separate comments here to make them more digestable. To start, a clarifying question: is "instance variable" indeed a "private member", or is there some other distinction I've missed? If so, why then the distinction between "instance variables" (which are private) and "hidden methods" (which are also, basically, private)? Seems like it would be semantically cleaner to refer to both of these either as "hidden" or "private". What am I missing here? |
I understand the desire for consistency and generally am very sympathetic to that argument. But I find this particular case to be deeply troubling because it significantly obscures how I (and others) teach people to look at the call-site of a function to determine what that call's f(); Is that call-site including an implicit The single biggest confusion I find that both experienced and new-learners-of-JS have around It's already very difficult -- and by that, I mean only "uncommon" -- for people to fully grok the call-site It's also a slippery slope, because as soon as you do it for hidden methods, people will clamor for regular public instance methods to also have the same shorthand. And while we're at it, let's go ahead and shorthand instance properties! |
Hi @getify !
We intentionally avoid using the term "private" for both instance variables and hidden methods, because it tends to cause confusion in users that expect a statically-typed language version of "private", or TypeScript's version of "private". Otherwise, the lookup semantics for instance variables match the lookup semantics for the current "private fields" proposal.
Instance variables are attached to the instance. You can think of them either like "internal slots" or WeakMap entries, depending on your point of view. If the instance doesn't "have" the instance variable slot, then you get a TypeError. Hidden methods are not attached to the instance at all. They are lexically scoped inside of the class body and can be used as if they were methods on the object by using the |
@zenparsing I understand everything you just said, I believe, but it all sounds like spec-speak reasoning and not about naming that keeps things clean and clear for end-developers. IOW, those differences seem more to be related to implementation or internal details of JS than to the ergonomics of how an end-developer uses them and observes their behavior. If you don't want to use "private", fine. But I still think that the end-developer will think about both of these as "hidden", and not pay that much attention to the nuance that one of them is on the prototype object and the other is on the From the perspective of a teacher, I would be more in favor of naming things in a way that makes sense to the end-developer than to the spec implementor. But I certainly understand why TC39's general inclination might be the opposite. |
With respect to shorthanding of "instance variables": class A {
var x;
foo() {
x = 2;
}
} I agree with @hax that this particular case of shorthanding is probably not that difficult for people to grok, if they're already familiar with the "closure-class" pattern. Of course, as time goes on, and more people either switch to ES6+ classes, or learn them exclusively, the number of people who will be familiar with that pattern will go down, relatively speaking. But that's not what troubles me here. What troubles me is the idea that we're mixing concepts of closure and That confusion becomes more obvious when we introduce an inner function: var x;
class A {
var y;
foo() {
var z;
x = 1;
y = 2;
this.y = 3;
this->y = 4;
z = 5;
return function inner() {
x = 10;
y = 20;
this.y = 30;
this->y = 40;
z = 50;
};
}
output() {
console.log( x, y, this.y, this->y, z );
}
}
var o = new A();
var f = o.foo();
var m = {};
f.call( m );
o.output(); // ??
m; // ?? Imagine having to explain the nuances of this example to a new learner of JS, specifically the differences between There's an open question... would the implicit I have experienced strong pushback from my students when they learn the What I'm getting at is that conceptually, we really need to preserve a strong barrier between Attempting to make a dynamic Unfortunately, the less they know about the differences, the easier it is to squint and pretend that one is the other. The more they learn, the more troubling the conflation will be. In my courses, I spend significant effort helping create a strong semantic boundary between static lexical closure and dynamic |
Another open question: is the suggested shorthanding mandatory or optional? IOW, can I would find that optionality troubling as it creates a larger surface area for learners. If you're going to "allow" shorthanding (which I'm not generally in favor of), I'd suggest it should be mandatory and all the notions of |
If we went this direction the binding would have to close over the "this" value of the method definition directly inside of the class body (and not be simple sugar for
The shorthand would have to be optional so that we can access the instance variables of other instances. That would be similar to other languages that allow eliding Regarding naming, early on we briefly considered Thank for the long example! Strangely enough, I find it pretty easy to follow. The Just to be clear, I'm not necessary trying to advocate for this "C++-style" shorthand, but I do want to probe what really is wrong with it, rather than just assume it is wrong because it does something that hasn't been done before. @getify Regarding the method extraction issue, do you think that we could solve that issue (not just for this shorthand, but for JS in general) by having a method-extraction operator? For example: let fn = &obj.method; Could we then just tell developers that |
@zenparsing @getify With regard to method extraction: For what it’s worth, smart pipelines with Additional Feature PF would address method extraction (along with function composition and partial application). It will be presented to TC39 next week as an option along with an alternative pipeline proposal. let fn = +> obj.method; |
Let me take a crack at it. First we haven't been using the term "members" instead we have say "class body elements" or "just class elements" but for the rest of this message I'll use "member" as a synonym for those terms. Both instance variables and hidden method definitions are "hidden members" and are access using the Instance variables (as the name hopefully suggests) are per instance hidden members. Each instance of the class has its own distinct replication of each declared instance variable. Hidden methods are shared members. Each instance of the class share access to a single distinct function corresponding to each hidden method. We could have (and might reconsider) substituted Thoughts? BTW, Hope you've read the rationale document |
@getify The implicit access is not part of the current Classes 1.1 proposal, largely because of the sort of issues you have been mentioning. I don't think I could support a proposal that included them. Yes, some (perhaps) many users think they would like to have them but IMO the conceptual complexity they introduce is not worth the minor textual brevity they provide. |
Sorry I didn't make it clear that it's not a claim of programmers intuitions, but the explanation of why they will be trapped in old closure private pattern, and how this proposal fix it. I just realized we use the ambiguous word "per-instance method" here. When I talk about old closure private pattern, "per-instance method" is not bound because you do not have arrow function in ES3 era. Of coz you can make them bound, but it not belongs to closure private pattern. It's clear to programmers (I suppose) that normal functions (in old closure private pattern) and class methods (whatever it access instance variable with @zenparsing "using public fields and arrow functions to create per-instance methods" which I see "per-instance bound method" may be much clear. The problem of "per-instance method binding" in closure private pattern is, even it's not bound, but the consequence is very like half of it is bound, so let's say it's semi-bound 😜 . This proposal fix it that you do not need to use buggy semi-bound "per-instance method binding" anymore but keep the good mapping of mental model of closure private pattern.
I understand it. If someone want to fix "semi-bound" problem before, the only choice is made them "full-bound", which will be clumsy in ES3 days but much easy by using arrow functions now. Unfortunately it can't solve the inconsistency between "per-instance bound method" and prototype-based/class methods which are always not bound. So they may hope/assume class methods are bound. I would sympathize with them, but if this was their "intuition", then such "intuition" is doomed and have to be conquered sooner or later. I think most programmers use closure private pattern don't fully realize the "semi-bound" problem, this is why they will be trapped. And the "per-instance bound method" pattern in React is not for private, but for other reason. So I believe this proposal's recall of closure private pattern does not imply "bound" semantic.
I think the incorrect predictions come from other place like event listeners. Anyway, the keypoint is wherever such predictions/intuitions come from, they should be conquered because you should never expect bound semantic of class methods.
As I explained (hope I make my words clear), even there are many programmers has such intuitions (I very doubt), it's not the problem of this proposal, it's the problem of other proposal like method extraction need to solve. |
Some words about terms. As a non-English speaker, I'm fine with any keyword. But I try to give my understanding about the technical terms for your consideration. When I see
I try to find a word in my poor English vocabulary, here is my list:
With these words, there could be several options:
Here is my two rationale:
@allenwb mentioned I agree with @getify that people don't pay much attention on prototype or not, and many (like me) who never use "per-instance method" just use a easier way to differentiate: all methods/accessors are on prototype and the rest are on instance. So use two keywords seems not helpful for this purpose. But I understand I have to admit I'm deeply attracted to shortcut form of Thank you for reading 🤓 |
@getify I would expect most js programmers have already dropped old I agree distinguish In most cases we write class as a self-contained module, and there are just instance variables and a few const bindings which easy to handle IMO. This very close to class practice in other languages, Java/C# for example. The omit of While we have the great power (with the risk of increasing difficulty to understand the code) of nesting classes in closures and versa, programmers use it for good purpose. If they use it wrong, nothing can save them. I don't think the nesting of closures and classes will be necessarily harder to understand than the corresponding deep nesting of only closures. The burden of |
@allenwb I still think it would be more clear to users to use "hidden instance variables" and "hidden methods". "Instance variables" is not going to sound different enough from "instance properties" to make it seem like they are hidden. When the inevitable question comes up: "then what are non-hidden instance variables?", the simple answer is either "There aren't" or... "They're called 'instance properties'." |
@zenparsing I don't think "method extraction" mechanisms are going to do anything to address the inner function use-case I showed. Or am I missing something? |
Another thing I think is perhaps a confused concept in this whole Let me illustrate with the function-based module pattern, first: var X = (function outer(){
var a = 1;
return {
foo() { return ++a; }
};
})();
X.foo(); // 2
X.foo(); // 3 Here, the Now consider: function outer() {
var a = 1;
return {
foo() { return ++a; }
};
}
var X = outer();
var Y = outer();
X.foo(); // 2
Y.foo(); // 2 Point I'm making is, that Now, consider this snippet: var inner = (function outer(){
var a = 1;
return function inner(){
return {
foo() { return ++a; }
};
};
})();
var X = inner();
var Y = inner();
X.foo(); // 2
Y.foo(); // 3!! Here, we have a So what does all of that have to do with the discussion of class whatever {
var a;
constructor() { this->a = 1; }
foo() { return ++this->a; }
}
var X = new whatever();
var Y = new whatever();
X.foo(); // 2
Y.foo(); // 2 For the same reasons as outlined above, to properly reason about the result there, you have to be thinking about the But... thinking about the I think it's troublesome and likely to lead to mental errors in new learners, to have to explain that the My point? Don't mix the idea of what's going on inside a class body with the idea of "closure". You're asking for people to trip over confusion if they try to think or reason about what's really going on. Moreover, don't use I like using Related: the "class initializer" in this proposal, which "only runs once at class definition time", is further reinforcement of the underlying assumed idea of this proposal, that everything in the class body is run "each" time, except for the stuff put inside that block. That's more confusing than it should be, and doesn't fit at all with my intuition. I think it should be opposite: by default, everything in the class body happens once, at definition time, and for anything you want to happen each instantiation, that's either the job of the constructor, or if we really need a special declarative initializer block, then have an initializer block like |
@getify I think your examples in last comment are interesting, which I get a very different view from them. Here is a modified example of class with instance var. var foo = 0;
class Whatever {
var bar;
constructor(v) {
bar = v;
}
test() {
++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 I think it's not difficult for programmers to determine the values in this example, (I created a test to see how programmers use their intuition, if you like , you could spread the links to others.) Because
Note, I intentionally use shortcut syntax which omit On the contrary, examples using nested closures are much harder to determine the semantics. But we need to figure out the factors. I try to restore the brain activity of reading them, see my comments to the code. var X = (function outer(){ // <- what's this function for? not very clear at this line
var a = 1; // <- local var, but currently not know the life cycle
// whether it's a one-off or persistent
return {
foo() { return ++a; } // <- ok, var a is captured, so it's a persistent private state.
};
})(); // <- ok, this is a function module pattern, but what it return?
// let's move back to previous lines...
// ok it returns an object with a closure private state referenced by the foo method
X.foo(); // 2
X.foo(); // 3 function outer() { // <- quite good, it's a function declaration,
// so don't need to consider function module pattern
var a = 1; // <- local var, but currently not know the life cycle
return { // <- ok, each call of this function will give a new object
foo() { return ++a; } // <- var a is captured, it's a closure private state
};
} // <- ok, outer is a object maker
var X = outer();
var Y = outer();
X.foo(); // 2
Y.foo(); // 2 var inner = (function outer(){ // what's this function for? not very clear at this line
var a = 1; // <- local var, but currently not know the life cycle
return function inner(){ // what's this inner function for? not very clear at this line
return { // ok, each call of this function will give a new object
foo() { return ++a; } // <- var a is captured, it's a closure private state,
// but from which function? need to look backward...
// ok, come from outer, but still don't know what outer is
};
}; // <- ok, inner is a object maker
})(); // <- ok, outer is a function module pattern, but what it return?
// let's move back to previous lines...
// yes it's return inner function which is a object maker which return object with the state a
// but where is state a come from? need to read again...
var X = inner();
var Y = inner();
X.foo(); // 2
Y.foo(); // 3!! So I think these examples show at least 2 problems of nested closure:
Let's rewrite example 2 and 3 by introducing class with instance var (example 1 do not need class) class outer { // <- quite clear, it's a class declaration
var a = 1; // <- var in class, per-instance state
foo() { return ++a; } // <- mutate per-instance a
}
var X = new outer();
var Y = new outer();
X.foo(); // 2
Y.foo(); // 2 var inner = (function outer(){ // IMO we do not need function module pattern because we
// already have real ESModule, but just leave it in this example
let a = 1; // <- I change `var` to `let` which reflect current practice
// `let` means it's very likely to be mutated at somewhere in the scope of `outer`
return class inner { // ok, return a class
foo() { return ++a; } // <- a is captured, and mutated,
// it's not a instance var, so all instances will refer to the same a
};
})();
var X = new inner();
var Y = new inner();
X.foo(); // 2
Y.foo(); // 3!! IMHO, they are much clear than the closure versions, and unlikely to introduce the confusions. What do you think? |
A report of the investigation I created: hax/hax.github.com#44 Up to now, 132 people think instance variable (only use shortcut form) match their intuition, 20 people think class-level var should same as function var as their intuition. This poll also guide people use 👎 to denote they never think code like that. I intentionally add this to check how negative feeling would be. So even 20 people feel instance var does not match their first intuition but only 6 show they may think it's "wrong". On the contrary I'm not very surprised that 42 people think class-level var should never have same semantic as function var. Note, most voters uptonow are Chinese programmers. But I keep the whole thread in bi-languages, so you can spread this investigation to other use your social media. And I guess/hope there will not be big difference between Chinese programmers and non-Chinese programmers 😅 |
I come from the chinese thread. (hax/hax.github.com#44 (comment)) Regardless of symbol selection or psychology, instance variable is against not only prototype but also implementation of existing native methods. Consider: var foo = 0;
class Whatever {
var bar;
constructor(v) {
bar = v;
}
test() {
++foo;
++bar;
return `${foo}:${bar}`;
}
}
var x = new Whatever(10);
x.test(); // "1:11"
var y = new Whatever(20);
y.test(); // "2:21"
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}); // new version of `with`?
(() => foo).call(class{var foo = 0}); // even in lambda? However, usual code looks like: [].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, hax/hax.github.com#44 (comment) is just ok. To support switching to different context, what we need is not syntax. |
@dou4cc If you still have other questions I'm glad to answer it in my original thread hax/hax.github.com#44 , and you can also contact me via telegram. |
As an alternative to #17, I would like to explore the possibility of making instance variables more "variable-like" by allowing unqualified access to instance variable names within class methods.
For example:
Unqualified access would apply to instance variables only, and not hidden methods.
Now that we've formalized the concept of "hidden member descriptors" (HMDs), I think we can do it like this:
For all methods defined in a class definition that has instance variable declarations, we store a list of those instance var HMDs in an internal slot, say
[[InstanceVariableParams]]
.On function declaration instantiation, for each HMD in
[[InstanceVariableParams]]
, we create an immutable binding in the function environment record from the instance var name to the instance var HMD.We modify
GetHiddenValue
andSetHiddenValue
such that if the reference's base value is an environment record (i.e. an unqualified reference), we use the result ofbase.GetThisBinding()
as the hidden reference receiver.Thoughts, @littledan @erights @BrendanEich ?
The text was updated successfully, but these errors were encountered: