Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
6 views

Java Script

Uploaded by

tempsuman9
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views

Java Script

Uploaded by

tempsuman9
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 23

JavaScript Notes

1. An Introduction to JavaScript
 Origin: Created to make web pages interactive, originally named "LiveScript," later renamed
JavaScript.
 Execution: Runs scripts directly within HTML, no special preparation or compilation needed.
 Independence: Evolved into an independent language with its own ECMAScript specification.
 Engines: Runs in browsers through engines like V8 (Chrome, Opera, Edge) and SpiderMonkey
(Firefox).
 Capabilities:
 Manipulates web content (HTML/CSS).
 Handles user interactions (clicks, key presses).
 Communicates with servers via AJAX and COMET.
 Stores data locally (local storage).
 Limitations:
 Restricted file system access for security.
 Limited cross-origin communication (Same Origin Policy).
 Uniqueness:
 Full integration with HTML/CSS.
 Simple syntax for basic tasks.
 Supported by all major browsers by default.
 Trans-piled Languages: Languages like TypeScript, Dart, and Coffee-Script extend JavaScript’s
functionality by adding features like strict data typing or syntactic sugar.

2. JavaScript Fundamentals
 We can use a <script> tag to add JavaScript code to a page.
 The type and language attributes are not required.
 A script in an external file can be inserted with <script src="path/to/script.js"></script>.
 We must choose either an external <script src="…"> or a regular <script> with code.
 JavaScript programs can be inserted almost anywhere into an HTML document using
the <script> tag.
 To attach several scripts, use multiple tags:
<script src="/js/script1.js"></script>
<script src="/js/script2.js"></script>
 JavaScript interprets the line break as an “implicit” semicolon. This is called an automatic
semicolon insertion.
 In most cases, a newline implies a semicolon. But “in most cases” does not mean “always”!
 There are cases when a newline does not mean a semicolon. For example:
 alert (3 +
1
+ 2);
 The directive looks like a string: "use strict" or 'use strict'. When it is located at the top of a
script, the whole script works the “modern” way, please make sure that "use strict" is at the
top of your scripts, otherwise strict mode may not be enabled.
 A Variable is a “named storage” for data
 Capital-named constants are only used as aliases for “hard-coded” values.
 Data types- There are eight basic data types in JavaScript, we can put any type in a variable.
For example, a variable can at one moment be a string and then store a number:
 Programming languages that allow such things, such as JavaScript, are called “dynamically
typed” meaning that there exist data types, but variables are not bound to any of them.
 if there’s a NaN somewhere in a mathematical expression, it propagates to the whole result
(there’s only one exception to that: NaN ** 0 is 1).
 The meaning of undefined is “value is not assigned”.
 There are 8 basic data types in JavaScript.
 Seven primitive data types:
 number for numbers of any kind: integer or floating-point, integers are limited by ±(253-1).
 bigint for integer numbers of arbitrary length.
 string for strings. A string may have zero or more characters, there’s no separate single-
character type.
 boolean for true/false.
 null for unknown values – a standalone type that has a single value null.
 undefined for unassigned values – a standalone type that has a single value undefined.
 symbol for unique identifiers.
 And one non-primitive data type:
 object for more complex data structures.
 The typeof operator allows us to see which type is stored in a variable.
 Usually used as typeof x, but typeof(x) is also possible, typeof is an operator, not a function.
The parentheses here aren’t a part of typeof. It’s the kind of parentheses used for
mathematical grouping.
 alert
shows a message.
 prompt
shows a message asking the user to input text. It returns the text or, if Cancel button or Esc is
clicked, null.
 confirm
shows a message and waits for the user to press “OK” or “Cancel”. It returns true for OK and
false for Cancel/Esc.
 All these methods are modal: they pause script execution and don’t allow the visitor to
interact with the rest of the page until the window has been dismissed.

Numeric conversion rules:


 Value Becomes…
 undefined NaN
 null 0
 true and false 1 and 0
 string Whitespaces (includes spaces, tabs \t, newlines \n etc.) from the start and end are
removed. If the remaining string is empty, the result is 0. Otherwise, the number is “read”
from the string. An error gives NaN.
Examples:
 alert( Number(" 123 ") ); // 123
 alert( Number("123z") ); // NaN (error reading a number at "z")
 alert( Number(true) ); // 1
 alert( Number(false) ); // 0
 Please note that null and undefined behave differently here: null becomes zero while
undefined becomes NaN.
 alert automatically converts any value to a string to show it. Mathematical operations
convert values to numbers.
 String conversion is mostly obvious. A false becomes "false", null becomes "null", etc.
 Some languages (namely PHP) treat "0" as false. But in JavaScript, a non-empty string is
always true.
 alert( Boolean("0") ); // true
 alert( Boolean(" ") ); // spaces, also true (any non-empty string is true)
 Most of these rules are easy to understand and memorize. The notable exceptions where
people usually make mistakes are:
 undefined is NaN as a number, not 0.
 "0" and space-only strings like " " are true as a boolean.

