avatar
Siz Long

My name is Siz. I am a computer science graduate student specializing in backend development with Golang and Python, seeking opportunities in innovative tech projects. My personal website is me.longsizhuo.com .Connect with me on LinkedIn: https://www.linkedin.com/in/longsizhuo/.

  • Resume
  • Archives
  • Categories
  • Photos
  • Music



{{ date }}

{{ time }}

avatar
Siz Long

My name is Siz. I am a computer science graduate student specializing in backend development with Golang and Python, seeking opportunities in innovative tech projects. My personal website is me.longsizhuo.com .Connect with me on LinkedIn: https://www.linkedin.com/in/longsizhuo/.

  • 主页
  • Resume
  • Archives
  • Categories
  • Photos
  • Music

React Fundamentals

  2023-06-09
字数统计: 3.6k字   |   阅读时长: 22min

React Fundamentals

React is a JavaScript library for building user interfaces, particularly single-page applications. It’s used to handle the view layer for web and mobile apps and allows you to create reusable UI components.

Introduction to React

React was created by Facebook and released in 2013. It’s maintained by Facebook and a community of individual developers and companies. React’s key features include:

  • Declarative: You describe what your UI should look like, and React efficiently updates and renders the right components when data changes.
  • Component-Based: Build encapsulated components that manage their own state, then compose them to make complex UIs.
  • Learn Once, Write Anywhere: React can also render on the server using Node, and power mobile apps using React Native.

Setting Up a React Application

Using Create React App

The easiest way to start with React is using Create React App:

1
2
3
npx create-react-app my-app
cd my-app
npm start

This creates a new React application with a development server, modern JavaScript features, and optimized production build.

Project Structure

A typical Create React App project has this structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
my-app/
README.md
node_modules/
package.json
public/
index.html
favicon.ico
src/
App.css
App.js
App.test.js
index.css
index.js
logo.svg

Manual Setup

For more control, you can set up React manually with webpack or another build tool:

1
2
3
4
5
mkdir my-react-app
cd my-react-app
npm init -y
npm install react react-dom
npm install --save-dev webpack webpack-cli webpack-dev-server babel-loader @babel/core @babel/preset-env @babel/preset-react html-webpack-plugin

Then create webpack and Babel configuration files.

React Components

Components are the core building blocks of React applications. Each component is a JavaScript function or class that optionally accepts inputs (props) and returns a React element describing what should appear on the screen.

Functional Components

Functional components are the simplest way to define a component:

1
2
3
4
5
6
7
8
9
10
11
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}

// Arrow function syntax
const Welcome = (props) => {
return <h1>Hello, {props.name}</h1>;
};

// Usage
<Welcome name="John" />

Class Components

Class components offer more features like local state and lifecycle methods:

1
2
3
4
5
6
7
8
9
10
import React, { Component } from 'react';

class Welcome extends Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}

// Usage
<Welcome name="John" />

Component Composition

Components can refer to other components in their output:

1
2
3
4
5
6
7
8
9
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}

Extracting Components

Break down components into smaller components for better reuse and separation of concerns:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}

function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}

function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}

JSX

JSX is a syntax extension to JavaScript that looks similar to HTML but comes with the full power of JavaScript. It’s recommended to use it with React to describe what the UI should look like.

JSX Basics

1
2
3
4
5
6
7
const element = <h1>Hello, world!</h1>;

const name = 'John Doe';
const element = <h1>Hello, {name}</h1>;

const user = { firstName: 'John', lastName: 'Doe' };
const element = <h1>Hello, {formatName(user)}</h1>;

JSX Represents Objects

Babel compiles JSX down to React.createElement() calls:

1
2
3
4
5
6
7
8
9
// This JSX:
const element = <h1 className="greeting">Hello, world!</h1>;

// Compiles to:
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);

JSX Attributes

1
2
3
4
5
6
7
8
// String literals
const element = <div tabIndex="0"></div>;

// JavaScript expressions
const element = <img src={user.avatarUrl}></img>;

// Self-closing tags
const element = <img src={user.avatarUrl} />;

JSX Children

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Empty elements
const element = <div></div>;

// Elements with children
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);

// Mixed content
const element = (
<div>
<h1>Hello, {name}!</h1>
<p>You have {unreadMessages.length} unread messages.</p>
</div>
);

JSX Prevents Injection Attacks

React DOM escapes any values embedded in JSX before rendering them, helping to prevent XSS attacks:

1
2
3
const title = response.potentiallyMaliciousInput;
// This is safe:
const element = <h1>{title}</h1>;

Props

Props (short for “properties”) are inputs to React components. They allow you to pass data from a parent component to a child component.

Passing Props

1
2
3
4
5
6
7
8
9
// Parent component passing props
function App() {
return <Welcome name="Sara" age={25} isAdmin={true} />;
}

// Child component receiving props
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}

Props are Read-Only

A component must never modify its own props. All React components must act like pure functions with respect to their props:

1
2
3
4
5
6
7
8
9
// Correct
function sum(a, b) {
return a + b;
}

// Incorrect (modifies input)
function withdraw(account, amount) {
account.total -= amount;
}

Default Props

You can define default values for props:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Button(props) {
return <button className={props.className}>{props.label}</button>;
}

Button.defaultProps = {
className: 'default-button',
label: 'Click Me'
};

// For class components
class Button extends React.Component {
// ...
}

Button.defaultProps = {
className: 'default-button',
label: 'Click Me'
};

Type Checking with PropTypes

You can use PropTypes to document the intended types of properties:

1
2
3
4
5
6
7
8
9
10
11
import PropTypes from 'prop-types';

function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}

Welcome.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
isAdmin: PropTypes.bool
};

State and Lifecycle

State allows React components to change their output over time in response to user actions, network responses, and anything else.

Adding State to a Class Component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}

render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

Lifecycle Methods

Components have lifecycle methods that allow you to run code at particular times:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}

// After the component output is rendered to the DOM
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}

// Before component is removed from the DOM
componentWillUnmount() {
clearInterval(this.timerID);
}

tick() {
this.setState({
date: new Date()
});
}

render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

Updating State

Never modify state directly; use setState() instead:

1
2
3
4
5
// Wrong
this.state.comment = 'Hello';

// Correct
this.setState({comment: 'Hello'});

State updates may be asynchronous:

1
2
3
4
5
6
7
8
9
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});

// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));

State updates are merged:

1
2
3
4
5
6
7
8
9
this.state = {
posts: [],
comments: []
};

// Only updates posts, comments remains the same
this.setState({
posts: ['New post']
});

Data Flows Down

State is often called local or encapsulated, and is not accessible to any component other than the one that owns and sets it. A component may pass its state down as props to its child components:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}

class Clock extends React.Component {
// ...
render() {
return (
<div>
<h1>Hello, world!</h1>
<FormattedDate date={this.state.date} />
</div>
);
}
}

Handling Events

React events are named using camelCase, and you pass a function as the event handler, rather than a string:

1
2
3
4
5
6
7
8
9
// HTML
<button onclick="activateLasers()">
Activate Lasers
</button>

// React
<button onClick={activateLasers}>
Activate Lasers
</button>

Event Handlers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};

// Binding is necessary to make `this` work in callback
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}

render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}

Alternative Class Syntax with Public Class Fields

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Toggle extends React.Component {
// No constructor needed
state = {isToggleOn: true};

// Public class fields syntax ensures `this` is bound
handleClick = () => {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}

render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}

Passing Arguments to Event Handlers

1
2
3
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>

<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

Conditional Rendering

In React, you can create distinct components that encapsulate the behavior you need, then render only some of them depending on the state of your application.

If Statements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}

function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}

function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}

Inline If with Logical && Operator

1
2
3
4
5
6
7
8
9
10
11
12
13
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}

Inline If-Else with Conditional Operator

1
2
3
4
5
6
7
8
9
10
11
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}

Preventing Component from Rendering

1
2
3
4
5
6
7
8
9
10
11
function WarningBanner(props) {
if (!props.warn) {
return null;
}

return (
<div className="warning">
Warning!
</div>
);
}

Lists and Keys

Rendering Multiple Components

1
2
3
4
5
6
7
8
9
10
11
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}

Keys

Keys help React identify which items have changed, added, or removed:

1
2
3
4
5
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);

Keys should be unique among siblings, but don’t need to be globally unique:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function Blog(props) {
const sidebar = (
<ul>
{props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);

const content = props.posts.map((post) =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);

return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}

Extracting Components with Keys

Keys only make sense in the context of the surrounding array:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function ListItem(props) {
// No need to specify the key here
return <li>{props.value}</li>;
}

function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Key should be specified inside the array
<ListItem key={number.toString()} value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}

Forms

HTML form elements work a bit differently in React because they naturally keep some internal state.

Controlled Components

In React, mutable state is typically kept in the state property of components, and only updated with setState(). Form elements that are controlled by React in this way are called “controlled components”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};

this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

handleChange(event) {
this.setState({value: event.target.value});
}

handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}

render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}

Handling Multiple Inputs

When you need to handle multiple controlled inputs, you can add a name attribute to each element and let the handler function choose what to do based on the value of event.target.name:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};

this.handleInputChange = this.handleInputChange.bind(this);
}

handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;

this.setState({
[name]: value
});
}

render() {
return (
<form>
<label>
Is going:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Number of guests:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}

Form Controls

React supports various HTML form elements:

  • Text inputs: <input type="text">, <textarea>
  • Checkboxes: <input type="checkbox">
  • Radio buttons: <input type="radio">
  • Select dropdowns: <select>, <option>
  • File inputs: <input type="file"> (uncontrolled)

Lifting State Up

Often, several components need to reflect the same changing data. It’s recommended to lift the shared state up to their closest common ancestor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>;
}
return <p>The water would not boil.</p>;
}

class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}

handleChange(e) {
this.setState({temperature: e.target.value});
}

render() {
const temperature = this.state.temperature;
return (
<fieldset>
<legend>Enter temperature in Celsius:</legend>
<input
value={temperature}
onChange={this.handleChange} />
<BoilingVerdict
celsius={parseFloat(temperature)} />
</fieldset>
);
}
}

When we need to support both Celsius and Fahrenheit, we lift state to the shared parent component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: '', scale: 'c'};
}

handleCelsiusChange(temperature) {
this.setState({scale: 'c', temperature});
}

handleFahrenheitChange(temperature) {
this.setState({scale: 'f', temperature});
}

render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange} />
<BoilingVerdict
celsius={parseFloat(celsius)} />
</div>
);
}
}

Composition vs Inheritance

React has a powerful composition model, and recommends using composition instead of inheritance to reuse code between components.

Containment

Components that don’t know their children ahead of time can use the special children prop to pass children elements directly into their output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}

function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}

Specialization

Sometimes we think about components as being “special cases” of other components. In React, this is also achieved by composition, where a more “specific” component renders a more “generic” one and configures it with props:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}

function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!" />
);
}

class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.state = {login: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
}

handleChange(e) {
this.setState({login: e.target.value});
}

handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}

render() {
return (
<Dialog
title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
}

Thinking in React

Here’s a thought process for building React apps:

  1. Start with a mock: Break the UI into a component hierarchy
  2. Build a static version: Build components that reuse other components and pass data using props. Don’t use state yet.
  3. Identify the minimal representation of UI state: Figure out what state your app needs.
  4. Identify where your state should live: Identify which component should own which state.
  5. Add inverse data flow: Support data flowing from forms back up to parent components.

React Developer Tools

React Developer Tools is a browser extension for Chrome and Firefox that allows you to inspect the React component hierarchy in the browser’s developer tools:

  • Inspect component props and state
  • Track which components re-render
  • Profile performance
  • Find components that cause unintended side effects

Best Practices

Keep Components Small and Focused

Each component should ideally do one thing well. If a component grows too large, consider breaking it down into smaller subcomponents.

Use Functional Components When Possible

Functional components are simpler, more concise, and generally easier to understand than class components.

Use PropTypes for Type Checking

Define PropTypes for your components to document the API and catch bugs early.

Use CSS-in-JS or CSS Modules

Consider using CSS-in-JS libraries like styled-components or CSS Modules to scope styles to specific components.

Use Fragment to Avoid Unnecessary DOM Nodes

Use React Fragments to group lists of children without adding extra nodes to the DOM:

1
2
3
4
5
6
7
8
9
function ListItems() {
return (
<>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</>
);
}

Use Short-Circuit Evaluation for Conditional Rendering

1
{isLoggedIn && <LogoutButton />}

Use Ternary Operator for Simple Conditionals

1
{isLoggedIn ? <UserGreeting /> : <GuestGreeting />}

Use Error Boundaries to Catch Rendering Errors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}

static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}

componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
logErrorToMyService(error, errorInfo);
}

render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}

return this.props.children;
}
}

// Usage
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>

Learning Resources

  • React Documentation - Official React docs
  • React Tutorial - Official React tutorial
  • React Blog - Official React blog
  • Create React App - Set up a modern web app by running one command
  • React DevTools - React Developer Tools extension
  • React Patterns - Common React patterns
  • Egghead.io React Courses - Video tutorials
  • React Enlightenment - React book
  • Web Development
  • JavaScript
  • React
  • Frontend Framework

show all >>

Browser APIs & DOM Manipulation

  2023-06-08
字数统计: 4.6k字   |   阅读时长: 29min

Browser APIs & DOM Manipulation

Modern web browsers provide a rich set of APIs (Application Programming Interfaces) that allow JavaScript to interact with the browser environment and manipulate web page content. This guide covers the DOM (Document Object Model) and various browser APIs that are essential for frontend development.

The Document Object Model (DOM)

The DOM is a programming interface for HTML documents. It represents the page as nodes and objects that JavaScript can interact with.

DOM Structure

The DOM represents an HTML document as a tree of nodes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
document
└── html
├── head
│ ├── title
│ ├── meta
│ └── link
└── body
├── header
│ └── nav
├── main
│ ├── h1
│ ├── p
│ └── div
└── footer

Selecting DOM Elements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// By ID (returns a single element)
const header = document.getElementById('header');

// By class name (returns a live HTMLCollection)
const paragraphs = document.getElementsByClassName('paragraph');

// By tag name (returns a live HTMLCollection)
const divs = document.getElementsByTagName('div');

// By CSS selector (returns the first match)
const mainSection = document.querySelector('main');

// By CSS selector (returns all matches as a static NodeList)
const buttons = document.querySelectorAll('.btn');

// Finding elements relative to another element
const firstListItem = document.querySelector('ul li');
const list = firstListItem.parentElement;
const nextItem = firstListItem.nextElementSibling;
const allItems = list.children;

Manipulating DOM Elements

Creating Elements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Create a new element
const newDiv = document.createElement('div');

// Add content to the element
newDiv.textContent = 'Hello, World!';
// or
newDiv.innerHTML = '<span>Hello</span>, World!';

// Add classes
newDiv.className = 'container highlight';
// or
newDiv.classList.add('container');
newDiv.classList.add('highlight');
newDiv.classList.remove('highlight');
newDiv.classList.toggle('active');
newDiv.classList.contains('container'); // true

// Set attributes
newDiv.id = 'main-container';
newDiv.setAttribute('data-test', 'value');
newDiv.getAttribute('id'); // 'main-container'
newDiv.hasAttribute('data-test'); // true
newDiv.removeAttribute('data-test');

// Set styles
newDiv.style.color = 'red';
newDiv.style.backgroundColor = 'white';
newDiv.style.cssText = 'color: red; background-color: white;';

Modifying the DOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Append elements
document.body.appendChild(newDiv);

// Insert before a specific element
const referenceElement = document.getElementById('reference');
document.body.insertBefore(newDiv, referenceElement);

// Modern insertion methods
parentElement.append(element1, element2); // Adds at the end, accepts multiple nodes
parentElement.prepend(element); // Adds at the beginning
referenceElement.before(element); // Adds before the reference element
referenceElement.after(element); // Adds after the reference element
referenceElement.replaceWith(element); // Replaces the reference element

// Remove elements
element.remove(); // Modern way
// or
element.parentElement.removeChild(element); // Old way, more compatible

Cloning Elements

1
2
3
// Clone an element (false = without children, true = with children)
const clone = element.cloneNode(false);
const deepClone = element.cloneNode(true);

DOM Properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Text content
element.textContent = 'New text'; // Sets text (safer, no HTML parsing)
element.innerText = 'New text'; // Similar, but respects CSS styling
element.innerHTML = '<b>Bold text</b>'; // Sets HTML content (careful with XSS)

// Element dimensions and position
const rect = element.getBoundingClientRect(); // Position relative to viewport
console.log(rect.width, rect.height, rect.top, rect.left);

element.offsetWidth; // Width including padding and border
element.offsetHeight; // Height including padding and border
element.clientWidth; // Width including padding but not border
element.clientHeight; // Height including padding but not border

// Scroll position
element.scrollTop; // Pixels scrolled vertically
element.scrollLeft; // Pixels scrolled horizontally
element.scrollTo(0, 0); // Scroll to specific coordinates
element.scrollIntoView(); // Scroll the element into view

DOM Events

DOM events allow JavaScript to register different event handlers on elements in an HTML document.

Event Handling

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Adding event listeners
element.addEventListener('click', function(event) {
console.log('Element clicked!', event);
});

// With arrow function
element.addEventListener('mouseover', (event) => {
console.log('Mouse over!');
});

// Removing event listeners (reference to the original function needed)
function handleClick(event) {
console.log('Clicked!');
}

element.addEventListener('click', handleClick);
element.removeEventListener('click', handleClick);

// Older methods (avoid using)
element.onclick = function() {
console.log('Clicked!');
};

Event Object Properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
element.addEventListener('click', function(event) {
// General event properties
console.log(event.type); // "click"
console.log(event.target); // Element that triggered the event
console.log(event.currentTarget); // Element the listener is attached to
console.log(event.timeStamp); // Time the event was created

// Mouse event properties
console.log(event.clientX, event.clientY); // Coordinates relative to viewport
console.log(event.pageX, event.pageY); // Coordinates relative to document
console.log(event.button); // Mouse button pressed

// Keyboard event properties
console.log(event.key); // Key value ("a", "Enter", etc.)
console.log(event.code); // Physical key code ("KeyA", "Enter", etc.)
console.log(event.ctrlKey, event.shiftKey, event.altKey); // Modifier keys
});

Event Propagation

Events in the DOM propagate in three phases:

  1. Capture Phase: From the root down to the target
  2. Target Phase: The event reaches the target
  3. Bubbling Phase: From the target back up to the root
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Capturing phase (third parameter true)
parent.addEventListener('click', function(event) {
console.log('Parent captured!');
}, true);

// Bubbling phase (default behavior)
child.addEventListener('click', function(event) {
console.log('Child bubbled!');

// Stop propagation to parent elements
event.stopPropagation();

// Prevent default browser behavior
event.preventDefault();
});

// Event delegation (handling events for multiple elements)
document.getElementById('todo-list').addEventListener('click', function(event) {
// Check if clicked element is a list item
if (event.target.tagName === 'LI') {
event.target.classList.toggle('completed');
}
});

Common DOM Events

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Mouse events
element.addEventListener('click', handler); // Single click
element.addEventListener('dblclick', handler); // Double click
element.addEventListener('mouseover', handler); // Mouse enters element
element.addEventListener('mouseout', handler); // Mouse leaves element
element.addEventListener('mousedown', handler); // Mouse button pressed
element.addEventListener('mouseup', handler); // Mouse button released
element.addEventListener('mousemove', handler); // Mouse moved

// Keyboard events
element.addEventListener('keydown', handler); // Key pressed
element.addEventListener('keyup', handler); // Key released
element.addEventListener('keypress', handler); // Key pressed (character keys)

// Form events
form.addEventListener('submit', handler); // Form submitted
input.addEventListener('focus', handler); // Element received focus
input.addEventListener('blur', handler); // Element lost focus
input.addEventListener('change', handler); // Value changed (after blur)
input.addEventListener('input', handler); // Value changed (immediately)

// Document/Window events
window.addEventListener('load', handler); // Page fully loaded
document.addEventListener('DOMContentLoaded', handler); // DOM fully loaded
window.addEventListener('resize', handler); // Window resized
window.addEventListener('scroll', handler); // Window scrolled

Custom Events

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Creating a custom event
const customEvent = new CustomEvent('userLoggedIn', {
detail: { userId: 123, username: 'john_doe' },
bubbles: true,
cancelable: true
});

// Dispatching the event
document.dispatchEvent(customEvent);

// Listening for the custom event
document.addEventListener('userLoggedIn', function(event) {
console.log('User logged in:', event.detail.username);
});

Browser Storage APIs

Browsers provide several ways to store data on the client side.

Local Storage

Data persists until explicitly cleared. Available across browser sessions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Store data
localStorage.setItem('username', 'john_doe');
localStorage.setItem('preferences', JSON.stringify({ theme: 'dark', fontSize: 16 }));

// Retrieve data
const username = localStorage.getItem('username');
const preferences = JSON.parse(localStorage.getItem('preferences'));

// Remove specific item
localStorage.removeItem('username');

// Clear all data
localStorage.clear();

// Storage event (fires when storage changes in another tab/window)
window.addEventListener('storage', function(event) {
console.log('Storage changed:', event.key, event.oldValue, event.newValue);
});

Session Storage

Similar to localStorage, but data is cleared when the session ends (window/tab closed).

1
2
3
4
5
// Same API as localStorage
sessionStorage.setItem('sessionId', 'abc123');
const sessionId = sessionStorage.getItem('sessionId');
sessionStorage.removeItem('sessionId');
sessionStorage.clear();

Cookies

Traditional way to store small amounts of data. Sent with HTTP requests.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Set a cookie
document.cookie = "username=john_doe; expires=Fri, 31 Dec 2023 23:59:59 GMT; path=/; Secure; SameSite=Strict";

// Read all cookies
const allCookies = document.cookie; // "username=john_doe; sessionId=abc123"

// Parse cookies
function getCookie(name) {
const cookies = document.cookie.split('; ');
for (const cookie of cookies) {
const [cookieName, cookieValue] = cookie.split('=');
if (cookieName === name) {
return cookieValue;
}
}
return null;
}

// Delete a cookie (set expiration in the past)
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/;";

IndexedDB

A more powerful, asynchronous client-side storage API for larger amounts of structured data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Open a database
const request = indexedDB.open('MyDatabase', 1);

// Create schema (called when DB is created or version changes)
request.onupgradeneeded = function(event) {
const db = event.target.result;
const store = db.createObjectStore('users', { keyPath: 'id' });
store.createIndex('by_name', 'name', { unique: false });
};

// On success
request.onsuccess = function(event) {
const db = event.target.result;

// Start a transaction
const transaction = db.transaction(['users'], 'readwrite');
const store = transaction.objectStore('users');

// Add data
store.add({ id: 1, name: 'John', email: 'john@example.com' });

// Get data
const getRequest = store.get(1);
getRequest.onsuccess = function() {
console.log('User:', getRequest.result);
};

// Transaction complete
transaction.oncomplete = function() {
console.log('Transaction completed');
db.close();
};
};

// On error
request.onerror = function(event) {
console.error('Database error:', event.target.error);
};

Network Requests

XMLHttpRequest (Legacy)

The older way to make HTTP requests, still supported by all browsers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const xhr = new XMLHttpRequest();

xhr.open('GET', 'https://api.example.com/data', true);

xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
const data = JSON.parse(xhr.responseText);
console.log('Data:', data);
} else {
console.error('Request failed with status:', xhr.status);
}
};

xhr.onerror = function() {
console.error('Request failed');
};

xhr.send();

// POST request with data
const postXhr = new XMLHttpRequest();
postXhr.open('POST', 'https://api.example.com/submit', true);
postXhr.setRequestHeader('Content-Type', 'application/json');
postXhr.send(JSON.stringify({ name: 'John', email: 'john@example.com' }));

Fetch API (Modern)

The modern way to make HTTP requests, based on Promises.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// Basic GET request
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json(); // Parse as JSON
})
.then(data => {
console.log('Data:', data);
})
.catch(error => {
console.error('Fetch error:', error);
});

// POST request with options
fetch('https://api.example.com/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
},
body: JSON.stringify({ name: 'John', email: 'john@example.com' })
})
.then(response => response.json())
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));

// Using async/await
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
console.log('Data:', data);
return data;
} catch (error) {
console.error('Fetch error:', error);
throw error;
}
}

// Fetch with abort controller
const controller = new AbortController();
const signal = controller.signal;

fetch('https://api.example.com/data', { signal })
.then(response => response.json())
.then(data => console.log('Data:', data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
});

// Abort the fetch after 5 seconds
setTimeout(() => controller.abort(), 5000);

Geolocation API

Access the user’s geographical location.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Check if geolocation is supported
if ('geolocation' in navigator) {
// Get current position
navigator.geolocation.getCurrentPosition(
// Success callback
function(position) {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
console.log(`Location: ${latitude}, ${longitude}`);

// Other available data
console.log('Accuracy:', position.coords.accuracy, 'meters');
console.log('Altitude:', position.coords.altitude);
console.log('Timestamp:', position.timestamp);
},
// Error callback
function(error) {
switch(error.code) {
case error.PERMISSION_DENIED:
console.error('User denied geolocation permission');
break;
case error.POSITION_UNAVAILABLE:
console.error('Location information unavailable');
break;
case error.TIMEOUT:
console.error('Location request timed out');
break;
default:
console.error('Unknown error:', error.message);
}
},
// Options
{
enableHighAccuracy: true, // More accurate position
timeout: 5000, // Time to wait for response in ms
maximumAge: 0 // Don't use cached position
}
);

// Watch position (gets updates when user moves)
const watchId = navigator.geolocation.watchPosition(
position => console.log(`Updated location: ${position.coords.latitude}, ${position.coords.longitude}`),
error => console.error('Error watching position:', error),
{ enableHighAccuracy: true }
);

// Stop watching
// navigator.geolocation.clearWatch(watchId);
} else {
console.log('Geolocation is not supported by this browser');
}

History API

Manipulate the browser history.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Navigate back
history.back();

// Navigate forward
history.forward();

// Go to a specific point in history
history.go(-2); // Back 2 pages
history.go(1); // Forward 1 page

// Add a new entry to the browser history
// This updates the URL without reloading the page
history.pushState({ pageId: 123 }, 'Page Title', '/new-url');

// Replace the current history entry
history.replaceState({ pageId: 124 }, 'New Title', '/updated-url');

// Listen for navigation events (back/forward buttons)
window.addEventListener('popstate', function(event) {
console.log('Navigation occurred:', event.state);
});

Web Storage API

Web Storage (localStorage and sessionStorage) is covered in the Browser Storage section above.

Canvas API

Create and manipulate graphics and animations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Get canvas element
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

// Set canvas dimensions
canvas.width = 500;
canvas.height = 300;

// Basic drawing
ctx.fillStyle = 'blue'; // Set fill color
ctx.fillRect(10, 10, 100, 50); // Draw filled rectangle

ctx.strokeStyle = 'red'; // Set stroke color
ctx.lineWidth = 5; // Set line width
ctx.strokeRect(150, 10, 100, 50); // Draw rectangle outline

// Draw a line
ctx.beginPath();
ctx.moveTo(10, 100);
ctx.lineTo(200, 150);
ctx.stroke();

// Draw a circle
ctx.beginPath();
ctx.arc(300, 100, 30, 0, Math.PI * 2); // x, y, radius, startAngle, endAngle
ctx.fill();

// Draw text
ctx.font = '20px Arial';
ctx.fillStyle = 'black';
ctx.fillText('Hello, Canvas!', 10, 200);

// Draw image
const img = new Image();
img.onload = function() {
ctx.drawImage(img, 350, 150, 100, 100); // image, x, y, width, height
};
img.src = 'image.jpg';

// Animation
let x = 0;
function animate() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);

// Draw a moving rectangle
ctx.fillRect(x, 50, 50, 50);

// Update position
x = (x + 2) % canvas.width;

// Request next frame
requestAnimationFrame(animate);
}
animate();

Web Audio API

Process and synthesize audio.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Create audio context
const AudioContext = window.AudioContext || window.webkitAudioContext;
const audioCtx = new AudioContext();

// Play a simple tone
function playTone(frequency = 440, duration = 1) {
// Create oscillator
const oscillator = audioCtx.createOscillator();
oscillator.type = 'sine'; // sine, square, sawtooth, triangle
oscillator.frequency.value = frequency; // frequency in Hz

// Create gain node (for volume control)
const gainNode = audioCtx.createGain();
gainNode.gain.value = 0.5; // 50% volume

// Connect the nodes
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);

// Start and stop
oscillator.start();
oscillator.stop(audioCtx.currentTime + duration);
}

// Play a 440Hz tone for 1 second when clicked
document.getElementById('playButton').addEventListener('click', () => {
playTone(440, 1);
});

// Load and play an audio file
async function playAudio(url) {
// Fetch the file
const response = await fetch(url);
const arrayBuffer = await response.arrayBuffer();

// Decode the audio
const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer);

// Create buffer source
const source = audioCtx.createBufferSource();
source.buffer = audioBuffer;

// Connect to destination
source.connect(audioCtx.destination);

// Play
source.start();
}

// Play audio file when clicked
document.getElementById('playSound').addEventListener('click', () => {
playAudio('sound.mp3');
});

WebRTC

Enables real-time communication in the browser.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// Basic WebRTC peer connection setup
const configuration = { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] };
const peerConnection = new RTCPeerConnection(configuration);

// Handle ICE candidates
peerConnection.onicecandidate = event => {
if (event.candidate) {
// Send the candidate to the remote peer via your signaling server
sendToSignalingServer({ type: 'ice-candidate', candidate: event.candidate });
}
};

// Handle connection state changes
peerConnection.onconnectionstatechange = event => {
console.log('Connection state:', peerConnection.connectionState);
};

// Handle receiving streams
peerConnection.ontrack = event => {
const remoteVideo = document.getElementById('remoteVideo');
if (remoteVideo.srcObject !== event.streams[0]) {
remoteVideo.srcObject = event.streams[0];
}
};

// Get local media
async function startVideoCall() {
try {
// Get local stream
const localStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});

// Display local video
const localVideo = document.getElementById('localVideo');
localVideo.srcObject = localStream;

// Add tracks to the peer connection
localStream.getTracks().forEach(track => {
peerConnection.addTrack(track, localStream);
});

// Create and send offer (if initiator)
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);

// Send the offer to the remote peer via your signaling server
sendToSignalingServer({ type: 'offer', offer });

} catch (error) {
console.error('Error starting video call:', error);
}
}

// Function to handle received offers, answers, and ICE candidates
// (You would implement this to work with your signaling server)
function handleSignalingMessage(message) {
switch(message.type) {
case 'offer':
peerConnection.setRemoteDescription(new RTCSessionDescription(message.offer));
peerConnection.createAnswer()
.then(answer => peerConnection.setLocalDescription(answer))
.then(() => sendToSignalingServer({
type: 'answer',
answer: peerConnection.localDescription
}));
break;
case 'answer':
peerConnection.setRemoteDescription(new RTCSessionDescription(message.answer));
break;
case 'ice-candidate':
peerConnection.addIceCandidate(new RTCIceCandidate(message.candidate));
break;
}
}

Web Workers

Run JavaScript in background threads.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Create a worker
const worker = new Worker('worker.js');

// Send message to worker
worker.postMessage({ action: 'calculate', data: [1, 2, 3, 4, 5] });

// Receive message from worker
worker.onmessage = function(event) {
console.log('Result from worker:', event.data);
};

// Handle errors
worker.onerror = function(error) {
console.error('Worker error:', error.message);
};

// Terminate worker when done
function terminateWorker() {
worker.terminate();
}

// Content of worker.js:
/*
// Listen for messages
self.onmessage = function(event) {
const { action, data } = event.data;

if (action === 'calculate') {
// Perform calculation
const result = data.reduce((sum, num) => sum + num, 0);

// Send result back
self.postMessage(result);
}
};

// Handle errors
self.onerror = function(error) {
console.error('Worker internal error:', error.message);
};
*/

Service Workers

A type of web worker that acts as a proxy server between web applications, the browser, and the network.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// Register a service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
.then(registration => {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch(error => {
console.error('Service Worker registration failed:', error);
});
}

// Check if a service worker is active
navigator.serviceWorker.ready
.then(registration => {
console.log('Service Worker is active');
});

// Send a message to the service worker
navigator.serviceWorker.controller.postMessage({
type: 'CACHE_NEW_ROUTE',
payload: '/new-route'
});

// Listen for messages from the service worker
navigator.serviceWorker.addEventListener('message', event => {
console.log('Message from Service Worker:', event.data);
});

// Content of service-worker.js:
/*
// Service Worker installation
self.addEventListener('install', event => {
// Perform install steps (like caching resources)
event.waitUntil(
caches.open('v1')
.then(cache => {
return cache.addAll([
'/',
'/index.html',
'/styles.css',
'/script.js',
'/image.jpg'
]);
})
);
});

// Service Worker activation (cleanup old caches)
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.filter(cacheName => {
return cacheName !== 'v1';
}).map(cacheName => {
return caches.delete(cacheName);
})
);
})
);
});

// Intercept fetch requests
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// Cache hit - return response
if (response) {
return response;
}

// Clone the request
const fetchRequest = event.request.clone();

return fetch(fetchRequest).then(response => {
// Check if valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}

// Clone the response
const responseToCache = response.clone();

caches.open('v1')
.then(cache => {
cache.put(event.request, responseToCache);
});

return response;
});
})
);
});

// Handle messages from the main thread
self.addEventListener('message', event => {
console.log('Message received in SW:', event.data);
// Respond to the message
event.source.postMessage('Message received!');
});
*/

Intersection Observer

Detect when an element is visible in the viewport.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Create the observer
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('Element is visible:', entry.target);

// Load images when they become visible
if (entry.target.dataset.src) {
entry.target.src = entry.target.dataset.src;
entry.target.removeAttribute('data-src');
// Stop observing the element after loading
observer.unobserve(entry.target);
}
}
});
}, {
root: null, // Use the viewport as root
rootMargin: '0px', // No margin
threshold: 0.1 // 10% of the element must be visible
});

// Observe multiple elements
document.querySelectorAll('.lazy-load-image').forEach(image => {
observer.observe(image);
});

// Lazy loading example:
/*
<img class="lazy-load-image" src="placeholder.jpg" data-src="actual-image.jpg" alt="Lazy loaded image">
*/

Mutation Observer

Watch for changes in the DOM.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Create a mutation observer
const observer = new MutationObserver((mutations, observer) => {
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
console.log('Child nodes added or removed:', mutation.addedNodes, mutation.removedNodes);
} else if (mutation.type === 'attributes') {
console.log(`Attribute '${mutation.attributeName}' changed:`,
mutation.target.getAttribute(mutation.attributeName));
}
});
});

// Start observing an element
const targetElement = document.getElementById('observed-element');
observer.observe(targetElement, {
childList: true, // Watch for changes to child elements
attributes: true, // Watch for attribute changes
characterData: true, // Watch for text content changes
subtree: true, // Also watch all descendants
attributeOldValue: true, // Record old attribute values
characterDataOldValue: true // Record old text content
});

// Stop observing
function stopObserving() {
observer.disconnect();
}

Web Animations API

Create and control animations in JavaScript.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Get element to animate
const element = document.getElementById('animated-element');

// Create animation
const animation = element.animate([
// Keyframes
{ transform: 'translateX(0px)', opacity: 1 },
{ transform: 'translateX(100px)', opacity: 0.5, offset: 0.8 }, // 80% mark
{ transform: 'translateX(200px)', opacity: 0 }
], {
// Timing options
duration: 2000, // 2 seconds
easing: 'ease-in-out',
delay: 500, // 0.5 second delay
iterations: 3, // Play 3 times
direction: 'alternate', // Alternate direction on each iteration
fill: 'forwards' // Keep final state
});

// Animation controls
animation.pause(); // Pause
animation.play(); // Resume
animation.reverse(); // Play in reverse
animation.finish(); // Jump to end
animation.cancel(); // Stop and remove effects

// Animation events
animation.onfinish = () => {
console.log('Animation finished');
};

animation.oncancel = () => {
console.log('Animation cancelled');
};

// Get animation state
console.log('Current time:', animation.currentTime);
console.log('Playback rate:', animation.playbackRate);

// Change playback speed
animation.playbackRate = 2; // Double speed

Drag and Drop API

Enable drag and drop functionality.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// Make an element draggable
const draggableElement = document.getElementById('draggable');
draggableElement.draggable = true;

// Drag events on the draggable element
draggableElement.addEventListener('dragstart', event => {
console.log('Drag started');

// Set data to be dragged
event.dataTransfer.setData('text/plain', draggableElement.id);

// Set drag image (optional)
const dragImage = new Image();
dragImage.src = 'dragicon.png';
event.dataTransfer.setDragImage(dragImage, 10, 10);

// Set drag effect
event.dataTransfer.effectAllowed = 'move'; // 'copy', 'move', 'link', 'copyMove', etc.
});

draggableElement.addEventListener('dragend', event => {
console.log('Drag ended');
});

// Set up drop target
const dropTarget = document.getElementById('drop-target');

// Prevent default to allow drop
dropTarget.addEventListener('dragover', event => {
event.preventDefault();
// Change appearance to indicate valid drop target
dropTarget.classList.add('drag-over');
});

// Handle when drag enters/leaves target
dropTarget.addEventListener('dragenter', event => {
event.preventDefault();
dropTarget.classList.add('drag-over');
});

dropTarget.addEventListener('dragleave', () => {
dropTarget.classList.remove('drag-over');
});

// Handle the drop
dropTarget.addEventListener('drop', event => {
event.preventDefault();
dropTarget.classList.remove('drag-over');

// Get the dragged data
const id = event.dataTransfer.getData('text/plain');
const draggedElement = document.getElementById(id);

// Append the element to the drop target
dropTarget.appendChild(draggedElement);

console.log('Element dropped');
});

File API

Working with files from the user’s filesystem.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// File input element
const fileInput = document.getElementById('file-input');

// Handle file selection
fileInput.addEventListener('change', event => {
const files = event.target.files;

for (const file of files) {
console.log('File name:', file.name);
console.log('File type:', file.type);
console.log('File size:', file.size, 'bytes');
console.log('Last modified:', new Date(file.lastModified));

// Read file contents
readFile(file);
}
});

// Read file as text
function readFile(file) {
const reader = new FileReader();

reader.onload = event => {
const content = event.target.result;
console.log('File content:', content);
};

reader.onerror = error => {
console.error('File reading error:', error);
};

if (file.type.startsWith('text/')) {
reader.readAsText(file); // Read as text
} else if (file.type.startsWith('image/')) {
reader.readAsDataURL(file); // Read as data URL for images

// Display the image
reader.onload = event => {
const imageElement = document.createElement('img');
imageElement.src = event.target.result;
document.body.appendChild(imageElement);
};
} else {
reader.readAsArrayBuffer(file); // Read as ArrayBuffer for binary files
}
}

// Drag and drop files
const dropZone = document.getElementById('drop-zone');

dropZone.addEventListener('dragover', event => {
event.preventDefault();
dropZone.classList.add('drag-over');
});

dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('drag-over');
});

dropZone.addEventListener('drop', event => {
event.preventDefault();
dropZone.classList.remove('drag-over');

const files = event.dataTransfer.files;
console.log('Files dropped:', files.length);

for (const file of files) {
readFile(file);
}
});

Broadcast Channel API

Communicate between browsing contexts (tabs, windows, iframes).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Create a channel
const channel = new BroadcastChannel('app_channel');

// Send a message to all other contexts
function sendMessage(message) {
channel.postMessage({
type: 'notification',
text: message,
timestamp: Date.now()
});
}

// Listen for messages
channel.onmessage = event => {
console.log('Message received:', event.data);
displayNotification(event.data);
};

// Handle errors
channel.onmessageerror = error => {
console.error('Message error:', error);
};

// Close the channel when done
function closeChannel() {
channel.close();
}

// Example usage
document.getElementById('send-button').addEventListener('click', () => {
const message = document.getElementById('message-input').value;
sendMessage(message);
});

// Display notification
function displayNotification(data) {
const notification = document.createElement('div');
notification.className = 'notification';
notification.textContent = `${data.text} (${new Date(data.timestamp).toLocaleTimeString()})`;
document.body.appendChild(notification);

// Remove after a few seconds
setTimeout(() => {
notification.remove();
}, 5000);
}

Learning Resources

  • MDN Web Docs - Web APIs
  • MDN Web Docs - Document Object Model (DOM)
  • JavaScript.info - Browser: Document, Events, Interfaces
  • Google Developers - Web Fundamentals
  • Can I Use - Browser compatibility tables
  • DOM Enlightenment - Free online book
  • Web Development
  • JavaScript
  • DOM
  • Browser APIs

show all >>

ES6+ Features

  2023-06-07
字数统计: 3.4k字   |   阅读时长: 21min

ES6+ Features

ES6 (ECMAScript 2015) introduced significant enhancements to JavaScript, and subsequent versions have continued to add powerful features. This guide explores the key features from ES6 and beyond that have transformed modern JavaScript development.

Overview of ECMAScript Evolution

  • ES6/ES2015: Major update with many new features
  • ES7/ES2016: Smaller update (Array.prototype.includes, Exponentiation Operator)
  • ES8/ES2017: Async/await, Object methods
  • ES9/ES2018: Rest/spread for objects, Promise.finally, async iteration
  • ES10/ES2019: Array.prototype.flat, Object.fromEntries
  • ES11/ES2020: Optional chaining, Nullish coalescing, BigInt
  • ES12/ES2021: String.prototype.replaceAll, Promise.any, Logical assignment
  • ES13/ES2022: Top-level await, Class fields, Error cause

ES6 (ES2015) Features

Let and Const Declarations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// var - function-scoped, can be redeclared
var x = 1;
var x = 2; // Valid

