22.1 JavaScript 基础

2023-09-12_00006

1. JavaScript

1.1 简介

JavaScript(简称'js'): 是一种广泛应用于网页开发的脚本语言.
它被用于增强网页的交互性和动态性, 可以让开发者对网页进行操作和控制.
JavaScript可用于处理用户输入, 改变网页的内容, 动态加载数据, 创建动画效果等.
它在现代的Web开发中扮演着至关重要的角色, 被支持和执行于几乎所有的Web浏览器中.

1.2 版本介绍

以下是一些常见的JavaScript版本的简介:

* 1. ECMAScript 5 (ES5): 发布于2009, 是JavaScript的第五个版本.
     ES5引入了一些重要的特性, 如严格模式, 数组迭代方法, JSON对象, 更强大的正则表达式和对象属性描述符等.

* 2. ECMAScript 2015 (ES6): 发布于2015, 是JavaScript的重要版本之一.
     ES6引入了许多新的语法特性, 如箭头函数, , 模块化, 解构赋值, for...of循环等.
     它也提供了一些新的API, 例如Promise, Set, Map和Symbol等.

* 3. ECMAScript 2016-2017 (ES7-ES8): 这两个版本是较小的更新, 引入了一些新的语法和功能, 
     如指数操作符, async/await, 共享内存和原子操作等. 
     它们在功能上对ES6进行了扩展和改进.

* 4. ECMAScript 2018 (ES9): 发布于2018, 引入了一些新的特性, 如异步迭代, 正则表达式命名捕获组, Rest/Spread属性等.
     此外, 它还对Promise进行了升级, 添加了一些有用的方法.

1.3 引入方式

JavaScript的引入方式有多种.
以下是一些常见的引入方式:

* 1. <script>标签引入: 在HTML文件中使用<script>标签来引入JavaScript文件.
     可以通过将src属性设置为JavaScript文件的URL来引入外部文件, 例如:
<script src="script.js"></script>
* 2. 内联方式引入: 直接在HTML文件中使用<script>标签嵌入JavaScript代码, 例如:
<script>
  // JavaScript代码
</script>
* 3. 外部文件引入: 通过使用<link>标签将外部JavaScript文件链接到HTML文件中, 例如:
<link rel="stylesheet" href="script.js">
* 4. 动态引入: 通过JavaScript的动态加载功能来引入其他JavaScript文件.
     可以使用'document.createElement()'创建一个'<script>'元素, 并设置其'src'属性来实现动态引入, 例如:
var script = document.createElement('script');
script.src = "script.js";
document.head.appendChild(script);

2. 使用规范

2.1 代码注释

使用注释可以对代码进行解释说明, 使其更易读和易于维护.
注释内容在代码运行时会被忽略, 不会影响程序的逻辑和执行结果.

JavaScript注释有两种常见的语法:
* 1. 单行注释: 以双斜线'//'开头, 后面跟着注释内容.
     单行注释用于在代码的某一行添加注释说明, 这些注释内容不会被解释为代码, 例如:
// 这是一个单行注释
* 2. 多行注释: 以斜线和星号'/*'开头, 以星号和斜线'*/'结尾.
   多行注释可用于注释一段连续的代码块, 可以跨越多行, 例如:
/*
这是一个多行注释
可以跨越多行
*/

2.2 代码缩进

代码缩进: 使用合适的缩进来增加代码的可读性, 以下是一个使用4个空格作为缩进的JavaScript代码示例:
function greet(name) {
    if (name) {
        console.log("Hello, " + name + "!");
    } else {
        console.log("Hello, stranger!");
    }
}

for (let i = 0; i < 5; i++) {
    console.log(i);
}

2.3 变量声明

在JavaScript中, 可以使用var, let或者const关键字来声明变量.

* 1. 使用var关键字声明的变量是函数作用域或者全局作用域的.
     它的作用范围是整个函数体内, 或者如果在函数外声明的话, 它的作用范围将是全局的.
     示例: var x = 5;

* 2. 使用let关键字声明的变量是块级作用域的.
     它的作用范围是最近的包含块(一般是在花括号{}内部的代码块).
     示例: let y = 10;

* 3. 使用const关键字声明的变量同样是块级作用域的, 但其值一旦被赋值后就不能再被修改.
     示例: const z = 15;

可以根据具体的需求选择合适的关键字进行变量声明.
let其用法类似于var, 但是所声明的变量只在let命令所在的代码块内有效。
例如: for循环的计数器就很适合使用let命令.
var i = 0


// let语句只在for代码块内有效.
for (let i = 0; i < 10; i++) {
}
console.log(i)  // 0
// 定义全局变量i
for (var i = 0; i < 10; i++) {
}
console.log(i)  // 10
// 定义局部变量i
let i = 0

function f1() {
    // 定义局部变量i
    let i = 1
    console.log(i)  // 1
}

f1()
console.log(i)  // 0
常量一旦被定义了就无法修改.
var i = 1
i = 2


const P = 3.1415926

P = 1  // TypeError: Assignment to constant variable.

2.4 命名规范

在JavaScript中, 以下是一些常见的命名规则:
* 1. 变量名必须以字母(a-z, A-Z)或下划线(_)或美元符号($)开头, 后面可以是字母, 数字, 下划线或美元符号.

* 2. 变量名区分大小写, 例如: myVariable和myvariable是两个不同的变量.

* 3. 避免使用JavaScript的保留字, 例如: let, const, function等作为变量名.

* 4. 变量名应具有描述性, 以便于理解其用途和含义.
     使用有意义的单词或短语, 避免使用不相关或无意义的名称.

* 5. 推荐使用驼峰式命名法来命名变量.
     驼峰式命名法分为两种: 小驼峰式(camelCase)和大驼峰式(PascalCase).
     - 小驼峰式命名法: 变量名的第一个字母小写, 后续的单词首字母大写. 例如: myVariable, firstName.
     - 大驼峰式命名法: 变量名的每个单词首字母都大写. 通常用于命名构造函数或类. 例如: Person, Car.

* 6. 常量通常使用全大写字母, 并使用下划线分隔单词. 例如: PI, MAX_VALUE.

* 7. 遵循团队或项目的命名约定以保持一致性.

遵循良好的命名规则可以提高代码的可读性和可维护性, 使其更易于理解和使用.
以下是一些符合命名规则的JavaScript变量名示例:
let myVariable = 10;
let firstName = "John";
const PI = 3.14;
let isUserLoggedIn = true;

2.5 语句结束符号

JavaScript的语法规定可以使用分号(;)作为语句的结束符号.
在大多数情况下, 如果你省略了分号, JavaScript解析器仍然可以正确地理解和执行代码, 
因为它会使用自动分号插入(Automatic Semicolon Insertion, ASI)机制来帮助补充缺失的分号.

ASI的原则是, 如果解析器发现代码中缺少了分号, 但根据上下文可以插入分号而不会引起语法错误, 那么解析器就会自动插入分号.

尽管JavaScript的确允许不写分号, 但在某些情况下会导致意外的行为.
特别是在使用一些特殊情况下的语法(如立即执行函数表达式)或在代码中压缩/合并时, 省略分号可能会导致错误.
因此, 为了代码的可读性和可维护性, 建议在语句结束时始终显式地使用分号.
在下述示例中, 每个语句的末尾都有分号, 以表示语句的结束.
// 定义变量并赋值:
let x = 5;

// 调用函数:
myFunction();

// 条件语句:
if (x > 10) {
  console.log("x 大于 10");
} else {
  console.log("x 不大于 10");
}

// 循环语句:
for (let i = 0; i < 5; i++) {
  console.log(i);
}

// 对象属性赋值:
let person = {
  name: "John",
  age: 30
};

3. 数据类型

