简体   繁体   中英

Primitives and memory allocation in JavaScript

Hi stackoverflow people:)

I'm a web developer diving in some JavaScript concepts.

I was reading ECMA docs and MDN to get really understanding of lexical environments, executions contexts, closures and memory management but I really got stuck in this topic.

EX:

const x = 1;

let y = x;

y = 2;

As far as I understand, 1 and 2 are primitives so immutable and every time I re-assign a variable, behind the scenes I'm changing the reference. So, in line 2, y and x are pointing to same value.

And here comes my headache: where is that value? Am I just pushing to the stack two references to the number 1 (in this case)?

I don't get if each primitive will have same memory's address for all the execution of the program or which is the behavior behind that.

There is so many information and I don't know in who I can trust because in large cases the information is contradictory haha.

Thanks for your time guys, and to be honest I'm learning English too so I hope you could understand me:)

There are primitive types (like number, boolean, string...) and complex types (object, array) in Javascript. So they will behave a little bit differently:

const x = 1; <- Assign the primitive value 1 to the variable x (somewhere in the memory heap)

let y = x; <- Assign the value of the variable 1 to the variable y (somewhere in the memory heap). Here, you will have 2 different pieces of memory, each for one of the vars.

y = 2; <- Change the value of the second variable to 2.

When you talk about memory addresses and pointers, you are talking mostly about complex types. Here, when you define an object, that object is stored in the heap, if you assign that object to another variable then you are pointing to the same value. An example:

const x = {a:1} <- Create the object and assign a reference to x

const y = x; <- Assign the same reference to y

ya = 2 <- Change the value of the object.

console.log(xa) <- will return 2.

For more information take a look at this website

Find out by using the Console in your browser

Open the Console in your browser

In most browsers pressing F12 opens the Console . If that doesn't work, then find the menu or submenu named Developer tools and click it.

Select the Console tab

Make sure the tab named Console is selected. Click it.

Create your test

Then type the statements you want to test into the Console .
For example:

const x = 1;
let y = x;
y = 2;

Show the results

Then type the following to show the result of your test - what you want to know:

[x,y]

Or:

console.log(x,y)

The result shows that x and y don't reference the same number.

Here is the output of typing [x,y] :

Array [ 1, 2 ]

And here is the output of typing console.log(x,y) :

1 2

The result shows that assigning to y doesn't affect x .

Rinse and repeat

Do something similar the next time you get in doubt about how it works.

The whole deal with the primitive values in JS is that they are not referential .

In an actual implementation, the JS numbers could be represented by some tag that identifies a JS value as a number, together with the 64-bits that hold the actual numeric value.

In most reasonable implementations, the x and y in your example do not store any references at any point. They are just two local variables that can be stored in the invocation record of the function in which they are declared (it's somewhat of an oversimplification; see below for when this is not the case) . Unless they are used in some (escaping) closure, they can be thought of as purely local variables stored either in registers or stack frame of the surrounding function. When you assign y = x , the value previously stored in x is copied to y as is. No references are created. No connection between x and y is established: they both hold their very own copy of the primitive number value.

Your formulation "pushing to the stack two references to the number 1" seems somewhat closer to the truth than the other ones, but it still seems not quite right:

  • Semantics of JS is not described in terms of a stack machine. Implementations will probably not be pushing individual values to any such stack. Instead, it's probably easier to think about it as pushing a stack frame of the function containing the variables x and y , then modifying the values of those variables, then waiting until the stack frame is popped of the stack, and all its contents are forgotten / overridden (no heap garbage collection involved anywhere).

  • In this stack frame (imagined or real), the two variables will each hold their separate copy of the primitive value describing the number 1 . There is no shared value on the heap, and there are no references pointing to it.

  • Even though it might be helpful to think of invocation records or stack frames, clever optimizations might decide to store the variables x and y elsewhere, eg they might determine that both x and y are strictly 64-bit numbers, and store them directly in some registers.


(a note on closures)

The case where x or y are used in some closure might be a tad more interesting, though. Consider this example:

function f() {
  var x = 0;
  function g() {
    return x;
  }
  function s(a) {
    x = a;
  }
  return {
    getX: g,
    setX: s,
  };
};

var { getX, setX } = f();

setX(42);
console.log(getX());

Here, the variable x is captured by the closures of g and s . Since both g and s escape in the returned value, and persist after f has terminated, the captured x must also remain in memory beyond the lifetime of f . Indeed, in such cases, the captured variable x ends up in a heap-allocated object, which is referenced by the closures g and s . The property corresponding to the captured variable x of this heap-allocated object would still hold a primitive number (still no references here).

The identifiers used for variables have a location where their value is stored, making them "lvalues" in that they can appear on the left hand side of an assignment expressions.

Because JavaScript is interpreted, a record of the name of the identifier and where it is stored needs to be maintained by the JavaScript engine. In standards that record is known as a "binding" of the name and is held in an environment record. Obtaining the value of a variable then looks up the binding of the its name to get its value. (Step 6 of a GetValue operation in standards)

So in the example

const x = 1;

let y = x;

y = 2;

x and y have different locations in an environment record.

Now JavaScript is loosely typed, meaning a variable can hold values of any data type supported in JavaScript. By implication, the representation of a value must hold information about it's type as well as its actual value.

This is the point at which people start to guess or hypothesize how (primitive) values are held internally by the JavaScript engine. You may have come across statements like "everything" in JavaScript is implemented as an internal (native code) object and passed around as a pointer. This assumption appears in the question:

So, in line 2, y and x are pointing to same value.

This is not necessarily true for all JavaScript engines. JavaScript programmers have no control over how the engine actually implements primitive values, and assuming a "pointer to internal object" model may be incorrect. One alternative I know of involves using tagged NaN values to convey non numeric information: with 16,777,214 different thirty two bit NaN values (more for 64 bit floating points), there are ample separate values that could be used for undefined, boolean and null values etc. Such an approach has the potential of avoiding the need to hold data type and value as separate properties of an internal object.

In regards

Does each primitive will have same memory's address for all the execution of the program?

it will depend on the efficiency, optimization and implementation of values by the JavaScript engine. Anecdotally some engines will attempt to consolidate string primitives and not create multiple multiple copies of the same string value but how good they are at doing that is another question. Whether other kinds of primitives have an address at all goes back to internal implementation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM