目录
初步认识
JavaScript 是一种高级的、解释执行的编程语言,它最初是为了在网页浏览器中实现交互性而设计的。然而,随着其不断发展和完善,JavaScript 已经远远超出了这一范畴,成为了一种全能的编程语言,能够用于开发各种类型的应用程序,包括但不限于网页应用、服务器应用、桌面应用以及移动应用等。
JavaScript 的特点
-
解释型语言:JavaScript 是一种解释型语言,这意味着它不需要在执行之前进行编译。JavaScript 代码在执行时,会由 JavaScript 引擎一行一行地解释执行。
-
面向对象:虽然 JavaScript 是一种基于原型的语言,但它支持面向对象的编程范式,包括封装、继承和多态等特性。
-
动态类型:JavaScript 是一种动态类型语言,变量在声明时不需要指定类型,其类型会在运行时根据赋值自动确定。
-
事件驱动:JavaScript 最初是为处理网页上的用户交互而设计的,因此它天生就支持事件驱动编程。用户的行为(如点击、滚动等)可以触发 JavaScript 代码的执行。
-
跨平台:由于几乎所有的现代浏览器都支持 JavaScript,因此它可以在不同的操作系统和浏览器上运行,实现了跨平台的能力。
-
丰富的库和框架:JavaScript 社区非常活跃,拥有大量的库和框架,如 jQuery、React、Vue.js、Angular 等,这些工具极大地简化了 Web 开发工作,提高了开发效率。
JavaScript 的应用场景
-
网页开发:JavaScript 是 Web 开发的核心技术之一,用于实现网页的动态效果和交互性。
-
服务器端开发:随着 Node.js 的出现,JavaScript 也可以用于服务器端开发,实现高性能的 Web 服务器和实时应用。
-
桌面应用开发:通过 Electron、NW.js 等框架,JavaScript 可以用来开发跨平台的桌面应用程序。
-
移动应用开发:React Native、Flutter(虽然不是纯 JavaScript,但支持 Dart 和 JavaScript)等框架允许开发者使用 JavaScript 来开发移动应用。
-
游戏开发:虽然 JavaScript 不是专门的游戏开发语言,但通过 Phaser、Three.js 等库,开发者也可以用它来创建 2D 和 3D 游戏。
-
物联网(IoT):JavaScript 也可以用于物联网设备的编程和控制,尤其是在与 Node.js 结合使用时。
优点
-
广泛的浏览器支持:几乎所有现代浏览器都内置了对 JavaScript 的支持,这意味着你可以使用 JavaScript 开发的网站或应用能够在大多数用户的设备上无缝运行。
-
丰富的生态系统:JavaScript 拥有一个庞大且活跃的社区,提供了大量的库、框架和工具,如 React、Vue、Angular、jQuery、Node.js 等,这些工具极大地简化了开发过程,提高了开发效率。
-
全栈开发能力:JavaScript 不仅可以用于前端开发,还可以通过 Node.js 等技术栈用于服务器端开发,实现全栈开发能力,这有助于开发者更全面地掌握项目,并减少不同技术栈之间的切换成本。
-
动态类型:JavaScript 的动态类型特性使得它非常灵活,可以在运行时改变变量的类型,这在某些情况下可以提高开发效率。
-
异步处理能力:JavaScript 支持异步编程,这对于处理 I/O 密集型任务(如网络请求、文件读写等)非常有用,可以提高应用的性能和响应能力。
-
易于学习:JavaScript 的语法相对简单,且其面向对象的概念也易于理解,这使得它成为许多初学者的首选编程语言。
缺点
-
性能问题:虽然 JavaScript 的性能在不断提升,但在处理复杂计算或大规模数据处理时,其性能可能不如编译型语言(如 C++、Java)。此外,由于 JavaScript 是单线程的,长时间运行的脚本可能会阻塞 UI 更新,影响用户体验。
-
安全性问题:JavaScript 运行在客户端,这意味着它可以被用户直接查看和修改。如果开发者没有采取适当的安全措施,JavaScript 代码可能会受到 XSS(跨站脚本攻击)等安全威胁。
-
全局变量污染:JavaScript 允许在全局作用域中声明变量,这可能导致全局变量污染的问题,特别是在大型项目中,这可能会使代码难以维护和理解。
-
错误处理复杂:JavaScript 的错误处理机制相对复杂,特别是当涉及到异步代码时。虽然 ES6 引入了 Promise 和 async/await 等新的异步处理机制,但错误处理仍然是 JavaScript 开发中的一个挑战。
-
回调地狱(Callback Hell):在 ES6 之前的 JavaScript 中,处理异步操作通常需要使用回调函数。当这些回调嵌套过多时,代码会变得难以阅读和维护,这被称为“回调地狱”。虽然 Promise 和 async/await 提供了更好的解决方案,但回调地狱仍然是 JavaScript 历史上的一个痛点。
-
浏览器兼容性:虽然现代浏览器对 JavaScript 的支持已经非常好,但仍然存在一些旧的浏览器或特定的浏览器环境可能不支持某些新特性或存在兼容性问题。这要求开发者在开发过程中注意测试不同浏览器环境下的表现,并可能需要使用 polyfills 或 Babel 等工具来确保兼容性。
JavaScript引入方式
1. 行内引入(内联脚本)
- 方式:直接在HTML标签的事件属性(如onclick、onmouseover等)中编写JavaScript代码。这种方式通常用于简单的交互效果,但不建议在生产环境中大量使用,因为它会导致HTML和JavaScript代码紧密耦合,不利于代码的维护和管理。
- 示例:
<button onclick="alert('按钮被点击了!')">点击我</button>
2. 内部引入(内嵌脚本)
- 方式:在HTML文档的
<head>
或<body>
部分,通过<script>
标签直接编写JavaScript代码。这种方式适合页面特有的、不需要复用的JavaScript代码。通常,为了提高页面加载速度,会将<script>
标签放在<body>
标签的底部。 - 示例:
<script> alert('这是内部引入的JavaScript代码!'); </script>
3. 外部引入
- 方式:将JavaScript代码编写在独立的
.js
文件中,然后在HTML文档中使用<script>
标签的src
属性引入该.js
文件。这种方式有利于代码的复用和维护,特别是当JavaScript代码量较大时,可以将其拆分成多个文件进行管理。 - 示例:
- 首先,创建一个名为
example.js
的JavaScript文件,并编写代码:// example.js 文件内容 alert('这是外部引入的JavaScript代码!');
- 然后,在HTML文件中引入该JavaScript文件:
<script src="example.js"></script>
- 首先,创建一个名为
4. 动态加载(可选)
- 方式:使用JavaScript的
createElement
和appendChild
方法动态地在HTML文档中创建并插入<script>
标签,然后指定其src
属性为要加载的JavaScript文件的路径。这种方式可以在页面加载完成后,根据用户的交互或其他条件动态地加载JavaScript代码。 - 示例:
var script = document.createElement('script'); script.src = 'dynamic.js'; document.body.appendChild(script);
5. 使用defer或async属性(可选)
- 方式:在
<script>
标签中使用defer
或async
属性来控制JavaScript文件的加载和执行时机。defer
属性表示脚本将在文档完全解析和显示后执行,而async
属性表示脚本将异步加载并执行,不会阻塞页面的解析和渲染。 - 示例:
- 使用
defer
属性:<script src="defer.js" defer></script>
- 使用
async
属性:<script src="async.js" async></script>
- 使用
注意:
<script>
标签在 HTML 中通常不被视为自闭合标签(也称为空元素或自结束标签)。自闭合标签通常用于那些不需要结束标签的 HTML 元素,比如<img />
、<br />
或<hr />
(尽管在 HTML5 中,<br>
和<hr>
可以省略斜杠/
,但理论上它们仍然被视为自闭合标签)。
<script>
标签用于包含或引用 JavaScript 代码,它需要有一个明确的开始标签<script>
和一个结束标签</script>
。这是因为<script>
标签内部可能会包含大量的 JavaScript 代码,这些代码需要被正确地包含在标签内部,以便浏览器能够解析和执行它们。如果你尝试使用自闭合的形式来编写
<script>
标签,比如<script src="script.js" />
,大多数浏览器会忽略结束标签中的斜杠/
,并尝试解析紧随其后的内容作为 JavaScript 代码,直到遇到真正的</script>
结束标签为止。然而,这种写法并不是标准的 HTML 写法,可能会导致跨浏览器兼容性问题或解析错误。
书写语法
区分大小写
JavaScript是区分大小写的语言。这意味着变量名、函数名、关键字(如if
、for
、var
等)以及其他标识符在JavaScript中都是大小写敏感的。例如,变量name
和Name
会被视为两个不同的变量。
let name = "John";
let Name = "Jane";
console.log(name); // 输出: John
console.log(Name); // 输出: Jane
每行结尾的分号
在JavaScript中,分号(;
)用于语句的结束。虽然大多数时候JavaScript引擎能够自动推断语句的结束(这称为自动分号插入,ASI),但在某些情况下,省略分号可能会导致意外的行为或错误。因此,为了代码的清晰和避免潜在的错误,建议在每个语句的末尾显式地添加分号。
let x = 5;
let y = 10;
// 虽然下面这行没有分号,但通常JavaScript引擎能正确处理
let z = 15
然而,在编写返回语句、使用++
或--
运算符等情况下,省略分号可能会导致问题。
注释
JavaScript支持两种类型的注释:
- 单行注释:以
//
开始,直到行尾的所有内容都被视为注释。// 这是一个单行注释 let a = 5; // 这也是注释,位于行尾
- 多行注释(或块注释):以
/*
开始,以*/
结束,可以跨越多行。/* 这是一个 多行注释 */ let b = 10; /* 这里可以写很多 行的注释 */
大括号表示代码块
在JavaScript中,大括号{}
用于定义代码块。代码块是一组语句,它们被视为一个单元。代码块在控制流语句(如if
、for
、while
等)中非常有用,因为它们允许你定义一组语句,这些语句仅在特定条件下执行。
if (true) {
console.log("条件为真");
// 这里是代码块的一部分
}
for (let i = 0; i < 5; i++) {
console.log(i);
// 这也是代码块的一部分
}
在函数定义中,大括号也用于包围函数体。
function greet(name) {
console.log("Hello, " + name);
// 函数体的一部分
}
变量声明
JavaScript使用var
、let
和const
来声明变量。var
声明的变量作用域为函数作用域或全局作用域,而let
和const
(ES6+)声明的变量具有块级作用域。
var x = 5; // 函数作用域或全局作用域
let y = 10; // 块级作用域
const PI = 3.14; // 块级作用域,且值不可变
JavaScript是一门弱类型语言,变量可以存放不同类型的值。
变量名需要遵循如下规则:
- 组成字符可以是任何字母 ,数字,下划线或美元符号$
- 数字不能开头
- 建议使用驼峰命名
数据类型
JavaScript是动态类型语言,变量可以在运行时改变类型。JavaScript的基本数据类型包括:String
、Number
、BigInt
、Boolean
、Symbol
(ES6+)、Undefined
和Null
。此外,还有Object
类型,它是所有非基本数据类型的统称。
let name = "Alice"; // String
let age = 30; // Number
let isStudent = false; // Boolean
let nothing = null; // Null
let something = undefined; // Undefined
let bigNumber = BigInt("123456789012345678901234567890"); // BigInt
let symbol = Symbol("description"); // Symbol
typeof
运算符在 JavaScript 中可以用来获取一个变量或值的类型。然而,需要注意的是,typeof
并不是在所有情况下都能提供完全准确的数据类型信息。它主要用于区分基本数据类型(如字符串、数字、布尔值、undefined
、null
和 symbol
(在 ES6 中引入))和对象(包括数组、函数、日期等复杂数据类型,它们都会被 typeof
归类为 "object"
)。
console.log(typeof 123); // "number"
console.log(typeof 'Hello'); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" (注意:这是 JavaScript 的一个历史遗留问题)
console.log(typeof {name: 'John'}); // "object"
console.log(typeof [1, 2, 3]); // "object" (数组也是对象类型)
console.log(typeof function(){}); // "function"
console.log(typeof Symbol('id')); // "symbol"
从上面的例子中可以看到,typeof
对于基本数据类型和函数类型给出了较为准确的描述,但对于 null
和对象(包括数组)都返回了 "object"
,这可能会导致一些混淆。特别是 null
被视为 "object"
是一个历史遗留问题,并不是因为它实际上是一个对象。
为了更准确地识别对象的具体类型(比如区分一个变量是数组还是普通对象),你可能需要使用其他方法,如 Array.isArray()
方法来检查一个值是否是数组,或者使用 instanceof
运算符来检查一个对象是否是某个构造函数的实例。
console.log(Array.isArray([1, 2, 3])); // true
console.log({} instanceof Object); // true
console.log([] instanceof Array); // true
这些方法提供了比 typeof
更精细的类型检测能力。
运算符
1. 算术运算符
- 加法 (
+
): 用于两个数相加或字符串连接。 - 减法 (
-
): 用于两个数相减。 - 乘法 (
*
): 用于两个数相乘。 - 除法 (
/
): 用于两个数相除。 - 模运算符 (
%
): 返回两个数相除的余数。 - 递增 (
++
): 增加变量的值(前置或后置)。 - 递减 (
--
): 减少变量的值(前置或后置)。
2. 赋值运算符
- 简单赋值 (
=
): 将右侧的值赋给左侧的变量。 - 复合赋值 (如
+=
,-=
,*=
,/=
): 结合算术运算符和赋值运算符。
3. 比较运算符
- 等于 (
==
): 比较两个值是否相等(类型不同时会尝试转换)。 - 严格等于 (
===
): 比较两个值及其类型是否完全相同。 - 不等于 (
!=
): 比较两个值是否不相等(类型不同时会尝试转换)。 - 严格不等于 (
!==
): 比较两个值及其类型是否不同。 - 大于 (
>
), 小于 (<
), 大于等于 (>=
), 小于等于 (<=
): 用于比较两个数字或字符串(字符串比较基于字典顺序)。
4. 逻辑运算符
- 逻辑与 (
&&
): 仅当两个操作数都为真时返回真。 - 逻辑或 (
||
): 当任一操作数为真时返回真。 - 逻辑非 (
!
): 反转操作数的真假值。
5. 位运算符
位运算符对整数的二进制表示进行操作,如 &
(按位与)、|
(按位或)、^
(按位异或)、~
(按位非)、<<
(左移)、>>
(有符号右移)、>>>
(无符号右移)。
6. 三元运算符
- 条件(三元)运算符 (
? :
):条件 ? 表达式1 : 表达式2
。如果条件为真,则评估表达式1,否则评估表达式2。
7. 字符串运算符
- 字符串连接 (
+
): 当+
运算符用于字符串时,它将它们连接成一个新字符串。
8. 类型运算符
typeof
: 返回操作数的数据类型。instanceof
: 用来检测一个对象在其原型链中是否存在一个构造函数的prototype
属性。
let a = 10;
let b = 20;
// 算术运算符
let sum = a + b; // 30
let product = a * b; // 200
// 赋值运算符
a += 5; // a 现在为 15
// 比较运算符
let isEqual = (a == b); // false
let isStrictlyEqual = (a === b); // false
// 逻辑运算符
let isBigger = (a > b) ? "a is bigger" : "b is bigger"; // "b is bigger"
// 字符串运算符
let greeting = "Hello, " + "world!"; // "Hello, world!"
// 类型运算符
console.log(typeof a); // "number"
console.log(a instanceof Number); // false,因为 a 是一个 Number 类型的原始值,不是 Number 对象
类型转换
JavaScript 中的类型转换(Type Conversion)是一种将值从一种类型转换为另一种类型的过程。这种转换可以是隐式的(自动发生的),也可以是显式的(通过特定的方法或函数)。以下是一些关于 JavaScript 类型转换的基本概念和示例。
1. 隐式类型转换
隐式类型转换是在某些操作符或函数调用中自动发生的类型转换。
字符串连接
当使用 +
操作符进行字符串连接时,如果任一操作数是字符串,则另一个操作数将被转换为字符串。
let num = 10;
let str = "The number is " + num; // "The number is 10"
逻辑运算
在逻辑运算(如 &&
、||
和 !
)中,JavaScript 会将操作数转换为布尔值。
let zero = 0;
let result = zero || "not zero"; // "not zero",因为 0 被视为 falsy
数字运算
在进行数学运算时,如果操作数不是数字,JavaScript 会尝试将它们转换为数字。
let num = "10";
let result = num * 2; // 20,因为 "10" 被转换成了数字 10
2. 显式类型转换
显式类型转换是通过 JavaScript 提供的函数或方法来手动完成的。
使用 String()
let num = 10;
let str = String(num); // "10"
使用 Number()
let str = "10";
let num = Number(str); // 10
// 如果字符串不能转换为数字,则结果为 NaN
let notANumber = Number("Hello"); // NaN
使用 Boolean()
let zero = 0;
let truthy = Boolean(zero); // false
let nonEmptyStr = "Hello";
let truthyStr = Boolean(nonEmptyStr); // true
使用 parseInt()
和 parseFloat()
这两个函数用于从字符串中提取数字。parseInt()
提取整数部分,而 parseFloat()
提取浮点数部分。
let numStr = "10.5";
let intNum = parseInt(numStr); // 10
let floatNum = parseFloat(numStr); // 10.5
3. 一元加号运算符
一元加号运算符 +
也可以用于将变量显式转换为数字。
let str = "10";
let num = +str; // 10
4. 注意事项
- 在进行类型转换时,需要特别注意 JavaScript 的类型转换规则,以避免意外的结果。
- 隐式类型转换可能会导致代码难以理解和维护,因此建议尽可能使用显式类型转换。
- 在处理字符串和数字时,始终要考虑到
NaN
和Infinity
等特殊值的可能性。 - 将字符串类型转为数字。如果字面值不是数字,则转为NaN。
- 其他类型转为boolean:Number只有0和NaN为false,其他均为true。String为空字符串时是false,其他均为true。Null和undefined均转为false。
5.测试
alert(parseInt("12"));//12
alert(parseInt("12A45"));//12
alert(parseInt("A12"));//NaN(not a number)
控制流语句
JavaScript提供了多种控制流语句,如if...else
、switch
、for
、while
、do...while
等,用于控制代码的执行流程。
if (age >= 18) {
console.log("成年人");
} else {
console.log("未成年人");
}
for (let i = 0; i < 5; i++) {
console.log(i);
}
函数
JavaScript中的函数是一等公民,可以像变量一样被赋值、传递和返回。函数可以声明为具名函数或匿名函数(常作为回调使用)。
function greet(name) {
console.log("Hello, " + name);
}
//function是函数的关键字
const sayHello = function(name) {
console.log("Hi, " + name);
};
greet("Alice");
sayHello("Bob");
数组和对象
JavaScript中的数组是特殊的对象,用于存储一系列的值。对象用于存储键值对集合。
let fruits = ["Apple", "Banana", "Cherry"];
let person = {
name: "Alice",
age: 30,
greet: function() {
console.log("Hello, my name is " + this.name);
}
};
person.greet();
模板字符串(ES6+)
模板字符串允许你嵌入表达式,并使用多行字符串和字符串插值功能。
let name = "Alice";
let greeting = `Hello, ${name}!`;
console.log(greeting);
箭头函数(ES6+)
箭头函数提供了一种更简洁的方式来写函数表达式。
const add = (a, b) => a + b;
console.log(add(5, 3));
异步编程
JavaScript是单线程的,但它提供了多种处理异步操作的方式,如回调函数、Promises、async/await等。
async function fetchData() {
let response = await fetch('https://api.example.com/data');
let data = await response.json();
console.log(data);
}
fetchData();
篇幅有点长了,剩下的下一篇再介绍吧。