Exponentiation **
 The exponentiation operator a ** b raises a to the power of b.
 alert( 2 ** 2 ); // 2² = 4
 alert( 2 ** 3 ); // 2³ = 8
 alert( 2 ** 4 ); // 2⁴ = 16
 Just like in maths, the exponentiation operator is defined for non-integer numbers as well.
 For example, a square root is an exponentiation by ½:
 alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root)
 alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic root)
 if the binary + is applied to strings, it merges (concatenates) them:
 let s = "my" + "string";
 alert(s); // mystring
 Note that if any of the operands is a string, then the other one is converted to a string too.
 alert( '1' + 2 ); // "12"
 alert( 2 + '1' ); // "21"
 See, it doesn’t matter whether the first operand is a string or the second one.
 Here’s a more complex example:
 alert(2 + 2 + '1' ); // "41" and not "221"
 Here, operators work one after another. The first + sums two numbers, so it returns 4, then
the next + adds the string 1 to it, so it’s like 4 + '1' = '41'.
 alert('1' + 2 + 2); // "122" and not "14"
 Here, the first operand is a string, the compiler treats the other two operands as strings too.
The 2 gets concatenated to '1', so it’s like '1' + 2 = "12" and "12" + 2 = "122".
 The binary + is the only operator that supports strings in such a way. Other arithmetic
operators work only with numbers and always convert their operands to numbers.
 Here’s the demo for subtraction and division:
 alert( 6 - '2' ); // 4, converts '2' to a number
 alert( '6' / '2' ); // 3, converts both operands to numbers
 "" + 1 + 0 = "10" // (1)
 "" - 1 + 0 = -1 // (2)
 true + false = 1
 6 / "3" = 2
 "2" * "3" = 6
 4 + 5 + "px" = "9px"
 "$" + 4 + 5 = "$45"
 "4" - 2 = 2
 "4px" - 2 = NaN
 " -9 " + 5 = " -9 5" // (3)
 " -9 " - 5 = -14 // (4)
 null + 1 = 1 // (5)
 undefined + 1 = NaN // (6)
 " \t \n" - 2 = -2 // (7)
 The addition with a string "" + 1 converts 1 to a string: "" + 1 = "1", and then we have "1" + 0,
the same rule is applied.
 The subtraction - (like most math operations) only works with numbers, it converts an empty
string "" to 0.
 The addition with a string appends the number 5 to the string.
 The subtraction always converts to numbers, so it makes " -9 " a number -9 (ignoring spaces
around it).
 null becomes 0 after the numeric conversion.
 undefined becomes NaN after the numeric conversion.
 Space characters are trimmed off string start and end when a string is converted to a
number. Here the whole string consists of space characters, such as \t, \n and a “regular”
space between them. So, similarly to an empty string, it becomes 0.

Numeric conversion, unary +


 The plus + exists in two forms: the binary form that we used above and the unary form.
 The unary plus or, in other words, the plus operator + applied to a single value, doesn’t do
anything to numbers. But if the operand is not a number, the unary plus converts it into a
number.
 // No effect on numbers
 let x = 1;
 alert( +x ); // 1
 // Converts non-numbers
 alert( +true ); // 1
 alert( +"" ); // 0
 It actually does the same thing as Number(...), but is shorter.
 let apples = "2";
 let oranges = "3";
 alert( apples + oranges ); // "23", the binary plus concatenates strings
 If we want to treat them as numbers, we need to convert and then sum them.
 let apples = "2";
 let oranges = "3";
 // both values converted to numbers before the binary plus
 alert( +apples + +oranges ); // 5
 // the longer variant
 // alert( Number(apples) + Number(oranges) ); // 5
 The call x = value writes the value into x and then returns it.
 Chained assignments evaluate from right to left. First, the rightmost expression 2 + 2 is
evaluated and then assigned to the variables on the left: c, b and a. At the end, all the
variables share a single value.
 a = b = c = 2 + 2;
 alert( a ); // 4
 alert( b ); // 4
 alert( c ); // 4
 let n = 2;
 n += 5; // now n = 7 (same as n = n + 5)
 n *= 2; // now n = 14 (same as n = n * 2)
 alert( n ); // 14
 Such operators have the same precedence as a normal assignment, so they run after most
other calculations.
 The prefix form returns the new value while the postfix form returns the old value (prior to
increment/decrement).
 The operators ++/-- can be used inside expressions as well. Their precedence is higher than
most other arithmetical operations.
 A capital letter "A" is not equal to the lowercase "a". Which one is greater? The
lowercase "a". Why? Because the lowercase character has a greater index in the internal
encoding table JavaScript uses (Unicode).

 alert( null === undefined ); // false


 For a non-strict check ==, There’s a special rule. These two are a “sweet couple”: they equal
each other (in the sense of ==), but not any other value.
 alert( null == undefined ); // true
 For maths and other comparisons < > <= >=
 null/undefined are converted to numbers: null becomes 0, while undefined becomes NaN.
 alert( null > 0 ); // (1) false
 alert( null == 0 ); // (2) false
 alert( null >= 0 ); // (3) true
 Mathematically, that’s strange. The last result states that "null is greater than or equal to
zero", so in one of the comparisons above it must be true, but they are both false.
 The reason is that an equality check == and comparisons > < >= <= work differently.
Comparisons convert null to a number, treating it as 0. That’s why (3) null >= 0 is true and (1)
null > 0 is false.
 On the other hand, the equality check == for undefined and null is defined such that, without