// let - block-scoped, can be reassigned
let y = 1;
y = 2; // Valid
// let y = 2; // Error: y has already been declared

// const - block-scoped, cannot be reassigned
const z = 1;
// z = 2; // Error: Assignment to constant variable

// Block scoping
if (true) {
let blockScoped = 'only available in this block';
const alsoBlockScoped = 'same here';
}
// console.log(blockScoped); // Error: blockScoped is not defined

// const with objects/arrays
const person = { name: 'John' };
person.name = 'Jane'; // Valid - the object properties can be changed
// person = {}; // Error - reassigning the binding is not allowed

const numbers = [1, 2, 3];
numbers.push(4); // Valid - you can modify array contents
// numbers = []; // Error - reassigning the binding is not allowed

Arrow Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Traditional function expression
const add = function(a, b) {
return a + b;
};

// Arrow function
const addArrow = (a, b) => a + b;

// Arrow function with single parameter (parentheses optional)
const square = x => x * x;

// Arrow function with multiple statements
const calculate = (a, b) => {
const result = a * b;
return result + 10;
};

// Empty parameter list requires parentheses
const getRandomNumber = () => Math.random();

// Arrow functions do not have their own this
const object = {
data: [1, 2, 3],
processData() {
// Arrow function inherits this from outer scope
this.data.forEach(item => {
console.log(item, this.data); // this refers to object
});
}
};

Template Literals

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const name = 'Alice';
const age = 28;

// String concatenation (old way)
const message1 = 'My name is ' + name + ' and I am ' + age + ' years old.';

// Template literals
const message2 = `My name is ${name} and I am ${age} years old.`;

// Multi-line strings
const multiLine = `This is a string
that spans multiple
lines without escape characters.`;

// Tagged templates
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i] || '';
return `${result}${str}<span class="highlight">${value}</span>`;
}, '');
}

const username = 'Admin';
const highlightedText = highlight`Hello ${username}, welcome back!`;
// "<span class="highlight">Hello </span>Admin<span class="highlight">, welcome back!</span>"

Destructuring Assignment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Object destructuring
const person = {
firstName: 'John',
lastName: 'Doe',
age: 30,
address: {
city: 'New York',
country: 'USA'
}
};

// Basic destructuring
const { firstName, lastName } = person;
console.log(firstName, lastName); // "John" "Doe"

// Destructuring with new variable names
const { firstName: first, lastName: last } = person;
console.log(first, last); // "John" "Doe"

// Nested destructuring
const { address: { city, country } } = person;
console.log(city, country); // "New York" "USA"

// Default values
const { zipCode = '10001' } = person;
console.log(zipCode); // "10001" (default value since it doesn't exist)

// Rest pattern
const { firstName: fName, ...rest } = person;
console.log(rest); // { lastName: "Doe", age: 30, address: { city: "New York", country: "USA" } }

// Array destructuring
const numbers = [1, 2, 3, 4, 5];

// Basic array destructuring
const [first, second, ...remaining] = numbers;
console.log(first, second, remaining); // 1 2 [3, 4, 5]

// Skipping elements
const [a, , c] = numbers;
console.log(a, c); // 1 3

// Default values
const [x, y, z = 10] = [1, 2];
console.log(x, y, z); // 1 2 10

// Swapping variables
let a1 = 'first';
let b1 = 'second';
[a1, b1] = [b1, a1];
console.log(a1, b1); // "second" "first"

// Function parameter destructuring
function printPersonInfo({ firstName, lastName, age = 'unknown' }) {
console.log(`${firstName} ${lastName}, ${age} years old`);
}
printPersonInfo(person); // "John Doe, 30 years old"

Default Parameters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Basic default parameters
function greet(name = 'Guest') {
return `Hello, ${name}!`;
}
console.log(greet()); // "Hello, Guest!"
console.log(greet('Alice')); // "Hello, Alice!"

// Default parameters with expressions
function calculateTax(amount, taxRate = amount * 0.1) {
return amount + taxRate;
}
console.log(calculateTax(100)); // 110
console.log(calculateTax(100, 20)); // 120

// Default parameters based on other parameters
function createUser(name, role = 'user', id = `${name}-${role}-${Date.now()}`) {
return { name, role, id };
}
console.log(createUser('john')); // { name: 'john', role: 'user', id: 'john-user-1623456789' }

Rest and Spread Operators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Rest operator in function parameters
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15

// Rest with other parameters
function multiply(multiplier, ...numbers) {
return numbers.map(num => num * multiplier);
}
console.log(multiply(2, 1, 2, 3)); // [2, 4, 6]

// Spread operator with arrays
const array1 = [1, 2, 3];
const array2 = [4, 5, 6];
const combined = [...array1, ...array2];
console.log(combined); // [1, 2, 3, 4, 5, 6]

// Spread to copy arrays
const original = [1, 2, 3];
const copy = [...original];
original.push(4);
console.log(original); // [1, 2, 3, 4]
console.log(copy); // [1, 2, 3]

// Spread with objects (ES9+)
const defaults = { theme: 'light', fontSize: 16 };
const userPrefs = { fontSize: 20, showSidebar: true };
const mergedPrefs = { ...defaults, ...userPrefs };
console.log(mergedPrefs); // { theme: 'light', fontSize: 20, showSidebar: true }

Enhanced Object Literals

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Property shorthand
const name = 'John';
const age = 30;
const person = { name, age };
console.log(person); // { name: 'John', age: 30 }

// Method shorthand
const calculator = {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
}
};
console.log(calculator.add(5, 3)); // 8

// Computed property names
const propertyName = 'status';
const value = 'active';
const user = {
[propertyName]: value,
[`user_${Date.now()}`]: true
};
console.log(user); // { status: 'active', user_1623456789: true }

Classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Class declaration
class Person {
// Constructor
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

// Method
getFullName() {
return `${this.firstName} ${this.lastName}`;
}

// Static method
static createAnonymous() {
return new Person('Anonymous', 'User');
}
}

const person1 = new Person('John', 'Doe');
console.log(person1.getFullName()); // "John Doe"

const anonymous = Person.createAnonymous();
console.log(anonymous.getFullName()); // "Anonymous User"

// Inheritance
class Employee extends Person {
constructor(firstName, lastName, position) {
// Call parent constructor
super(firstName, lastName);
this.position = position;
}

// Override parent method
getFullName() {
return `${super.getFullName()} (${this.position})`;
}
}

const employee = new Employee('Jane', 'Smith', 'Developer');
console.log(employee.getFullName()); // "Jane Smith (Developer)"

Modules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Exporting (in math.js)
export const PI = 3.14159;

export function add(a, b) {
return a + b;
}

export function subtract(a, b) {
return a - b;
}

const privateFunction = () => {
console.log('This is private');
};

export default class Calculator {
multiply(a, b) {
return a * b;
}
}

// Importing (in app.js)
import Calculator, { PI, add } from './math.js';
import * as math from './math.js';

console.log(PI); // 3.14159
console.log(add(2, 3)); // 5

const calc = new Calculator();
console.log(calc.multiply(4, 5)); // 20

console.log(math.subtract(10, 4)); // 6

Promises

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Creating a promise
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { id: 1, name: 'Product' };
const success = true;

if (success) {
resolve(data);
} else {
reject(new Error('Failed to fetch data'));
}
}, 1000);
});
};

// Using a promise
fetchData()
.then(data => {
console.log('Success:', data);
return { ...data, processed: true };
})
.then(processedData => {
console.log('Processed:', processedData);
})
.catch(error => {
console.error('Error:', error.message);
})
.finally(() => {
console.log('Operation completed');
});

// Promise.all - wait for all promises to resolve
Promise.all([
fetchData(),
fetchData() // Another promise
])
.then(results => {
console.log('All results:', results);
})
.catch(error => {
console.error('At least one promise failed:', error);
});

// Promise.race - resolve when first promise resolves/rejects
Promise.race([
fetchData(),
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 2000))
])
.then(result => {
console.log('First to complete:', result);
})
.catch(error => {
console.error('First to fail:', error);
});

Iterators and Generators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Iterators
const array = [1, 2, 3];
const iterator = array[Symbol.iterator]();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