在JavaScript中, 变量的类型是在运行时动态确定的, 变量可以存储任意类型的值, 并且可以随时改变变量的类型.
var num = 1;  // 变量num最初被定义为整型, 赋值为 1.
num = 'abc'  // 然而, 当将字符串'abc'赋给num后, 变量的类型发生了变化, 变成了字符串类型.

3.1 数值类型

数值类型(number): 包括整数和浮点数(具有小数部分的数值).
浮点数的小数部分如果全是0, 会被省略成整数形式. 这是因为JavaScript在表示数值时会进行自动转换和优化, 以减少内存占用和提高性能.
例如, 浮点数10.00会被自动转换为整数10. 这种行为在大多数情况下不会影响数值计算的精确性, 但在一些特殊情况下可能需要注意.
如果需要精确控制小数位数, 则可以使用其他方式(如字符串)来表示数值.
// typeof 操作符检查变量类型.
var num1 = 1;
var num2 = 1.11;
var num3 = 1.0;
console.log(typeof num1);  // number
console.log(typeof num2);  // number
console.log(num3);  // 1 --> 1.0 省略成 1
3.1.1 字符串转数值
字符串转数值类型的函数:
parseInt()函数: 用于将字符串转换为整数.
它从字符串的开头开始解析, 直到遇到非数字字符为止.
如果字符串的开头是数字字符, 则将其解析为整数值. 如果字符串的开头不是数字字符, 则返回NaN(不是一个数字).

parseFloat()函数: 用于将字符串转换为浮点数.
它从字符串的开头开始解析, 直到遇到非数字字符或多个小数点为止.
如果字符串的开头是数字字符或单个小数点, 则将其解析为浮点数值.
如果字符串的开头不是数字字符或多个小数点, 则返回NaN.

NaN表示不是一个数字, 它是JavaScript中的特殊值.
当进行无法执行数值转换的操作时, 比如字符串中包含非数字字符或解析结果超出了数值类型的范围, 就会返回NaN.
console.log(parseInt('123456'));  // 123456 --> 纯数字字符串

console.log(parseFloat('11.11'));  // 11.11

console.log(parseInt('11.11'));   // 11  保留整数部分

console.log(parseInt('123asdas')); // 123 识别数字部分,遇到非数字断开,结束转换。

console.log(parseInt('asdas'));    // NaN 非数字

3.2 字符类型

字符类型被称为字符串(string): 是由一系列字符组成的数据类型, 用于表示文本.
字符串可以使用单引号''或双引号""来定义, 例如:
var str1 = 'Hello World!';
var str2 = "Hello World!";
console.log(typeof str1)  // string
ES6引入了模板字符串(template string), 用反引号``括起来.
模板字符串可以包含占位符,  ${expression}的形式嵌入变量或表达式. 例如:
var name = 'John';
var age = 18;
var message = `My name is ${name} and I'm ${age} years old.`;
console.log(message)  // My name is John and I'm 18 years old.
3.2.1 字符串的拼接
js中推荐使用+号拼接字符.
var str1 = 'aaa'
var str2 = 'bbb'

var str3 = str1 + str2

console.log(str3)  // aaabbb
3.2.2 字符串常用方法
方法说明
.length返回长度
.trim()移除空白
.trimLeft()移除左边空白
.trimRight()移除右边空白
.chaerAt()返回第xx个字符
.concat()拼接
.indexOf()子序列位置
.substring()根据索引获取子序列
.slice()切片
.toLowerCase()小写
.toUpperCase()大写
.splist()分割
1. 获取长度
var name = 'kid'
console.log(name.length)  // 3
2. 移除空白
只能移除空白,不能在括号内指定字符移除。
var str4 = ' abc  '
console.log(str4.trim())  // abc

// 指定左边空白
console.log(str4.trimLeft()) 

// 指定移除右边空白
console.log(str4.trimRight())
3. 索引取值
参数的默认值为0, 索引从0开始.
var str5 = '123456789'

console.log(str5.charAt(0))  // 1
console.log(str5.charAt())  // 1
4. 查找索引
参数如果是一个字符组合, 返回的首个字符的索引.
var str6 = '123456789'

console.log(str6.indexOf(2))  // 1
console.log(str6.indexOf(23))  // 1
5. 切片
.sbustring() 不可以使用负数索引.
.slice()     可以使用负数索引.
var str7 = '0123456789'

console.log(str7.substring(0, 5))  // 01234
console.log(str7.slice(-10, -1))  // 012345678
6. 大小写转换
var str8 = 'aabbcc'
var str9 = 'AABBCC'

console.log(str8.toUpperCase())  // AABBCC
console.log(str9.toLowerCase())  // aabbcc
7. 切分
括号内指定以什么切分, 后面跟的数字为切分的次数.
var names ="kid|qz|qq|pp|qaq"

console.log(names.split('|'))  // [ 'kid', 'qz', 'qq', 'pp', 'qaq' ]
console.log(names.split('|', 2))  // [ 'kid', 'qz' ]
8. 字符串拼接
js弱类型语言, concat拼接会自动转换类型, 不同的类型会转换相同的类型,
var str10 = "123"
var num = 123

var str11 = str10 + num
console.log(typeof str11, str11)  // string 123123

3.3 布尔值

布尔值: 表示逻辑真假的数据类型, 只有两个可能的值: true()false().
布尔值通常用于条件判断和控制流程.

下面是JavaScript中的'假值列表:
- 布尔值 false
- 数字 0
- 空字符串 ''
- 空值 null
- 未定义的值 undefined
- 非法数字 NaN

其他所有值都被认为是'真', 包括布尔值true, 非零数字, 非空字符串, 非空对象和函数等等.

3.4 空值

3.4.1 null
null是一个特殊的值, 用于表示一个空值或不存在的对象.
当变量或属性没有被赋予实际的值时, 可以将其设置为null.

常见用法和注意事项:
* 1. null是一个原始值, 不是对象.
     尽管在typeof操作符中返回的是'object', 但实际上它不是对象类型.

* 2. null表示空值或缺少值, 与undefined有所不同.
     null是有意设置的, 而undefined表示值未定义.

* 3. null可以被赋值给任何变量, 以清空其值或重置为初始状态.
     例如: let myVar = null;

* 4. 在条件判断中, null被视为'假', 表示为'false'.
     这意味着在if语句中, null会被转换为false, 而不执行相应的代码块.

* 5. 当使用null进行操作时, 可能会引发TypeError. 
     这是因为null不是对象, 所以无法调用属性或方法.
     例如: null.toString()会引发错误.

总之, null是JavaScript中用于表示空值或不存在对象的特殊值.
它在某些情况下可以用于清空变量的值或表示缺少值, 但请注意, 对null值执行操作可能会引发错误.
var var1 = 1
console.log(var1)  // 1
console.log(typeof var1)  // number

var1 = null;
console.log(var1)  // null
console.log(typeof var1)  // object


// null.toString()
// TypeError: Cannot read properties of null (reading 'toString')
3.4.2 undefined
undefined是一个特殊的值: 表示一个未定义或不存在的值.
当声明一个变量但未赋予它任何值时, 它的默认值就是undefined.

常见用法和注意事项:
* 1. undefined是一个原始值, 表示一个变量没有被赋予实际的值.

* 2. undefined是全局对象的一个属性, 也是全局变量.
     它的初始值是未定义的.

* 3. 在条件判断中, undefined被视为'假', 表示为false.
     这意味着在if语句, undefined会被转换为false, 而不执行相应的代码块.

* 4. 当函数没有返回值时, 默认返回undefined.

* 5. 当使用undefined进行操作时, 可能会引发TypeError. 
     这是因为undefined不是对象, 所以无法调用属性或方法. 
     例如: undefined.toString()会引发错误.

* 6. undefined与null有所不同.
     null表示一个空值或缺少值, 是有意设置的, 而undefined表示值未定义.

总之, undefined是JavaScript中用于表示未定义的值的特殊值.
它可以用于判断变量是否被赋值, 或作为函数的默认返回值.
需要注意的是, 对undefined值执行操作可能会引发错误.
var var1
console.log(typeof var1)  // undefined