any conversions, they equal each other and don’t equal anything else. That’s why (2) null ==
0 is false.
 An incomparable undefined
 The value undefined shouldn’t be compared to other values:
 alert( undefined > 0 ); // false (1)
 alert( undefined < 0 ); // false (2)
 alert( undefined == 0 ); // false (3)
 Why does it dislike zero so much? Always false!
 Comparisons (1) and (2) return false because undefined gets converted to NaN and NaN is a
special numeric value which returns false for all comparisons.
 The equality check (3) returns false because undefined only equals null, undefined, and no
other value.
 dictionary comparison, first char "2" is greater than the first char "12". Hence “2”>”12”.
 alert( undefined || null || 0 ); // 0 (all falsy, returns the last value)
 // if the first operand is truthy,
 // AND returns the second operand:
 alert( 1 && 0 ); // 0
 alert( 1 && 5 ); // 5
 // if the first operand is falsy,
 // AND returns it. The second operand is ignored
 alert( null && 5 ); // null
 alert( 0 && "no matter what" ); // 0
 The precedence of AND && operator is higher than OR ||.
 So the code a && b || c && d is essentially the same as if the && expressions were in
parentheses: (a && b) || (c && d).
 alert( alert(1) && alert(2) );
 The call to alert returns undefined (it just shows a message, so there’s no meaningful return).
 Because of that, && evalufates the left operand (outputs 1), and immediately stops, because
undefined is a falsy value. And && looks for a falsy value and returns it, so it’s done.
 || finds a truthy value and return that value when found otherwise last value
 && finds a falsy value and return when found otherwise last value
 A double NOT !! is sometimes used for converting a value to boolean type:
 alert( !!"non-empty string" ); // true
 alert( !!null ); // false
 That is, the first NOT converts the value to boolean and returns the inverse, and the second
NOT inverses it again. In the end, we have a plain value-to-boolean conversion.
 There’s a little more verbose way to do the same thing – a built-in Boolean function:
 alert( Boolean("non-empty string") ); // true
 alert( Boolean(null) ); // false
 The precedence of NOT ! is the highest of all logical operators, so it always executes first,
before && or ||.
 The nullish coalescing operator ?? provides a short way to choose the first “defined” value
from a list.
 It’s used to assign default values to variables:
 // set height=100, if height is null or undefined
 height = height ?? 100;
 The operator ?? has a very low precedence, only a bit higher than ? and =, so consider
adding parentheses when using it in an expression.
 It’s forbidden to use it with || or && without explicit parentheses.

The "switch" statement


 let a = 2 + 2;
 switch (a) {
 case 3:
 alert( 'Too small' );
 break;
 case 4:
 alert( 'Exactly!' );
 break;
 case 5:
 alert( 'Too big' );
 break;
 default:
 alert( "I don't know such values" );
 }
 Here the switch starts to compare a from the first case variant that is 3. The match fails.
 Then 4. That’s a match, so the execution starts from case 4 until the nearest break.
 If there is no break then the execution continues with the next case without any checks.
 Any expression can be a switch/case argument
 Both switch and case allow arbitrary expressions.

 Several variants of case which share the same code can be grouped.
 For example, if we want the same code to run for case 3 and case 5:
 let a = 3;
 switch (a) {
 case 4:
 alert('Right!');
 break;
 case 3: // (*) grouped two cases
 case 5:
 alert('Wrong!');
 alert("Why don't you take a math class?");
 break;
 default:
 alert('The result is strange. Really.');
 }
 Now both 3 and 5 show the same message.
 The ability to “group” cases is a side effect of how switch/case works without break. Here the
execution of case 3 starts from the line (*) and goes through case 5, because there’s no
break.

 Type matters
 Let’s emphasize that the equality check is always strict. The values must be of the same type
to match.
 For example, let’s consider the code:
 let arg = prompt("Enter a value?");
 switch (arg) {
 case '0':
 case '1':
 alert( 'One or zero' );
 break;
 case '2':
 alert( 'Two' );
 break;
 case 3:
 alert( 'Never executes!' );
 break;
 default:
 alert( 'An unknown value' );
 }
 For 0, 1, the first alert runs.
 For 2 the second alert runs.
 But for 3, the result of the prompt is a string "3", which is not strictly equal === to the
number 3. So we’ve got a dead code in case 3! The default variant will execute.

Loops: while and for


 We can actually remove everything, creating an infinite loop:
 for (;;) {
 // repeats without limits
 }
 Please note that syntax constructs that are not expressions cannot be used with the ternary
operator ?. In particular, directives such as break/continue aren’t allowed there.

if (i > 5) {
alert(i);
} else {
continue;
}
…and rewrite it using a question mark
(i > 5) ? alert(i) : continue; // continue isn't allowed
here

Labels for break/continue


 Sometimes we need to break out from multiple nested loops at once.
 For example, in the code below we loop over i and j, prompting for the coordinates (i,
j) from (0,0) to (2,2):
 for (let i = 0; i < 3; i++) {
 for (let j = 0; j < 3; j++) {
 let input = prompt(`Value at coords (${i},${j})`, '');
 // what if we want to exit from here to Done (below)?
 }
 }
 alert('Done!');
 We need a way to stop the process if the user cancels the input.
 The ordinary break after input would only break the inner loop. That’s not sufficient – labels,