// Custom iterable
const customIterable = {
data: [10, 20, 30],

[Symbol.iterator]() {
let index = 0;
const data = this.data;

return {
next() {
if (index < data.length) {
return { value: data[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};

for (const item of customIterable) {
console.log(item); // 10, 20, 30
}

// Generators
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}

const numGen = numberGenerator();
console.log(numGen.next()); // { value: 1, done: false }
console.log(numGen.next()); // { value: 2, done: false }
console.log(numGen.next()); // { value: 3, done: false }
console.log(numGen.next()); // { value: undefined, done: true }

// Generator with data received via next()
function* conversation() {
const name = yield 'What is your name?';
const hobby = yield `Hello, ${name}! What is your hobby?`;
yield `${name} likes ${hobby}. That's great!`;
}

const convo = conversation();
console.log(convo.next().value); // "What is your name?"
console.log(convo.next('John').value); // "Hello, John! What is your hobby?"
console.log(convo.next('coding').value); // "John likes coding. That's great!"

Map and Set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// Map - key-value pairs (any type of keys)
const userMap = new Map();

// Adding entries
userMap.set('name', 'John');
userMap.set(42, 'answer');
userMap.set(true, 'boolean key');

// Object as key
const userObj = { id: 1 };
userMap.set(userObj, 'object as key');

// Getting values
console.log(userMap.get('name')); // "John"
console.log(userMap.get(userObj)); // "object as key"

// Checking if key exists
console.log(userMap.has(true)); // true
console.log(userMap.has('missing')); // false

// Size and deletion
console.log(userMap.size); // 4
userMap.delete(42);
console.log(userMap.size); // 3

// Iteration
for (const [key, value] of userMap) {
console.log(key, value);
}

// Set - collection of unique values
const uniqueNumbers = new Set([1, 2, 3, 3, 4, 4, 5]);
console.log(uniqueNumbers); // Set(5) { 1, 2, 3, 4, 5 }

// Adding values
uniqueNumbers.add(6);
uniqueNumbers.add(1); // Ignored as 1 already exists

// Checking if value exists
console.log(uniqueNumbers.has(3)); // true

// Size and deletion
console.log(uniqueNumbers.size); // 6
uniqueNumbers.delete(4);
console.log(uniqueNumbers.size); // 5

// Iteration
for (const num of uniqueNumbers) {
console.log(num);
}

// Converting Set to Array
const uniqueArray = [...uniqueNumbers];
console.log(uniqueArray); // [1, 2, 3, 5, 6]

// Using Set to remove duplicates from array
const numbers = [1, 2, 2, 3, 4, 4, 5];
const unique = [...new Set(numbers)];
console.log(unique); // [1, 2, 3, 4, 5]

Symbol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Creating symbols
const sym1 = Symbol();
const sym2 = Symbol('description');
const sym3 = Symbol('description'); // sym2 !== sym3

// Symbols as object keys
const obj = {
[sym1]: 'value for sym1',
regularProp: 'regular value'
};

console.log(obj[sym1]); // "value for sym1"

// Symbols are not enumerable with for...in
for (const key in obj) {
console.log(key); // Only "regularProp"
}

// Getting symbols
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol()]

// Well-known symbols
class CustomIterable {
constructor(items) {
this.items = items;
}

// Implement iteration protocol
[Symbol.iterator]() {
let index = 0;
const items = this.items;

return {
next() {
if (index < items.length) {
return { value: items[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
}

const customList = new CustomIterable(['a', 'b', 'c']);
for (const item of customList) {
console.log(item); // "a", "b", "c"
}

Post-ES6 Features

Async/Await (ES2017)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Async function declaration
async function fetchUser(id) {
try {
const response = await fetch(`https://api.example.com/users/${id}`);
if (!response.ok) {
throw new Error('Failed to fetch user');
}
const user = await response.json();
return user;
} catch (error) {
console.error('Error fetching user:', error);
throw error;
}
}

// Using an async function
fetchUser(1)
.then(user => console.log('User:', user))
.catch(error => console.error('Failed:', error));

// Async arrow functions
const fetchProduct = async (id) => {
const response = await fetch(`https://api.example.com/products/${id}`);
return response.json();
};

// Async with array methods
const fetchMultipleUsers = async (ids) => {
// Sequential execution
const users = [];
for (const id of ids) {
const user = await fetchUser(id);
users.push(user);
}
return users;

// Parallel execution
// return Promise.all(ids.map(id => fetchUser(id)));
};

// Async with class methods
class UserAPI {
async getUser(id) {
// Implementation...
}

static async getAllUsers() {
// Implementation...
}
}

Object Methods (ES2017)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const person = {
name: 'John',
age: 30,
city: 'New York'
};

// Object.values
console.log(Object.values(person)); // ["John", 30, "New York"]

// Object.entries
console.log(Object.entries(person));
// [["name", "John"], ["age", 30], ["city", "New York"]]

// Using Object.entries with destructuring
for (const [key, value] of Object.entries(person)) {
console.log(`${key}: ${value}`);
}

// Object.fromEntries (ES2019)
const entries = [
['name', 'Alice'],
['age', 25],
['city', 'London']
];

const personObj = Object.fromEntries(entries);
console.log(personObj); // { name: "Alice", age: 25, city: "London" }

// Object.fromEntries with Map
const map = new Map([
['name', 'Bob'],
['age', 40]
]);

const obj = Object.fromEntries(map);
console.log(obj); // { name: "Bob", age: 40 }

Rest/Spread for Objects (ES2018)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Rest with object destructuring
const { name, ...rest } = { name: 'John', age: 30, city: 'New York' };
console.log(name); // "John"
console.log(rest); // { age: 30, city: "New York" }

// Spread with objects
const defaults = { theme: 'light', fontSize: 16 };
const userSettings = { fontSize: 20, showSidebar: true };

// Merging objects (later properties override earlier ones)
const settings = { ...defaults, ...userSettings };
console.log(settings);
// { theme: "light", fontSize: 20, showSidebar: true }

// Conditional spread
const hasPermission = true;
const userWithPerms = {
name: 'Jane',
...(hasPermission && { role: 'admin' })
};
console.log(userWithPerms); // { name: "Jane", role: "admin" }

Optional Chaining (ES2020)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const user = {
name: 'John',
address: {
street: '123 Main St',
city: 'New York'
},
getFullName() {
return `${this.name} Doe`;
}
};

// Without optional chaining
let cityOld;
if (user && user.address && user.address.city) {
cityOld = user.address.city;
}

// With optional chaining
const city = user?.address?.city;
console.log(city); // "New York"

const zipCode = user?.address?.zipCode;
console.log(zipCode); // undefined

// Optional chaining with function calls
const fullName = user?.getFullName?.();
console.log(fullName); // "John Doe"

// Optional chaining with array elements
const users = [{ name: 'John' }];
console.log(users?.[0]?.name); // "John"
console.log(users?.[1]?.name); // undefined

Nullish Coalescing (ES2020)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Logical OR - returns right side when left is falsy
// Falsy values: false, 0, "", null, undefined, NaN
const name1 = "" || "Default Name";
console.log(name1); // "Default Name" (problem if empty string is valid)

const count1 = 0 || 10;
console.log(count1); // 10 (problem if 0 is valid)

// Nullish coalescing - only falls back when left is null or undefined
const name2 = "" ?? "Default Name";
console.log(name2); // "" (empty string is respected as a valid value)

const count2 = 0 ?? 10;
console.log(count2); // 0 (zero is respected as a valid value)

const nullValue = null ?? "Default";
console.log(nullValue); // "Default"

const undefinedValue = undefined ?? "Default";
console.log(undefinedValue); // "Default"

// Combining with optional chaining
const user = null;
console.log(user?.name ?? "Guest"); // "Guest"

Logical Assignment (ES2021)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Logical OR assignment (||=)
let user1 = { name: 'John' };
user1.name ||= 'Guest';
console.log(user1.name); // "John"

user1.role ||= 'User';
console.log(user1.role); // "User"

// Logical AND assignment (&&=)
let user2 = { name: 'Admin', verified: true };
user2.verified &&= false;
console.log(user2.verified); // false

// Nullish coalescing assignment (??=)
let user3 = { name: 'Jane' };
user3.name ??= 'Guest';
console.log(user3.name); // "Jane"

user3.role ??= 'User';
console.log(user3.role); // "User"

// Empty string example
let user4 = { name: '' };
user4.name ||= 'Guest'; // Assigns 'Guest' because empty string is falsy
user4.name ??= 'Default'; // Doesn't assign because empty string is not null/undefined
console.log(user4.name); // "Guest"

BigInt (ES2020)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Creating BigInt values
const bigInt1 = 9007199254740991n; // Append 'n' to create a BigInt literal
const bigInt2 = BigInt(9007199254740991); // Or use the BigInt() function

// Operations with BigInt
const sum = bigInt1 + BigInt(1000);
console.log(sum); // 9007199254741991n

// BigInt can represent integers of arbitrary size
const reallyBig = 9007199254740991n * 9007199254740991n;
console.log(reallyBig); // 81129638414606663681390495662081n

// Cannot mix BigInt and regular numbers
// const invalid = bigInt1 + 1; // TypeError

// Comparisons work
console.log(10n === BigInt(10)); // true
console.log(10n == 10); // true (type coercion)

// BigInt with conditionals
if (bigInt1 > 1000n) {
console.log('Really big!');
}

Private Class Fields (ES2022)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class BankAccount {
// Public field
owner;

// Private field (with # prefix)
#balance;

// Private static field
static #interestRate = 0.01;

constructor(owner, initialBalance) {
this.owner = owner;
this.#balance = initialBalance;
}

// Public method
deposit(amount) {
this.#balance += amount;
return this.#balance;
}

// Public method accessing private field
getBalance() {
return this.#balance;
}

// Private method
#calculateInterest() {
return this.#balance * BankAccount.#interestRate;
}

// Public method using private method
addInterest() {
const interest = this.#calculateInterest();
this.#balance += interest;
return interest;
}
}

const account = new BankAccount('John', 1000);
console.log(account.owner); // "John"
console.log(account.getBalance()); // 1000

account.deposit(500);
console.log(account.getBalance()); // 1500

console.log(account.addInterest()); // 15 (interest amount)

// Cannot access private fields directly
// console.log(account.#balance); // SyntaxError
// account.#calculateInterest(); // SyntaxError

Top-level Await (ES2022)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Before ES2022, await could only be used inside async functions
async function getData() {
const response = await fetch('https://api.example.com/data');
return response.json();
}

// With ES2022, await can be used at the top level in modules
// Note: This only works in ES modules, not CommonJS
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);

// Useful for conditional imports
let mathModule;
if (data.useAdvancedMath) {
mathModule = await import('./advanced-math.js');
} else {
mathModule = await import('./basic-math.js');
}

// Or for initializing an app
const config = await fetch('/api/config').then(r => r.json());
const db = await initDatabase(config);
startApp(db);

Learning Resources

  • MDN Web Docs - JavaScript
  • Exploring ES6
  • ES6 Features
  • What’s New in ES2022
  • JavaScript Info - Modern JavaScript
  • Babel - JavaScript compiler that allows you to use newer syntax
  • Web Development
  • JavaScript
  • ES6
  • ECMAScript
  • Modern JavaScript

show all >>

JavaScript Advanced Concepts

  2023-06-06
字数统计: 3.2k字   |   阅读时长: 19min

JavaScript Advanced Concepts

Moving beyond the basics, JavaScript has many powerful advanced features that enable sophisticated application development. This guide covers the key advanced concepts that every frontend developer should understand.

Scope and Closures

Execution Context and Scope Chain

Every time JavaScript code runs, it’s within an execution context. There are three types:

  • Global Execution Context: Default context for code that’s not inside any function
  • Function Execution Context: Created when a function is called
  • Eval Execution Context: Created when code is executed in an eval function

Each context has:

  • Variable Environment: Variables, functions, and arguments
  • Scope Chain: References to outer variable environments
  • this Value: The value of this in that context
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let globalVar = "I'm global";

function outer() {
let outerVar = "I'm in outer";

function inner() {
let innerVar = "I'm in inner";
console.log(innerVar); // Accessible: own scope
console.log(outerVar); // Accessible: outer function's scope
console.log(globalVar); // Accessible: global scope
}

inner();
// console.log(innerVar); // Error: not accessible (inner scope)
}

outer();
// console.log(outerVar); // Error: not accessible (outer function scope)

Closures

A closure is created when a function retains access to its lexical scope even when executed outside that scope:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function createCounter() {
let count = 0; // Private variable

return {
increment() {
count++;
return count;
},
decrement() {
count--;
return count;
},
getValue() {
return count;
}
};
}

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getValue()); // 2
console.log(counter.decrement()); // 1

// count is not directly accessible
// console.log(counter.count); // undefined

Closures are commonly used for:

  • Data encapsulation (private variables)
  • Factory functions
  • Event handlers and callbacks
  • Partially applied functions

Module Pattern

Closures enable the module pattern, a way to create private variables and methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const calculator = (function() {
// Private variables/methods
let result = 0;

function validateNumber(num) {
return typeof num === 'number' && !isNaN(num);
}

// Public API
return {
add(num) {
if (validateNumber(num)) {
result += num;
}
return this; // For method chaining
},
subtract(num) {
if (validateNumber(num)) {
result -= num;
}
return this;
},
getResult() {
return result;
},
reset() {
result = 0;
return this;
}
};
})(); // IIFE - Immediately Invoked Function Expression

calculator.add(5).add(10).subtract(3);
console.log(calculator.getResult()); // 12
calculator.reset();
console.log(calculator.getResult()); // 0

This Binding

The value of this depends on how a function is called:

Default Binding

When a function is called without any context, this refers to the global object (or undefined in strict mode):

1
2
3
4
5
function showThis() {
console.log(this);
}

showThis(); // window in browser (or global in Node.js), or undefined in strict mode

Implicit Binding

When a function is called as a method of an object, this refers to that object:

1
2
3
4
5
6
7
8
9
10
11
12
const user = {
name: "Alice",
greet() {
console.log(`Hello, I'm ${this.name}`);
}
};

user.greet(); // "Hello, I'm Alice"

// But if we extract the method:
const greetFunction = user.greet;
greetFunction(); // "Hello, I'm undefined" (default binding applies)

Explicit Binding

You can explicitly set this using call, apply, or bind:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function introduce(greeting, punctuation) {
console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}

const person = { name: "Bob" };

// Using call (comma-separated arguments)
introduce.call(person, "Hi", "!"); // "Hi, I'm Bob!"

// Using apply (arguments as array)
introduce.apply(person, ["Hello", "."]); // "Hello, I'm Bob."

// Using bind (returns a new function with bound this)
const boundIntroduce = introduce.bind(person, "Hey");
boundIntroduce("..."); // "Hey, I'm Bob..."

New Binding

When a function is used as a constructor with new, this refers to the newly created object:

1
2
3
4
5
6
7
8
9
function Person(name) {
this.name = name;
this.sayName = function() {
console.log(`My name is ${this.name}`);
};
}

const alice = new Person("Alice");
alice.sayName(); // "My name is Alice"

Arrow Functions and Lexical This

Arrow functions don’t have their own this. They inherit this from the surrounding scope:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const obj = {
name: "Object",
regularMethod: function() {
console.log(`Regular method: ${this.name}`);

// Inner function loses this context
function innerFunction() {
console.log(`Inner function: ${this.name}`); // undefined (or global)
}

// Arrow function preserves this context
const arrowFunction = () => {
console.log(`Arrow function: ${this.name}`); // "Object"
};

innerFunction();
arrowFunction();
}
};

obj.regularMethod();

Prototypes and Inheritance

Prototype Chain

JavaScript uses a prototype chain for inheritance. Every object has a prototype (except for the base Object.prototype):

1
2
3
4
const arr = [];
console.log(arr.__proto__ === Array.prototype); // true
console.log(Array.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null

Creating Objects with Prototypes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Constructor function
function Animal(name) {
this.name = name;
}

Animal.prototype.makeSound = function() {
return "Generic animal sound";
};

// Inheritance with constructor functions
function Dog(name, breed) {
Animal.call(this, name); // Call parent constructor
this.breed = breed;
}

// Set up prototype chain
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // Fix constructor property

// Override method
Dog.prototype.makeSound = function() {
return "Woof!";
};

// Add new method
Dog.prototype.fetch = function() {
return `${this.name} is fetching!`;
};

const animal = new Animal("Generic Animal");
const dog = new Dog("Rex", "German Shepherd");

console.log(animal.makeSound()); // "Generic animal sound"
console.log(dog.makeSound()); // "Woof!"
console.log(dog.fetch()); // "Rex is fetching!"
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true

Class Syntax (ES6+)

ES6 introduced a cleaner syntax for working with prototypes via classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Animal {
constructor(name) {
this.name = name;
}

makeSound() {
return "Generic animal sound";
}

// Static method (on the class, not instances)
static isAnimal(obj) {
return obj instanceof Animal;
}
}

class Dog extends Animal {
constructor(name, breed) {
super(name); // Call parent constructor
this.breed = breed;
}

makeSound() {
return "Woof!";
}

fetch() {
return `${this.name} is fetching!`;
}

// Getter
get description() {
return `${this.name} is a ${this.breed}`;
}

// Setter
set nickname(value) {
this._nickname = value;
}

get nickname() {
return this._nickname;
}
}

const dog = new Dog("Max", "Labrador");
console.log(dog.makeSound()); // "Woof!"
console.log(dog.description); // "Max is a Labrador"
dog.nickname = "Maxie";
console.log(dog.nickname); // "Maxie"
console.log(Animal.isAnimal(dog)); // true

Asynchronous JavaScript

Callbacks

Traditional way to handle asynchronous operations, but can lead to “callback hell”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: "Product" };
callback(null, data); // Error-first callback pattern
}, 1000);
}

fetchData((error, data) => {
if (error) {
console.error("Error:", error);
return;
}

console.log("Data:", data);

// Nested callback - leads to "callback hell" with more nesting
fetchRelatedData(data.id, (error, relatedData) => {
if (error) {
console.error("Error:", error);
return;
}

console.log("Related data:", relatedData);
});
});

Promises

Promises provide a cleaner way to work with asynchronous code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { id: 1, name: "Product" };
// Simulate success
resolve(data);
// Or failure: reject(new Error("Failed to fetch data"))
}, 1000);
});
}

function fetchRelatedData(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([{ id: 101, productId: id, name: "Related Item" }]);
}, 1000);
});
}

// Promise chaining
fetchData()
.then(data => {
console.log("Data:", data);
return fetchRelatedData(data.id);
})
.then(relatedData => {
console.log("Related data:", relatedData);
})
.catch(error => {
console.error("Error:", error);
})
.finally(() => {
console.log("Operation complete (success or failure)");
});

// Promise combinators
Promise.all([fetchData(), fetchRelatedData(1)])
.then(([data, relatedData]) => {
console.log("All results:", data, relatedData);
})
.catch(error => {
console.error("Any error:", error);
});

Promise.race([
fetchData(),
new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), 2000))
])
.then(result => console.log("First to complete:", result))
.catch(error => console.error("First to fail:", error));

Async/Await

Built on promises, async/await provides an even cleaner syntax for asynchronous code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
async function fetchAllData() {
try {
// await pauses execution until the promise resolves
const data = await fetchData();
console.log("Data:", data);

const relatedData = await fetchRelatedData(data.id);
console.log("Related data:", relatedData);

return { data, relatedData }; // This is wrapped in a Promise
} catch (error) {
console.error("Error in fetchAllData:", error);
throw error; // Re-throw for callers to handle
}
}

// Calling the async function
fetchAllData()
.then(result => console.log("Final result:", result))
.catch(error => console.error("Caught at call site:", error));

// Parallel operations with async/await
async function fetchInParallel() {
try {
// Start both operations at once
const dataPromise = fetchData();
const relatedPromise = fetchRelatedData(1);

// Wait for both to complete
const [data, related] = await Promise.all([dataPromise, relatedPromise]);

console.log("Parallel results:", data, related);
} catch (error) {
console.error("Parallel error:", error);
}
}