// undefined.toString()
// TypeError: Cannot read properties of undefined (reading 'toString')

3.5 对象

在JavaScript中, 有几种常见的对象类型, 它们用于处理不同的数据和功能.

以下是一些常见的对象类型:
* 1. 内建对象(Built-in Objects): JavaScript提供了许多内建的对象类型, 如字符串对象(String), 数字对象(Number), 
     数组对象(Array), 日期对象(Date), 正则表达式对象(RegExp).
     这些内建对象提供了许多方法和属性, 用于操作和处理特定类型的数据.

* 2. 全局对象(Global Objects): 全局对象是在全局作用域中访问的对象, 例如浏览器环境中的window对象.
     全局对象提供了一些常用的属性和方法, 如console对象用于控制台输出, Math对象用于数学运算JSON, 对象用于处理JSON数据等.

* 3. 自定义对象(Custom Objects): 可以使用构造函数或对象字面量的方式定义自己的对象类型.
     自定义对象可以具有自定义的属性和方法, 以便根据需求进行数据封装和功能扩展.

* 4. 函数对象(Function Objects): 在JavaScript中, 函数也是一种对象.
     函数对象有一些特殊的属性和方法, 如call()和apply()用于显式调用函数并指定上下文, 
     以及bind()用于创建一个新的函数并绑定指定的上下文.

* 5. 包装对象(Wrapper Objects): JavaScript中的基本类型(如字符串, 数字, 布尔值)都有对应的包装对象, 用于提供一些额外的功能和方法.
     对于每个基本类型, 当你访问其属性或方法时, JavaScript会临时创建一个对应的包装对象, 使得你可以对基本类型值进行对象样式的操作.
3.5.1 数组
数组: 是一种用于存储多个值的数据结构, 它是一种有序的, 可变的列表.
数组的值用中括号括起来,元素之间用逗号隔开.
var 变量 = [元素1, 元素2 ...]

数组可以包含各种类型的元素, 包括数字, 字符串, 布尔值, 对象等.
可以使用索引来访问数组中的元素, 索引从0开始, 表示数组中元素的位置.
* 只能使用非负整数作为有效的索引值, 使用负数索引是无效的, 将会返回undefined.
const arr = [1, 2, 3, 4, 5];
console.log(typeof arr)  // object
console.log(arr[4]); // 输出 5, 代表最后一个元素
console.log(arr[3]); // 输出 4, 代表倒数第二个元素
在JavaScript中, 数组是一种特殊类型的对象.
虽然数组是对象的一种, 但它们与常规的普通对象有一些区别.
因此, 当你使用typeof运算符检查一个数组时, 它会返回字符串'object', 而不是array.

这是因为在JavaScript中, 数组被视为对象的一种特殊形式, 
由于对象是JavaScript中的基本数据类型之一, typeof运算符返回object作为数组的类型.

要判断一个值是否为数组, 可以使用Array.isArray()方法, 它是JavaScript提供的用于判断一个值是否为数组的方法.
它返回一个布尔值, 如果给定的值是数组, 则返回true, 否则返回false.
console.log(Array.isArray(arr)); // 输出: true
3.5.1.1 常用方法
方法说明
.length长度
.push()尾部追加元素
.pop()获取尾部元素
.undhilt()头部插入元素
shife()头部移除元素
.slice()切片
.reverse()反转
.join()将数组元素拼接程字符串
concat()连接数据
sort()排序
.forEach()将数组每个元素传递给回调函数
.splice()删除元素
.map()返回一个数组元素调用函数处理后的值的新数组
1. 获取长度
获取元素的个数。
var arr1 = [11, 22, 33, 44];
console.log(arr1.length)  // 4
2. 元素增删
.push():   尾部追加元素.
.pop():    弹出尾部元素.
.shife():  头部元素移除.
.splice(start, deleteCount, item1, item2, ...): 按切片的方式, 移除/替换元素.
    start: 指定要操作的起始索引位置。
    deleteCount: 可选参数, 指定要删除的元素数量, 如果省略或为0, 则不删除任何元素.
    item1, item2, ...: 可选参数, 指定要插入到数组的元素.
var arr2 = [11, 22, 33, 44];
console.log(arr2.push(55))  // 5 返回数组的元素个数
console.log(arr2)  // [ 11, 22, 33, 44, 55 ]

console.log(arr2.pop())  // 55 返回尾部弹出元素
console.log(arr2)  // [ 11, 22, 33, 44 ]

console.log(arr2.shift())  // 11 返回头部弹出元素
console.log(arr2)  //  [ 22, 33, 44 ]


// 删除元素
var arr3 = [11, 22, 33, 44];
// 从索引0开始删除两个元素
console.log(arr3.splice(0, 2))  // 11 22 返回被移除的元素
console.log(arr3)  // [ 33, 44 ]

// 替换元素
var arr4 = [1, 2, 3, 4, 5];
arr4.splice(2, 2, 'a', 'b'); // 替换索引为 2 和 3 的元素为 'a' 和 'b'
console.log(arr4); // 输出: [1, 2, 'a', 'b', 5]

// 插入元素
var arr5 = [1, 2, 3, 4, 5];
arr5.splice(2, 0, 'x', 'y'); // 在索引为 2 的位置插入 'x' 和 'y'
console.log(arr5); // 输出: [1, 2, 'x', 'y', 3, 4, 5]
3. 排序与反转
.sort(): 排序
.reverse(): 反转
var attr6 = [2, 5, 1, 4, 3];
console.log((attr6.sort()))  // [ 1, 2, 3, 4, 5 ]

var attr7 = [1, 2, 3, 4, 5];
console.log(attr7.reverse())  // [ 5, 4, 3, 2, 1 ]
4. 拼接与合并
.join():   使用指定符号将数组内的元素拼接成字符串.
.concat(): 合并数组.
var attr8 = [1, 2, 3];
var attr8 = l1.join('|')

console.log(typeof attr8, attr8)  // string 1|2|3

