JavaScript在Web编程中的核心要点
一、JavaScript 的基础语法
(一)变量声明与作用域
在 JavaScript 中,变量声明主要通过 var
、let
和 const
关键字来实现。var
是 ES5 及以前使用的声明方式,它存在函数作用域和变量提升的特性。例如:
function varScopeExample() {
console.log(x); // 输出 undefined,变量提升
var x = 10;
console.log(x); // 输出 10
}
varScopeExample();
而 let
和 const
是 ES6 引入的声明方式,它们具有块级作用域。let
声明的变量可以重新赋值,const
声明的常量一旦赋值就不能再改变。
function letScopeExample() {
if (true) {
let y = 20;
console.log(y); // 输出 20
}
console.log(y); // 报错,y 在此作用域未定义
}
letScopeExample();
function constExample() {
const PI = 3.14159;
// PI = 3.14; // 报错,常量不能重新赋值
console.log(PI); // 输出 3.14159
}
constExample();
(二)数据类型
- 基本数据类型
JavaScript 有六种基本数据类型:
string
、number
、boolean
、null
、undefined
和symbol
(ES6 新增)。
- 字符串(string):用于表示文本数据,用单引号或双引号包裹。
let str1 = 'Hello';
let str2 = "World";
- 数字(number):可以表示整数和浮点数。
let num1 = 10;
let num2 = 3.14;
- 布尔值(boolean):只有
true
和false
两个值。
let isDone = true;
let isFailed = false;
- 空值(null):表示一个空的对象引用,通常用于手动释放内存或初始化变量。
let myObject = null;
- 未定义(undefined):当变量声明但未赋值时,其值为
undefined
。
let someVar;
console.log(someVar); // 输出 undefined
- 符号(symbol):ES6 新增的数据类型,每个
symbol
值都是唯一的。常用于创建对象的唯一属性。
let sym1 = Symbol('description');
let obj = {};
obj[sym1] = 'value';
console.log(obj[sym1]); // 输出 'value'
- 引用数据类型
主要包括
Object
、Array
、Function
等。引用数据类型的值存储在堆内存中,变量保存的是指向堆内存中实际数据的引用。
- 对象(Object):是键值对的集合,用于组织和存储数据。
let person = {
name: 'John',
age: 30,
sayHello: function() {
console.log('Hello, I\'m'+ this.name);
}
};
person.sayHello(); // 输出 'Hello, I'm John'
- 数组(Array):用于存储有序的元素集合。
let numbers = [1, 2, 3, 4, 5];
console.log(numbers[0]); // 输出 1
- 函数(Function):是可执行的代码块,可以接收参数并返回值。
function add(a, b) {
return a + b;
}
let result = add(3, 5);
console.log(result); // 输出 8
(三)操作符
- 算术操作符
包括
+
、-
、*
、/
、%
等,用于基本的数学运算。
let a = 10;
let b = 3;
console.log(a + b); // 输出 13
console.log(a - b); // 输出 7
console.log(a * b); // 输出 30
console.log(a / b); // 输出 3.3333333333333335
console.log(a % b); // 输出 1
- 比较操作符
如
>
、<
、>=
、<=
、==
、===
、!=
、!==
等,用于比较两个值的大小或相等性。
let x = 5;
let y = 10;
console.log(x > y); // 输出 false
console.log(x < y); // 输出 true
console.log(x == y); // 输出 false
console.log(x === y); // 输出 false
console.log(x!= y); // 输出 true
console.log(x!== y); // 输出 true
- 逻辑操作符
&&
(逻辑与)、||
(逻辑或)、!
(逻辑非)。
let isTrue = true;
let isFalse = false;
console.log(isTrue && isFalse); // 输出 false
console.log(isTrue || isFalse); // 输出 true
console.log(!isTrue); // 输出 false
- 赋值操作符
=
用于赋值,还有复合赋值操作符如+=
、-=
、*=
、/=
等。
let num = 5;
num += 3; // 相当于 num = num + 3;
console.log(num); // 输出 8
- 位操作符
&
(按位与)、|
(按位或)、^
(按位异或)、~
(按位取反)、<<
(左移)、>>
(右移)、>>>
(无符号右移)。位操作符对二进制表示的数字进行操作。
let num1 = 5; // 二进制 00000101
let num2 = 3; // 二进制 00000011
console.log(num1 & num2); // 按位与,输出 1,二进制 00000001
console.log(num1 | num2); // 按位或,输出 7,二进制 00000111
console.log(num1 ^ num2); // 按位异或,输出 6,二进制 00000110
console.log(~num1); // 按位取反,输出 -6,二进制 11111010
console.log(num1 << 2); // 左移 2 位,输出 20,二进制 00010100
console.log(num1 >> 1); // 右移 1 位,输出 2,二进制 00000010
console.log(num1 >>> 1); // 无符号右移 1 位,输出 2,二进制 00000010
二、JavaScript 的函数与作用域链
(一)函数定义与调用
- 函数声明
function addNumbers(a, b) {
return a + b;
}
let sum = addNumbers(2, 3);
console.log(sum); // 输出 5
- 函数表达式
let subtractNumbers = function(a, b) {
return a - b;
};
let difference = subtractNumbers(5, 3);
console.log(difference); // 输出 2
- 箭头函数 ES6 引入的箭头函数语法更加简洁。
let multiplyNumbers = (a, b) => a * b;
let product = multiplyNumbers(4, 3);
console.log(product); // 输出 12
(二)函数的参数与返回值
- 参数 函数可以接受零个或多个参数。在 ES6 中,还支持默认参数值。
function greet(name = 'Guest') {
console.log('Hello,'+ name);
}
greet(); // 输出 'Hello, Guest'
greet('John'); // 输出 'Hello, John'
- 返回值
函数可以通过
return
语句返回一个值。如果没有return
语句,函数默认返回undefined
。
function square(x) {
return x * x;
}
let result = square(4);
console.log(result); // 输出 16
(三)作用域链
JavaScript 采用词法作用域(静态作用域),即函数的作用域在定义时就确定了,而不是在调用时确定。作用域链是由多个执行上下文的变量对象组成的链表。当访问一个变量时,JavaScript 引擎会首先在当前执行上下文的变量对象中查找,如果找不到,就会沿着作用域链向上查找,直到全局执行上下文。
let globalVar = 'global';
function outer() {
let outerVar = 'outer';
function inner() {
let innerVar = 'inner';
console.log(globalVar); // 输出 'global'
console.log(outerVar); // 输出 'outer'
console.log(innerVar); // 输出 'inner'
}
inner();
}
outer();
在这个例子中,inner
函数可以访问到自己作用域内的 innerVar
,以及其父作用域 outer
的 outerVar
和全局作用域的 globalVar
。
三、JavaScript 的对象与原型链
(一)对象的创建与属性操作
- 对象字面量创建
let car = {
brand: 'Toyota',
model: 'Corolla',
year: 2020
};
console.log(car.brand); // 输出 'Toyota'
- 使用
new
关键字创建对象
function Person(name, age) {
this.name = name;
this.age = age;
}
let person1 = new Person('Alice', 25);
console.log(person1.name); // 输出 'Alice'
- 属性操作
可以使用点语法(
.
)或方括号语法([]
)来访问和修改对象的属性。
let obj = {
key1: 'value1',
key2: 'value2'
};
console.log(obj.key1); // 输出 'value1'
console.log(obj['key2']); // 输出 'value2'
obj.key1 = 'new value1';
obj['key2'] = 'new value2';
console.log(obj.key1); // 输出 'new value1'
console.log(obj['key2']); // 输出 'new value2'
(二)原型链
每个 JavaScript 对象都有一个 [[Prototype]]
内部属性(在现代 JavaScript 中可以通过 __proto__
访问),它指向其原型对象。原型对象本身也是一个对象,也有自己的原型,这样就形成了一条原型链。当访问对象的属性或方法时,如果在当前对象中找不到,就会沿着原型链向上查找,直到找到该属性或方法,或者到达原型链的顶端(Object.prototype
)。
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name +'makes a sound.');
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log(this.name +'barks.');
};
let myDog = new Dog('Buddy', 'Golden Retriever');
myDog.speak(); // 输出 'Buddy makes a sound.'
myDog.bark(); // 输出 'Buddy barks.'
在这个例子中,Dog
继承自 Animal
,myDog
对象可以访问 Animal.prototype
上的 speak
方法和 Dog.prototype
上的 bark
方法。
四、JavaScript 在 DOM 操作中的应用
(一)获取 DOM 元素
- 通过 ID 获取
使用
document.getElementById()
方法,通过元素的id
属性获取单个元素。
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<div id="myDiv">This is a div</div>
<script>
let myDiv = document.getElementById('myDiv');
console.log(myDiv.textContent); // 输出 'This is a div'
</script>
</body>
</html>
- 通过标签名获取
document.getElementsByTagName()
方法返回一个包含所有指定标签名元素的 HTMLCollection。
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<script>
let listItems = document.getElementsByTagName('li');
for (let i = 0; i < listItems.length; i++) {
console.log(listItems[i].textContent);
}
</script>
</body>
</html>
- 通过类名获取
document.getElementsByClassName()
方法返回一个包含所有指定类名元素的 HTMLCollection。
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<div class="myClass">Div 1</div>
<div class="myClass">Div 2</div>
<script>
let divs = document.getElementsByClassName('myClass');
for (let i = 0; i < divs.length; i++) {
console.log(divs[i].textContent);
}
</script>
</body>
</html>
- 使用
querySelector
和querySelectorAll
document.querySelector()
方法返回匹配指定 CSS 选择器的第一个元素,document.querySelectorAll()
方法返回所有匹配指定 CSS 选择器的元素的 NodeList。
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<p class="highlight">Paragraph 1</p>
<p>Paragraph 2</p>
<script>
let firstHighlighted = document.querySelector('.highlight');
console.log(firstHighlighted.textContent); // 输出 'Paragraph 1'
let allParagraphs = document.querySelectorAll('p');
for (let i = 0; i < allParagraphs.length; i++) {
console.log(allParagraphs[i].textContent);
}
</script>
</body>
</html>
(二)修改 DOM 元素
- 修改文本内容
通过修改元素的
textContent
或innerHTML
属性来改变元素的文本内容。
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<p id="myPara">Original text</p>
<script>
let myPara = document.getElementById('myPara');
myPara.textContent = 'New text';
console.log(myPara.textContent); // 输出 'New text'
</script>
</body>
</html>
- 修改属性
使用
setAttribute()
方法来修改元素的属性。
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<img id="myImg" src="old.jpg" alt="Old Image">
<script>
let myImg = document.getElementById('myImg');
myImg.setAttribute('src', 'new.jpg');
myImg.setAttribute('alt', 'New Image');
</script>
</body>
</html>
- 添加和移除类
可以通过操作元素的
classList
属性来添加和移除类。
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
<style>
.highlight {
background - color: yellow;
}
</style>
</head>
<body>
<p id="myPara">Some text</p>
<script>
let myPara = document.getElementById('myPara');
myPara.classList.add('highlight');
setTimeout(() => {
myPara.classList.remove('highlight');
}, 3000);
</script>
</body>
</html>
(三)创建与插入 DOM 元素
- 创建元素
使用
document.createElement()
方法创建新的 DOM 元素。
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<div id="parentDiv"></div>
<script>
let newPara = document.createElement('p');
newPara.textContent = 'This is a new paragraph';
let parentDiv = document.getElementById('parentDiv');
parentDiv.appendChild(newPara);
</script>
</body>
</html>
- 插入元素
除了
appendChild()
方法,还可以使用insertBefore()
方法在指定元素之前插入新元素。
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<ul id="myList">
<li>Item 2</li>
<li>Item 3</li>
</ul>
<script>
let newItem = document.createElement('li');
newItem.textContent = 'Item 1';
let myList = document.getElementById('myList');
let firstChild = myList.firstChild;
myList.insertBefore(newItem, firstChild);
</script>
</body>
</html>
(四)事件处理
- 事件绑定 可以通过多种方式绑定事件处理函数。
- 直接在 HTML 标签中绑定
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<button onclick="handleClick()">Click me</button>
<script>
function handleClick() {
console.log('Button clicked');
}
</script>
</body>
</html>
- 通过 JavaScript 属性绑定
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<button id="myButton">Click me</button>
<script>
let myButton = document.getElementById('myButton');
myButton.onclick = function() {
console.log('Button clicked');
};
</script>
</body>
</html>
- 使用
addEventListener
方法绑定
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<button id="myButton">Click me</button>
<script>
let myButton = document.getElementById('myButton');
myButton.addEventListener('click', function() {
console.log('Button clicked');
});
</script>
</body>
</html>
- 事件对象 事件处理函数会接收到一个事件对象,该对象包含了与事件相关的信息,如鼠标位置、键盘按键等。
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<div id="myDiv">Click me</div>
<script>
let myDiv = document.getElementById('myDiv');
myDiv.addEventListener('click', function(event) {
console.log('Client X:'+ event.clientX);
console.log('Client Y:'+ event.clientY);
});
</script>
</body>
</html>
- 事件冒泡与捕获
事件在 DOM 树中传播有两种方式:冒泡和捕获。冒泡是从最具体的元素(触发事件的元素)开始,向上传播到 DOM 树的根节点;捕获则相反,从根节点开始,向下传播到最具体的元素。
addEventListener
方法的第三个参数可以指定是否使用捕获模式(true
表示捕获,false
表示冒泡,默认false
)。
<!DOCTYPE html>
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<div id="outerDiv">
<div id="innerDiv">Click me</div>
</div>
<script>
let outerDiv = document.getElementById('outerDiv');
let innerDiv = document.getElementById('innerDiv');
outerDiv.addEventListener('click', function() {
console.log('Outer div clicked (bubbling)');
});
innerDiv.addEventListener('click', function() {
console.log('Inner div clicked');
}, true);
outerDiv.addEventListener('click', function() {
console.log('Outer div clicked (capturing)', true);
}, true);
</script>
</body>
</html>
在这个例子中,当点击 innerDiv
时,会先触发 outerDiv
的捕获阶段事件,然后触发 innerDiv
的事件,最后触发 outerDiv
的冒泡阶段事件。
五、JavaScript 的异步编程
(一)回调函数
回调函数是 JavaScript 异步编程的基础。它是作为参数传递给另一个函数,并在该函数完成操作后被调用的函数。
function getData(callback) {
setTimeout(() => {
let data = { message: 'Data fetched' };
callback(data);
}, 2000);
}
getData((result) => {
console.log(result.message); // 输出 'Data fetched'
});
(二)Promise
Promise 是 ES6 引入的用于处理异步操作的对象。它有三种状态:pending
(进行中)、fulfilled
(已成功)和 rejected
(已失败)。
function asyncOperation() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let success = true;
if (success) {
resolve('Operation successful');
} else {
reject('Operation failed');
}
}, 2000);
});
}
asyncOperation()
.then((result) => {
console.log(result); // 输出 'Operation successful'
})
.catch((error) => {
console.log(error);
});
(三)async/await
async/await
是基于 Promise 的异步编程语法糖,使异步代码看起来更像同步代码。
async function main() {
try {
let result = await asyncOperation();
console.log(result); // 输出 'Operation successful'
} catch (error) {
console.log(error);
}
}
main();
(四)事件循环
JavaScript 是单线程语言,但通过事件循环机制实现异步操作。事件循环不断检查调用栈是否为空,如果为空,就从任务队列中取出一个任务放入调用栈执行。任务队列分为宏任务队列和微任务队列,微任务会在宏任务之前执行。
console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
// 输出:Start, End, Promise, Timeout
在这个例子中,console.log('Start')
和 console.log('End')
首先执行,然后 Promise.resolve().then()
中的回调函数被放入微任务队列,setTimeout()
的回调函数被放入宏任务队列。由于微任务先于宏任务执行,所以输出顺序为 Start
、End
、Promise
、Timeout
。
六、JavaScript 的模块化
(一)ES6 模块
ES6 引入了官方的模块化系统。模块可以导出变量、函数、类等,其他模块可以通过 import
语句导入。
- 导出模块
// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
- 导入模块
// main.js
import { add, subtract } from './math.js';
console.log(add(3, 5)); // 输出 8
console.log(subtract(5, 3)); // 输出 2
(二)CommonJS 模块
CommonJS 是 Node.js 中使用的模块化规范。通过 exports
或 module.exports
导出模块,使用 require
函数导入模块。
- 导出模块
// math.js
exports.add = (a, b) => a + b;
exports.subtract = (a, b) => a - b;
// 或者
module.exports = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
- 导入模块
// main.js
const math = require('./math.js');
console.log(math.add(3, 5)); // 输出 8
console.log(math.subtract(5, 3)); // 输出 2
(三)AMD 模块(Asynchronous Module Definition)
AMD 是为浏览器环境设计的模块化规范,主要用于异步加载模块。常用的实现库有 RequireJS。
- 定义模块
// math.js
define(['exports'], function(exports) {
exports.add = function(a, b) {
return a + b;
};
exports.subtract = function(a, b) {
return a - b;
};
});
- 加载模块
<!DOCTYPE html>
<html>
<head>
<title>AMD Example</title>
<script data-main="main.js" src="require.js"></script>
</head>
<body>
</body>
</html>
// main.js
require(['math'], function(math) {
console.log(math.add(3, 5)); // 输出 8
console.log(math.subtract(5, 3)); // 输出 2
});
(四)UMD 模块(Universal Module Definition)
UMD 模块旨在兼容多种模块规范,既可以在 Node.js 环境中作为 CommonJS 模块使用,也可以在浏览器环境中作为 AMD 模块或全局变量使用。
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD 环境
define(['exports'], factory);
} else if (typeof exports === 'object') {
// CommonJS 环境
factory(exports);
} else {
// 全局变量环境
root.math = factory({});
}
}(this, function (exports) {
exports.add = function (a, b) {
return a + b;
};
exports.subtract = function (a, b) {
return a - b;
};
}));
在不同的环境中,可以根据模块加载器的类型来加载 UMD 模块。这样的设计使得代码可以在多种环境下复用,提高了代码的可移植性。
通过对以上核心要点的深入理解和掌握,开发者能够更加熟练地运用 JavaScript 进行高效、健壮的 Web 编程。无论是构建简单的网页交互,还是复杂的单页应用程序,这些知识都将是坚实的基础。同时,随着 JavaScript 不断发展,如 ES 新特性的持续推出,开发者需要保持学习,以跟上技术的步伐,充分发挥 JavaScript 在 Web 编程中的强大能力。