come to the rescue!
 A label is an identifier with a colon before a loop:
 labelName: for (...) {
 ...
 }
 The break <labelName> statement in the loop below breaks out to the label:
 outer: for (let i = 0; i < 3; i++) {
 for (let j = 0; j < 3; j++) {
 let input = prompt(`Value at coords (${i},${j})`, '');
 // if an empty string or canceled, then break out of both loops
 if (!input) break outer; // (*)
 // do something with the value...
 }
 }

 alert('Done!');
 In the code above, break outer looks upwards for the label named outer and breaks out of
that loop.
 So the control goes straight from (*) to alert('Done!').
 We can also move the label onto a separate line:
 outer:
 for (let i = 0; i < 3; i++) { ... }
 The continue directive can also be used with a label. In this case, code execution jumps to
the next iteration of the labeled loop.
 Labels do not allow to “jump” anywhere
 Labels do not allow us to jump into an arbitrary place in the code.
 For example, it is impossible to do this:
 break label; // jump to the label below (doesn't work)
 label: for (...)
 A break directive must be inside a code block. Technically, any labelled code block will do,
e.g.:
 label: {
 // ...
 break label; // works
 // ...
 }

Functions

 The function has full access to the outer variable. It can modify it as well.
 An argument is the value that is passed to the function when it is called (it’s a call time term).
 The default value also jumps in if the parameter exists, but strictly equals undefined, like this:
 showMessage("Ann", undefined); // Ann: no text given

function showMessage(from, text = anotherFunction()) {


// anotherFunction() only executed if no text given
// its result becomes the value of text }
 A function with an empty return or without it returns undefined
 Never add a newline between return and the value
 For a long expression in return, it might be tempting to put it on a separate line, like this:
 return
 (some + long + expression + or + whatever * f(a) + f(b))
 That doesn’t work, because JavaScript assumes a semicolon after return. That’ll work the
same as:
 return;
 (some + long + expression + or + whatever * f(a) + f(b))
 So, it effectively becomes an empty return.
 If we want the returned expression to wrap across multiple lines, we should start it at the
same line as return. Or at least put the opening parentheses there as follows:
 return (
 some + long + expression
 + or +
 whatever * f(a) + f(b)
 )
 And it will work just as we expect it to.

Function expressions

 Functions are values. They can be assigned, copied or declared in any place of the code.
 If the function is declared as a separate statement in the main code flow, that’s called a
“Function Declaration”.
 If the function is created as a part of an expression, it’s called a “Function Expression”.
 As the function creation happens in the context of the assignment expression, this is a
Function Expression.
 Function Declarations are processed before the code block is executed. They are visible
everywhere in the block.
 Function Expressions are created when the execution flow reaches them.
 In most cases when we need to declare a function, a Function Declaration is preferable,
because it is visible prior to the declaration itself. That gives us more flexibility in code
organization, and is usually more readable.
 So we should use a Function Expression only when a Function Declaration is not fit for the
task.
 Why is there a semicolon at the end?
 You might wonder, why do Function Expressions have a semicolon ; at the end, but Function
Declarations do not:
 function sayHi() {
 // ...
 }
 let sayHi = function() {
 // ...
 };
 The answer is simple: a Function Expression is created here as function(…) {…} inside the
assignment statement: let sayHi = …;. The semicolon ; is recommended at the end of the
statement, it’s not a part of the function syntax.
 The semicolon would be there for a simpler assignment, such as let sayHi = 5;, and it’s also
there for a function assignment.

Code Quality
 We can also pause the code by using the debugger command in it, like this:
 function hello(name) {
 let phrase = `Hello, ${name}!`;
 debugger; // <-- the debugger stops here
 say(phrase);
 }
 Such command works only when the development tools are open, otherwise the browser
ignores it.
 A transpiler is a special piece of software that translates source code to another source code.
It can parse (“read and understand”) modern code and rewrite it using older syntax
constructs, so that it’ll also work in outdated engines.
 A script that updates/adds new functions is called “polyfill”. It “fills in” the gap and adds
missing implementations.

Debugging in the browser

 A breakpoint is a point of code where the debugger will automatically pause the JavaScript
execution.
 Watch – shows current values for any expressions.
 Call Stack – shows the nested calls chain.
 Scope – current variables.
 “Resume”: continue the execution, hotkey F8.
 “Step”: run the next command, hotkey F9.
 “Step over”: run the next command, but don’t go into a function, hotkey F10.
 “Step into”, hotkey F11.
 “Step out”: continue the execution till the end of the current function, hotkey Shift+F11.

3. Objects: the basics


 An empty object (“empty cabinet”) can be created using one of two syntaxes:
 let user = new Object(); // "object constructor" syntax
 let user = {}; // "object literal" syntax
 add a boolean one, user.isAdmin = true;
 To remove a property, we can use the delete operator:
 delete user.age;
 We can also use multiword property names, but then they must be quoted:
 let user = {
 name: "John",
 age: 30,
 "likes birds": true // multiword property name must be quoted
 };
 For multiword properties, the dot access doesn’t work:
 // this would give a syntax error
 user.likes birds = true
 JavaScript doesn’t understand that. It thinks that we address user.likes, and then gives a
syntax error when comes across unexpected birds.
 The dot requires the key to be a valid variable identifier. That implies: contains no spaces,
doesn’t start with a digit and doesn’t include special characters ($ and _ are allowed).
 There’s an alternative “square bracket notation” that works with any string:
 let user = {};
 // set
 user["likes birds"] = true;

 // get
 alert(user["likes birds"]); // true
 // delete
 delete user["likes birds"];
 Square brackets also provide a way to obtain the property name as the result of any
expression – as opposed to a literal string – like from a variable as follows:
 let key = "likes birds";
 // same as user["likes birds"] = true;
 user[key] = true;
 Here, the variable key may be calculated at run-time or depend on the user input. And then
we use it to access the property. That gives us a great deal of flexibility.
 For instance:
 let user = {
 name: "John",
 age: 30
 };
 let key = prompt("What do you want to know about the user?", "name");
 // access by variable
 alert( user[key] ); // John (if enter "name")
 The dot notation cannot be used in a similar way:
 let user = {
 name: "John",
 age: 30
 };
 let key = "name";
 alert( user.key ) // undefined
 We can use more complex expressions inside square brackets:
 let fruit = 'apple';
 let bag = {
 [fruit + 'Computers']: 5 // bag.appleComputers = 5
 };
 We can use both normal properties and shorthands in the same object:
 let user = {
 name, // same as name:name
 age: 30
 };
 a variable cannot have a name equal to one of the language-reserved words like “for”, “let”,
“return” etc.
 But for an object property, there’s no such restriction:

// these properties are all right


let obj = {
for: 1,
let: 2,
return: 3};
alert( obj.for + obj.let + obj.return ); // 6
 In short, there are no limitations on property names. They can be any strings or symbols (a
special type for identifiers, to be covered later).
 Other types are automatically converted to strings.
 For instance, a number 0 becomes a string "0" when used as a property key:
let obj = {
0: "test" // same as "0": "test"
};
// both alerts access the same property (the number 0 is
converted to string "0")
alert( obj["0"] ); // test
alert( obj[0] ); // test (same property)
 it’s possible to access any property. There will be no error if the property doesn’t exist!
 Reading a non-existing property just returns undefined.
 let user = {};
 alert( user.noSuchProperty === undefined ); // true means "no such property"