var attr10 = [4, 5, 6]
console.log(attr8.concat(attr10)  //  [ 1, 2, 3, 4, 5, 6 ]  返回合并后的值
5. 遍历与映射
.forEach()方法: 会遍历数组中的每个元素, 并将每个元素传递给回调函数进行处理.
它没有返回值, 仅用于执行某些操作或副作用.
回调函数接受三个参数: 当前元素的值, 当前元素的索引和被遍历的数组本身.
var arr11 = [1, 2, 3, 4, 5];
arr.forEach((element, index, array) => {
  console.log(element); // 输出每个元素的值
  console.log(index);   // 输出每个元素的索引
  console.log(array);   // 输出被遍历的数组
});
1
0
[ 1, 2, 3, 4, 5 ]
2
1
[ 1, 2, 3, 4, 5 ]
3
2
[ 1, 2, 3, 4, 5 ]
4
3
[ 1, 2, 3, 4, 5 ]
5
4
[ 1, 2, 3, 4, 5 ]
.map()方法: 会将数组中的每个元素传递给回调函数进行处理, 并返回一个由处理结果组成的新数组.
回调函数接受三个参数: 当前元素的值, 当前元素的索引和被遍历的数组本身.
该方法常用于根据原始数组生成一个新的经过映射后的数组.
const arr12 = [1, 2, 3, 4, 5];

const doubledArr = arr12.map((element, index, array) => {
  return element * 2; // 将每个元素乘以 2
});
console.log(doubledArr); // 输出: [2, 4, 6, 8, 10]
3.5.2 自定义对象
自定义对象: 是由开发人员根据自己的需求来创建的.
它是一种用来组织和表示数据的数据结构.
自定义对象由属性和方法组成, 属性存储数据, 方法用于操作和处理数据.

自定义对象允许你根据具体的应用程序或问题定义并组织数据. 
可以自由决定对象的属性名称, 属性值以及对象的行为(方法).

创建自定义对象的方式:
* 1. 使用字面量方式创建对象: 可以使用花括号{}创建一个自定义对象。
     对象中的元素以键值对的形式表示, 用逗号,分隔, 键和值之间使用冒号:进行赋值.
     
* 2. 使用new Object()构造函数创建对象: 使用new Object()构造函数创建一个空的自定义对象.
     随后通过分配属性和值来设置对象的属性.
var obj = {'k1': 'v1'}
console.log(typeof obj, obj)  // object { k1: 'v1' }

//  取值方式1
console.log(obj.k1)  // v1  对象.键 取值

// 取值方式2
console.log(obj['k1'])  // v1 对象[键] 取值

// 取值方式3

for (let i in obj){
    console.log(obj[i], i)  // v1 遍历字典的键  再 对象[键] 取值
}
var obj2 = new Object()
obj2.name = 'kid'
obj2['age'] = 18
console.log(obj2)  // { name: 'kid', age: 18 }

4. 运算符

4.1 算数运算符

运算符描述示例
+加法运算符, 将两个值相加2 + 3 的结果为 5
-减法运算符, 将第一个值减去第二个值5 - 3 的结果为 2
*乘法运算符, 将两个值相乘2 * 3 的结果为 6
/除法运算符, 将第一个值除以第二个值6 / 3 的结果为 2
%取模运算符, 返回两个值相除的余数5 % 2 的结果为 1
**指数运算符, 将第一个值的幂次方设为第二个值2 ** 3 的结果为 8
++自增运算符, 将变量的值增加 1++numnum++
--自减运算符, 将变量的值减少 1--numnum--
console.log(1 * 1.0)  // 1
console.log(1 / 1.0)  // 1
console.log(2 ** 3)  // 8
console.log(11 % 3)  // 2
var x = 10;

var res1 = x++;  // 先赋值再自增
console.log(res1, x)  // 10 11

var res2 = ++x;  // 先自增再赋值
console.log(res2, x)  // 12 12

4.2 比较运算符

运算符描述示例
==等于运算符, 检查两个值是否相等2 == 3 的结果为 false
===全等运算符, 检查两个值是否严格相等(值和类型都相等)'2' === 2 的结果为 false
!=不等于运算符, 检查两个值是否不相等2 != 3 的结果为 true
!==不全等运算符, 检查两个值是否不严格相等'2' !== 2 的结果为 true
>大于运算符, 检查第一个值是否大于第二个值3 > 2 的结果为 true
<小于运算符, 检查第一个值是否小于第二个值2 < 3 的结果为 true
>=大于等于运算符, 检查第一个值是否大于或等于第二个值3 >= 2 的结果为 true
<=小于等于运算符, 检查第一个值是否小于或等于第二个值2 <= 3 的结果为 true
// 弱等于-->内部自动转换成相同的类型进行比较.
console.log(1 == '1') // true
console.log(1 != '1') // false

// 强等于-->内部不做类型转换.
console.log(1 === '1') // false
console.log(1 !== '1') // true

4.3 逻辑运算符

运算符运算符示例
&&逻辑与运算符,当所有操作数都为真时返回真true && true 的结果为 true
||逻辑与运算符,当所有操作数都为真时返回真true || false 的结果为 true
逻辑与运算符,当所有操作数都为真时返回真!true 的结果为 false
* 需要住注意什么时候返回布尔值, 什么时候返回数据.
console.log(5 && '5') // 5  第一个值为真,就显示第二个值.
console.log(0 && '5') // 0  第一个值为假,结果直接为假(短路运算).

console.log(0 || 1) // 1
console.log(1 || 0) // 1

console.log(!true) // false
console.log(!false) // true

4.4 赋值运算符号

运算符描述示例
=简单赋值运算符,将右侧的值赋给左侧的变量x = 5 将数值 5 赋给变量 x
+=加赋值运算符,将右侧的值加到左侧的变量上x += 3 相当于 x = x + 3
-=减赋值运算符,将右侧的值从左侧的变量中减去x -= 2 相当于 x = x - 2
*=乘赋值运算符,将右侧的值乘以左侧的变量x *= 4 相当于 x = x * 4
/=除赋值运算符,将左侧的变量除以右侧的值x /= 2 相当于 x = x / 2
%=取余赋值运算符,将左侧的变量取余右侧的值x %= 3 相当于 x = x % 3
**=幂赋值运算符,将左侧的变量进行幂运算x **= 2 相当于 x = x ** 2
<<=左移赋值运算符,将左侧的变量向左移动指定位数x <<= 1 相当于 x = x << 1
>>=右移赋值运算符,将左侧的变量向右移动指定位数x >>= 1 相当于 x = x >> 1
&=位与赋值运算符,将左侧的变量与右侧的值进行位与操作x &= 2 相当于 x = x & 2
`=`位或赋值运算符,将左侧的变量与右侧的值进行位或操作
^=位异或赋值运算符,将左侧的变量与右侧的值进行位异或操作x ^= 4 相当于 x = x ^ 4
这些赋值运算符用于给变量赋值并进行相应的操作.
例如, 使用简单赋值运算符(=)可以将右侧的值赋给左侧的变量,
而使用加赋值运算符(+=)可以将右侧的值加到左侧的变量上, 并更新变量的值.

需要注意的是, 赋值运算符的操作数是右结合的, 即先计算右侧的值, 然后将结果赋给左侧的变量.
此外, 还可以通过组合赋值运算符来简化代码, 实现对变量的多次操作并赋值的操作.
var num = 3
console.log(num += 3) // 6

var num = 3
console.log(num -= 3) // 0

var num = 3
console.log(num *= 3) // 9

var num = 3
console.log(num /= 3) // 1

var num = 3
console.log(num %= 3) // 0

5. 流程控制

5.1 if判断

if语句: 用于条件判断, 它根据条件是否为真来决定执行哪些代码.

如果条件为真, 将执行if块中的代码; 如果条件为假, 将执行else块中的代码(如果有的话).
if语句的基本语法如下:
if (条件) {
  // 在条件为真时执行的代码
} else {
  // 在条件为假时执行的代码(可选)
}
if语句的嵌套形式, 可以根据多个条件进行判断. 
语法格式如下:
if (条件1) {
  // 在条件1为真时执行的代码
} else if (条件2) {
  // 在条件2为真时执行的代码
} else {
  // 在条件1和条件2都为假时执行的代码(可选)
}
var num = 10;
if (num > 0) {
  console.log("数字是正数");
} else if (num < 0) {
  console.log("数字是负数");
} else {
  console.log("数字是零");
}

5.2 switch分支

switch语句用于根据不同的条件值执行不同的代码块.
它可以替代多个if-else语句, 提供更简洁的代码逻辑.

switch语句的基本结构如下:
switch (表达式) {
  case1:
    // 当表达式等于值1时执行的代码
    break;
  case2:
    // 当表达式等于值2时执行的代码
    break;
  // 可以添加更多的case语句
  default:
    // 当表达式不等于任何一个case时执行的代码
}
表达式的值将依次与每个case语句后指定的值进行比较, 当找到与表达式值相等的case语句时, 将执行对应的代码块.
并且使用break语句来终止switch语句的执行, 避免继续执行其他case语句.
如果表达式的值没有与任何case匹配, 将执行default语句后的代码块(可选).
var fruit = "apple";
switch (fruit) {
  case "apple":
    console.log("It's an apple.");
    break;
  case "banana":
    console.log("It's a banana.");
    break;
  case "orange":
    console.log("It's an orange.");
    break;
  default:
    console.log("It's an unknown fruit.");
}

// 根据变量fruit的值, 将输出对应的水果信息. 由于fruit的值为"apple", 所以将输出"It's an apple.".

5.3 for循环

for循环是一种常用的循环结构, 它允许你重复执行一段代码, 直到指定的条件不再满足.
for循环由三个表达式组成: 初始化表达式, 条件表达式和递增(或递减)表达式.
5.3.1 基本语法
for循环的基本语法如下:
for (初始化表达式; 条件表达式; 递增表达式) {
  // 执行的代码
}
for (var i = 0; i < 5; i++) {
    console.log(i); 
}
在这个例子中, 循环从0开始, 执行到i小于5为止.
在每次循环中, 将打印出当前的i值.
循环体执行完后, 递增表达式i++会执行, 将i的值增加1, 然后进行下一次循环.
这个循环将输出0, 1, 2, 3, 4.
// 不推荐这样写, 因为let定义变量在一个外部的名称空间, 不在for的名称空间.
let i = 0;  
for (; i < 9; i++) {
    console.log(i)
}
5.3.2 遍历对象
可以使用for循环来迭代数组, 对象属性等.
for (let = 变量 in 对象){
    console.log(变量)  // 取的是索引
    console.log(对象[索引])  // 取值
}
s1 = ['朝阳区', '海淀区', '昌平区']
for (let i in s1){
    console.log(s1[i], i)  // 取值 i 是索引
}
5.3.3 break语句
使用break语句时, 它会立即终止当前所在的循环, 并跳出循环体执行循环后的代码.

以下是一个使用break语句的示例, 将在找到目标值时提前结束循环:
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var target = 6;

for (var i = 0; i < numbers.length; i++) {
  if (numbers[i] === target) {
    console.log("目标值已找到!");
    break;
  }
  console.log(numbers[i]); 
}
在这个例子中, 遍历数组numbers, 如果当前元素等于目标值target, 则输出"目标值已找到"并使用break语句提前结束循环.
如果没有找到目标值, 将打印当前元素.
5.3.4 continue语句
当使用continue语句时, 它会跳过当前循环体中余下的代码, 直接进行下一次循环的迭代.

以下是一个使用continue语句的示例, 将在遇到偶数时跳过余下的代码:
for (var i = 0; i < 5; i++) {
  if (i % 2 === 0) {
    continue;
  }
  console.log(i);  // 输出: 1 3
}
在这个例子中, 当遇到偶数时, 使用continue语句跳过打印当前值的代码, 直接进行下一次循环迭代.
因此, 只有奇数会被输出, 结果为1, 3.

5.4 while循环

while循环: 是一种重复执行代码块的结构, 只要给定条件为真, 它就会一直执行.

在每次循环开始之前, 都会检查给定的条件是否为真. 如果条件为真, 代码块将被执行.
然后, 条件再次被检查, 并且重复这个过程, 直到条件不再为真.

需要注意的是, 如果条件一直为真, 而没有适当的终止条件, 循环可能会变成无限循环, 导致程序崩溃或陷入死循环.
因此, 在使用while循环时, 务必确保条件能够在某个时刻变为假, 以结束循环.

while循环可以结合使用break和continue语句来控制循环的执行.
break立即终止整个循环, continue跳过当前迭代, 继续下一次迭代.
根据具体的逻辑需求, 可以在循环体内使用它们来处理不同的情况.
5.4.1 基本语句
下面是一个while循环的基本语法:
while (condition) {
  // 当条件为真时, 会重复执行此处的代码
}
以下是一个简单的例子, 展示了如何使用while循环打印数字15:
let i = 1;

while (i <= 5) {
  console.log(i);  // 输出: 1 - 5
  i++;
}
在上述示例中, 变量i被初始化为1.
然后, while循环检查条件i <= 5是否为真.
如果为真, 循环体内的代码将被执行.
在每次循环的末尾, i的值会递增1(i++), 以便在下一次循环中, 条件检查的结果可能不同.
循环将继续执行, 直到i的值大于5为止, 此时条件不再满足, 循环结束.
5.4.2 break语句
break语句: 用于完全退出循环, 无论循环条件是否为真.
let i = 0;

while (i < 5) {
  i++;

  if (i === 3) {
    break;
  }

  console.log(i);  // 输出: 1 2
}
在这个例子中, 当i等于3, break语句被执行, 导致循环立即终止, 并且退出循环. 
因此, 只打印数字12.
5.4.2 break语句
continue语句: 用于跳过当前循环中的剩余代码, 直接开始下一次循环.
let i = 0;

while (i < 5) {
  i++;

  if (i === 3) {
    continue;
  }

  console.log(i);  // 输出: 1 2 4 5
}
在这个例子中, 当i等于3, continue语句被执行, 跳过了打印数字3的部分.
然后, 循环继续执行, 打印数字1, 2, 45.

5.5 三元运算

三元运算符(也称为条件运算符): 是一种简洁的方式来根据条件选择不同的值.

它的语法是: 条件 ? 1 : 2

如果条件为真, 则返回值1, 否则返回值2.
下面是一个简单的示例:
var age = 18;
var message = age >= 18 ? "成年人" : "未成年人";
console.log(message); // 输出: "成年人"
在上面的示例中, 当年龄大于等于18, 条件为真, 返回"成年人"; 否则, 返回"未成年人".
三元运算符可以嵌套使用, 以实现更复杂的条件逻辑.
但是请注意, 在使用三元运算符时要注意代码的可读性和简洁性, 避免嵌套过深, 以免影响代码的可维护性.

6. 函数

函数: 是一段可重复使用的代码块, 用于执行特定的任务或计算.

6.1 无参函数

无参函数: 定义的函数没有任何参数.

可以随时根据需要定义无参函数, 无需传递任何参数.
它们在执行一些固定的任务时很有用, 不需要外部数据或上下文的影响.

以下是一个示例:
function sayHello() {
  console.log("Hello!");
}

sayHello();  // 输出: "Hello!"
在上述示例中, sayHello函数没有定义任何参数.
当调用这个函数时, 它会执行其中的代码块, 输出"Hello!".
在这种情况下, 函数不依赖于传递的参数进行操作, 它只是执行固定的操作或打印固定的值.

6.2 有参函数

有参函数: 可以接受一个或多个参数作为输入.
以下是一个示例:
function sayHello(name) {
    console.log("Hello, " + name + "!");
}

sayHello("Alice");  // 输出: "Hello, Alice!"
sayHello("Bob");    // 输出: "Hello, Bob!"
在上述示例中, sayHello函数接受一个名为name的参数.
当调用这个函数并传递一个参数时, 函数会在输出中使用传递的参数来构建特定的消息.
可以定义多个参数, 如下所示:
function addNumbers(a, b) {
    let sum = a + b;
    console.log("The sum of " + a + " and " + b + " is " + sum);
}

addNumbers(2, 3);  // 输出: "The sum of 2 and 3 is 5"
addNumbers(5, 7);  // 输出: "The sum of 5 and 7 is 12"
在这个例子中, addNumbers函数接受两个参数a和b. 它会计算这两个参数的和, 并输出一个描述性的消息.
通过定义参数, 函数可以接受外部的数据或信息, 然后在函数体内使用这些参数执行相应的逻辑操作. 
函数的参数个数可以根据需要进行调整.
如果传递的参数多于函数声明时定义的参数个数, 多余的参数将被忽略.
而如果参数少于定义的参数个数, 缺失的参数将会被设置为undefine.
function func2(a, b) {
    console.log(a, b);
}

// 正常传值
func2(1, 2);  // 1 2


// 参数多了
func2(1, 2, 3);   // 1 2 多了的值不管


// 参数少了
func2(1);         // 1 undefined
正常传递两个参数: func2(1, 2); 将打印出1 2, 因为参数1将赋值给a, 参数2将赋值给b.

传递多个参数: func2(1, 2, 3);  尽管有第三个参数3, 但它将被忽略, 因为函数定义只有两个参数.
因此,打印的结果仍然是1 2.

参数不足: func2(1); 在这种情况下, 只有一个参数被传递, 1.
因为没有为第二个参数提供值, 所以第二个参数b将被设置为undefined.
因此, 打印的结果是1 undefined.

6.3 获取所有参数

arguments: 是一个特殊对象, 它在函数内部可用, 并包含传递给函数的所有参数.
它允许在函数内部访问所有传递的参数, 即使未在函数定义中指定参数.

arguments.length: 可以获取传递给函数的参数个数.
arguments[index]: 可以访问具体的参数值.
function sum() {
  let total = 0;
  
  for (let i = 0; i < arguments.length; i++) {
    total += arguments[i];
  }
  
  return total;
}

console.log(sum(1, 2, 3));           // 输出 6
console.log(sum(5, 10, 15, 20));     // 输出 50
console.log(sum(2, 4, 6, 8, 10));    // 输出 30
在上述示例中, sum()函数没有定义任何参数, 但在函数内部使用了arguments对象.
通过使用arguments.length, 可以获取传递给函数的参数个数.
使用arguments[index]可以访问具体的参数值.

arguments对象类似于一个数组, 但它实际上是一个类数组对象.
它没有数组的所有方法, 例如push()和forEach(), 但可以通过迭代arguments对象的索引来访问参数值.

需要注意的是, arguments对象是与函数绑定的, 只能在函数内部使用.
此外, 使用命名参数更加推荐, 因为它们在代码中更具可读性和明确性.
只有在需要处理不定数量的参数时, 才需要使用arguments对象.
当需要判断参数的数量是否符合预期时, 可以编写一个专门的函数来进行参数数量的检查.
这个函数可以通过比较传入参数的个数和期望的参数个数来判断是否有参数缺失或多余.
在判断后, 可以根据情况打印相应的提示信息.
function Parameter(count, amount) {
  if (count > amount) {
    let extra = count - amount;
    console.log(`参数多了${extra}`);
  } else if (count < amount) {
    let missing = amount - count;
    console.log(`参数少了${missing}`);
  } else {
    console.log("参数数量正确");
  }
}

function f1(a, b) {
  Parameter(arguments.length, 2);
}

f1(1, 2);  // 参数数量正确
f1(1);     // 参数少了1个
f1();      // 参数少了2个

6.4 返回值

返回值是指函数执行完成后返回给调用者的结果.
函数可以使用return语句来定义返回值.

以下是一个简单的示例, 演示如何在函数中返回一个值:
function add(a, b) {
  return a + b;
}

let result = add(3, 5);
console.log(result);  // 输出 8
在上述示例中, add函数接收两个参数a和b, 并使用return语句将它们的和作为返回值返回.
调用add(3, 5)会返回结果8, 而将其赋值给result变量后, 可以在后续的代码中使用该返回值.
需要注意的是, return语句会立即终止函数的执行, 并将指定的值作为函数的返回值.
因此, 在函数内部可以根据需要使用多个return语句来提前结束函数的执行, 并返回不同的值.

如果没有在函数中使用return语句, 或者return语句没有指定返回值, 则函数执行完成后会自动返回undefined.
function greet(name) {
  if (!name) {
    return "Hello!";
  }

  return "Hello, " + name + "!";
}

console.log(greet());       // 输出 "Hello!"
console.log(greet("Alice"));  // 输出 "Hello, Alice!"
在上述示例中, greet函数根据传入的name参数返回相应的问候语.
如果name参数为空, 则返回默认的问候语"Hello!", 否则返回带有名称的问候语.
如果返回多个值, 只会将最后那个值返回. 
但是, 可以使用容器类型的值来组织和返回多个值.
function func1() {
    return 1, 2, 3;
}

res = func1();

console.log(res)  // 3
function func2() {
    return [1, '字符串数据', '其他..']
}

res = func2();

console.log(res)  // [ 1, '字符串数据', '其他..' ]

6.5 匿名函数

匿名函数是一种没有名称的函数, 也称为函数表达式.
可以使用匿名函数创建一个函数并将其分配给一个变量或传递给另一个函数作为参数.
以下是创建和使用匿名函数的示例:
* 1. 将匿名函数分配给一个变量, 格式: var name = function (参数1, ...){代码块; return 返回值;}
let sayHello = function() {
  console.log("Hello!");
};

sayHello();  // 输出 "Hello!"
在上述示例中,创建了一个匿名函数并将其分配给名为sayHello的变量.
然后, 可以通过调用变量sayHello来执行匿名函数.
* 2. 作为参数传递给另一个函数:
function runFunction(func) {
  func();
}

runFunction(function() {
  console.log("Hello from anonymous function!");
});
在上述示例中, 定义了一个名为runFunction的函数, 并接受一个函数参数func.
将一个匿名函数作为参数传递给runFunction函数, 并在匿名函数中打印一条消息.
* 3. 立即调用函数表达式 (Immediately Invoked Function Expression,IIFE) 的写法.
     它通常用于创建一个匿名函数并立即执行它。
(function(){
  console.log("This is an IIFE!");
})();
在该表达式中, 函数被包裹在圆括号中, 形成一个函数表达式, 后面的空括号()触发了该函数的立即执行.
IIFE可以包含参数, 通过在括号内传递实参来传递这些参数.
(function(name){
  console.log("Hello, " + name + "!");
})("John");
在上述示例中, 创建了一个接收一个参数name的匿名函数, 并在调用IIFE时传递了实参'John'.
这将在控制台输出 "Hello, John!".

IIFE是一种常见的使用匿名函数的模式, 它可以帮助我们创造一个独立的作用域, 避免变量污染全局命名空间.
同时, 它也可用于在函数执行前进行一些初始化操作.
匿名函数在某些情况下非常有用, 特别是当需要在特定的上下文中创建临时的, 一次性的函数时.
它们常用于回调函数, 立即调用函数表达式(IIFE), 或者在需要创建闭包时.

注意, 匿名函数只能通过其分配给的变量或传递给的函数来调用, 无法直接引用匿名函数本身.

6.6 箭头函数

箭头函数是ES6引入的一种新的函数定义语法, 它提供了一种更简洁的语法来编写函数.
箭头函数是一种简洁且常用的函数定义语法, 特别适用于函数体较简单的情况.
它可以减少代码量并改善可读性.
需要注意的是, 箭头函数不能用作构造函数, 因此不能使用new操作符调用箭头函数.

箭头函数的基本语法如下:
(parameters) => { 
  // 函数体
}
箭头函数使用箭头符号=>来表示函数的定义, 并可以在括号内指定参数列表.
如果只有一个参数, 可以省略括号; 如果没有参数, 需要使用空括号().
6.6.1 无参箭头函数
let sayHello = () => {
  console.log("Hello!");
};

sayHello();  // 输出: "Hello!"
在上述示例中, 使用箭头函数定义了一个无参数的函数, 并通过调用sayHello()来执行它.
6.6.2 有参箭头函数
let add = (a, b) => {
  return a + b;
};

console.log(add(3, 5));  // 输出: 8
在上述示例中, 使用箭头函数定义了一个有两个参数的函数, 并返回这两个参数的和.
6.6.3 箭头函数的隐式返回
let multiply = (a, b) => a * b;

console.log(multiply(3, 5));  // 输出: 15
在上述示例中, 使用箭头函数定义了一个有两个参数的函数, 并直接通过表达式a * b来隐式地返回函数的结果.
这种简化语法适用于只包含单个表达式的函数体.
箭头函数中的this关键字在定义时就被绑定, 而不是在运行时绑定.
它会捕获箭头函数所在的上下文的this值, 而不是创建一个新的函数作用域.
这使得箭头函数在某些情况下更方便地使用和理解.

6.7 全局变量和局部变量

区分局部变量和全局变量非常重要, 因为它们在作用域内的可见性和生命周期上存在差异.
使用局部变量可以在函数内部封装数据, 实现更好的模块化和封装性.
6.7.1 局部变量
在JavaScript中, 使用let关键字声明的变量具有块级作用域, 它们只在声明它们的代码块(通常是函数内部)内部可见与可访问.
这样的变量被称为局部变量.

在函数内部声明的局部变量只在该函数的作用域内生效.
一旦函数执行完毕, 局部变量就会被销毁, 无法在函数外部访问到它们.
这种机制有助于避免变量名的冲突和全局命名空间的污染.

以下是一个示例:
function greet() {
  let message = 'Hello';  // 在函数内部声明局部变量

  console.log(message);  // 在函数内部访问局部变量
}

greet();  // 输出: "Hello"

// console.log(message);  // 报错, 无法访问函数内的局部变量
在上述示例中, 在函数greet内部声明了一个名为message的局部变量.
可以在同一函数内部访问和使用该变量, 但在函数外部则无法访问.
6.7.2 全局变量
全局变量是在JavaScript中在全局作用域中声明的变量, 它们可以在代码的任何地方访问和使用.

在JavaScript中, 如果在任何函数内部或代码块中未使用关键字var, let或const声明变量, 那么该变量将自动成为全局变量.
如果在顶层作用域中声明的变量也是全局变量.
let name = 'John';  // 全局变量
var age = 25;      // 全局变量

function greet() {
    console.log('Hello, ' + name + '! You are ' + age + ' years old.');
}

greet();            // 输出: "Hello, John! You are 25 years old."

console.log(name);  // 访问全局变量, 输出: "John"
console.log(age);   // 访问全局变量, 输出: 25
在上述示例中, 在全局作用域中声明了两个变量name和age.
这意味着它们可以在任何函数内部或代码块中访问和使用.
在greet函数内部和函数外部, 都能够访问这两个全局变量.
当在任何函数内部或代码块中未使用关键字var, let或const声明变量时, 该变量将自动成为全局变量. 下面是一个示例:
function foo() {
  globalVariable = 'I am a global variable';  // 未使用 var, let或const关键字声明变量
}

foo();  // 调用函数

console.log(globalVariable);  // 输出: "I am a global variable"
在上面的示例中, 变量globalVariable在函数foo内部被赋值, 但未使用var, let或const关键字声明.
它将自动成为全局变量, 并且可以在函数外部访问.

请注意, 在严格模式下(使用"use strict"), 不使用var, let或const声明变量将导致错误(ReferenceError).
建议始终使用var, let或const显式声明变量, 以确保在适当的作用域中定义变量, 避免意外创建全局变量.
全局变量的生命周期较长, 它们在整个页面加载和执行过程中都会存在.
因此, 应该谨慎使用全局变量, 避免命名冲突和全局命名空间的污染.

为了避免全局变量的问题, 推荐使用块级作用域声明变量(如let或const)来限制变量的作用域, 使其仅在需要的范围内可见.
这有助于提高代码的可维护性和可读性.

6.8 生命周期

在JavaScript中, 变量的生命周期取决于它们的作用域.
作用域: 控制如何访问和管理变量.

* 1. 局部变量的生命周期: 局部变量是在函数内部声明的变量, 它们存在于函数调用的生命周期内.
     当函数执行完毕, 局部变量将被销毁, 它们的内存空间将被释放.
     这意味着局部变量只在函数的执行期间存在, 并且在每次函数执行时都会重新创建.
     
* 2. 全局变量的生命周期: 全局变量是在全局作用域中声明的变量, 它们在整个页面加载和执行过程中存在. 
     全局变量在页面关闭后才会被销毁.

6.9 作用域

作用域是指在程序中定义变量的区域, 该变量在该区域内可被访问, 而在区域外不可被访问.

在JavaScript中, 存在以下几种作用域:
* 1. 全局作用域: 全局作用域是指在程序的最外层定义的变量, 它可以在整个程序中访问.

* 2. 函数作用域: 函数作用域是指在函数内部定义的变量, 它们只能在函数内部访问.

* 3. 块级作用域: 块级作用域是指在代码块(由花括号{}包围的区域)内定义的变量, 它们只能在该代码块内部访问.
6.9.1 变量查找顺序
作用域决定了变量的可见性和访问性.
JavaScript使用词法作用域规则, 按照定义变量的位置来确定变量的作用域.
当访问变量时, JavaScript引擎会按照作用域链逐级查找变量.
它首先查找当前作用域(函数内部), 然后逐级向外层作用域查找, 直到找到该变量或达到全局作用域.
var city = "BeiJing";

function outer() {
    var city = "ShangHai";

    function inner() {
        var city = "ShenZhen"; 
        console.log(city);
    }

    inner();
}

outer();  //输出结果是?  答案: ShenZhen
6.9.2 函数定义阶段发生的事情
函数定义阶段发生的事: 函数的查找关系在定义阶段就确定了, 与调用的位置无关.
var city = "BeiJing";

function Bar() {
    console.log(city);
}

function f() {
    var city = "ShangHai";
    return Bar;
}

var ret = f();

ret();  // 打印结果是? 答案: BeiJing
6.9.3 闭包函数
闭包函数: 函数内部使用外层函数的名称.
var city = "BeiJing";

function f() {
    var city = "ShangHai";

    function inner() {
        console.log(city);
    }

    return inner;
}

var ret = f();
ret();  // 打印结果是? 答案: ShangHai

7. Date对象

Date: 是内置对象, 用于处理日期和时间.
要创建一个日期对象, 需要使用关键字new加上Date构造函数, 并根据需要提供相应的参数.

7.1 创建时间对象

可以有多种方式定义和创建时间对象:
* 1. 最常用的方式是使用new Date()构造函数来创建一个表示当前时间的时间对象.
var currentDate = new Date();
console.log(currentDate);  // 2023-09-12T01:20:34.369Z  (与中国的时差为8)
* 2. 使用日期字符串作为参数来创建一个时间对象, 
     日期字符串的格式可以是标准格式(: "yyyy-MM-dd")或特定格式(: "March 15, 2023").
     '2023-03-15'使用ISO 8601格式的日期字符串, 该格式被广泛接受并具有一致性, 通常与UTC时间一起使用.
     因此, 它会被解析为UTC时间, 并且转换为对应的本地时间.
     'March 15, 2023'使用的是一个相对自然语言的日期格式, 它的解析比较依赖于浏览器或操作系统的本地化设置.
     因此, 解析结果会根据不同的本地化设置而有所差异.
var dateFromStr = new Date("2023-03-15");
console.log(dateFromStr)  // 2023-03-15T00:00:00.000Z

var dateFromStr2 = new Date("March 15, 2023");
console.log(dateFromStr2)  // 2023-03-14T16:00:00.000Z
* 3. 使用时间戳(以毫秒为单位)来创建一个时间对象.
     时间戳是自 1970  1  1  00:00:00 UTC(协调世界时)以来经过的毫秒数.
var timestamp = 1631358000000; // 2021-09-11 00:00:00 UTC
var dateFromTimestamp = new Date(timestamp);
console.log(dateFromTimestamp) // 2021-09-11T11:00:00.000Z
无论使用哪种方式, 创建一个时间对象后, 可以使用Date对象的方法来获取和操作日期和时间的各个部分, 例如获取年份, 月份, 日等.
需要注意的是, JavaScript中的月份是从0开始的, 0表示一月, 1表示二月, 以此类推.

7.2 内置方法

方法描述
.toLocaleString()返回日期和时间的本地化字符串形式, 包括年月日时分秒.
.toLocaleDateString()返回日期的本地化字符串形式, 包括年月日.
.getDate()返回日期对象中的日期(几号).
.getDay()返回日期对象中的星期几(从0开始, 其中0表示星期天).
.getMonth()返回日期对象中的月份(从0开始, 0 表示一月).
.getFullYear()返回日期对象中的完整年份.
.getHours()返回日期对象中的小时.
.getMinutes()返回日期对象中的分钟.
.getSeconds()返回日期对象中的秒钟.
.getMilliseconds()返回日期对象中的毫秒.
.getTime()返回日期对象的时间戳.
// 本地化字符, 字符串形式
let d1 = new Date();
console.log(d1.toLocaleString())      // 2023/9/12 09:36:16
console.log(d1.toLocaleDateString())  // 2023/9/12
console.log(d1.getDate())  // 12
console.log(d1.getDay())  // 2
console.log(d1.getMonth())  // 8
console.log(d1.getFullYear())  // 2023
console.log(d1.getHours())  // 9
console.log(d1.getMinutes())  // 37
console.log(d1.getSeconds())  //  37
console.log(d1.getMilliseconds())   //  164
console.log(d1.getTime())  // 1694482657164
编写代码, 将当前日期按'2022-1-27 11:01:01 星期三'格式输出.
                                     ↑分与秒的默认格式没有0, 需要自己组织格式.
// 定义一个数字与星期的对应关系对象的对象
const week_map = {
    0: '星期天',
    1: '星期一',
    2: '星期二',
    3: '星期三',
    4: '星期四',
    5: '星期五',
    6: '星期六',
}


function getTime() {
    // 定义一个时间对象
    var td = new Date()

    // 获取 年 月 日
    var year = td.getFullYear()
    var month = td.getMonth() + 1 // 月份是从0开始 所有的月份加1
    var date = td.getDate()

    // 获取时 间 分 秒
    var hour = td.getHours()   // 三元运算 值小于10 就 '0' + 值 
    var minutes = td.getMinutes() < 10 ? '0' + td.getMinutes() : td.getMinutes()
    var seconds = td.getSeconds() < 10 ? '0' + td.getSeconds() : td.getSeconds()

    // 获取星期几
    var week = week_map[td.getDay()]

    console.log(`${year}-${month}-${date} ${hour}:${minutes}:${seconds} ${week}`)
    // 2023-9-12 9:43:49 星期二
}

getTime()

18.JSON对象

序列化和反序列化是用于在JavaScript中转换对象和JSON字符串之间的过程.

序列化: 将JavaScript对象转换为JSON字符串的过程.
       可以使用JSON.stringify()方法来实现序列化. 例如:
var obj = { name: "John", age: 30 };
var jsonString = JSON.stringify(obj);
console.log(jsonString); // '{"name":"John","age":30}'
反序列化: 将JSON字符串转换回JavaScript对象的过程.
         可以使用JSON.parse()方法来实现反序列化. 例如:
var jsonString = '{"name":"John","age":30}';
var obj = JSON.parse(jsonString);
console.log(obj.name); // 'John'
console.log(obj.age); // 30
JSON.stringify()方法: 将对象转换为JSON字符串, 如果对象中包含函数, RegExp等非JSON支持的数据类型, 则会进行相应的转换或忽略.
JSON.parse()方法: 将JSON字符串转换回原始对象, 如果JSON字符串不是有效的JSON格式, 或者包含了非法的数据结构, 则会抛出错误.
序列化和反序列化JSON是常用的操作, 它允许我们在不同平台和应用程序之间交换数据, 并在JavaScript中方便地操作这些数据.

8. ReGxp对象

RegExp(正则表达式)对象: 用于定义一个模式, 该模式可以与文本进行匹配, 搜索和替换操作.
RegExp对象在JavaScript中提供了对正则表达式的支持.

8.1 创建方法

可以使用new RegExp(pattern, flags)创建一个RegExp对象, 其中pattern是正则表达式的字符串, flags是用于指定模式的标志. 例如:
var regex = new RegExp("pattern", "flags");
常见的正则表达式标志(flags)包括:

- i: 忽略大小写.
- g: 全局匹配, 找到所有匹配项.
- m: 多行匹配, 用于跨行搜索.
另外, 还可以使用字面量形式的正则表达式来创建RegExp对象, 例如:
var regex = /pattern/flags;

8.2 匹配方式

RegExp对象提供了多个方法来执行匹配, 搜索和替换操作, 常用的包括:

- test(str):  检查指定的字符串是否与正则表达式匹配, 返回布尔值.
- exec(str):  在指定字符串中执行正则表达式搜索, 返回匹配的结果对象.
- match(regexp):  在字符串中执行正则表达式搜索, 返回匹配的结果数组.
- search(regexp): 在字符串中搜索与正则表达式匹配的位置, 返回匹配的下标.
- replace(regexp, replacement):  在字符串中执行正则表达式的替换, 返回替换后的新字符串.
- split(regexp):  通过正则表达式将字符串拆分为数组, 返回拆分后的数组.
// 匹配以字母开头的, 长度为5-11位的字符串.      ↓逗号后面不要加空格.
var reg1 = new RegExp('^[a-zA-Z][a-zA-Z0-9]{5,11}');
var reg2 = /^[a-zA-Z][a-zA-Z0-9]{5,11}/;

console.log(reg1.test('aaaaaa'))  // true
console.log(reg2.test('aaaaaa'))  // true


var s1 = "hello world";
console.log(s1.match(/o/g))
// ["o", "o"]   全局匹配, 查找所有符合条件的字符, 以数组格式放回.


console.log(s1.search(/o/g))
// 4            返回匹配的下标, 全局搜索无效.


console.log(s1.split(/o/g))
// ["hell", " w", "rld"]  按匹配字符进行全局切分.


console.log(s1.replace(/o/g, "s"))
// "hells wsrld"    按匹配字符进行全局替换.

8.1 正则表达式的预期行为

在JavaScript中, 正则表达式的预期行为与上面描述的相符.
具体来说, 在全局匹配模式(使用g标志), 正则表达式对象会跟踪一个属性lastIndex, 该属性表示下一次匹配开始的位置索引.

当调用正则表达式的test()方法时, 它会尝试在字符串中查找与正则表达式匹配的文本.
如果成功找到匹配项, test()方法返回true并且lastIndex属性会被更新为匹配项的结束位置索引加一.
如果没有找到匹配项, test()方法返回false并且lastIndex属性会被重置为0.

这种行为允许你在一个字符串中多次执行正则表达式的全局匹配, 从上一次匹配结束的位置继续.
使用test()方法是其中的一种方式, 还可以使用exec()方法来实现同样的效果.
let reg2 = /^[a-zA-Z][a-zA-Z0-9]{5,11}/g;

console.log(reg2.test('aaaaaaa'))  // true
console.log(reg2.lastIndex)  // 7


console.log(reg2.test('aaaaaaa'))  // false
console.log(reg2.lastIndex)  // 0
匹配第一个aaaaa, 匹配成功在为真, 指针停止在最后一个a的位置后面, 下次匹配还是从这个位置开始匹配, a后面为空, 所以为false.
下次才会匹配才跳出上次匹配的语句, 开始新的匹配,到匹配开头的位置.

9. Math对象

Math对象是JavaScript中的一个内置对象, 它提供了一些常用的数学函数和常量.
通过Math对象, 可以执行数值计算和处理.
函数描述示例
abs(x)返回数的绝对值abs(-5) 返回 5
exp(x)返回 e 的指数exp(2) 返回 7.3890560989306495
floor(x)对数进行下舍入floor(3.7) 返回 3
log(x)返回数的自然对数(底为e)log(10) 返回 2.302585092994046
max(x,y)返回 x 和 y 中的最高值max(5, 3) 返回 5
min(x,y)返回 x 和 y 中的最低值min(5, 3) 返回 3
pow(x,y)返回 x 的 y 次幂pow(2, 3) 返回 8
random()返回 0 ~ 1 之间的随机数random() 可能返回 0.123456789
round(x)把数四舍五入为最接近的整数round(3.2) 返回 3
sin(x)返回数的正弦值sin(0) 返回 0
sqrt(x)返回数的平方根sqrt(9) 返回 3
tan(x)返回角的正切tan(0) 返回 0
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值