- Introduction to reference types in JavaScript with examples
- primitive
- reference
- Arrays
- Example
- Functions
- Objects
- Primitive vs Reference Data Types in JavaScript
- Primtive data types in JavaScript
- How are primitive data types treated in JavaScript?
- Reference data types in JavaScript
- What’s the difference between primitive and reference data types?
- Wrapping Up
- Ссылочный тип
- Ссылочный тип: объяснение
- Итого
Introduction to reference types in JavaScript with examples
In this article, we try to understand the reference types in JavaScript. This article is for beginners only. In the previous article, we looked into primitive types I would recommend you to read it here before going through this article. The basic difference between primitive and reference types is that in primitive types the values are stored in the variable whereas in reference types the reference/address to that variable is stored in the variable. Let us understand the difference between both using an example. Example:
// primitive let x = "JS"; let y = x; console.log(y); // "JS" x = "Java"; console.log(x); // "Java" console.log(y); // "JS" // reference let a = language:"JavaScript">; let b = a; console.log(b); // a.language = "Java"; console.log(a); // console.log(b); //
primitive
reference
Another difference between primitive and reference types is that primitive types are stored in the stack whereas reference types are stored in a heap as their size varies dynamically. In primitive types we use typeof operator to find whether a given datatype is primitive or not, whereas in reference types we use instanceof operator to find whether the given type is a reference type or not. JavaScript has 3 reference data types, we will understand each one with an example. 1. Arrays
2. Functions
3. Objects
Arrays
In JavaScript, if you assign an array to a variable it is the reference to the array that the variable holds not the value so any changes to the array will reflect on the original array lets us look at an example to understand better
Example
let languages = ["c","c++","java"]; let lang = languages; languages[2] = "javascript"; console.log(lang); // ["c","c++","javascript"]
Functions
In functions when you pass primitive type data, any changes only happen to formal arguments but doesn’t reflect on actual arguments. Let us look at an example.
function foo(val) val+="script"; > let lang = "java"; let result = foo(lang); console.log(lang); // java console.log(result); // javascript
In the above example, you can see that changes in the formal arguments are not reflected in actual arguments. However, in reference types when you can pass an object to a function you can modify its properties but not the object. Look at the example below to understand better
// Example 1 function modifyProperty(obj) obj.value = 10; > let x = value : 1; > modifyProperty(x); console.log(x); // // Example 2 function modifyObject(obj) obj = value = 20; > > ley y = value: 2 >; modifyObject(y); console.log(y); //
Objects
In JavaScript, a variable that stores an object is accessed by its reference instead of value. Refer to the first example to get a better understanding. Thank you for reading the article please give your feedback and suggestions below in the comments.
Primitive vs Reference Data Types in JavaScript
Njong Emy
Data types can be a bit of a mind boggling concept. But as programmers, we use data types everyday – so they’re something we should understand.
Question is, how does the computer store these data types? It can’t possibly treat every data type the same.
In JavaScript, data types are split in two categories, and the computer treats each one differently. We have primitive data types and reference data types. But what are these? And why is it important to know the difference? That’s what we’ll learn in this article.
Primtive data types in JavaScript
These data types are pretty simple, and are sometimes treated as the lowest level of implementation of a programming language. They are not objects, and do not have methods.
Examples of such data types are numbers, strings, booleans, null, and undefined.
But you might be wondering about strings, because they do have methods. The fact is, JavaSvript converts primitive strings to string objects, so that it is possible to use string object methods.
How are primitive data types treated in JavaScript?
When you declare a primitive data type in JavaScript, it is stored on a stack. A stack is a simple data structure that the computer uses to store and retrieve data quickly.
A primitive data type on the stack is identified by the variable name you used for declaration in your program. With each primitive data type you create, data is added to the stack.
To implement this, say we declare a variable, numOne , and give it a value of 50. We go on to create another variable, numTwo , and assign it the same value of 50. So both variables have the same value.
What happens on the stack is that, the computer creates room for numOne and stores its assigned value on the stack. When numTwo is created, the computer again creates room, and stores 50 on the stack. It does not matter that both variables are assigned the same value.
What if during the coding process, we decided to update the value of numOne to say, 100? Does it mean numTwo will change too? The answer is no.
Since numOne and numTwo were stored differently on the stack, updating one of them will not affect the other. And we can experiment with that by actually trying it out in our code editor.
Logging numOne to the console will output 100, and logging numTwo will output 50. So, in effect, the two variables have no relationship to each other.
let numOne = 50; let numTwo = numOne; //numTwo=numOne=50 numOne = 100; console.log(numOne); //outputs 100 console.log(numTwo); //outputs 50
Now that we’ve seen how easy it is to handle primitive data types, let’s see how similarly reference data types work.
Reference data types in JavaScript
Reference data types, unlike primitive data types, are dynamic in nature. That is, they do not have a fixed size.
Most of them are considered as objects, and therefore have methods. Examples of such data types include arrays, functions, collections, and all other types of objects.
What’s the difference between primitive and reference data types?
The difference comes in when the computer has to store a reference data type. When you create a variable and assign it a value that is a reference data type, the computer does not directly store that data type in that variable (as is the case with primitive types).
What you have assigned to that variable is a pointer that points to the location of that data type in memory. Confusing? I know.
As you can see in the image above, we have two data structures now. A stack, and a heap. Say we declared an object, for example. The object itself is stored on a heap, and its pointer is stored on a stack. The pointer is identified by the object’s variable name, and points to that object.
Now, we could create a variable, object1 , and assign an object to it. What if like before, we create another variable object2 , and assign it to object1 . Does that mean another object will be created on the heap? The answer is no.
Since the object already exists on the heap, object2 and object1 will both point to the same object.
Another difference comes in when we update object1 . If we log both variables to the console, we see that the change affected them both. This is because they are pointing to the same object on the heap – and updating one variable of course affects the other.
let object1 = < name:'Bingeh', age:18 >; let object2 = object1; //updating object1, object1.age = 20; console.log(object2); //we see that object2 also updates the age attribute
Wrapping Up
Now you know the difference between primitive and reference data types. It is important to know these differences – especially when you get errors like ‘null pointer reference’ – so you can figure out why they’re happening.
This sometimes happens with Java developers, so I hope that this article helps you clear up any doubts.
Ссылочный тип
Эта статья охватывает продвинутую тему, чтобы лучше понять некоторые нестандартные случаи.
Она несильно важна. Многие опытные разработчики прекрасно живут, даже не подозревая об этом. Читайте дальше, если хотите узнать, как все работает под капотом.
Некоторые хитрые способы вызова метода приводят к потере значения this , например:
let user = < name: "Джон", hi() < alert(this.name); >, bye() < alert("Пока"); >>; user.hi(); // Джон (простой вызов метода работает хорошо) // теперь давайте попробуем вызывать user.hi или user.bye // в зависимости от имени пользователя user.name (user.name == "Джон" ? user.hi : user.bye)(); // Ошибка!
В последней строчке кода используется условный оператор ? , который определяет, какой будет вызван метод ( user.hi или user.bye ) в зависимости от выполнения условия. В данном случае будет выбран user.hi .
Затем метод тут же вызывается с помощью скобок () . Но вызов не работает как положено!
Вы можете видеть, что при вызове будет ошибка, потому что значением «this» внутри функции становится undefined (полагаем, что у нас строгий режим).
Так работает (доступ к методу объекта через точку):
Так уже не работает (вызываемый метод вычисляется):
(user.name == "John" ? user.hi : user.bye)(); // Ошибка!
Почему? Если мы хотим понять, почему так происходит, давайте разберёмся (заглянем под капот), как работает вызов методов ( obj.method() ).
Ссылочный тип: объяснение
Присмотревшись поближе, в выражении obj.method() можно заметить две операции:
- Сначала оператор точка ‘.’ возвращает свойство объекта – его метод ( obj.method ).
- Затем скобки () вызывают этот метод (исполняется код метода).
Итак, каким же образом информация о this передаётся из первой части во вторую?
Если мы поместим эти операции в отдельные строки, то значение this , естественно, будет потеряно:
let user = < name: "John", hi() < alert(this.name); >>; // разделим получение метода объекта и его вызов в разных строках let hi = user.hi; hi(); // Ошибка, потому что значением this является undefined
Здесь hi = user.hi сохраняет функцию в переменной, и далее в последней строке она вызывается полностью сама по себе, без объекта, так что нет this .
Для работы вызовов типа user.hi() , JavaScript использует трюк – точка ‘.’ возвращает не саму функцию, а специальное значение «ссылочного типа», называемого Reference Type.
Этот ссылочный тип (Reference Type) является внутренним типом. Мы не можем явно использовать его, но он используется внутри языка.
Значение ссылочного типа – это «триплет»: комбинация из трёх значений (base, name, strict) , где:
- base – это объект.
- name – это имя свойства объекта.
- strict – это режим исполнения. Является true, если действует строгий режим ( use strict ).
Результатом доступа к свойству user.hi является не функция, а значение ссылочного типа. Для user.hi в строгом режиме оно будет таким:
// значение ссылочного типа (Reference Type) (user, "hi", true)
Когда скобки () применяются к значению ссылочного типа (происходит вызов), то они получают полную информацию об объекте и его методе, и могут поставить правильный this ( user в данном случае, по base ).
Ссылочный тип – исключительно внутренний, промежуточный, используемый, чтобы передать информацию от точки . до вызывающих скобок () .
При любой другой операции, например, присваивании hi = user.hi , ссылочный тип заменяется на собственно значение user.hi (функцию), и дальше работа уже идёт только с ней. Поэтому дальнейший вызов происходит уже без this .
Таким образом, значение this передаётся правильно, только если функция вызывается напрямую с использованием синтаксиса точки obj.method() или квадратных скобок obj[‘method’]() (они делают то же самое). Существуют различные способы решения этой проблемы: одним из таких является func.bind().
Итого
Ссылочный тип – это внутренний тип языка.
Чтение свойства, например, с точкой . в obj.method() возвращает не точное значение свойства, а специальное значение «ссылочного типа», в котором хранится как значение свойства, так и объект, из которого оно было взято.
Это нужно для последующего вызова метода () , чтобы получить объект и установить для него this .
Для всех остальных операций ссылочный тип автоматически становится значением свойства (в нашем случае функцией).
Вся механика скрыта от наших глаз. Это имеет значение только в особых случаях, например, когда метод динамически извлекается из объекта с использованием выражения.