- 1. Getting Started
- 2. Language Basics, Base Syntax
- 3. Control Structures (if, Loops, Error…)
- 4. "Behind the Scenes of JS The (Weird) Past (ES3, ES5) & Present (ES6+)"
- 5. A Closer Look at Functions
- 6. DOM Basics
- 7. Arrays & Iterables
- 8. Objects
- 9. Classes & Object-oriented Programming (OOP)
- 10. Constructor Functions & Prototypes
- 11. Practice: OOP & Classes
- 12. More about DOM & Browser APIs
- 13. Events
- 14. Advanced Function Concepts
- 15. More about Numbers & Strings
- 16. Asynchronous/Async JS: Promises & Callbacks
- 17. Background Http (Ajax)
- 18. 3rd Party Libraries
- 19. JavaScript Modules
- 20. Tooling (Webpack, Babel, …)
- 21. Working with Browser Storage
- 22. Browser Support
- 23. Practice
- 24. JavaScript Frameworks
- 25. Meta-programming: Symbols, Iterators, Generators, Reflect API & Proxy API
- 26. NodeJS Introduction
- 27. Security
- 28. Deployment
- 29. Performance Optimizations & Memory Leaks
- 30. Testing
- 31. Bonus: Programming Paradigms (Procedural vs Object Oriented vs Functional)
- 32. Bonus: Data Structures & Algorithms Introduction
- 33. Bonus: TypeScript Introduction
- 34. Bonus: Web Components
- 35. Roundup & Next Steps
- 36. Efficient Development & Debugging
(类型转换)
change the string to number.
parseInt("10", 2); //change to a number, then to radix 2
parseFloat(10.1);
let newResult = result + +userInput.value;
// **also work as*
let newResult = result + parseInt(userInput.value);
if the number's length is more than 16, it wouldn't work. try use BigInt().
change the number to string.
result.toString();
const num = 10;
console.log(num.toString(2)); //1010
check is it a number.
isNan(inputNumber);
JSON.stringify();
JSON.parse();
var str = "Hello World!";
var enc = window.btoa(str);
var dec = window.atob(enc);
// Encoded String: SGVsbG8gV29ybGQh
// Decoded String: Hello World!
delete white space
inputValue.trim();
font setting
xxx.toUpperCase();
A random number
Math.random();
change contents of an array
Array.prototype.splice()
arrayName.push(objectName);
arrayName.splice(startIndexNumber, deleteCount, addItem1, addItem2, ...);
btn.addEventListener('click', function)
alert()
prompt()//alert and input
variable: A data container where the data can be changed during program execution.
set up as a global value:
it's also quite common to use all uppercase characters there and separate words with an underscore, to make it really clear that this is a global value which you just 'hard coded' into the code.
const ATTACK_VALUE = 10;
number = number + newNumber; //same as:
number += newNumber;
number = number + 1; //same as:
number++;
String
Javascript template literal (``)
. links
let currentResult = "(" + defaultResult + "+ 10) * 3 / 2 -1";
let currentResult = `(${defaultResult} + 10) * 3 / 2 -1`;
string Escape notation. Link
- css:
white-space: pre
.
A function defines code which doesn't execute right away but which can be executed multiple times by calling the function.
A function is "code on demand".
Function parameters are Data that can be provided to the function (input) - available only inside of the function, like local variables.
Most importantly: A function is not "an alternative to a variable". It supports a totally different use-case. But it's also worth pointing out that variables are NOT restricted to storing numbers or strings.
defines
function functionName(parameter1, parameter2) {
const result = parameter1 + parameter2;
alert(`The result is ${result}`);
}
call function
functionName();
- Build-in function alert(), addEventListener(), prompt()
弹窗
function functionName(parameter1, parameter2) {
const result = parameter1 + parameter2;
return result;
}
Any code that have write after return
, won't execute.
It can be confusing to see that there seem to be two ways of executing a function:
function add() {
something = someNum + someOtherNum;
}
add()
vs add
It's important to understand why we have these "two ways"!
In general, you call a function that you defined by using its name (e.g. add
) and adding parentheses (with any parameters the function might need - or empty parentheses if no parameters are required like in the above example).
=> add()
This is how you execute a function from your code. Whenever JavaScript encounters this statement, it goes ahead and runs the code in the function. Period!
Sometimes however, you don't want to execute the function immediately. You rather want to "tell JavaScript" that it should execute a certain function at some point in the future (e.g. when some event occurs).
That's when you don't directly call the function but when you instead just provide JavaScript with the name of the function.
=> someButton.addEventListener('click', add);
This snippet would tell JavaScript: "Hey, when the button is clicked, go ahead and execute add.".
someButton.addEventListener('click', add());
would be wrong.
Why? Because JavaScript would encounter that line when it parses/ executes your script and register the event listener AND immediately execute add - because you added parentheses => That means (see above): "Please execute that function!".
Just writing add somewhere in your code would do nothing by the way:
let someVar = 5;
add;
alert("Do something else...");
Why?
Because you just throw the name of the function in there but you don't give any other information to JavaScript. It basically doesn't know what to do with that name ("Should I run that when a click occurs? After a certain amount of time? I don't know...") and hence JavaScript kind of ignores this statement.
You can't use local/block-scope variables(= declared inside of functions) outside of them.
shadowing
What happens if you have this code?
let userName = "Max";
function greetUser(name) {
let userName = name;
alert(userName);
}
userName = "Menu";
greetUser("Max");
This will actually show an alert that says 'Max'
(NOT 'Menu'
).
You might've expected that an error gets thrown because we use and declare userName
more than once - and as you learned, that is not allowed.
It indeed is not allowed on the same level/ in the same scope.
So this would fail:
let userName = "Max";
let userName = "Menu";
Why does it work in the first code snippet though?
Because we first create a global variable userName
via
let userName = "Max";
But then we never re-declare that on the global level (that would not be allowed).
We only declare another variable inside of the function. But since variables in functions get their own scope, JavaScript does something which is called "shadowing".
It creates a new variable on a different scope - this variables does not overwrite or remove the global variable by the way - both co-exist.
When referring to userName
inside of the greetUser
function we now always refer to the local, shadowed variable. Only if no such local variable existed, JavaScript would fall back to the global variable.
类型(数值)转换
change the string to number.
parseInt(10);
parseFloat(10.1);
also work as
result + +userInput.value = result + parseInt(userInput.value);
if the number's length is more than 16, it wouldn't work. try use BigInt().
change the number to string.
result.toString();
check is it a number.
isNan(inputNumber);
font setting
xxx.toUpperCase();
Mix number and string
You saw the example with a number and a "text number" being added
3 + '3'
=> '33'
in JavaScript.
That happens because the + operator also supports strings (for string concatenation).
It's the only arithmetic operator that supports strings though. For example, this will not work:
'hi' - 'i'
=> NaN
NaN
is covered a little later, the core takeaway is that you can't generate a string of 'h' with the above code. Only +
supports both strings and numbers.
Thankfully, JavaScript is pretty smart and therefore is actually able to handle this code:
3 * '3'
=> 9
Please note: It yields the number (!) 9
, NOT a string '9'
!
Similarly, these operations also all work:
3 - '3'
=> 0
3 / '3'
=> 1
Just 3 + '3'
yields '33'
because here JavaScript uses the "I can combine text" mode of the +
operator and generates a string instead of a number.
Section 8 will explain more. Link
Array: A list of data of any kind.
newArray = [1, 2, 3];
newArray.push("4");
console.log(newArray[0]);
Section 9 will explain more. Link
object: grouped data, structured in key-value pairs.
newObject = {
name: 'max';
age: 27;
};
console.log(newObject.name);
undefined
& null
- whilst the two values are similar, they're not equal. undefined is a special type and the default value for undefined variables, null is actually of type object and never a default value of anything.
typeof [1, 2, 3]
is an Object
typeof undefined
is undefined
typeof null
is Object
typeof NaN
is number
插入(引用)JS, only for external file
- conditional Statements(if) & Expressions
- Boolean Values & Operators
- Loops in JavaScript
- Error Handing
Understanding the "Condition"
Always keep in mind that condition
in
if (condition) { ... }
simply has to be a boolean value.
Often, you'll generate such a boolean value with the help of ===
, >
, <
etc. All these operators yield boolean values (without changing the variables/ values you're using them on).
Since if
only wants a boolean, you of course don't have to use such an operator. If you already got a variable that holds a boolean, you can use it without any extra operator.
Example:
const isLoggedIn = true;
if (isLoggedIn) {
// This code will execute because isLoggedIn is true => A valid condition
}
You could write
const isLoggedIn = true;
if (isLoggedIn === true) {
...
}
but that would be redundant. You'd generate another new boolean where you already got one.
You can use the !
operator to negate ("invert") the value:
const isLoggedIn = true;
if (!isLoggedIn) {
// This code will NOT execute because isLoggedIn is true but ! inverts it (in this check)
} else {
// This would execute because !isLoggedIn yields false => else block executes
}
Again, that would be similar to:
const isLoggedIn = true;
if (isLoggedIn !== true) {
// This would NOT execute
} else {
// This would execute because isLoggedIn is true and hence !== true yields false
}
But again, that would be redundant.
More on Text (String) Comparisons
Strings can also be compared with greater than (>
) or lower/ smaller than (<
) operators.
JavaScript compares strings based on standard lexicographical ordering, using Unicode values.
That means that b
is greater than a
for example.
JavaScript always looks at the first character and only considers other characters if the first character is similar. In addition, capital characters are considered to be smaller than lowercase characters.
See these examples:
"ab" > "aa"; // true
"a" > "B"; // true
"a" > "b"; // false
Example:
if (x > 50 && ( x = 30 || x = 20) {
/* do the right thing */
} else if (x > 5) {
/* do the right thing */
} else {
/* do the right thing */
}
compare object and array
ArrayOne = [1, 2, 3];
ArrayTwo = [1, 2, 3];
ArrayOne == ArrayTwo; //return false
same as object
Falsy and Truthy value (Print out)
Example:
let maxDamage;
let logEvent;
if (mode === MODE_ATTACK) {
maxDamage = ATTACK_VALUE;
logEvent = LOG_EVENT_PLAYER_ATTACK;
} else if (mode === MODE_STRONG_ATTACK) {
maxDamage = STRONG_ATTACK_VALUE;
logEvent = LOG_EVENT_PLAYER_STRONG_ATTACK;
}
const maxDamage = mode === MODE_ATTACK ? ATTACK_VALUE : STRONG_ATTACK_VALUE;
const logEvent =
mode === MODE_ATTACK
? LOG_EVENT_PLAYER_ATTACK
: LOG_EVENT_PLAYER_STRONG_ATTACK;
||:
if the first value
is true, return the first value
.
if the first value
is false, return the second value
.
const usrInput = "";
const userName = usrInput || "Max";
userName;
//return: "Max"
const realUserInput = "Ivy";
const realUserName = realUserInput || "Max";
realUserName;
//return: "ivy"
&&:
if the first value
is true, return the second value
.
if the first value
is false, return the first value
.
const isLoggedIn = true;
const shoppingCart = isLoggedIn && ["Books"];
shoppingCart;
//return: ["Books"]
isLoggedIn = false;
isLoggedIn && ["Books"];
//return: false
null && ["Books"];
//return: null
(Print out)
As a reference which you can come back to (or print out), here's a quick summary of how logical operators and comparison operators behave in JavaScript:
const userName = "Max";
const altName = "";
console.log(userName === "Max"); // generates and prints a boolean => true
console.log(userName); // wasn't touched, still is a string => 'Max'
console.log(userName || null); // userName is truthy and therefore returned by || => 'Max'
console.log(altName || "Max"); // altName is falsy (empty string), hence 'Max' is returned => 'Max'
console.log(altName || ""); // both altName and '' are falsy but if the first operand is falsy, the second one is always returned => ''
console.log(altName || null || "Anna"); // altName and null are falsy, 'Anna' is returned => 'Anna'
console.log(userName && "Anna"); // userName is truthy, hence second (!) value is returned => 'Anna'
console.log(altName && "Anna"); // altName is falsy, hence first value is returned => ''
console.log(userName && ""); // userName is truthy, hence second value is returned => ''
Always keep in mind: NO operator (neither ===
, >
etc. nor &&
or ||
) changes the variable you might be using in the comparison. In the above examples, the values stored in userName
and altName
are NEVER changed.
===
, >
etc. just generate new boolean values which are used in the comparison. ||
and &&
generate NO booleans, they just treat the values before and after them as conditions (which therefore need to yield boolean values and are coerced to booleans if required).
Because of the above-described behaviors, you often use ||
in JavaScript to assign default/ fallback values to variables/ constants:
const enteredValue = ""; // let's assume this is set based on some input provided by the user, therefore it might be an empty string
const userName = enteredValue || "PLACEHOLDER"; // will assign 'PLACEHOLDER' if enteredValue is an empty string
Switch with 'greater than' ect. Link
example:
let logEntry = {
event: ev,
value: val,
finalMonsterHealth: monsterHealth,
finalPlayerHealth: playerHealth,
};
if (ev === LOG_EVENT_PLAYER_ATTACK) {
logEntry.target = "MONSTER";
} else if (ev === LOG_EVENT_MONSTER_ATTACK) {
logEntry = {
event: ev,
value: val,
target: "PLAYER",
finalMonsterHealth: monsterHealth,
finalPlayerHealth: playerHealth,
};
}
switch (ev) {
case LOG_EVENT_PLAYER_ATTACK:
logEntry.target = 'MONSTER';
break;
case LOG_EVENT_MONSTER_ATTACK:
logEntry = {
event: ev,
value: val,
target: 'MONSTER',
finalMonsterHealth: mon sterHealth,
finalPlayerHealth: playerHealth
};
break;
default:
logEntry = { };
break;
}
for (let i = 0; i < 3; i++) {
console.log(i);
}
for (let i = 10; i > 0) {
i--;//from 9 to 0
console.log(i);
//i--; from 10 to 1
}
for (let i = 0; i < battleLog.length, i++) {
console.log(battleLog[i]);
}
for (const logEntry of battleLog){
console.log(logEntry);
}
let i = 1;
for (const logEntry of battleLog) {
console.log(`Round ${i}`);
for (const key in logEntry) {
console.log(`${key} : ${logEntry[key]}`);
}
i++;
}
let randomNumber = [];
let finished = false;
while (!finished) {
let number1 = Math.random();
randomNumber.push(number1);
if (number1 > 0.8) {
finished = true;
}
}
console.log(randomNumber);
do-while
let j = 3;
while (j < 3) {
console.log(j);
j++;
}
//no output
let j = 3;
do {
console.log(j);
j++;
} while (j < 3);
//output: 3
forEach()
read more on array section forEach() & map()
Break indeed does stop the entire loop execution immediately and moves on to code execution after the loop.
Continue The current iteration is stopped/ skipped and the next iteration (of the same loop) starts.
for (let i = 0; i < 5; i++) {
console.log(i);
}
//output: 0, 1, 2, 3, 4.
for (let i = 0; i < 5; i++) {
if (i === 3) {
break;
}
console.log(i);
}
//output: 0, 1, 2.
for (let i = 0; i < 5; i++) {
if (i === 3) {
continue;
}
console.log(i);
}
//output: 0, 1, 2, 4.
Labeled Statements
let j = 0;
outerWhile: do {
console.log("Outer: ", j);
innerFor: for (let k = 0; k < 5; k++) {
if (k === 3) {
continue outerWhile; //infinite loop, dangerous!
}
console.log("Inner", k);
}
j++;
} while (j < 3);
try {
} catch (error) {
} finally {
}
- ES5 vs ES6+ Syntax
- How JavaScript Works (heap, stack, Garbage collection & Memory Management)
- The Weird Parts
var
has global/function(local) scope, let
and const
have block scope.
var name = "Max";
var name = "Ivy";
console.log(name);
//Output: Ivy
//This will actually not throw an error.
if (name === "Ivy") {
var hobbies = ["Cooking", "Coding"];
//let hobbies = ["Cooking", "Coding"];
}
console.log(hobbies);
//throw an error while using let.
In the new project, do not use var
anymore.
变量提升
console.log(name);
var name = "Max";
//output: undefined
console.log(name);
let name = "Max";
//error: ReferenceError. Cannot access 'name' before initialization.
"use strict"; //first line
heap and stack Link-CN
primitive vs reference Values
7 个基本原始类型
- Seven data types that are primitives:
Boolean
Null
Undefined
Number
BigInt
String
Symbol - and Object (reference)
Garbage collection & Memory Management
Very important this section. Need to come back and really understand what they are. More on JavaScript Memory Management (Garbage Collection): MDN
V8's Garbage Collection Logic: V8-Link
V8's JavaScript Engine in Great Detail: Link
More on Primitive vs Reference Values: Link
- Different way of creating Function
- Anonymous Function (匿名函数)
- Callback Function & function in function
- Default Arguments & Rest Operator
- bind() & more
Throughout this course, you'll hear me use the words "parameters" and "arguments" interchangeably.
Technically, there is a difference though:
Parameters are these variables which you specify between parentheses when defining a function.
function sayHi(name) { ... }
In this example, name
is a parameter.
Arguments then are the concrete values you pass to a function when calling that function:
sayHi("Max");
'Max'
is an argument of the function therefore - for the name
parameter to be precise.
Since both concepts obviously are extremely close connected, I will often say "let's define which arguments a function receives" or something comparable, since defining the arguments of a function in the end means that you set up its parameters (and vice-versa).
Function & object
function can be a object.
object can be a function.
Function in Variable
Function Expressions vs Function Declarations
Function declarations automatically create variables that hold the function objects, function expressions don't do that - they return an object instead, it's your job to then do something with it (e.g. store it in a variable).
https://www.jianshu.com/p/435bd1e773cd
function normalFc(name) {
console.log(name);
}
const normalFc = function (name) {
console.log(name);
};
const arrowFc = (name) => {
console.log(name);
};
//normalFc
const add = function (a, b) {
return a + b;
};
//arrowFc
const add = (a, b) => a + b;
For arrow functions, you got a couple of different syntaxes which you can use - here's a summary.
Important: Don't miss the "function only returns an object" special case at the end of this article!
1) Default syntax:
const add = (a, b) => {
const result = a + b;
return result; // like in "normal" functions, parameters and return statement are OPTIONAL!
};
Noteworthy: Semi-colon at end, no function keyword, parentheses around parameters/ arguments.
2) Shorter parameter syntax, if exactly one parameter is received:
const log = (message) => {
console.log(message); // could also return something of course - this example just doesn't
};
Noteworthy: Parentheses around parameter list can be omitted (for exactly one argument).
3) Empty parameter parentheses if NO arguments are received:
const greet = () => {
console.log("Hi there!");
};
Noteworthy: Parentheses have to be added (can't be omitted)
4) Shorter function body, if exactly one expression is used:
const add = (a, b) => a + b;
Noteworthy: Curly braces and return statement can be omitted, expression result is always returned automatically
5) Function returns an object (with shortened syntax as shown in 4)):
const loadPerson = (pName) => ({ name: pName });
Noteworthy: Extra parentheses are required around the object, since the curly braces would otherwise be interpreted as the function body delimiters (and hence a syntax error would be thrown here).
That last case can be confusing: Normally, in JavaScript, curly braces always can have exactly one meaning.
const person = { name: 'Max' }; // Clearly creates an object
if (something) { ... } // Clearly used to mark the if statement block
But when using arrow functions, curly braces can have two meanings:
-
Mark the function body (in default form)
-
Create an object which you want to return (in shorter function body form)
To "tell" JavaScript what you want to do, wrap the expression (e.g. object creation) in parentheses like shown above.
- explain more in other section.
function can be called with less(or without any) arguments.
const defaultValue = 1;
const sumUp = (number1, number2 = defaultValue) => {
return number1 + number2;
};
console.log(sumUp(2));
//output: 3
Default arguments are not a "validation mechanism".
Rest Operator must be the last Parameters.
const sumUp = (...number) => {
let sum = 0;
for (const num of number) {
sum += num;
}
return sum;
};
console.log(sumUp(1, 2, 3, 4));
//output: 10
It bundles all arguments beyond the first 2 argument into an array.
const sumUp = (a, b, ...number) => {
let sum = 0;
for (const num of number) {
sum += num;
}
return sum;
};
console.log(sumUp(1, 2, 3, 4));
//output: 7
*arguments
not work in arrow function. (don't use that in you code)
const sumUp = function () {
let sum = 0;
for (const num of arguments) {
// don't use that
sum += num;
}
return sum;
};
console.log(sumUp(1, 2, 3, 4));
//output: 10
Will dive deeper.
const sumUp = (resultHandler, ...number) => {
let sum = 0;
for (const num of number) {
sum += num;
}
resultHandler(sum);
};
const showResult = (result) => {
alert("Total result is:" + result);
};
sumUp(showResult, 1, 2, 3, 4);
//output: 10
In situations where you want to "pre-configure" a function's arguments, when you're not calling the function on your own.
The following resources may be helpful.
More on Functions : MDN
bind(): MDN
Will dive deeper.
- HTML, DOM & JavaScript
- Nodes & Elements
- Querying DOM Nodes & Traversing the Dom
- Evaluating & Manipulation DOM Nodes
- Creating & Removing DOM Nodes
console.dir(document);
window.alert("Max") = alert("Max");
$0;
//$0 always gives you access to the last element you selected in the "Elements" tab!
look at 6.11. live node lists vs static node lists
document.getElementById("id-name"); //live
document.querySelector("tab-name/.class-name/#id-name"); //live
//return the first one
//document.getElementsByClassName("class-name");
document.querySelectorAll("tab-name/.class-name"); //none live
document.getElementsByTagName("tab-name"); //live
Summary: Node Query Methods
Here's a summary of the various methods you got to reach out to DOM elements (note: you can only query for element nodes).
Besides the below query methods, you also got these special properties on the document object to select parts of the document:
document.body
=> Selects the <body>
element node.
document.head
=> Selects the <header>
element node.
document.documentElement
=> Selects the <html>
element node
... QUERY METHODS ....
document.querySelector(<CSS selector>);
Takes any CSS selector (e.g. '#some-id'
, '.some-class'
or 'div p.some-class'
) and returns the first (!) matching element in the DOM. Returns null
if no matching element could be found.
More information: MDN
document.getElementById(<ID>);
Takes an ID (without #
, just the id name) and returns the element that has this id. Since the same ID shouldn't occur more than once on your page, it'll always return exactly that one element. Returns null
if no element with the specified ID could be found.
More information: MDN
document.querySelectorAll(<CSS selector>);
Takes any CSS selector (e.g. '#some-id'
, '.some-class'
or 'div p.some-class'
) and returns all matching elements in the DOM as a static (non-live) NodeList
. Returns and empty NodeList
if no matching element could be found.
More information: MDN
document.getElementsByClassName(<CSS CLASS>);
Takes a CSS class g (e.g. 'some-class'
) and returns a live HTMLCollection
of matched elements in your DOM. Returns an empty HTMLCollection
if not matching elements were found.
More information: MDN
document.getElementsByTagName(<HTML TAG>);
Takes an HTML tag (e.g. 'p'
) and returns a live HTMLCollection
of matched elements in your DOM. Returns an empty HTMLCollection
if not matching elements were found.
More information: MDN
There also is the getElementsByName()
method which really isn't used commonly MDN.
https://developer.mozilla.org/en-US/docs/Web/API
child nodes
const ul = document.querySelector("ul");
ul.children; //show all elements (li) in the ul
ul.children[1]; // select the second element (li)
ul.childNodes; //show all node in the ul
parent node
document.documentElement.parentElement; //null
document.documentElement.parentNode; //#document
<html>
<body>
<header></header>
<ul>
<li></li>
<li></li>
</ul>
</body>
</html>;
const firstLi = document.querySelector("li");
li.parentNode; //select parent node (ul)
firstLi.closest("body"); //<body></body>
firstLi.closest("header"); //null
sibling element
ul.previousSibling; //text node
ul.previousElementSibling; //<header></header>
ul.nextElementSibling;
const task1 = document.querySelector("li");
task1.style.backgroundColor = "black";
task1.style["background-color"] = "black";
task1.style.color = "white";
const h1 = document.getElementsByTagName("h1");
h1[0].textContent = "Assignment - Solved!";
via class
const section = document.querySelector("section");
//example:
section.className = "xxx"; //change the section css class-name to xxx
section.classList.add("xxx"); //add class name.
section.classList.toggle("xxx"); //Toggles between a class name for an element
section.classList.contains("xxx"); //Returns a Boolean value, indicating whether an element has the specified class name.
creating via HTML (not recommended)
const ulList = document.querySelector("ul");
const p = document.querySelector("p");
p.textContent = "New content"; //rewrite
ulList.innerHTML = "<li>new li<li>"; //rewrite
ulList.innerHTML = ulList.innerHTML + "<li>add li<li>"; //add
//not good for the performance. render other-element you don't want to change.
p.insertAdjacentHTML(position, text);
<!-- beforebegin --> //Before the element itself.
<p>
<!-- afterbegin --> //Just inside the element, before its first child.
foo
<!-- beforeend --> //Just inside the element, after its last child.
</p>
<!-- afterend --> //After the element itself.
creating via createElement()
const list = document.querySelector("ul");
const newLi = document.createElement("li"); //create element
newLi.textContent = "4"; //add text to the element
list.appendChild(newLi); //add element to the `ul`, at the end
chrome may wrong. append: node, appendChild: element.
_append, before, after
can take multiple nodes
_append()
, prepend()
not support IE
*appendChild()
not support insert a node
. (recommended)
ul.appendChild(newLi); //add element in the `ul`, be the last child element
ul.prepend(newLi); //add element in the `ul`, be the first child element
ul.lastElementChild.before(newLi); //before the last child.
ul.lastElementChild.after(newLi);
ul.firstElementChild.replaceWith(newLi); //replace the first child.
*before, after
not support IE
& safari
, so if you want to insert an element to a specific position, use insertAdjacentElement
const list = document.querySelector("ul");
const secondLi = list.children[1];
const newLi = document.createElement("li");
newLi.textContent = "Number 4";
secondLi.insertAdjacentElement("afterend", newLi); //position same as insertAdjacentHTML
list.insertBefore(newLi, firstLi);
//add new element to the `ul`, will be the before the refernceNode.
const newLi2 = newLi.cloneNode(true);
//different between true and false?
//false: no nested elements
example:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
if you get all the li
first, add an new element (<li>4</li>
) to the ul, and get all li
again after.
Live node list will get a array HTMLCollection(4)[]
;
Static node lists will get a array NodeList(3)[]
, it is not going to get the new li
which you just add.
Look at the example below:
*Live node not means it is better.
It could lead to a higher memory consumption if you're managing a lot of such collections which change all the time.
const list = document.querySelector("ul");
list.remove(); //not work in IE
list.parentElement.removeChild(list); //for all browsers.
There are many ways of creating, inserting, replacing and removing DOM elements - here's a summary of the options you have.
For browser support, check the provided links and also the "Browser Support" module you find later in this documents.
Create & Insert
You got two main options: Provide an HTML snippet (e.g. via innerHTML
) to a valid HTML snippet and let the browser render it OR create a DOM object in JS code and append/ insert it manually. The latter approach has the advantage of giving you direct access to the DOM object (useful for setting its properties or adding event listeners). The downside is that you have to write more code.
Adding HTML Code:
const root = document.getElementById("root-el"); // selects something like <div id="root-el">
root.innerHTML = `
<div>
<h2>Welcome!</h2>
<p>This is all create & rendered automatically!</p>
</div>
`;
Important: Any existing content in root
is completely replaced when using innerHTML
. If you want to append/ insert HTML code, you can use insertAdjacentHTML
instead: MDN
const root = document.getElementById("root-el"); // selects something like <div id="root-el">
root.insertAdjacentHTML(
"afterbegin",
`
<div>
<h2>Welcome!</h2>
<p>This is all create & rendered automatically!</p>
</div>
`
);
Creating & Inserting DOM Objects Manually:
const someParagraph = document.createElement("p"); // creates a "p" element (i.e. a <p> element)
const root = document.getElementById("root-el"); // selects something like <div id="root-el">
root.append(someParagraph);
In this example, we create a paragraph and append it to root
- append means that it's inserted at the end of root
(i.e. inside of it but AFTER all other child nodes it holds).
Insertion Methods:
append()
=> MDN-Link
Browser support is decent but for IE, appendChild()
could be preferred => MDN-Link
prepend()
=> MDN-Link
Browser support is decent but for IE, insertBefore()
could be preferred => MDN-Link
before(), after()
=> MDN-Before & MDN-After
Browser support is okay but IE and Safari don't support it. Consider insertBefore()
MDN-Link or insertAdjacentElement()
MDN-Link as substitutes.
Important (no matter how you insert elements): Whenever you insert elements, you MOVE the element to that new place if you already inserted it before. It's NOT copied (you can copy an element via someElement.cloneNode(true)
though).
Replace
You can replace elements in the DOM with two main methods:
replaceWith()
=> MDN-Link
replaceChild()
=> MDN-Link
replaceWith()
is a bit easier to use and has decent browser support - with IE being the exception. To support that as well, consider using replaceChild()
.
Remove
You can remove elements with three main methods:
someElement.innerHTML = ''
=> Clears all HTML content of someElement
and hence removes any objects rendered in there.
someElement.remove()
=> Removes a single element (someElement
) from the DOM MDN-Link. Browser support is good, IE again doesn't like it though. Use removeChild
(see below) instead.
someElement.parentNode.removeChild(someElement)
=> Removes the provided child element (NOT the element on which you call it). Provides broad browser support but of course requires a bit more code MDN-Link.
What about Text Nodes?
You can easily create & insert text nodes in one go:
someElement.textContent = "Hi there!";
This creates and inserts the text node with a content of 'Hi there!'
.
Want to append to existing text?
Just use:
someElement.textContent = someElement.textContent + "More text!";
Something need be read again:
clone dom, replace
confirmDeleteMovieBtn.replaceWith(confirmDeleteMovieBtn.cloneNode(true));
bind()
- Creating Arrays
- Important Arrays Methods
- Maps & Sets
const numbers = [1, 2, 3];
//return [1, 2, 3]
const moreNumbers = new Array(1, 5);
//return [1, 5]
const yetMoreNumbers = Array.of(5);
//return [5]
const moreNumbers1 = new Array(5);
//return [empty * 5]
const moreNumbersFrom = Array.from("Hi!");
console.log(moreNumbersFrom);
//return ["H", "i", "!"]
增加/删除 (改变原数组) push(), unshift(), pop(), shift()
const numbers = [2, 2, 2];
numbers.push(6); //[2, 2, 2, 6]
numbers.unshift(1); //[1, 2, 2, 2, 6]//at the beginning
numbers.pop(); //return [1, 2, 2, 2] delete last one
numbers.shift(); //return [2, 2, 2] delete first one
numbers[1] = 3; //[2, 3, 2]
numbers[6] = 6; //[2, 3, 2, empty × 3, 6]
slice(), 不会改变原数组
arrayName.splice(startIndexNumber, deleteCount, addItem1, addItem2, ...);
const newArray = arrayName.slice(arrayIndexStart, arrayIndexEnd);
//return a new array.
concat()
Return (copy) a new array
after add items at the end. 不改变原数组
const newArray = arrayName.concat([newItem1, newItem2, ...]);
return the index number. *not work for object.
arrayName.indexOf(itemValue); //from first one, return index-number
arrayName.lastIndexOf(itemValue); //from last one
finding stuff in object.
find()
dose not create a copy.
const inventory = [
{ name: "apples", quantity: 2 },
{ name: "cherries", quantity: 5 },
];
const result = inventory.find((item, index, items) => {
return item.name === "cherries";
});
const resultIndex = inventory.findIndex((item, index, items) => {
return item.name === "cherries";
});
console.log(result, resultIndex);
// { name: 'cherries', quantity: 5 } , 1
查找 If cant find, return '-1'.
console.log(arrayName.includes(itemValue)); //return true/false
排序
const arrayName = [71, 22, 103, 54, 65, 86];
const tax = 0.1;
const newArray = arrayName.sort((a, b) => {
if (a > b) {
return 1;
} else if (a === b) {
return 0;
} else {
return -1;
}
});
console.log(newArray);
//[22, 54, 65, 71, 86, 103]
const arrayName = [1, 2, 3, 4, 5, 6];
const tax = 0.1;
const newArray = [];
arrayName.forEach((element, idx, elements) => {
const newElement = { index: idx, number: element + tax };
newArray.push(newElement);
});
console.log(newArray);
//[{index: 0, number: 1.1}, {index: 1, number: 2.1}, {…}, {…}, {…}, {…}]
循环,返回新数组,不改变原数组
const arrayName = [1, 2, 3, 4, 5, 6];
const tax = 0.1;
const newArray = arrayName.map((element, idx, elements) => {
const newElement = { index: idx, number: element + tax };
return newElement;
});
console.log(newArray);
//[{index: 0, number: 1.1}, {index: 1, number: 2.1}, {…}, {…}, {…}, {…}]
筛选,返回新数组,不改变原数组
const arrayName = [71, 22, 103, 54, 65, 86];
const filterArray = arrayName.filter((element, index, elements) => {
return element > 50;
});
console.log(`copy:`, filterArray); //[71, 103, 54, 65, 86]
处理
const arrayName = [1.1, 2.2, 3, 4, 5, 6];
const sum = arrayName.reduce((prevValue, curValue, index, Elements) => {
return prevValue + curValue;
}, 0);
console.log(sum); //21.3x
With all these useful array methods you learned about, it's important to understand how you can combine them. Let's take map()
and reduce()
as an example:
const originalArray = [{ price: 10.99 }, { price: 5.99 }, { price: 29.99 }];
const transformedArray = originalArray.map((obj) => obj.price); // produces [10.99, 5.99, 29.99]
const sum = transformedArray.reduce((sumVal, curVal) => sumVal + curVal, 0); // => 46.97
Of course, you could skip the map step and just add the extraction logic to reduce()
:
const originalArray = [{ price: 10.99 }, { price: 5.99 }, { price: 29.99 }];
const sum = originalArray.reduce((sumVal, curVal) => sumVal + curVal.price, 0); // => 46.97
But let's say you have a more complex extraction logic and hence want to split this into multiple method calls. Or you have a re-usable map function which you want to be able to use in different places of your app. Then you can still write the initial example in a more concise way if you leverage method chaining:
const originalArray = [{ price: 10.99 }, { price: 5.99 }, { price: 29.99 }];
const sum = originalArray
.map((obj) => obj.price)
.reduce((sumVal, curVal) => sumVal + curVal, 0); // => 46.97
We call .reduce()
directly on the result of map()
(which produces an array, that's why this is possible). Hence we can avoid storing the mapped array in a separate constant or variable that we might not need in any other place.
string to array
const data = "Sydney;Canberra;Melbourne";
const array = data.split(";");
console.log(array); //["Sydney", "Canberra", "Melbourne"]
const nameFragments = ["Max", "Zhang"];
const name = nameFragments.join(" ");
console.log(name); //Max Zhang
spread for copy or
const numbers = [1, 2, 3];
const newNumber = [...numbers];
numbers.push(4);
console.log(numbers, newNumber);
//[1, 2, 3, 4] //[1, 2, 3]
console.log(Math.min(...numbers)); //1
�Copy:
包括上面的所有复制,object 的 refer 都是相同的。(详情看堆内存详解)
改变原有 array 的 object 值,复制的 array 也会随之改变。(引申:浅拷贝,深拷贝)
const name = [{ age: 1 }, { age: 2 }];
const newName = [...name];
name.push({ age: 3 });
console.log(name, newName);
//[{age: 1}, {age: 2}, {age: 3}]
//[{age: 1}, {age: 2}]
name[0].age = 4;
console.log(name, newName);
//[{age: 4}, {age: 2}, {age: 3}]
//[{age: 4}, {age: 2}]
const nameFragments = ["Max", "Zhang", "male", 28];
const [firstName, LastName, ...others] = nameFragments;
console.log(firstName, LastName, others);
//Max Zhang ["male", 28]
3 major iterable data structures.(Array, Set, Map)
???????????????????????????????????
*两个 array 变成一个 object:{array1 : array2}
*array 前面的 property 名字变换:
const array = [1, 2, 3, 4, 5, 6];
const arrayMonth = ["Jan", "Feb", "Mar", "Apr", "May"];
const mapArray = arrayMonth.map((val) => {
return { [array[idx] + `月`]: val };
});
console.log(mapArray);
// 0: {1月: "Jan"}
// 1: {2月: "Feb"}
// 2: {3月: "Mar"}
// 3: {4月: "Apr"}
// 4: {5月: "May"}
Objects are reference values - you learned that.
It might not have been obvious yet but it's also important to recognize that, in the end, objects are of course made up of primitive values.
Here's an example:
const complexPerson = {
name: 'Max',
hobbies: ['Sports', 'Cooking'],
address: {
street: 'Some Street 5',
stateId: 5,
country: 'Germany',
phone: {
number: 12 345 678 9,
isMobile: true
}
},
};
Event though complexPerson
has multiple nested reference values (nested arrays and objects), you end up with primitive values if you drill into the object.
name
holds a string ('Max'
) => Primitive value
hobbies
holds an array (i.e. a reference value) which is full of strings ('Sports'
, 'Cooking'
) => Primitive values
address
holds an object which in turn holds a mixture of primitive values like 'Some Street 5'
and nested objects (phone
), but if you dive into phone
, you find only numbers and booleans in there => Primitive values
So you could say: Primitive values are the core building blocks that hold your data, objects (and arrays) are helpful for organizing and working with that data.
Adding, Modifying & Deleting Properties
const person = {
name: "Max",
age: 30,
hobbies: ["Sports", "Cooking"],
greet: function () {
alert("Hi there!");
},
};
//person.greet();
person.admin = true;
delete person.age;
console.log(person);
//{name: "Max", hobbies: Array(2), admin: true, greet: ƒ}
Property Types & Property Order
const person = {
99: 20,
1: 30,
};
console.log(person); //return {1: 30, 99: 20}
//1 will be the first one. only numbers.
const person = {
"first name": "Max",
1.9: 30,
};
console.log(person["first name"]); //Max
console.log(person[1.9]); //30
const propKey = "field 12";
const person = {
[propKey]: "Max",
};
console.log(person["field 12"]);
output dynamic properties
const movie = {
info: {
title: newTitle,
[newExtraName]: newExtraValue,
},
id: Math.random(),
};
movies.forEach((movie) => {
const movieEleLi = document.createElement("li");
let text = movie.info.title + " - ";
for (const key in movie.info) {
if (key !== "title") {
text = text + `${key} : ${movie.info[key]}`;
}
}
movieEleLi.textContent = text;
movieList.appendChild(movieEleLi);
});
const newObject = {
propertyName: propertyName,
//same as
propertyName1,
};
const movie = {
info: {
title: newTitle,
},
};
const filterText = document.getElementById("filter-title").value;
const filterMovie = !filterText
? movies
: movies.filter((movie) => movie.info.title.includes(filterText));
xx.xx.xx
const movie = {
info: {
title: newTitle,
},
};
console.log(movie.info.title);
(...)
link to array
Deep copy
const movie = {
info: {
title: "New",
},
id: 123,
};
const { info, ...others } = movie;
console.log(info.title); //New
console.log(others); //{id: 123}
//rename
const { info: newInfo, ...others } = movie;
console.log(newInfo.title); //New
const movie = {
info: {
title: "New",
},
};
if ("info" in movie) {
}
Very important part!!!
关于 this 的详解,网上有很多解释得特别好的文章。毕竟面试必问。推荐直接看下列文章。这章的笔记请无视。
45 Question about this
This 关键字
what is context?
You Don't Know JS, CN
避免使用的场景
根据箭头函数的特性,所以我们应该避免在以下四种场景中使用它:
- 使用箭头函数定义对象的方法
let obj = {
value: "LinDaiDai",
getValue: () => console.log(this.value),
};
obj.getValue(); // undefined
- 定义原型方法
function Foo(value) {
this.value = value;
}
Foo.prototype.getValue = () => console.log(this.value);
const foo1 = new Foo(1);
foo1.getValue(); // undefined
- 构造函数使用箭头函数
const Foo = (value) => {
this.value = value;
};
const foo1 = new Foo(1);
// 事实上直接就报错了 Uncaught TypeError: Foo is not a constructor
console.log(foo1);
- 作为事件的回调函数
const button = document.getElementById("myButton");
button.addEventListener("click", () => {
console.log(this === window); // => true
this.innerHTML = "Clicked button";
});
5 rules with this binding - call site
- Default Binding
- Implicit Binding
- Explicit Binding
- New Binding
- arrow function binding
const dragon = {
weapon: "fire",
attack: function () {
console.log(`attacking with ${this.weapon}`);
},
};
dragon.attack(); //第一:attacking with fire
const abc = dragon.attack;
abc(); //第二:attacking with undefined
const abc2 = "defined?: " + dragon.attack(); //第三:attacking with fire
console.log(abc2); //defined?: undefined
第一种情况,this 指的是 object(dragon), 所以输出的值是 attacking with fire.
第二种情况,会返回 undefined(window?),因为只是赋予了变量 abc 一个 function,当执行 abc 的时候,this 前面没有 object,返回 undefined。
第三种情况(谁会这么写???为了防止不懂,解释一下。。。),当 abc2 定义的时候,**dragon.attack()
**后面加括号,等于立马执行了这个函数,所以会出现 attacking with fire 的结果,但是当再次读取 abc2 时候,会是 undefined。
- return
Bind is useful whenever you want to pre-configure a function for the future execution
executing the function right away
function.call(this, argArray,argArray...);
function.apply(this, [Array]);
const newTitle = "Default";
const movie = {
info: {
set title(value) {
if (value.trim() === "") {
this._title = "undefined";
return;
}
this._title = value;
},
get title() {
return this._title;
},
},
id: Math.random(),
};
movie.info.title = "Java";
console.log(movie.info.title); //Java
- what is OOP
- class & instances
- properties, fields & methods
- inheritance
class Product {
// title;
constructor(title) {
this.title = title;
}
render() {
return;
}
}
Fields are in the end simply translated to properties you would otherwise set up in the constructor method.
constructor(); 创建实例的时候的数据传值 super(); 从父级拿数据
overriding methods
Add the 'product' to the DOM, but you render the 'DOM' after you add(create) 'product'.
(254, 4:12)
super() constructor
A property should be used internally only.
Object.getOwnPropertyDescriptors(ObjectName);
Object.defineProperty(person, "name", {
configurable: true,
enumerable: true,
value: "Max",
writable: false,
});
//this is a way to change/locking down the property
the class itself is not the object. objects based on classes.
Syntactical sugar
color,theme, Icon, extensions,
Shortcuts
Toggle block comment: option + shift + a
;
Toggle line comment: cmd + /
;
Add Selection To Next Find Match: cmd+d
;
Add Selection all Match: cmd + shift + L
;
move line: option + top/button
;
copy line: option + shift + top/button
;
delete whole line: shift + cmd + K
auto-completion
open the suggestion: control + space
;
hints: shift + cmd + space
;
console.log()
,
breakpoint
with IDE and chrome.
this
let
&const
(let for changing value)
#project
- project 1
about basics js. - project 2
if, &&, loop, switch(break & continue), try-catch - project 3
DOM(add, delete...) - project 4
object - project 5
//react
https://unpkg.com/[email protected]/umd/react-dom.development.js https://unpkg.com/[email protected]/umd/react.development.js