operator "in"

let user = { name: "John", age: 30 };


alert( "age" in user ); // true, user.age exists
alert( "blabla" in user ); // false, user.blabla doesn't
exist
 Please note that on left side of in there must be a property name. Usually a quoted string.
 If we omit quotes, that means a variable should contain the actual name to be tested. For
instance: let user = { age: 30 };
 let key = "age";
 alert( key in user ); // true, property "age" exists
 Well, most of the time the comparison with undefined works fine. But there’s a special case
when it fails, but "in" works correctly.
 It’s when an object property exists, but stores undefined:
 let obj = {
 test: undefined
 };
 alert( obj.test ); // it's undefined, so - no such property?
 alert( "test" in obj ); // true, the property does exist!

The "for..in" loop

 for (key in object) { // executes the body for each key among object properties}
 let user = {
 name: "John",
 age: 30,
 isAdmin: true
 };

 for (let key in user) {


 // keys
 alert( key ); // name, age, isAdmin
 // values for the keys
 alert( user[key] ); // John, 30, true
 }
 Note that all “for” constructs allow us to declare the looping variable inside the loop, like let
key here.
 Also, we could use another variable name here instead of key. For instance, "for (let prop in
obj)" is also widely used.

Object references and copying

 One of the fundamental differences of objects versus primitives is that objects are stored and
copied “by reference”, whereas primitive values: strings, numbers, booleans, etc – are always
copied “as a whole value”.
 A variable assigned to an object stores not the object itself, but its “address in memory” –
in other words “a reference” to it
 When an object variable is copied, the reference is copied, but the object itself is not
duplicated.
 Two objects are equal only if they are the same object.
 An important side effect of storing objects as references is that an object declared
as const can be modified.
 The value of user is constant, it must always reference the same object, but properties of
that object are free to change.
 In other words, the const user gives an error only if we try to set user=... as a whole.
 So, copying an object variable creates one more reference to the same object.

Duplicating an object completely into other.

 Object.assign(dest, source1, source2, source3, ….)


 Object.assign(user, { name: "Pete" });
 let clone = Object.assign({}, user);
 clone = {...user}

structuredClone

 The call structuredClone(object) clones the object with all nested properties.
 The structuredClone method can clone most data types, such as objects, arrays, primitive
values.
 It also supports circular references, when an object property references the object itself
(directly or via a chain or references).
 let user = {};
 // let's create a circular reference:
 // user.me references the user itself
 user.me = user;
 let clone = structuredClone(user);
 alert(clone.me === clone); // true
 Although, there are cases when structuredClone fails.
 For instance, when an object has a function property:
 structuredClone({ // error
 f: function() {}
 });
 Function properties aren’t supported.
 To handle such complex cases we may need to use a combination of cloning methods, write
custom code or, to not reinvent the wheel, take an existing implementation, for instance
_.cloneDeep(obj) from the JavaScript library lodash.

To make a “real copy” (a clone) we can use Object.assign for the so-called “shallow copy” (nested
objects are copied by reference) or a “deep cloning” function structuredClone or use a custom
cloning implementation, such as _.cloneDeep(obj).

