How to modify the scope chain without `with` ?

Coroutines coroutines at gmail.com
Mon Feb 22 14:04:30 UTC 2016


On Mon, Feb 22, 2016 at 4:07 AM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
> FYI, this part might not work as you might expect. I've already been
> tripped up by similar.
>
> ```js
> var o = {
>   index: 0,
>   func() { return this.index++ },
> }
>
> with (o) {
>   func() // TypeError: Cannot read property 'index' of undefined
> }
> ```

To me this is expected...  I am accustomed to 'use strict'.

> The other reasons `with` is deprecated are because dynamic scoping is
> extremely hard to optimize and it's no longer obvious what variables
> are defined in that block (static analysis no longer works).

All I'm proposing is that we be given the ability to replace the
global scope/Object by assigning to a special reference (_ENV = {}) -
which would be scoped to the block, not forever-changed throughout the
entire project/runtime.  It shouldn't change the layout of how the
global scope is referenced on the stack in any implementation (v8?
Chakra?).  And then of course that global scope object could be
prototyped like any other object to provide inheritance/additional
scoping.

I am used to having this control in Lua - it works really nicely
there.  I hate to be "that guy" but I've seen this used to help people
manage and keep their code readable.  It's all just managing
namespaces.  If I'm writing a module that defines a lot of Math
functions I want the scope I define these functions in to be imbued
with the existing facilities from `Math`.  I want to directly invoke
cos() not Math.cos().  From the point of view of my math-related code
I don't want to have the main, global perspective of the script that
is loading my module.  I might make use of other modules that do not
have a Math focus, and for that I will associate identifiers that go
with their function: let log = require('logging');

To me this makes perfect sense, but the only way to do this (assuming
we never use `with` again) is for every module to be referenced
through a local, possibly shortened identifier and not touch the
global scope at all.  Repeating identifiers over and over is not
concision, it's not DRY - it's tedium.  I cannot be as expressive as I
want, nor can I make use of the environment I need - and mess it up
(temporarily).  On that 2nd note, I wish module loading were as simple
as this to prevent global destruction:

Loading a module should be:
1) Resolve to the path of the module (or <script> href?)
2) Fetch/read the file/script
3) Construct a global env/object that inherits from the existing environment
4) Run the script as a function/block within this new environment
5) Cache the `exports` value.
6) Return this value to the caller

I think Node does this essentially (which is why we have a `global`
reference to the "real" environment), but "normal" JS does not.  We
hide what we can in closures, but code that does not export in the
many fabulous ways we've come up with can have a lasting effect on the
global scope.  I like that to explicitly mutilate the global scope
(not module-scope) I have to type `global[prop] = ...`.

Personal opinion: I prefer runtime analysis to static analysis.  Not a
huge fan of type annotations and "strong mode" trying to cement a
type's ABI to eek out more performance.  I mean yes, optimizations are
great but I'd rather have flexiblity to express myself how I want over
limitations that save me negligible amounts of time..


More information about the es-discuss mailing list