Generators and Iterators

Iterators

Objects that implement a next() method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const rangeIterator = {
start: 0,
end: 3,
current: 0,

// Required iterator method
next() {
if (this.current < this.end) {
return { value: this.current++, done: false };
}
return { value: undefined, done: true };
}
};

let result = rangeIterator.next();
while (!result.done) {
console.log(result.value); // 0, 1, 2
result = rangeIterator.next();
}

Iterables

Objects that implement the iterator protocol via Symbol.iterator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const rangeIterable = {
start: 0,
end: 3,

// This makes the object iterable
[Symbol.iterator]() {
let current = this.start;
const end = this.end;

return {
next() {
if (current < end) {
return { value: current++, done: false };
}
return { value: undefined, done: true };
}
};
}
};

// Now we can use for...of
for (const num of rangeIterable) {
console.log(num); // 0, 1, 2
}

// Or spread operator
const numbers = [...rangeIterable]; // [0, 1, 2]

Generators

Functions that can pause and resume execution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function* rangeGenerator(start, end) {
for (let i = start; i < end; i++) {
yield i;
}
}

// Using the generator
const gen = rangeGenerator(0, 3);
console.log(gen.next()); // { value: 0, done: false }
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: undefined, done: true }

// Generator is iterable
for (const num of rangeGenerator(0, 3)) {
console.log(num); // 0, 1, 2
}

// Infinite generator with control
function* idGenerator() {
let id = 1;
while (true) {
const increment = yield id++; // yield returns control and accepts a value
if (increment) {
id += increment;
}
}
}

const ids = idGenerator();
console.log(ids.next().value); // 1
console.log(ids.next().value); // 2
console.log(ids.next(10).value); // 13 (2 + 1 + 10)

Async Generators and Iterators

Combining generators with async/await:

1
2
3
4
5
6
7
8
9
10
11
12
13
async function* asyncGenerator() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}

async function useAsyncGenerator() {
for await (const value of asyncGenerator()) {
console.log(value); // 1, 2, 3
}
}

useAsyncGenerator();

Functional Programming Concepts

Pure Functions

Functions that:

  1. Always return the same output for the same input
  2. Have no side effects (don’t modify external state)
1
2
3
4
5
6
7
8
9
10
11
// Pure function
function add(a, b) {
return a + b;
}

// Impure function (side effect)
let total = 0;
function addToTotal(value) {
total += value; // Side effect: modifies external state
return total;
}

Higher-Order Functions

Functions that take and/or return other functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Function that returns a function
function multiply(factor) {
return function(number) {
return number * factor;
};
}

const double = multiply(2);
const triple = multiply(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15

// Function that takes a function
function applyOperation(a, b, operation) {
return operation(a, b);
}

const sum = applyOperation(5, 3, (a, b) => a + b); // 8
const difference = applyOperation(5, 3, (a, b) => a - b); // 2

Composition

Combining multiple functions to create a new function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Utility for function composition
const compose = (...functions) => input =>
functions.reduceRight((acc, fn) => fn(acc), input);

const pipe = (...functions) => input =>
functions.reduce((acc, fn) => fn(acc), input);

// Example functions
const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;

// Compose: execute from right to left
const transformCompose = compose(square, double, addOne);
console.log(transformCompose(3)); // square(double(addOne(3))) = square(double(4)) = square(8) = 64

// Pipe: execute from left to right
const transformPipe = pipe(addOne, double, square);
console.log(transformPipe(3)); // square(double(addOne(3))) = square(double(4)) = square(8) = 64

Currying

Transforming a function with multiple arguments into a sequence of functions each with a single argument:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Regular function
function add(a, b, c) {
return a + b + c;
}

// Curried version
function curriedAdd(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}

// Usage
console.log(add(1, 2, 3)); // 6
console.log(curriedAdd(1)(2)(3)); // 6

// Partial application
const addOne = curriedAdd(1);
const addOneAndTwo = addOne(2);
console.log(addOneAndTwo(3)); // 6

// Currying utility
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
};
}
};
}

const curriedAddUtil = curry(add);
console.log(curriedAddUtil(1)(2)(3)); // 6
console.log(curriedAddUtil(1, 2)(3)); // 6
console.log(curriedAddUtil(1, 2, 3)); // 6

Immutability

Working with data without modifying the original:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Modifying arrays
const numbers = [1, 2, 3, 4];

// Bad (mutates original)
numbers.push(5);

// Good (returns new array)
const newNumbers = [...numbers, 5];
const filteredNumbers = numbers.filter(n => n > 2);
const doubledNumbers = numbers.map(n => n * 2);

// Modifying objects
const person = { name: "Alice", age: 30 };

// Bad (mutates original)
person.age = 31;

// Good (returns new object)
const updatedPerson = { ...person, age: 31 };
const withoutAge = (({ age, ...rest }) => rest)(person);

Proxy and Reflect

Proxy

Objects that intercept and redefine operations for another object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const target = {
name: "Target",
age: 25
};

const handler = {
get(target, prop, receiver) {
console.log(`Getting ${prop}`);
return prop in target ? target[prop] : `No property ${prop}`;
},

set(target, prop, value, receiver) {
console.log(`Setting ${prop} to ${value}`);
if (prop === "age" && typeof value !== "number") {
throw new TypeError("Age must be a number");
}
target[prop] = value;
return true; // Indicate success
},

has(target, prop) {
console.log(`Checking if ${prop} exists`);
return prop in target;
},

deleteProperty(target, prop) {
console.log(`Deleting ${prop}`);
delete target[prop];
return true; // Indicate success
}
};

const proxy = new Proxy(target, handler);

// Test operations
console.log(proxy.name); // Getting name, Target
console.log(proxy.unknown); // Getting unknown, No property unknown

proxy.age = 30; // Setting age to 30
// proxy.age = "thirty"; // TypeError: Age must be a number

console.log("name" in proxy); // Checking if name exists, true

delete proxy.age; // Deleting age

Reflect

Object with static methods for interceptable operations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Using Reflect with Proxy
const target = { name: "Target", age: 25 };

const handler = {
get(target, prop, receiver) {
console.log(`Getting ${prop}`);
return Reflect.get(target, prop, receiver);
},

set(target, prop, value, receiver) {
console.log(`Setting ${prop} to ${value}`);
return Reflect.set(target, prop, value, receiver);
}
};

const proxy = new Proxy(target, handler);
proxy.name = "New Target"; // Setting name to New Target
console.log(proxy.name); // Getting name, New Target

// Direct use of Reflect
console.log(Reflect.has(target, "age")); // true
console.log(Reflect.ownKeys(target)); // ["name", "age"]
console.log(Reflect.getPrototypeOf(target)); // [Object: null prototype] {}
Reflect.defineProperty(target, "property", { value: 42 });

Memory Management and Optimization

Memory Lifecycle

  1. Allocation: When memory is allocated by the JavaScript engine
  2. Usage: When the program reads/writes to that memory
  3. Release: When the memory is no longer needed and can be freed

Garbage Collection

JavaScript automatically manages memory using garbage collection:

  • Mark and Sweep: The most common algorithm used in modern browsers
    1. The garbage collector builds a list of “roots” (global objects)
    2. It marks these roots and all references from these roots
    3. It collects all non-marked objects

Memory Leaks

Common causes of memory leaks:

  1. Accidental Global Variables:
1
2
3
function leak() {
leakedVariable = "I'm leaked"; // Missing var/let/const
}
  1. Forgotten Timers or Callbacks:
1
2
3
4
5
6
7
8
function setupTimer() {
const largeData = new Array(10000000).fill('x');

setInterval(() => {
// This keeps largeData in memory indefinitely
console.log(largeData.length);
}, 1000);
}
  1. Closures Capturing Variables:
1
2
3
4
5
6
7
8
function createLeakyFunction() {
const largeData = new Array(10000000).fill('x');

return function() {
// This inner function captures largeData
console.log(largeData[0]);
};
}
  1. Detached DOM Elements:
1
2
3
4
5
6
7
8
9
10
11
12
function detachedDOM() {
const div = document.createElement('div');
div.innerHTML = 'A lot of content...';

// Store reference to the DOM element but never append it to the DOM
// or remove the reference when done
const detachedElements = [];
detachedElements.push(div);

// Later remove from DOM but keep in array
// detachedElements still holds a reference, preventing garbage collection
}

Performance Optimization

  1. Object Pooling:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Reuse objects instead of creating new ones
const objectPool = [];

function getObject() {
if (objectPool.length > 0) {
return objectPool.pop();
}
return { x: 0, y: 0 }; // Create new if pool is empty
}