Reachability

 The main concept of memory management in JavaScript is reachability.


 Simply put, “reachable” values are those that are accessible or usable somehow. They are
guaranteed to be stored in memory.
 There’s a background process in the JavaScript engine that is called garbage collector It
monitors all objects and removes those that have become unreachable.
 Garbage collection is performed automatically. We cannot force or prevent it.
 Objects are retained in memory while they are reachable.
 Being referenced is not the same as being reachable (from a root): a pack of interlinked
objects can become unreachable as a whole, as we’ve seen in the example above.

Internal algorithms

 The basic garbage collection algorithm is called “mark-and-sweep”.


 The following “garbage collection” steps are regularly performed:
 The garbage collector takes roots and “marks” (remembers) them.
 Then it visits and “marks” all references from them.
 Then it visits marked objects and marks their references. All visited objects are remembered,
so as not to visit the same object twice in the future.
 …And so on until every reachable (from the roots) references are visited.
 All objects except marked ones are removed.
 JavaScript engines apply many optimizations to make it run faster and not introduce any
delays into the code execution.

Optional chaining

 The optional chaining ?. stops the evaluation if the value before ?. is undefined or null and
returns undefined.
 The optional chaining ?. syntax has three forms:
 obj?.prop – returns obj.prop if obj exists, otherwise undefined.
 obj?.[prop] – returns obj[prop] if obj exists, otherwise undefined.
 obj.method?.() – calls obj.method() if obj.method exists, otherwise returns undefined.
 We can use ?. for safe reading and deleting, but not writing
 The optional chaining ?. has no use on the left side of an assignment.
 let user = null;
 user?.name = "John"; // Error, doesn't work // because it evaluates to: undefined = "John"

Object methods, "this"

 A function that is a property of an object is called its method.

 // these objects do the same


 user = {
 sayHi: function() {
 alert("Hello");
 }
 };
 // method shorthand looks better, right?
 user = {
 sayHi() { // same as "sayHi: function(){...}"
 alert("Hello");
 }
 };

“this” in methods

 It’s common that an object method needs to access the information stored in the object to
do its job. For instance, the code inside user.sayHi() may need the name of the user.
 To access the object, a method can use the this keyword.
 The value of this is the object “before dot”, the one used to call the method.
 Here during the execution of user.sayHi(), the value of this will be user.
 It’s also possible to access the object without this, by referencing it via the outer variable:
 …But such code is unreliable. If we decide to copy user to another variable, e.g. admin =
user and overwrite user with something else, then it will access the wrong object.
 If we used this.name instead of user.name inside the alert, then the code would work.
 “this” is not bound - In JavaScript, keyword this behaves unlike most other programming
languages. It can be used in any function, even if it’s not a method of an object.
 Functions that are stored in object properties are called “methods”.
 Methods allow objects to “act” like object.doSomething().
 Methods can reference the object as this.
 The value of this is defined at run-time.
 When a function is declared, it may use this, but that this has no value until the function is
called. A function can be copied between objects.
 The rule is simple: if obj.f() is called, then this is obj during the call of f.
 In JavaScript this is “free”, its value is evaluated at call-time and does not depend on where
the method was declared, but rather on what object is “before the dot”.
 We can even call the function without an object at all:
 function sayHi() {
 alert(this);
 }
 sayHi(); // undefined
 In this case this is undefined in strict mode. If we try to access this.name, there will be an
error. In non-strict mode the value of this in such case will be the global object (window in a
browser)
 When a function is called in the “method” syntax: object.method(), the value of this during
the call is object.
 Please note that arrow functions are special: they have no this. When this is accessed inside
an arrow function, it is taken from outside. That’s a special feature of arrow functions, it’s
useful when we actually do not want to have a separate this, but rather to take it from the
outer context.
 When a function is called in the “method” syntax: object.method(), the value of this during
the call is objects
 this is context-dependent: The value of this depends on how a function is called.
 Object literals do not change this: Defining an object inside a function does not change the
value of this.
 Method vs. Function Call:
 When a function is called as a method (object.method()), this is set to the object.
 When a function is called as a regular function (functionName()), this is either undefined (in
strict mode) or the global object.
 For making the code chainable, we should return object. return this

Constructor Operator

 Constructor functions technically are regular functions. There are two conventions though:
 They are named with capital letter first.
 They should be executed only with "new" operator.
 When a function is executed with new, it does the following steps:
 A new empty object is created and assigned to this.
 The function body executes. Usually it modifies this, adds new properties to it.
 The value of this is returned.
 Main purpose of constructors – to implement reusable object creation code.
 Let’s note once again – technically, any function (except arrow functions, as they don’t have
this) can be used as a constructor. It can be run with new, and it will execute the algorithm
above. The “capital letter first” is a common agreement, to make it clear that a function is to
be run with new.

new function() { … }

 Constructor functions or, briefly, constructors, are regular functions, but there’s a common
agreement to name them with capital letter first.
 Constructor functions should only be called using new. Such a call implies a creation of
