JavaScript基础阶段一
JavaScript介绍
JavaScript(是什么?)
是一种运行在客户端(浏览器)的编程语言,实现人机交互效果。
作用(做什么?)
—— 网页特效(监听用户的一些行为让网页作出对应的发馈)
—— 表单验证(针对表单数据的合法性进行判断)
—— 数据交互(获取后台的数据,渲染到前端)
js初体验
JavaScript的书写位置
JavaScript可以放在body部分也可以放在head部分
外部 JavaScript 的优势
在外部文件中放置脚本有如下优势:
分离了 HTML 和代码
使 HTML 和 JavaScript 更易于阅读和维护
已缓存的 JavaScript 文件可加速页面加载
实例方法
Window.alert(message(返回值))方法
参数:message
显示在对话框中的字符串,传入其他类型值,也会转会成字符串
1 |
|
外部引用
1 | <body> |
示例
JavaScript 显示方案
JavaScript 能够以不同方式“显示”数据:
使用 window.alert() 写入警告框
使用 document.write() 写入 HTML 输出
使用 innerHTML 写入 HTML 元素
使用 console.log() 写入浏览器控制台
1 | <script> |
1 | <script> |
JavaScript 关键词
JavaScript 语句常常通过某个关键词来标识需要执行的 JavaScript 动作。
JavaScript 变量
- 变量声明
在 JavaScript 中,您可以使用 let、const 和 var 关键字来声明变量。
let: 这是最常见的关键字,用于临时声明变量。
1 | let age = 25; // 变量 name 是一个字符串类型的值 25 |
const: 该关键字用于最终声明变量。一旦赋值给 const,您就不能再修改该变量的值。
1 | const PI = 3.14159; // 变量 PI 是一个数字类型的值 3.14159 |
var: 该关键字用于声明全局变量。不过,由于 var 的使用频率非常高,现在很少使用它。
1 | var greeting = "Hello, World!"; // 变量 greeting 是一个字符串类型的值 Hello, World! |
- 变量类型
JavaScript 不强制指定变量的类型。您可以在声明时直接赋值给变量。
1 | let num = 10; // num 是数字类型 |
如果在没有明确类型的情况下,JavaScript 会根据赋值的内容推断变量的类型。
- 变量的作用域
全局变量: 使用 let 或 var 声明的变量属于全局作用域。
1 | let a = 10; |
局部变量: 使用 let 和函数括号 () 声明的变量属于该函数的作用域(局部作用域)。
1 | function foo() { |
块级变量: 使用 const 声明的变量属于该块级元素的作用域(块级作用域)。
- 变量的赋值
JavaScript 提供多种方式来赋值给变量:
1 | // 直接赋值 |
- 注意事项
JavaScript 的变量是弱引用,这意味着它们存储的是内存地址,而不是直接的值。如果主线程中的脚本退出,这些变量会丢失。
使用 let 和 const 声明变量时,默认情况下会在全局作用域或当前块级中声明变量。
示例代码
1 | // 变量声明和赋值示例 |
总结
JavaScript 的变量系统非常灵活,允许您在运行时动态地声明和修改变量。使用 let 和 const 是现代 JavaScript 开发中常用的实践,它们分别用于临时变量和最终声明变量。
JavaScript 数组
一、数组的创建与初始化
空数组
初始化一个空数组非常简单,可以通过以下方式:
1 | let arr = []; // 创建一个空数组 |
初始化数组
使用[]构造函数初始化数组:
1 | let arr = [](); // 初始化一个空数组 |
将多个元素放入数组:
1 | let fruits = ['苹果', '香蕉', '橘子']; // 直接将元素赋值给数组 |
利用字符串拆分
可以通过split()方法将一个字符串按指定分隔符分割成多个元素:
1 | let str = "one,two,three"; |
推、弹出和删除数组
使用push()向数组末尾添加元素:
1 | arr.push('apple'); |
使用pop()移除并返回数组最后一个元素:
1 | let lastElement = arr.pop(); // 返回'apple' |
使用unshift()移除并返回数组第一个元素(只能添加到数组头部):
1 | let firstElement = arr.unshift('banana'); // 结果:['banana', 'one'] |
数组长度属性
获取或设置数组的长度:
1 | console.log(arr.length); // 输出当前数组长度 |
处理超出索引范围的情况
使用arr[undefined]或arr[number outside of bounds]时会返回undefined:
1 | console.log(arr[10]); // 输出undefined(假设arr有3个元素) |
要避免这样的问题,可以使用for…of循环或者检查索引的有效性。
二、数组的遍历与操作
使用for…in循环
对数组中的每个键进行遍历:
1 | for (let key in arr) { |
使用forEach()方法
遍历并执行某个函数操作:
1 | arr.forEach((element, index) => { |
条件遍历与过滤
使用filter()创建一个新的数组,包含所有符合条件的元素:
1 | let evenNumbers = arr.filter(num => num % 2 === 0); // 筛选出偶数 |
find()和findIndex()方法用于查找满足条件的第一个元素:
1 | const firstEven = arr.find(num => num % 2 === 0); |
高阶数组操作
map():创建一个新数组,将原数组中的每个元素转换成新的形式:
1 | let squaredNumbers = arr.map(num => num * num); // 筛选出平方后的数 |
reduce()(或fold):将数组中的所有元素依次累积,得到一个单一的返回值:
1 | const sum = arr.reduce((acc, current) => acc + current, 0); |
遍历索引与值对
使用indexOf()和lastIndexOf()方法查找特定值的位置:
1 | let index = arr.indexOf('apple'); // 查找第一个出现的'apple' |
三、数组的扩展与裁剪
数组扩展
使用unshift()将元素添加到数组头部:
1 | arr.unshift('new element'); |
使用push()或直接赋值的方式在数组末尾增加元素:
1 | arr.push('add element'); // 或者 arr = [1,2,3]; |
数组裁剪
通过截取操作符(…)移除数组中的某些元素:
1 | const newArr = [...arr, 'a', ...arr]; // 在原数组末尾添加与之相同的元素 |
删除重复项
使用unique()方法去除数组中所有重复的值,保留第一个出现的位置:
1 | let uniqueArray = arr.unique(); // 去除重复项并返回新数组 |
四、处理特殊情况
空值和无效索引
使用hasOwnProperty()检查是否是自己具有的属性,避免访问不存在的元素:
1 | if (arr.hasOwnProperty('index')) { |
使用for…of循环遍历数组
对数组中的每个元素进行处理,较为简洁和高效的方式:
1 | for (const element of arr) { |
解决NaN类型的问题
遇到无法被转换为数字的字符串时,使用Number()函数或强制类型转换:
1 | const num = Number('apple'); // 结果:NaN(表示无效) |
避免数组未初始化问题
使用|| null强制进行类型转换和赋值:
1 | let element = undefined || arr[undefined] ? '默认值' : undefined; |
五、优化与性能考虑
避免重复计算
在频繁操作数组时,尽量提前处理数据或缓存结果,减少不必要的计算。
使用内置方法代替循环
对于需要遍历和操作的情况,优先使用forEach(), map(), filter()等内置方法,这些方法经过优化,比自定义循环更高效。
避免不必要的数组创建
多次使用push()或unshift()会导致多个小数组合并成一个大数组,可以考虑一次性完成所有操作。
处理多维数组时的性能优化
如果频繁操作二维或三维数组,可以尝试将它们转换为单维数组处理,并根据具体需求进行分析和优化。
六、总结
创建与初始化:灵活使用[], push(), unshift()等方法。
遍历:利用for…in, forEach(), indexOf(), lastIndexOf()等方法高效处理数组元素。
高阶操作:熟练运用map(), filter(), reduce(), find()和findIndex()进行复杂的数据转换与查询。
扩展与裁剪:合理使用数组扩展和裁剪方法,避免不必要的重复计算。
使用 const 关键字声明变量
使用 const 关键字声明的变量是不可变的,一旦赋值后不能再被修改。
1 | const PI = 3.14; |
使用 !+ 运算符和 let 关键字
使用 !+ 运算符可以强制变量为数值类型,但仍然可以通过后续赋值来修改其值。
1 | let greeting = !+ "Hello"; // 将字符串转为数字并存储到 `greeting` 变量中。 |
使用 var 关键字
使用 var 关键字声明的变量在现代JavaScript中不被推荐,因为它可能导致代码不可变。建议使用 const 或 let。
1 | var PI = 3.14; // 这条语句会报错,因为 `var` declare immutable variable |
总结:
在JavaScript中,“常量”通常指的是使用 const 关键字声明的不可变变量。
使用 !+ 运算符可以在当前语境下强制变量为数值类型,但并不阻止后续赋值修改该变量的值。
js数据类型分为两大类
Primitive 类型:
这些是不可变的数据类型,包括:
number(数字)
string(字符串)
boolean(布尔值)
null
undefined
symbol(符号值)
map(映射)
Object 对象:
这些是可变的,可以包含属性,并且在运行时可以被修改。构造出来的对象属于这一类。
详细说明:
Primitive 类型:这些是不可变的值,一旦赋值后无法再改变。例如:
1 | let num = 5; // 原始数值类型 |
Object 对象:这些是可以被修改和扩展的对象。例如:
1 | const obj = {}; |
注意事项:
null 和 undefined 是两种不同的值,null 表示没有值,而 undefined 表示变量未被赋值。
symbol 类型主要用于性能优化和不可变属性的实现。
模板字符串
模板字符串(Template Literals)
定义:一种语法糖,允许在字符串内直接嵌入表达式或变量,而无需使用拼接符或其他方法手动拼接。
语法结构:使用反斜杠\$开始模板字符串,并在其内部插入变量或表达式的计算结果。
示例:
1 | const name = "Alice"; |
优势:简化手动拼接字符串的操作,提高代码的简洁性和可读性。
注意事项:
反斜杠前面要加反斜杠(如\$)以表示模板字符串开始。
忽略符号问题时,记得检查引号和反斜杠是否匹配。
类型转换
JavaScript 可以将不同类型的数据相互转换:
使用 typeof 操作符可以获取变量的类型。
使用布尔值 (true/false) 可以强制类型转换。
1 | let str = "42"; |
- 布尔值和 NaN
NaN 表示“不是一个数字”,它是一个特殊的数值类型。布尔值 (true/false) 是特殊类型的变量,它们是 boolean 类型。1
2console.log(NaN); // 输出: NaN
console.log(NaN === NaN); // 输出: false (因为NaN不能比较)
隐式转换(Implicit Conversion)和显式转换(Explicit Conversion)是JavaScript中处理数据类型时的两种方式。以下是它们的区别及示例:
隐式转换:
定义: 隐式转换是指在编程过程中,不同数据类型的变量或值会自动地转换为相同的数据类型,以便进行运算或操作。
例子:
1 | // 隐式转换示例 1: 字符串和数字相加 |
这里,“5”被隐式地转换为数字5,然后与3相加。
1 | // 隐式转换示例 2: 字符串比较 |
这里,“apple”被隐式地转换为字符串“apple”,与“banana”进行比较。
显式转换:
定义: 显式转换是指通过特定的函数或运算符将数据从一种类型强制转换到另一种类型。
示例:
1 | // 显式转换示例 1: 使用Number()函数将字符串转换为数字 |
1 | // 显式转换示例 2: 使用强制类型转换运算符++ |
总结:
隐式转换在编程过程中自动发生,无需额外的代码。它适用于运算和比较操作。
显式转换需要通过特定函数或运算符来完成,可以更精确地控制数据类型,但可能增加代码复杂性。
逻辑运算符与优先级
值解释(Value Interpretation)
在 JavaScript 中,许多操作都会涉及对不同类型的值进行“值解释”。这种解释过程确保运算可以在统一的数据类型下执行。
示例:
比较运算符:当使用 == 或 === 进行比较时,JavaScript 会将两个操作数转换为相同的数据类型(通常是数值或字符串)后再进行比较。
1 | console.log("5" == 5); // 输出:true (值解释) |
字符串拼接:当使用 + 运算符将一个数值和一个字符串相加时,JavaScript 会将数值转换为字符串。
1 | console.log(3 + "2"); // 输出:"5" |
布尔运算:在条件运算中(如 &&, ||, 和 !),JavaScript 会将某些值解释为布尔值。例如,数字、空字符串、数组等会被转换为 true 或 false。
1 | console.log(0 || "hello"); // 输出:"hello" |
总结:
值解释确保运算可以在一致的数据类型下执行。
不同类型的值在运算中会自动转换,但这种转换是隐式的,不是显式的。
逻辑运算符的优先级
JavaScript 的逻辑运算符 (&&, ||, 和 !) 的优先级低于算术、比较和字符串操作符。理解这一点可以帮助你准确预测表达式的执行顺序。
示例:
以下表达式中,typeof null 和 typeof undefined 分别会被计算为 “object” 和 “undefined”:
1 | typeof null === 'object' && typeof undefined === 'undefined'; // 输出:true |
运算符优先级总结(从高到低):
算术运算符 (+, -, *, /, %, ** 等)
比较运算符 (>, <, ==, ===, !=, !==)
运算符 (&, |, ^, ~)
字符串操作符 ([], ., () 等)
逻辑运算符 (&&, ||, !)
括号
示例:
以下表达式的执行顺序遵循运算符的优先级:
1 | console.log(2 + 3 > 5 && "10" < "2"); // 输出:false |
首先计算 2 + 3(结果为 5)。
然后比较 5 > 5,结果为 false。
最后执行逻辑与运算 (&&),因为前一个条件为 false,整个表达式的结果为 false。
运算符优先级总结
以下是 JavaScript 中主要运算符的优先级(从高到低):
算术运算符:
+, -, *, /, %, **
比较运算符:
<, >, ==, ===, !=, !==
运算符:
&, |, ^, ~, <<, >>, >>>,
字符串操作符:
[], ., ()
逻辑运算符:
&&, || !
括号 (()):用于改变执行顺序
示例:
以下表达式中,typeof null === ‘object’ && typeof undefined === ‘undefined’ 的优先级如下:
首先计算 typeof null(结果为 “object”)。
然后计算 typeof undefined(结果为 “undefined”)。
最后执行逻辑与运算 (&&)。
常见问题
问题1:为什么 null || undefined 的结果是 undefined?
这是因为逻辑或运算符 (||) 在遇到 false(或空值)时会优先返回后面的值。在 JavaScript 中:
typeof null === ‘object’,但在逻辑运算中会被解释为 false。
typeof undefined === ‘undefined’。
因此:
1 | null || undefined; // 输出:undefined |
问题2:为什么 “5” + “6” 的结果是 “11”?
这是因为字符串拼接的优先级低于算术加法。JavaScript 会将两个字符串相加,而不是尝试将它们转换为数值。
1 | console.log("5" + "6"); // 输出:"11" |
问题3:为什么 0 || “” 的结果是 “”?
这是因为逻辑或运算符 (||) 在遇到 false(或空值)时会优先返回后面的值。在这里:
0 被视为 false。
“ “ 被视为非空字符串,因此会被返回。
1 | console.log(0 || ""); // 输出:"" |