function releaseObject(obj) {
obj.x = 0;
obj.y = 0;
objectPool.push(obj);
}
  1. Throttling and Debouncing:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Throttle: Execute at most once every X ms
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function(...args) {
if (!lastRan) {
func.apply(this, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
if (Date.now() - lastRan >= limit) {
func.apply(this, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}

// Debounce: Execute only after X ms have passed without another call
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}

// Example usage
window.addEventListener('resize', throttle(() => {
console.log('Resize event throttled');
}, 200));

document.getElementById('search').addEventListener('input', debounce((e) => {
console.log('Search query:', e.target.value);
}, 300));

Learning Resources

  • You Don’t Know JS (book series)
  • JavaScript: The Good Parts
  • Functional-Light JavaScript
  • Exploring ES6
  • JavaScript Allongé
  • MDN Web Docs - JavaScript
  • JavaScript.info
  • Frontend Masters - Advanced JavaScript courses
  • Web Development
  • JavaScript
  • Advanced

show all >>

JavaScript Basics

  2023-06-05
字数统计: 1.9k字   |   阅读时长: 11min

JavaScript Basics

JavaScript is a high-level, interpreted programming language that powers dynamic behavior on the web. It’s an essential skill for frontend development.

Introduction to JavaScript

JavaScript (JS) enables interactive web pages and is an essential part of web applications. It was created in 1995 by Brendan Eich and has since become one of the world’s most popular programming languages.

Where JavaScript Runs

  • Browser: Client-side JS runs in the user’s browser
  • Server: Node.js allows JS to run on servers
  • Mobile Apps: Frameworks like React Native use JS for mobile development
  • Desktop Apps: Electron enables JS-powered desktop applications

Setting Up JavaScript

In HTML

1
2
3
4
5
6
7
<!-- Internal JavaScript -->
<script>
console.log("Hello, World!");
</script>

<!-- External JavaScript -->
<script src="script.js"></script>

Console

You can also run JavaScript in the browser’s developer console (F12 or Right-click > Inspect > Console).

Variables and Data Types

Variable Declaration

There are three ways to declare variables:

1
2
3
4
5
6
7
8
9
10
// var - function-scoped (older way)
var age = 25;

// let - block-scoped, can be reassigned
let name = "John";
name = "Jane"; // Valid reassignment

// const - block-scoped, cannot be reassigned
const PI = 3.14159;
// PI = 3; // Error: Assignment to constant variable

Primitive Data Types

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// String - text
let greeting = "Hello, World!";
let phrase = 'JavaScript is fun';
let template = `My name is ${name}`; // Template literal

// Number - integers and floating points
let integer = 42;
let float = 3.14;
let scientific = 2.998e8; // Scientific notation

// Boolean - true or false
let isActive = true;
let isComplete = false;

// Undefined - variable declared but not assigned
let undefinedVar;
console.log(undefinedVar); // undefined

// Null - intentional absence of value
let emptyValue = null;

// Symbol - unique and immutable
const uniqueID = Symbol('id');

// BigInt - for integers larger than Number can handle
const bigNumber = 9007199254740991n;

Checking Types

1
2
3
4
5
6
7
typeof "Hello"; // "string"
typeof 42; // "number"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" (this is a known JavaScript quirk)
typeof Symbol('id'); // "symbol"
typeof 10n; // "bigint"

Complex Data Types

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Object - collection of key-value pairs
let person = {
firstName: "John",
lastName: "Doe",
age: 30,
isEmployed: true,
greet: function() {
return `Hello, my name is ${this.firstName}`;
}
};

// Accessing object properties
console.log(person.firstName); // Dot notation
console.log(person["lastName"]); // Bracket notation
console.log(person.greet()); // Method call

// Array - ordered collection of values
let colors = ["red", "green", "blue"];
let mixed = [1, "two", true, { id: 4 }]; // Can hold different types

// Accessing array elements (zero-indexed)
console.log(colors[0]); // "red"
console.log(colors.length); // 3

// Function - reusable block of code
function add(a, b) {
return a + b;
}

// Arrow function
const subtract = (a, b) => a - b;

// Date
const today = new Date();

Operators

Arithmetic Operators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let a = 10;
let b = 3;

let sum = a + b; // Addition: 13
let difference = a - b; // Subtraction: 7
let product = a * b; // Multiplication: 30
let quotient = a / b; // Division: 3.33...
let remainder = a % b; // Modulus (remainder): 1
let exponent = a ** b; // Exponentiation: 1000

// Increment/Decrement
let counter = 5;
counter++; // Equivalent to: counter = counter + 1
counter--; // Equivalent to: counter = counter - 1

Assignment Operators

1
2
3
4
5
6
7
8
9
let x = 10; // Basic assignment

// Compound assignment
x += 5; // x = x + 5
x -= 3; // x = x - 3
x *= 2; // x = x * 2
x /= 4; // x = x / 4
x %= 3; // x = x % 3
x **= 2; // x = x ** 2

Comparison Operators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let a = 5;
let b = "5";

// Equal (value)
a == b; // true (converts types before comparison)

// Strict equal (value and type)
a === b; // false (different types)

// Not equal
a != b; // false

// Strict not equal
a !== b; // true

// Greater/less than
a > 4; // true
a < 10; // true
a >= 5; // true
a <= 5; // true

Logical Operators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let isAdult = true;
let hasPermission = false;

// AND - both must be true
isAdult && hasPermission; // false

// OR - at least one must be true
isAdult || hasPermission; // true

// NOT - inverts boolean value
!isAdult; // false
!hasPermission; // true

// Short-circuit evaluation
let name = null;
let displayName = name || "Anonymous"; // "Anonymous"

Control Flow

Conditional Statements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// if statement
let age = 18;

if (age >= 18) {
console.log("You are an adult");
}

// if-else statement
if (age >= 18) {
console.log("You are an adult");
} else {
console.log("You are a minor");
}

// if-else if-else statement
if (age < 13) {
console.log("Child");
} else if (age < 18) {
console.log("Teenager");
} else {
console.log("Adult");
}

// Ternary operator - condition ? expressionIfTrue : expressionIfFalse
let status = age >= 18 ? "Adult" : "Minor";

// switch statement
let day = 3;
switch (day) {
case 1:
console.log("Monday");
break;
case 2:
console.log("Tuesday");
break;
case 3:
console.log("Wednesday");
break;
default:
console.log("Another day");
}

Loops

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// for loop
for (let i = 0; i < 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}

// while loop
let count = 0;
while (count < 5) {
console.log(count); // 0, 1, 2, 3, 4
count++;
}

// do-while loop (runs at least once)
let num = 0;
do {
console.log(num); // 0, 1, 2, 3, 4
num++;
} while (num < 5);

// for...of loop (for iterables like arrays)
let fruits = ["apple", "banana", "cherry"];
for (let fruit of fruits) {
console.log(fruit); // "apple", "banana", "cherry"
}

// for...in loop (for object properties)
let person = { name: "John", age: 30, job: "developer" };
for (let key in person) {
console.log(`${key}: ${person[key]}`);
}

// break and continue
for (let i = 0; i < 10; i++) {
if (i === 3) continue; // Skip this iteration
if (i === 7) break; // Exit the loop
console.log(i); // 0, 1, 2, 4, 5, 6
}

Functions

Function Declaration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Basic function declaration
function greet(name) {
return `Hello, ${name}!`;
}

console.log(greet("Alice")); // "Hello, Alice!"

// Function with default parameters
function greetWithDefault(name = "Guest") {
return `Hello, ${name}!`;
}

console.log(greetWithDefault()); // "Hello, Guest!"

// Function with multiple parameters
function calculateArea(length, width) {
return length * width;
}

console.log(calculateArea(5, 10)); // 50

Function Expressions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Function expression (anonymous)
const add = function(a, b) {
return a + b;
};

// Named function expression
const subtract = function sub(a, b) {
return a - b;
};

// Arrow functions
const multiply = (a, b) => a * b;

// Multi-line arrow function
const divide = (a, b) => {
if (b === 0) {
throw new Error("Cannot divide by zero");
}
return a / b;
};

Function Scope

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Global scope
let globalVar = "I'm global";

function exampleFunction() {
// Function scope
let localVar = "I'm local";
console.log(globalVar); // Accessible
console.log(localVar); // Accessible
}

exampleFunction();
console.log(globalVar); // Accessible
// console.log(localVar); // Error: localVar is not defined

// Block scope with let/const
if (true) {
let blockVar = "I'm in a block";
const anotherBlockVar = "Me too";
var notBlockScoped = "I'll leak out"; // var is not block-scoped
}

// console.log(blockVar); // Error
// console.log(anotherBlockVar); // Error
console.log(notBlockScoped); // "I'll leak out" - accessible!

Higher-Order Functions

Functions that take other functions as arguments or return them:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Function as an argument
function executeOperation(operation, a, b) {
return operation(a, b);
}

const result = executeOperation(add, 5, 3); // 8

// Returning a function
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}

const double = createMultiplier(2);
console.log(double(10)); // 20

Working with Arrays

Array Methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
let fruits = ["apple", "banana", "cherry"];

// Basic array operations
fruits.push("orange"); // Add to end: ["apple", "banana", "cherry", "orange"]
fruits.pop(); // Remove from end: ["apple", "banana", "cherry"]
fruits.unshift("mango"); // Add to beginning: ["mango", "apple", "banana", "cherry"]
fruits.shift(); // Remove from beginning: ["apple", "banana", "cherry"]

// Finding elements
fruits.indexOf("banana"); // 1
fruits.includes("cherry"); // true

// Transforming arrays
let upperFruits = fruits.map(fruit => fruit.toUpperCase()); // ["APPLE", "BANANA", "CHERRY"]
let longFruits = fruits.filter(fruit => fruit.length > 5); // ["banana", "cherry"]
let allLengths = fruits.reduce((total, fruit) => total + fruit.length, 0); // Sum of all lengths

// Iterating
fruits.forEach(fruit => console.log(fruit));

// Sorting
fruits.sort(); // Alphabetical sort: ["apple", "banana", "cherry"]
fruits.reverse(); // Reverse: ["cherry", "banana", "apple"]

// Slicing and splicing
let citrus = fruits.slice(1, 3); // Shallow copy of portion: ["banana", "cherry"]
fruits.splice(1, 1, "blueberry", "blackberry"); // Remove and insert: ["apple", "blueberry", "blackberry", "cherry"]

Working with Objects

Object Methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let person = {
firstName: "John",
lastName: "Doe",
age: 30
};

// Getting information
Object.keys(person); // ["firstName", "lastName", "age"]
Object.values(person); // ["John", "Doe", 30]
Object.entries(person); // [["firstName", "John"], ["lastName", "Doe"], ["age", 30]]

// Checking properties
person.hasOwnProperty("age"); // true

// Adding/modifying properties
person.email = "john@example.com";
Object.assign(person, { phone: "123-456-7890", age: 31 }); // Updates age, adds phone

// Preventing modifications
const frozen = Object.freeze({ x: 1, y: 2 }); // Cannot be modified
const sealed = Object.seal({ a: 1, b: 2 }); // Properties can't be added/removed but can be modified

Object Destructuring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const user = {
id: 1,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};

// Basic destructuring
const { id, displayName } = user;
console.log(id); // 1
console.log(displayName); // "jdoe"

// Nested destructuring
const { fullName: { firstName, lastName } } = user;
console.log(firstName); // "John"

// Default values
const { admin = false } = user;
console.log(admin); // false

// Renaming
const { displayName: username } = user;
console.log(username); // "jdoe"

Error Handling

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// try...catch
try {
// Code that might throw an error
const result = nonExistentFunction();
} catch (error) {
console.error("An error occurred:", error.message);
} finally {
// Code that always runs
console.log("This always executes");
}

// Throwing custom errors
function divide(a, b) {
if (b === 0) {
throw new Error("Division by zero");
}
return a / b;
}

try {
console.log(divide(10, 0));
} catch (error) {
console.error(error.message); // "Division by zero"
}

JavaScript Best Practices

  1. Use strict mode to catch common coding errors:

    1
    2
    'use strict';
    // Your code here
  2. Prefer const/let over var for more predictable scoping

  3. Use meaningful variable and function names for better readability

  4. Add comments for complex logic, but make code self-documenting when possible

  5. Handle errors appropriately using try/catch

  6. Avoid global variables to prevent namespace pollution

  7. Use === and !== instead of == and != to avoid unexpected type coercion

  8. Use default parameters instead of checking for undefined

  9. Use template literals for string concatenation

  10. Use array and object destructuring for cleaner code

Learning Resources

  • MDN JavaScript Guide
  • JavaScript.info
  • Eloquent JavaScript
  • freeCodeCamp JavaScript Course
  • Web Development
  • JavaScript
  • Programming

show all >>

CSS Layout & Responsive Design

  2023-06-04
字数统计: 1.2k字   |   阅读时长: 7min

CSS Layout & Responsive Design

Modern web design requires layouts that adapt to different screen sizes and devices. This guide covers the essential CSS layout techniques and approaches to responsive design.

Traditional Layout Methods

1. Normal Flow

The default layout mode where elements are displayed one after another:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.box {
/* Block elements occupy full width */
display: block;
width: 100%;
margin-bottom: 20px;
}

.inline-element {
/* Inline elements flow within text */
display: inline;
margin: 0 5px;
}

.inline-block {
/* Combines features of both */
display: inline-block;
width: 100px;
margin: 0 10px;
}

2. Floats

Originally designed for wrapping text around images, floats have been used for creating multi-column layouts:

1
2
3
4
5
6
7
8
9
10
11
12
.container::after {
content: "";
display: table;
clear: both; /* Clearfix to contain floats */
}

.column {
float: left;
width: 33.33%;
padding: 15px;
box-sizing: border-box;
}

3. Positioning

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
.static {
/* Default positioning */
position: static;
}

.relative {
/* Positioned relative to its normal position */
position: relative;
top: 10px;
left: 20px;
}

.absolute {
/* Positioned relative to the nearest positioned ancestor */
position: absolute;
top: 0;
right: 0;
}

.fixed {
/* Positioned relative to the viewport */
position: fixed;
bottom: 20px;
right: 20px;
}

.sticky {
/* Acts like relative, becomes fixed when scrolled */
position: sticky;
top: 0;
}

Modern Layout Methods

1. Flexbox

Flexbox is designed for one-dimensional layouts - either rows or columns:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.container {
display: flex;
flex-direction: row; /* or column */
flex-wrap: wrap; /* or nowrap */
justify-content: space-between; /* horizontal alignment */
align-items: center; /* vertical alignment */
gap: 20px; /* spacing between items */
}

.item {
flex: 1; /* grow and shrink equally */
}

.important-item {
flex: 2; /* takes twice as much space */
}

.fixed-item {
flex: 0 0 200px; /* don't grow, don't shrink, stay at 200px */
}

Common flexbox properties:

For the container (parent):

  • display: flex
  • flex-direction: row | row-reverse | column | column-reverse
  • flex-wrap: nowrap | wrap | wrap-reverse
  • justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly
  • align-items: stretch | flex-start | flex-end | center | baseline
  • align-content: flex-start | flex-end | center | space-between | space-around | stretch
  • gap: [row-gap] [column-gap]

For the items (children):

  • flex: [grow] [shrink] [basis]
  • align-self: auto | flex-start | flex-end | center | baseline | stretch
  • order: <integer> (default is 0)

2. CSS Grid

Grid is designed for two-dimensional layouts - rows and columns simultaneously:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 3 equal columns */
grid-template-rows: 100px auto 100px; /* specific row heights */
gap: 20px;
}

.header {
grid-column: 1 / -1; /* spans all columns */
}

.sidebar {
grid-row: 2 / 3; /* specific row position */
grid-column: 1 / 2; /* specific column position */
}

.content {
grid-row: 2 / 3;
grid-column: 2 / -1; /* from column 2 to the end */
}

.footer {
grid-column: 1 / -1;
}

More complex grid example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.advanced-grid {
display: grid;
grid-template-columns: [start] 1fr [content-start] 2fr [content-end] 1fr [end];
grid-template-rows: [top] auto [content-top] 1fr [content-bottom] auto [bottom];
grid-template-areas:
"header header header"
"sidebar content related"
"footer footer footer";
min-height: 100vh;
}

.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.related { grid-area: related; }
.footer { grid-area: footer; }

Responsive Design Principles

1. Viewport Meta Tag

1
<meta name="viewport" content="width=device-width, initial-scale=1.0">

2. Media Queries

Media queries allow applying different styles based on device characteristics:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* Base styles for all screen sizes */
.container {
padding: 15px;
}

/* Styles for medium screens and up */
@media screen and (min-width: 768px) {
.container {
padding: 30px;
}
}

/* Styles for large screens */
@media screen and (min-width: 1024px) {
.container {
max-width: 1200px;
margin: 0 auto;
}
}

/* Print styles */
@media print {
.no-print {
display: none;
}
}

Common media query breakpoints:

  • Mobile: 320px - 480px
  • Tablets: 481px - 768px
  • Laptops: 769px - 1024px
  • Desktops: 1025px - 1200px
  • TV/Large screens: 1201px and above

3. Fluid Layouts

Rather than fixed pixel sizes, use percentage-based widths for flexible layouts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
}

.column {
width: 100%;
}

@media (min-width: 768px) {
.column {
width: 50%;
float: left;
}
}

@media (min-width: 1024px) {
.column {
width: 33.33%;
}
}

4. Responsive Units

Use relative units instead of fixed pixels:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
html {
font-size: 16px; /* Base font size */
}

.container {
width: 100%;
padding: 1rem; /* relative to root font size */
}

h1 {
font-size: 2.5rem; /* 40px if base is 16px */
}

.sidebar {
width: 30%; /* percentage of parent */
}

.full-height {
height: 100vh; /* viewport height */
}

.half-width {
width: 50vw; /* viewport width */
}

.flexible-padding {
padding: calc(1rem + 2vw); /* combines fixed and relative units */
}

Common responsive units:

  • %: Relative to parent element
  • em: Relative to the font-size of the element
  • rem: Relative to the font-size of the root element
  • vh: 1% of viewport height
  • vw: 1% of viewport width
  • vmin: 1% of the smaller dimension (height or width)
  • vmax: 1% of the larger dimension (height or width)

5. Responsive Images

1
2
3
4
img {
max-width: 100%;
height: auto;
}

HTML picture element for art direction:

1
2
3
4
5
<picture>
<source media="(min-width: 1024px)" srcset="large.jpg">
<source media="(min-width: 768px)" srcset="medium.jpg">
<img src="small.jpg" alt="Responsive image">
</picture>

6. Mobile-First Approach

Start with styles for mobile devices and progressively enhance for larger screens:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Base styles for mobile */
.navigation {
display: flex;
flex-direction: column;
}

.nav-item {
margin-bottom: 10px;
}

/* Enhanced styles for tablets and up */
@media (min-width: 768px) {
.navigation {
flex-direction: row;
justify-content: space-between;
}

.nav-item {
margin-bottom: 0;
margin-right: 20px;
}
}

CSS Layout Best Practices

  1. Choose the right layout technique for the job:

    • Flexbox for one-dimensional layouts (rows or columns)
    • Grid for two-dimensional layouts (rows and columns)
    • Positioning for specific placement needs
  2. Create a consistent grid system to maintain alignment and spacing

  3. Use a mobile-first approach to ensure good performance on all devices

  4. Test on real devices whenever possible, not just browser resizing

  5. Consider accessibility in your layout choices (e.g., logical tab order)

  6. Avoid mixing layout techniques unnecessarily which can lead to unexpected results

  7. Use CSS custom properties (variables) for consistent spacing and breakpoints:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