empty this at the start and returning the populated one at the end.
 we can omit parentheses after new: new User() is same as new User
 // create a function and immediately call it with new
 let user = new function() {
 this.name = "John";
 this.isAdmin = false;
 // ...other code for user creation
 // maybe complex logic and statements
 // local variables etc
 };

 Return from constructors


 Usually, constructors do not have a return statement. Their task is to write all necessary stuff
into this, and it automatically becomes the result.
 But if there is a return statement, then the rule is simple:
 If return is called with an object, then the object is returned instead of this.
 If return is called with a primitive, it’s ignored.
 This constructor can’t be called again, because it is not saved anywhere, just created and
called. So this trick aims to encapsulate the code that constructs the single object, without
future reuse.

Symbol Type

 Only two primitive types may serve as object property keys: string type, or symbol type.
 Otherwise, if one uses another type, such as number, it’s auto converted to string.
 A “symbol” represents a unique identifier.
 A value of this type can be created using Symbol(): let id = Symbol();
 Upon creation, we can give symbols a description (also called a symbol name), mostly useful
for debugging purposes:
 // id is a symbol with the description "id"
 let id = Symbol("id");
 Symbols are guaranteed to be unique. Even if we create many symbols with exactly the same
description, they are different values. The description is just a label that doesn’t affect
anything. A symbol is a “primitive unique value” with an optional description.
 Most values in JavaScript support implicit conversion to a string. For instance, we can alert
almost any value, and it will work. Symbols are special. They don’t auto-convert.
 If we really want to show a symbol, we need to explicitly call .toString() on it, like here:
 let id = Symbol("id"); alert(id.toString());
 Symbols allow us to create “hidden” properties of an object, that no other part of code can
accidentally access or overwrite. For instance, if we’re working with user objects, that belong
to a third-party code. We’d like to add identifiers to them.
 let user = { // belongs to another code
 name: "John"
 };
 let id = Symbol("id");
 user[id] = 1;
 alert( user[id] );
 What’s the benefit of using Symbol("id") over a string "id"?
 As user objects belong to another codebase, it’s unsafe to add fields to them, since we might
affect pre-defined behavior in that other codebase. However, symbols cannot be accessed
accidentally. The third-party code won’t be aware of newly defined symbols, so it’s safe to
add symbols to the user objects.
 Also, imagine that another script wants to have its own identifier inside user, for its own
purposes.
 Then that script can create its own Symbol("id"), like this:
 // ...
 let id = Symbol("id");
 user[id] = "Their id value";
 There will be no conflict between our and their identifiers, because symbols are always
different, even if they have the same name.
 …But if we used a string "id" instead of a symbol for the same purpose, then there would be
a conflict:
 If we want to use a symbol in an object literal {...}, we need square brackets around it.
 let id = Symbol("id");
 let user = {
 name: "John",
 [id]: 123 // not "id": 123
 }; // That’s because we need the value from the variable id as the key, not the string “id”.
 Symbolic properties do not participate in for..in loop.
 Object.keys(user) also ignores them. That’s a part of the general “hiding symbolic
properties” principle. If another script or a library loops over our object, it won’t
unexpectedly access a symbolic property. In contrast, Object.assign copies both string and
symbol properties:

Data Types

Method of Primitives

 Objects are “heavier” than primitives. They require additional resources to support the
internal machinery.
 There are many things one would want to do with a primitive, like a string or a number. It
would be great to access them using methods.
 Primitives must be as fast and lightweight as possible.
 The solution looks a little bit awkward, but here it is:
 Primitives are still primitive. A single value, as desired.
 The language allows access to methods and properties of strings, numbers, booleans and
symbols.
 In order for that to work, a special “object wrapper” that provides the extra functionality is
created, and then is destroyed.
 The “object wrappers” are different for each primitive type and are called: String, Number,
Boolean, Symbol and BigInt. Thus, they provide different sets of methods.
 Here’s what actually happens in str.toUpperCase():
 The string str is a primitive. So in the moment of accessing its property, a special object is
created that knows the value of the string, and has useful methods, like toUpperCase().
 That method runs and returns a new string (shown by alert).
 The special object is destroyed, leaving the primitive str alone.
 alert( typeof 0 ); // "number"
 alert( typeof new Number(0) ); // "object"!
 Objects are always truthy in if, so here the alert will show up:
 let zero = new Number(0);
 if (zero) { // zero is true, because it's an object
 alert( "zero is truthy!?!" );
 }
 On the other hand, using the same functions String/Number/Boolean without new is totally
fine and useful thing. They convert a value to the corresponding type: to a string, a number,
or a boolean (primitive).
 For example, this is entirely valid:
 let num = Number("123"); // convert a string to number
 null/undefined have no methods
 The special primitives null and undefined are exceptions. They have no corresponding
“wrapper objects” and provide no methods. In a sense, they are “the most primitive”.
 An attempt to access a property of such value would give the error:
 alert(null.test); // error
 Try running it:
 let str = "Hello";
 str.test = 5; // (*)
 alert(str.test);
 Depending on whether you have use strict or not, the result may be:
 undefined (no strict mode)
 An error (strict mode).
 Why? Let’s replay what’s happening at line (*):
 When a property of str is accessed, a “wrapper object” is created.
 In strict mode, writing into it is an error.
 Otherwise, the operation with the property is carried on, the object gets the test property,
