First of all you have to be aware that all of these terms just describe concepts. They don't dictate any kind of implementation. But because this can be hard to imagine/visualize it can be helpful to think about these concepts as instantiations of something you know, like maps or tables.
Declarative environment records (DER) and object environment records (OER) have one thing in common: They are both environment records (ER), which are defined in the specification as:
An Environment Record records the identifier bindings that are created within the scope of its associated Lexical Environment.
This basically means that an ER keeps track of variable and function names and their associated values.
Consider this example:
var foo = 42;
function bar() { };
The corresponding ER would have two entries, one for foo
and one for bar
. If you imagine an ER to be a table, then it would look like
name value
----------------------
foo 42
bar <function object>
Now on to the difference between DER and OER. A DER might be the easiest to understand.
Declarative Environment Record
The term declarative should sound familiar since we are often talking of variable declarations and function declarations. The specification says:
Each declarative environment record is associated with an ECMAScript program scope containing variable and/or function declarations. A declarative environment record binds the set of identifiers defined by the declarations contained within its scope.
So, when you see
var foo = 42;
or
function bar() {
}
then you can assume that their names and values are stored in a DER.
Object Environment Record
OERs are less common, but in each JS application there exist at least one OER. The specification describes it as
Each object environment record is associated with an object called its binding object. An object environment record binds the set of identifier names that directly correspond to the property names of its binding object.
Have you ever wondered why the properties of the window
object are global variables? That's because the ER of the global scope is an OER: window
is the binding object and for each of its properties a corresponding entry is created in the OER. This is also in the specification:
The global environment’s Environment Record is an object environment record whose binding object is the global object.
Here is an example: Lets assume out binding object is
var obj = {
answer: 42
};
then the OER would be
name value
------------------------
answer 42
Note that in this case, the binding object (obj
) is really a JavaScript object. You are in the same situation when you are using the with
statement:
var obj = { foo: 42; };
with (obj) {
foo = foo / 2;
}
console.log(obj);
with
creates a OER and populates it with the property names and values from the passed object. That's why you can access them without explicitly referring to them via obj.*
. The OER also makes sure to update the binding object with the new value if one was assigned to one of the identifiers.
Activation Object
It looks like that in ES3, activation objects (AO) where automatically created when a function was executed and it was holding a reference to the special arguments
object. This seems to be related to DERs, but still to be something independent.
The concept of AOs doesn't seem to exist anymore in ES5 and I assume that it was unnecessary, since arguments
can be added directly to the DER of the execution context.
Execution Context
A new execution context (EC) is established whenever a function is executed and is used to keep track of the state of the execution:
An execution context contains whatever state is necessary to track the execution progress of its associated code.
This means the engine can add whatever information it needs to track the execution progress. But the specification also defines components that an EC must have, one of which is the VariableEnvironment, which is an ER (probably always a DER, but I don't know for sure). That means an ER is a part of an EC.