:root {
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 2rem;
--breakpoint-sm: 576px;
--breakpoint-md: 768px;
--breakpoint-lg: 992px;
--breakpoint-xl: 1200px;
}

.container {
padding: var(--spacing-md);
}

@media (min-width: var(--breakpoint-md)) {
.container {
padding: var(--spacing-lg);
}
}

Learning Resources

  • MDN - CSS Layout
  • CSS-Tricks Guide to Flexbox
  • CSS-Tricks Guide to Grid
  • Responsive Web Design Fundamentals
  • Every Layout - Sophisticated layouts using simple CSS
  • Smashing Magazine - Responsive Design Articles
  • Web Development
  • CSS
  • Layout
  • Responsive Design
  • Flexbox
  • Grid

show all >>

CSS Fundamentals

  2023-06-03
字数统计: 843字   |   阅读时长: 5min

CSS Fundamentals

CSS (Cascading Style Sheets) is a stylesheet language used to describe the presentation of a document written in HTML. CSS controls the layout, colors, fonts, and overall visual appearance of web pages.

CSS Syntax

CSS consists of selectors and declarations:

1
2
3
4
selector {
property: value;
another-property: another-value;
}

For example:

1
2
3
4
h1 {
color: blue;
font-size: 24px;
}

Ways to Include CSS

1. External CSS

1
2
3
<head>
<link rel="stylesheet" href="styles.css">
</head>

2. Internal CSS

1
2
3
4
5
6
7
<head>
<style>
body {
background-color: #f0f0f0;
}
</style>
</head>

3. Inline CSS

1
<p style="color: red; font-size: 16px;">This is a paragraph.</p>

CSS Selectors

Selectors determine which elements the styles will be applied to.

Basic Selectors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* Element selector */
p {
color: gray;
}

/* Class selector */
.highlight {
background-color: yellow;
}

/* ID selector */
#header {
font-size: 32px;
}

/* Universal selector */
* {
margin: 0;
padding: 0;
}

Combinators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Descendant selector (elements inside other elements) */
article p {
font-style: italic;
}

/* Child selector (direct children only) */
ul > li {
list-style-type: square;
}

/* Adjacent sibling selector (element immediately after) */
h2 + p {
font-weight: bold;
}

/* General sibling selector (all siblings after) */
h2 ~ p {
color: purple;
}

Attribute Selectors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Elements with a specific attribute */
a[target] {
color: red;
}

/* Elements with specific attribute value */
input[type="text"] {
border: 1px solid gray;
}

/* Elements with attribute value containing a word */
[class~="card"] {
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

Pseudo-classes and Pseudo-elements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* Pseudo-classes (special states) */
a:hover {
text-decoration: underline;
}

button:focus {
outline: 2px solid blue;
}

li:first-child {
font-weight: bold;
}

li:nth-child(odd) {
background-color: #f2f2f2;
}

/* Pseudo-elements (specific parts of elements) */
p::first-line {
font-variant: small-caps;
}

p::before {
content: "→ ";
}

p::after {
content: " ←";
}

The CSS Box Model

Every HTML element is a box consisting of:

  • Content: The actual content of the element
  • Padding: Space between the content and the border
  • Border: A line around the padding
  • Margin: Space outside the border
1
2
3
4
5
6
div {
width: 300px;
padding: 20px;
border: 5px solid black;
margin: 30px;
}

By default, width and height only set the content dimensions. The total width of an element is:
width + padding-left + padding-right + border-left + border-right + margin-left + margin-right

Box-sizing Property

1
2
3
4
/* Makes width/height include content, padding, and border */
* {
box-sizing: border-box;
}

Typography

1
2
3
4
5
6
7
8
9
10
11
12
p {
font-family: 'Arial', sans-serif;
font-size: 16px;
font-weight: 400;
line-height: 1.5;
text-align: justify;
text-decoration: underline;
text-transform: uppercase;
letter-spacing: 1px;
word-spacing: 3px;
color: #333;
}

Web Fonts

1
2
3
4
5
6
7
@font-face {
font-family: 'MyCustomFont';
src: url('fonts/mycustomfont.woff2') format('woff2'),
url('fonts/mycustomfont.woff') format('woff');
}

@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');

Colors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.example {
/* Keyword */
color: red;

/* Hex */
background-color: #ff0000;

/* RGB */
border-color: rgb(255, 0, 0);

/* RGBA (with opacity) */
box-shadow: 0 0 10px rgba(255, 0, 0, 0.5);

/* HSL (hue, saturation, lightness) */
outline-color: hsl(0, 100%, 50%);

/* HSLA (with opacity) */
text-shadow: 1px 1px 3px hsla(0, 100%, 50%, 0.7);
}

CSS Cascade, Specificity, and Inheritance

Cascade

CSS rules cascade based on:

  1. Importance (!important)
  2. Specificity
  3. Source order (last defined rule wins)

Specificity

Specificity determines which rules take precedence:

  • Inline styles: 1000
  • IDs: 100
  • Classes, attributes, pseudo-classes: 10
  • Elements, pseudo-elements: 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Specificity: 1 */
p {
color: red;
}

/* Specificity: 11 (10 + 1) */
.content p {
color: blue;
}

/* Specificity: 101 (100 + 1) */
#main p {
color: green;
}

Inheritance

Some CSS properties are inherited from parent to child elements, like color, font-family, and text-align. Others, like margin and padding, are not.

1
2
3
4
5
6
7
8
body {
font-family: Arial, sans-serif; /* Inherited by all child elements */
color: #333; /* Inherited by all child elements */
}

h1 {
color: blue; /* Overrides inherited color */
}

CSS Variables (Custom Properties)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
--padding-standard: 15px;
}

.button {
background-color: var(--primary-color);
padding: var(--padding-standard);
}

.alert {
border-color: var(--secondary-color);
padding: calc(var(--padding-standard) * 2);
}

Best Practices

  1. Use a CSS reset or normalize.css to ensure consistent styling across browsers
  2. Follow a naming convention like BEM (Block Element Modifier)
  3. Organize CSS logically by grouping related styles
  4. Keep selectors simple to improve performance and maintainability
  5. Minimize use of !important as it breaks the natural cascade
  6. Use shorthand properties when appropriate
  7. Comment your code for complex sections
  8. Avoid inline styles except for dynamic content

Learning Resources

  • MDN Web Docs - CSS
  • CSS-Tricks
  • W3Schools CSS Tutorial
  • freeCodeCamp CSS Course

Understanding CSS fundamentals is essential before moving on to more advanced layout techniques like Flexbox and Grid.

  • Web Development
  • CSS
  • Styling

show all >>

HTML Basics

  2023-06-02
字数统计: 802字   |   阅读时长: 5min

HTML Basics

HTML (HyperText Markup Language) is the standard markup language for documents designed to be displayed in a web browser. It defines the structure and content of web pages.

Document Structure

A basic HTML document has this structure:

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document Title</title>
</head>
<body>
<!-- Content goes here -->
</body>
</html>
  • <!DOCTYPE html>: Declares the document type and HTML version
  • <html>: The root element of an HTML page
  • <head>: Contains meta-information about the document
  • <meta charset="UTF-8">: Specifies character encoding
  • <meta name="viewport">: Controls viewport behavior on mobile devices
  • <title>: Specifies the title shown in the browser tab
  • <body>: Contains the visible page content

Essential HTML Elements

Headings

HTML offers six levels of headings:

1
2
3
4
5
6
<h1>Main Heading</h1>
<h2>Secondary Heading</h2>
<h3>Tertiary Heading</h3>
<h4>Fourth Level Heading</h4>
<h5>Fifth Level Heading</h5>
<h6>Sixth Level Heading</h6>

Paragraphs

1
<p>This is a paragraph of text.</p>

Links

1
<a href="https://example.com">Link text</a>

Images

1
<img src="image.jpg" alt="Description of image">

Lists

Unordered list:

1
2
3
4
5
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>

Ordered list:

1
2
3
4
5
<ol>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ol>

Divisions and Spans

1
2
<div>A block-level container for grouping elements</div>
<span>An inline container for text</span>

Semantic HTML

Semantic elements clearly describe their meaning to both the browser and the developer:

1
2
3
4
5
6
7
<header>Document or section header</header>
<nav>Navigation links</nav>
<main>Main content area</main>
<article>Self-contained content</article>
<section>Thematic grouping of content</section>
<aside>Sidebar content</aside>
<footer>Document or section footer</footer>

Benefits of semantic HTML:

  • Improves accessibility
  • Enhances SEO
  • Makes code more maintainable

Forms

Forms allow user input:

1
2
3
4
5
6
7
8
9
<form action="/submit" method="post">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>

<label for="email">Email:</label>
<input type="email" id="email" name="email" required>

<input type="submit" value="Submit">
</form>

Common input types:

  • text: Single-line text input
  • email: Email address input
  • password: Password input
  • number: Numeric input
  • checkbox: Checkboxes for multiple selections
  • radio: Radio buttons for single selection
  • textarea: Multi-line text input
  • select: Dropdown selection

Tables

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<table>
<thead>
<tr>
<th>Header 1</th>
<th>Header 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Row 1, Cell 1</td>
<td>Row 1, Cell 2</td>
</tr>
<tr>
<td>Row 2, Cell 1</td>
<td>Row 2, Cell 2</td>
</tr>
</tbody>
</table>

HTML5 Features

  • Audio and Video: <audio> and <video> elements
  • Canvas: <canvas> for drawing graphics
  • SVG: Scalable Vector Graphics
  • Local Storage: Client-side data storage
  • Geolocation: Access to user’s location
  • Web Workers: Background processing
  • Drag and Drop: Native drag-and-drop functionality

Best Practices

  1. Use semantic elements whenever possible
  2. Validate your HTML using the W3C validator
  3. Keep markup clean and properly indented
  4. Provide alt text for all images
  5. Use descriptive link text instead of “click here”
  6. Keep accessibility in mind at all times
  7. Separate structure (HTML) from presentation (CSS)

Learning Resources

  • MDN Web Docs - HTML
  • W3Schools HTML Tutorial
  • HTML.com
  • freeCodeCamp HTML Course

HTML is the foundation of web development. Master these concepts before moving on to CSS and JavaScript.

  • Web Development
  • HTML

show all >>

Frontend Development Learning Path

  2023-06-01
字数统计: 206字   |   阅读时长: 1min

Frontend Development Learning Path

This learning path provides a structured approach to becoming proficient in frontend development. Follow these modules in sequence for a comprehensive understanding of frontend technologies and best practices.

Module 1: HTML & CSS Fundamentals

  • HTML Basics
  • CSS Fundamentals
  • CSS Layout & Responsive Design

Module 2: JavaScript Core

  • JavaScript Basics
  • JavaScript Advanced Concepts
  • ES6+ Features
  • Browser APIs & DOM Manipulation

Module 3: Frontend Frameworks

  • React Fundamentals
  • React Hooks & Advanced Patterns
  • State Management
  • Vue.js Overview

Module 4: Frontend Engineering

  • Build Tools & Module Bundlers
  • Testing Strategies
  • Performance Optimization
  • Deployment & CI/CD

Module 5: Advanced Topics

  • TypeScript
  • Web Security
  • Browser Rendering & Performance
  • Network & HTTP
  • Progressive Web Apps

Learning Approach

  1. Theory First: Understand the concepts before writing code
  2. Practice Projects: Build real projects to reinforce learning
  3. Code Review: Get feedback on your code from peers or mentors
  4. Stay Updated: Frontend technologies evolve rapidly - follow blogs, podcasts, and GitHub trends

Recommended Learning Resources

  • Official documentation (MDN, React docs, etc.)
  • Online platforms: freeCodeCamp, Frontend Masters, Codecademy
  • YouTube channels: Traversy Media, Web Dev Simplified, Academind
  • Books: “Eloquent JavaScript”, “You Don’t Know JS”

Start with Module 1 and progress sequentially. Each topic builds on previous knowledge, creating a solid foundation for frontend development expertise.

  • Learning Path
  • Web Development

show all >>

1004.Maximum continuity1Number III Maximum continuity1Number III

  2022-12-07
字数统计: 297字   |   阅读时长: 1min

Today’s daily question is too difficult,So find the problem by yourself。Today’s question is a hash table+Sliding window algorithm,虽然感觉只用了Sliding window algorithm。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

class Solution:
def longestOnes(self, nums: List[int], k: int) -> int:
k_mean = k
# Used to record how many arrays there are now
flag = 0
# Used to record the maximum land number
max_flag = 0
for start in range(len(nums)):
tail = start
while k >= 0 and tail <= len(nums) - 1:
if nums[tail] == 1:
tail += 1
flag += 1
elif nums[tail] == 0 and k > 0:
tail += 1
k -= 1
flag += 1
elif nums[tail] == 0 and k == 0:
k = k_mean
max_flag = max(max_flag, flag)
flag = 0
break
if tail == len(nums):
max_flag = max(max_flag, flag)
flag = 0
break
return max_flag

This is my approach at the beginning,Although the double pointer is used,But there is no flexibility,Very empty feeling,Very dry slide。
the following@Lincoln@Lincoln Big practice,Just as one record of yourself,不作为我的answer发表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution:
def longestOnes(self, nums: List[int], k: int) -> int:
"""
Thinking:1. k=0It can be understood as the problem of the maximum duplication sub -string
2. If the current window value-In the window1Number <= k: Then expand the window(right+1)
If the current window value-In the window1Number > k: Swipe the window to the right(left+1)
method:Hash table + Sliding window
"""
n = len(nums)
o_res = 0
left = right = 0
while right < n:
if nums[right]== 1: o_res += 1
if right-left+1- o_res > k:
if nums[left]== 1: o_res -= 1
left += 1
right += 1
return right - left

Look atLincolnBigThinking,very clearly,remember

  • Python
  • solved,answer

show all >>

<< 上一页1…131415

150 篇 | 131.7k
次 | 人
这里自动载入天数这里自动载入时分秒
2022-2025 loong loong | 新南威尔士龙龙号