but after that the “wrapper object” disappears, so in the last line str has no trace of the
property.
 This example clearly shows that primitives are not objects.
 They can’t store additional data.

Number
 Imagine we need to write 1 billion. The obvious way is: let billion = 1000000000;
 We also can use underscore _ as the separator: let billion = 1_000_000_000;
 let billion = 1e9; // 1 billion, literally: 1 and 9 zeroes
 alert( 7.3e9 ); // 7.3 billions (same as 7300000000 or 7_300_000_000)
 let mcs = 1e-6; // five zeroes to the left from 1
 The method num.toString(base) returns a string representation of num in the numeral
system with the given base.
 let num = 255
 alert( num.toString(16) ); // ff
 alert( num.toString(2) ); // 11111111
 The base can vary from 2 to 36. By default, it’s 10.
 alert( 123456..toString(36) ); // 2n9c
 Two dots to call a method
 Please note that two dots in 123456..toString(36) is not a typo. If we want to call a method
directly on a number, like toString in the example above, then we need to place two dots ..
after it.
 If we placed a single dot: 123456.toString(36), then there would be an error, because
JavaScript syntax implies the decimal part after the first dot. And if we place one more dot,
then JavaScript knows that the decimal part is empty and now goes the method.
 Also could write (123456).toString(36).
 The method toFixed(n) rounds the number to n digits after the point and returns a string
representation of the result.
 Please note that the result of toFixed is a string. If the decimal part is shorter than required,
zeroes are appended to the end:
 The numeric format IEEE-754 solves this by rounding to the nearest possible number. These
rounding rules normally don’t allow us to see that “tiny precision loss”, but it exists.
 We can see this in action:
 alert( 0.1.toFixed(20) ); // 0.10000000000000000555
 And when we sum two numbers, their “precision losses” add up.
 That’s why 0.1 + 0.2 is not exactly 0.3.
 let sum = 0.1 + 0.2;
 alert( sum.toFixed(2) ); // "0.30"
 Please note that toFixed always returns a string. It ensures that it has 2 digits after the
decimal point. That’s actually convenient if we have an e-shopping and need to show $0.30.
For other cases, we can use the unary plus to coerce it into a number:
 let sum = 0.1 + 0.2;
 alert( +sum.toFixed(2) ); // 0.3
 isNaN(value) converts its argument to a number and then tests it for being NaN:
 alert( isNaN(NaN) ); // true
 alert( isNaN("str") ); // true
 But do we need this function? Can’t we just use the comparison === NaN? Unfortunately
not. The value NaN is unique in that it does not equal anything, including itself:
 alert( NaN === NaN ); // false
 isFinite(value) converts its argument to a number and returns true if it’s a regular number,
not NaN/Infinity/-Infinity:
 Please note that an empty or a space-only string is treated as 0 in all numeric functions
including isFinite.
 Number.isNaN and Number.isFinite methods are the more “strict” versions of isNaN and
isFinite functions. They do not autoconvert their argument into a number, but check if it
belongs to the number type instead.

 Number.isNaN(value) returns true if the argument belongs to the number type and it is NaN.
In any other case, it returns false.

 alert( Number.isNaN(NaN) ); // true
 alert( Number.isNaN("str" / 2) ); // true

 // Note the difference:
 alert( Number.isNaN("str") ); // false, because "str" belongs to the string type, not the
number type
 alert( isNaN("str") ); // true, because isNaN converts string "str" into a number and gets NaN
as a result of this conversion
 Number.isFinite(value) returns true if the argument belongs to the number type and it is not
NaN/Infinity/-Infinity. In any other case, it returns false.

 alert( Number.isFinite(123) ); // true
 alert( Number.isFinite(Infinity) ); // false
 alert( Number.isFinite(2 / 0) ); // false

 // Note the difference:
 alert( Number.isFinite("123") ); // false, because "123" belongs to the string type, not the
number type
 alert( isFinite("123") ); // true, because isFinite converts string "123" into a number 123
 There is a special built-in method Object.is that compares values like ===, but is more reliable
for two edge cases:
 It works with NaN: Object.is(NaN, NaN) === true, that’s a good thing.
 Values 0 and -0 are different: Object.is(0, -0) === false, technically that’s correct because
internally the number has a sign bit that may be different even if all other bits are zeroes.
 In all other cases, Object.is(a, b) is the same as a === b.
 parseInt and parseFloat are for.

 They “read” a number from a string until they can’t. In case of an error, the gathered number
is returned. The function parseInt returns an integer, whilst parseFloat will return a floating-
point number:

 alert( parseInt('100px') ); // 100
 alert( parseFloat('12.5em') ); // 12.5

 alert( parseInt('12.3') ); // 12, only the integer part is returned
 alert( parseFloat('12.3.4') ); // 12.3, the second point stops the reading
 There are situations when parseInt/parseFloat will return NaN. It happens when no digits
could be read:

 alert( parseInt('a123') ); // NaN, the first symbol stops the process
 Can write numbers directly in hex (0x), octal (0o) and binary (0b) systems.
 parseInt(str, base) parses the string str into an integer in numeral system with given base, 2 ≤
base ≤ 36.
 num.toString(base) converts a number to a string in the numeral system with the given base.
 Round using Math.floor, Math.ceil, Math.trunc, Math.round or num.toFixed(precision).

You might also like