目录
- JavaScript基础语法
- 1. 变量声明和赋值
- 2. 数据类型
- 1) 基本数据类型
- 2) 复合数据类型(引用类型)
- 3) 特殊数据类型
- 3. 运算符
- 1) 算术运算符
- 2) 赋值运算符
- 3) 比较运算符
- 4) 逻辑运算符
- 5) 三元运算符
- 4. 控制流程
- 1) 条件语句
- 2) 循环语句
- 5. 函数
- 1) 函数的基本使用
- ① 什么是函数
- ② 函数的定义和调用
- 2) 递归
- 3) 作用域和闭包
- 4) 立即执行函数 IIFE
- 小案例
- 1.小小计算器
- 2.是否为闰年
- 3.BMI指数
- 4.水仙花数
- 5.猜数字小游戏
- 6.鸡兔同笼
- 7.寻找100以内的质数
- 8.do while[dx,dy]
- 9.计算圆周率
- 10.递归求阶乘
- 11.随机样本
- 12.数组去重
- 13.浅克隆
- 14.冒泡排序
- 15.sort()方法排序
- 16.斐波那契数列
- 17.喇叭花数
- 18.实现深克隆
- 19.用闭包模拟私有变量
- 20.IIFE的作用
JavaScript基础语法
JavaScript的基础语法包括变量声明和赋值、数据类型、运算符、控制流程和函数等方面:
1. 变量声明和赋值
在变量声明和赋值时,有一些需要注意的地方,下面列举了一些常见的情况:
-
变量命名规则:
- 变量名必须以字母、下划线或美元符号开头。
- 变量名可以包含字母、数字、下划线或美元符号。
- 变量名是区分大小写的。
-
声明变量:
- 使用
var
关键字可以声明一个变量,例如:var x;
。 - 在同一个作用域中,相同的变量名只能被声明一次。如果多次声明同一个变量名,后续的声明将被忽略,而且不会引发错误。
- 使用
-
赋值变量:
- 可以使用赋值操作符
=
为变量赋值。 - 在声明变量的同时进行赋值是很常见的做法,例如:
var x = 5;
。 - 可以多次为同一个变量赋值,后续的赋值将覆盖之前的值。
- 可以使用赋值操作符
-
变量提升(Hoisting):
- JavaScript中的变量声明会被提升到其所在作用域的顶部,但初始化的赋值操作仍然保留在原始位置。
- 这意味着你可以在变量声明之前引用变量,但它的值将是
undefined
。 - 示例:
console.log(x); // 输出:undefined var x = 5;
-
块级作用域变量(ES6新增):
- 使用
let
或const
关键字可以声明块级作用域的变量。 - 块级作用域变量仅在其被声明的块内部可见,并且不会被提升。
- 示例:
{ let y = 10; // 块级作用域变量 const z = 15; // 块级作用域常量 console.log(y); // 输出:10 } console.log(y); // 报错:y is not defined
- 使用
-
常量声明:
- 使用
const
关键字声明的变量是一个常量,一旦赋值后就不能更改。 - 声明常量时必须同时进行赋值。
- 示例:
const PI = 3.14; // 声明一个常量PI并赋值为3.14 PI = 3.14159; // 报错:Assignment to constant variable.
- 使用
-
变量作用域:
- 变量的作用域指的是变量的可访问范围。
- 全局作用域中声明的变量可以在整个代码中都访问到。
- 函数作用域中声明的变量只能在函数内部访问到。
- 块级作用域中声明的变量只能在块内部访问到。
- 尽量避免使用全局变量,以避免命名冲突和不必要的复杂性。
2. 数据类型
1) 基本数据类型
-
数字(Number):
- 用于表示数字,可以包括整数和浮点数。
- JavaScript使用IEEE-754标准来表示所有数字,因此在进行浮点数运算时可能会出现精度丢失的问题。
- 最大安全整数是2^53 - 1,超过这个范围的整数将无法精确表示。
-
字符串(String):
-
用于表示文本数据,由单引号(')或双引号(")括起来。
-
字符串是不可变的,即一旦创建就不能修改。对字符串的操作通常会返回一个新的字符串。
-
-
布尔值(Boolean):
- 用于表示真(true)或假(false)的逻辑值。
- 布尔值常用于条件语句和逻辑判断。
-
空(Null):
-
表示一个空值。
-
null是一个特殊的关键字,不是对象也不是未定义(undefined)。
-
使用typeof运算符检测null会返回"object",这是一个历史遗留的错误。
-
-
未定义(Undefined):
-
表示未赋值。
-
使用typeof运算符检测未定义的变量会返回"undefined"。
-
避免显式地将变量设为undefined,如果需要清空一个变量的值,可以使用null。
-
2) 复合数据类型(引用类型)
-
对象(Object):
-
表示复杂的数据结构,可以存储多个相关属性和方法。对象由键值对集合组成。
-
对象是引用类型,赋值时实际上是复制了引用而不是对象本身。
-
使用点(.)或方括号([])可以访问对象的属性。
-
-
数组(Array):
-
表示一个有序的集合,可以存储多个值。
-
数组是一种特殊的对象,使用数字索引(从0开始)来访问元素。
-
可以使用length属性获取数组的长度。
-
JavaScript中的数组是一个有序的数据集合,可以包含任意类型的元素。在JavaScript中,可以使用数组字面量或Array()构造函数来创建数组。
以下是一些常见的操作和用法:
创建数组:
const arr = [1, 2, 3]; // 使用数组字面量创建数组 const arr2 = new Array(); // 使用Array()构造函数创建一个空数组 const arr3 = new Array(1, 2, 3); // 使用Array()构造函数创建一个带有初始值的数组
访问和修改元素:
console.log(arr[0]); // 输出数组的第一个元素,索引从0开始 arr[1] = 5; // 修改数组的第二个元素为5
数组的长度:
console.log(arr.length); // 输出数组的长度
向数组末尾添加元素:
arr.push(4); // 在数组的末尾添加一个元素4
从数组末尾删除元素:
arr.pop(); // 删除数组的最后一个元素
向数组开头添加元素:
arr.unshift(0); // 在数组的开头添加一个元素0
从数组开头删除元素:
arr.shift(); // 删除数组的第一个元素
迭代数组:
arr.forEach(function(element) { console.log(element); }); // 遍历数组并输出每个元素
数组切片:
const slicedArr = arr.slice(1, 3); // 返回一个新的数组,包含索引从1到3(不包括3)的元素
注意:
索引和长度:JavaScript数组的索引从0开始,而数组的长度是基于索引的最大值加1。例如,一个长度为3的数组有索引0、1、2,对应三个元素。
类型灵活:JavaScript数组可以包含不同类型的元素,甚至可以同时包含字符串、数字、对象等多种类型。
动态性:JavaScript数组的长度是可变的,可以随时添加或删除元素,并且可以根据需要自动调整大小。
循环遍历:可以使用for循环、forEach()、map()、filter()等方法来遍历数组的元素。
注意数组拷贝:当将一个数组赋值给另一个变量时,实际上是将引用传递。如果修改其中一个数组,另一个数组也会受到影响。如果需要创建一个新的独立副本,可以使用slice()或spread运算符(…)进行浅拷贝(克隆),或使用深拷贝方法(如JSON.parse(JSON.stringify(arr)))进行深拷贝。
注意数组边界:在访问数组元素时,要确保索引不超出数组的范围,否则会抛出"Index out of range"错误。
数组的比较:JavaScript中的数组比较是基于引用的,而不是内容。即使两个数组具有相同的元素,但它们不会被认为是相等的,除非它们引用相同的内存空间。
3) 特殊数据类型
-
函数(Function):
-
表示可重复执行的代码块,可以接收参数,并返回一个值。
-
函数可以作为变量、参数、返回值、对象属性等。
-
在JavaScript中,函数也是对象。
-
-
日期(Date):
-
表示日期和时间。
-
JavaScript使用内置的Date对象来表示日期和时间。
-
Date对象提供了各种方法来操作和格式化日期。
-
-
正则表达式(RegExp):
-
表示用于模式匹配的文本字符串。
-
正则表达式是对字符串进行模式匹配和替换的强大工具。
-
在JavaScript中,正则表达式由正则表达式字面量或RegExp对象表示。
-
示例:
var num = 5; // 数字类型
var str = "Hello"; // 字符串类型
var bool = true; // 布尔类型
var empty = null; // 空类型
var undef = undefined; // 未定义类型
var obj = {name: "John", age: 25}; // 对象类型
var arr = [1, 2, 3]; // 数组类型
- 基本类型值与引用类型值的区别
3. 运算符
JavaScript提供了一系列的运算符,用于在表达式中执行各种操作。以下是一些常见的JavaScript运算符:
1) 算术运算符
- 加法(+):对两个值进行相加。
- 减法(-):从第一个值中减去第二个值。
- 乘法(*):将两个值相乘。
- 除法(/):将第一个值除以第二个值。
- 取余(%):返回第一个值除以第二个值的余数。
2) 赋值运算符
- 赋值(=):将右侧的值赋给左侧的变量。
- 加等于(+=):将右侧的值与左侧的变量相加,并将结果赋给左侧的变量。
- 减等于(-=):从左侧的变量中减去右侧的值,并将结果赋给左侧的变量。
- 乘等于(*=):将左侧的变量与右侧的值相乘,并将结果赋给左侧的变量。
- 除等于(/=):将左侧的变量除以右侧的值,并将结果赋给左侧的变量。
- 取余等于(%=):将左侧的变量与右侧的值取余,并将结果赋给左侧的变量。
3) 比较运算符
- 等于(==):检查两个值是否相等。
- 全等于(===):检查两个值是否严格相等(值和类型都相等)。
- 不等于(!=):检查两个值是否不相等。
- 不全等于(!==):检查两个值是否严格不相等。
- 大于(>)、小于(<)、大于等于(>=)、小于等于(<=):用于比较两个值的大小关系。
4) 逻辑运算符
- 逻辑与(&&):仅当两个操作数都为真时,结果才为真。
- 逻辑或(||):当至少有一个操作数为真时,结果为真。
- 逻辑非(!):对操作数取反,如果是真则变为假,如果是假则变为真。
5) 三元运算符
- 三元运算符(条件运算符)根据条件选择返回不同的值。
- 语法:条件 ? 结果1 : 结果2
注意事项:
-
类型转换:
- 在某些情况下,JavaScript会自动进行类型转换以匹配运算符操作数的类型。
- 例如,加法运算符(+)既可以执行数字相加,也可以执行字符串拼接。如果一个值是字符串,另一个是数字,JavaScript会将数字转换为字符串然后进行拼接。
- 这种类型转换可能会导致意外的结果,所以在进行比较或者运算时,要注意操作数的类型。
-
短路评估:
- 在逻辑与(&&)和逻辑或(||)运算中,当决定整个表达式的结果后,剩余的操作数将不再计算。
- 对于逻辑与(&&),如果第一个操作数为假,则整个表达式的结果为假,第二个操作数将不再计算。
- 对于逻辑或(||),如果第一个操作数为真,则整个表达式的结果为真,第二个操作数将不再计算。
- 这种短路特性可以用于提高代码效率及避免潜在的错误。
-
隐式类型转换:
- 在某些情况下,JavaScript会自动进行隐式类型转换。例如,在使用比较运算符进行比较时,如果操作数的类型不同,JavaScript会尝试进行类型转换以便进行比较。
- 隐式类型转换可能会导致不可预期的结果,所以建议在进行比较时尽量显式地进行类型转换,确保比较行为符合预期。
-
运算符优先级和结合性:
- 运算符优先级( 非运算 > 数学运算 > 关系运算 > 逻辑运算)
- 如果一个表达式中包含多个运算符,需要注意运算符的优先级和结合性,可以使用小括号明确指定运算顺序,以保证表达式的计算结果正确。
-
NaN(非数值):
- 如果一个运算无法产生数值结果,JavaScript会返回特殊值NaN,表示“非数值”。
- NaN与任何值都不相等,包括它自己,所以NaN == NaN的结果为false。
- 当进行数学运算时,要小心处理可能产生NaN的情况,并根据具体需求进行判断和处理。
4. 控制流程
JavaScript的控制流程由条件语句和循环语句组成,用于根据条件的真假和重复执行代码块。
1) 条件语句
条件语句根据给定的条件来执行不同的代码块。
- if语句是最简单的条件语句,只有在条件为真时才会执行后续的代码块。
if (条件) {
// 执行语句
}
可以使用逻辑运算符(如&&、||)和比较运算符(如==、!=)来构建复杂的条件。
- if-else语句在条件为真时执行if代码块,否则执行else代码块。
if (条件) {
// 执行语句1
} else {
// 执行语句2
}
- switch语句根据表达式的值来选择要执行的代码块。
switch (表达式) {
case 值1:
// 执行语句1
break;
case 值2:
// 执行语句2
break;
default:
// 执行默认语句
break;
}
如果表达式的值与某个case的值相等,则执行该case的代码块,并使用break语句跳出switch语句。如果没有匹配的case,则执行default的代码块。
示例:
let num = 10;
if (num > 0) {
console.log("num是一个正数");
} else if (num < 0) {
console.log("num是一个负数");
} else {
console.log("num是零");
}
// 输出:num是一个正数
let day = "Monday";
switch (day) {
case "Monday":
console.log("星期一");
break;
case "Tuesday":
console.log("星期二");
break;
case "Wednesday":
console.log("星期三");
break;
default:
console.log("其他日子");
break;
}
// 输出:星期一
2) 循环语句
循环语句用于多次执行相同或类似的代码块。
- for循环按照指定的条件和步长执行代码块,通常用于已知循环次数的情况。
for (初始化; 条件; 步长) {
// 执行语句
}
其中,初始化用于设置循环变量的初始值,条件用于判断是否继续循环,步长用于更新循环变量的值。
- while循环在条件为真时重复执行代码块,条件在循环开始前进行判断,不满足条件则跳出循环。
while (条件) {
// 执行语句
}
- do-while循环先执行一次代码块,然后在条件为真时重复执行代码块,条件在循环结束后进行判断。
do {
// 执行语句
} while (条件);
即使条件为假,do-while循环至少会执行一次。
示例:
for (let i = 0; i < 5; i++) {
console.log(i);
}
// 输出:0, 1, 2, 3, 4
let num = 0;
while (num < 5) {
console.log(num);
num++;
}
// 输出:0, 1, 2, 3, 4
let num = 0;
do {
console.log(num);
num++;
} while (num < 5);
// 输出:0, 1, 2, 3, 4
- break和continue
for (let i = 1; i <= 10; i++) {
if (i % 3 === 0) {
continue; // 跳过3的倍数
}
if (i === 7) {
break; // 结束循环
}
console.log(i);
}
// 输出:1, 2, 4, 5, 6
let count = 0;
while (true) {
console.log(count);
count++;
if (count === 5) {
break; // 结束无限循环
}
}
// 输出:0, 1, 2, 3, 4
注意事项:
- 条件语句和循环语句内部的代码块可以包含多条语句,通常使用花括号{ }来定义代码块。
- 可以在条件语句中嵌套其他条件语句,或在循环内部再使用条件语句。但是要注意代码的可读性和复杂度,避免过多的嵌套和混乱的逻辑。
- 可以使用break语句提前终止循环,使用continue语句跳过当前循环的剩余代码并进入下一轮循环。
- 在使用循环时,要注意循环变量的更新操作,以确保循环能向前推进或合理退出。
5. 函数
1) 函数的基本使用
① 什么是函数
函数是一种在编程中用于封装代码的机制。它是一段可重复使用的代码块,通过给它一个名称,可以在程序中多次调用该代码块,从而实现代码的模块化和复用。
函数由以下几个主要组成部分:
-
函数名:函数具有一个名称,用于标识和调用函数。函数名应具有描述性,能够清晰地表达函数的功能。
-
参数:函数可以接受零个或多个参数(也称为形式参数)。参数是函数在调用时传递给函数内部的值。参数可以在函数内部使用,用于执行特定的操作和计算。参数允许将数据传递到函数中,从而使函数更加通用和灵活。
-
函数体:函数体包含了函数要执行的一系列语句或代码块。函数体是函数的主要执行逻辑,在函数调用时被执行。
-
返回值:函数可以返回一个值作为其执行结果。返回值使用return语句定义,并在函数体中指定要返回的值。返回值可以使用在函数调用表达式中进行进一步处理和使用。
函数的好处包括:
- 代码的模块化:函数将一段代码封装成一个独立的模块,使代码结构清晰,易于维护和复用。
- 提高代码的可读性和可维护性:将一段复杂的逻辑封装在函数中,并为它赋予一个有意义的名称,使代码更易于理解和修改。
- 代码的复用:可以在程序中多次调用同一个函数,避免重复编写相同功能的代码。
- 提高程序的可测试性:函数可以独立地进行单元测试,更容易发现和修复问题。
② 函数的定义和调用
函数的定义和调用是使用函数的基本步骤。下面是函数的定义和调用的说明:
- 函数的定义:
- 使用关键字
function
来定义一个函数。 - 给函数命名,可以根据需求自定义函数名称。
- 使用关键字
语法:
function functionName() {
// 函数代码块
}
示例:
function greet() {
console.log("Hello, world!");
}
- 函数的调用:
- 函数调用通过函数名称后跟着一对括号
()
来实现。 - 在调用函数时,函数体内的代码将会执行。
- 函数调用通过函数名称后跟着一对括号
语法:
functionName();
示例:
greet(); // 输出:Hello, world!
- 带参数的函数:
- 函数可以带有参数,在函数定义时使用参数名称声明。
- 在函数调用时,将具体的值传递给这些参数。
语法:
function functionName(parameter1, parameter2, ...) {
// 函数代码块
}
示例:
function greet(name) {
console.log("Hello, " + name + "!");
}
greet("John"); // 输出:Hello, John!
- 返回值:
- 函数可以返回一个值,使用
return
语句指定要返回的值。 - 调用函数时,可以将函数的返回值赋值给一个变量,或者直接使用函数调用表达式中的返回值。
- 函数可以返回一个值,使用
语法:
function functionName() {
// 函数代码块
return value;
}
示例:
function add(a, b) {
return a + b;
}
let result = add(3, 5);
console.log(result); // 输出:8
- 函数的优先提升
总结:
- 定义函数使用
function
关键字,给函数命名,并编写函数体内的代码。 - 调用函数使用函数名后加上一对括号
()
,如果有参数,可以在括号中传递参数值。 - 函数可以带有参数,并且可以返回一个值。
2) 递归
递归指的是函数在其自身内部调用自己的一种技术或方法。换句话说,递归是一种通过不断调用自己来解决问题的求解方法。
递归函数包含两个主要部分:
-
基本情况(Base case):递归函数中必须包含一个或多个基本情况,以防止无限递归。基本情况是函数停止递归的条件。
-
递归调用(Recursive call):递归函数调用自身来解决规模更小的子问题,直到达到基本情况为止。
递归的实现可以更简洁和优雅地解决一些问题,例如:
- 数学中的阶乘计算。
- 斐波那契数列的生成。
- 文件夹的深度遍历等。
递归的思想是将大问题划分为与原问题相似但规模更小的子问题,并反复调用函数来解决这些子问题,直到达到基本情况从而结束递归。
注意:
如果递归没有正确设置基本情况或递归调用的条件,它可能会导致无限递归,这会使得程序运行时陷入无限循环,并最终耗尽可用的系统资源。
3) 作用域和闭包
-
作用域:
- 作用域指的是变量和函数在代码中可访问的范围。
- JavaScript 采用了词法作用域,即变量的作用域在代码编写阶段就确定了。
- 根据声明变量的位置,作用域分为全局作用域和局部作用域(函数作用域和块级作用域):
- 全局作用域:在整个程序中都可以访问的变量,声明在任何函数外部的变量都属于全局作用域。
- 函数作用域:在函数内部声明的变量,只能在该函数内部访问。
- 块级作用域:由一对花括号
{}
封闭的代码块内声明的变量,只能在该块级作域内部访问。
-
闭包:
- 闭包是指函数可以访问其词法作用域以及上层作用域中的变量,即使在函数被调用执行时,这些变量已经超出了其作用域的范围。
- 当一个函数内部定义了另一个函数,并返回该内部函数时,内部函数就形成了一个闭包。
- 闭包可以捕获并保留其所在作用域的变量和状态,从而实现函数和数据的封装与隐藏。
闭包的应用场景包括:
- 保护私有变量:通过闭包可以创建私有变量,只能通过内部函数来访问和修改。
- 记忆上下文:在异步操作中,通过闭包可以记住异步操作发起时的上下文环境,以便在异步操作完成后继续使用。
- 函数柯里化:通过闭包可以将接受多个参数的函数转换为接受一个参数并返回一个新函数的形式,从而实现函数的复用和延迟执行。
4) 立即执行函数 IIFE
立即执行函数(Immediately Invoked Function Expression,简称IIFE)是一种 JavaScript 中的函数表达式,它可以立即执行而无需显式调用。
常见的 IIFE 格式如下:
(function() {
// 函数体
})();
基本原理和用法如下:
- 创建函数表达式:将函数定义放入一对括号
()
内部,形成一个函数表达式。 - 立即执行:在函数表达式后面添加另一对括号
()
,使其成为一个立即执行的函数。这样函数将会在定义后立即执行。
IIFE 的主要用途是创建一个独立的作用域,并且避免变量污染全局作用域。由于 IIFE 的执行结果不会被保存在全局变量中,所以其内部声明的变量不会影响到全局作用域。
示例:
(function() {
var name = "John";
console.log("Hello, " + name); // 输出:Hello, John
})();
console.log(name); // 报错,name 不在全局作用域中可见
此外,IIFE 还可以接收参数:
(function(message) {
console.log(message);
})("Hello, World!"); // 输出:Hello, World!
通过传递参数,可以在立即执行的函数中使用外部的值。
小案例
1.小小计算器
var a = Number(prompt("请输入第一个数字"));
var b = Number(prompt("请输入第二个数字"));
var sum = a + b;
alert('第一个数字'+a+'加上第二个数字'+b+'等于'+ sum);
2.是否为闰年
var a = Number(prompt("请输入年份:"));
// 1.能被4整除,但是不能被100整除
// 2.能被100整除,也能被400整除
// 是闰年输出为true,不是输出为false
alert(a%4==0 && a%100!=0 || a%100==0 && a%400==0);
3.BMI指数
var height = Number(prompt("请输入你的身高(m)"));
var weight = Number(prompt("请输入你的体重(公斤)"));
var bmi = weight / Math.pow(height,2);
alert('您的BMI指数为:'+bmi);
if(bmi < 18.5){
alert('偏瘦');
}else if(bmi < 24){
alert('正常');
}else if(bmi < 28){
alert('过胖');
}else if(bmi < 32){
alert('肥胖');
}else{
alert('非常肥胖');
}
4.水仙花数
//水仙花数:一个 n 位数等于其每个数字的立方和
//例如:1^3 + 5^3 + 3^3 = 153
var n = Number(prompt("请输入一个三位数"));
// 对用户输入的值进行合法的验证
if(!isNaN(n) && 100 <=n && n<=999){
var a = Math.floor(n/100);
var b = Math.floor((n /10) % 10);
var c = n % 10
if(Math.pow(a,3)+Math.pow(b,3)+Math.pow(c,3)==n){
alert('这个数是水仙花数');
}else{
alert('这个数不是水仙花数')
}
}else{
alert('您输入的数字不合法')
}
5.猜数字小游戏
//产生一个随机数,2-99
var num = parseInt(Math.random()*98)+2;
var min = 1;
var max =100;
//为了不断地重复执行询问,必须使用死循环
while(true){
// 用户输入一个数字
var n = Number(prompt('请猜测数字'+min+'~'+max));
//验证用户输入的语句是否在范围内
if(n<min || n>max){
//不在区间内,直接放弃这次循环,开启下一次迭代
alert('您输入的数字不在范围内!');
continue;
if(n<num){
alert('您输入的数字偏小');
min = n
}else if(n>num){
alert(('您输入的数字偏大'));
max = n
}else{
alert(('恭喜您,猜对了!'));
break;
}
}
}
6.鸡兔同笼
// 鸡兔同笼问题,头:35 脚:94
//方法1:假设小鸡有a只,兔子有b只
// for(var a = 0;a <= 35; a++){
// for(var b = 0; b <= 35 ;b++){
// if(a + b == 35 && 2 * a + 4 * b == 94){
// console.log('小鸡有'+a+'只,兔子有'+b+'只');
// }
// }
// }
// 方法2:假设小鸡有a只,兔子就是(35-a)只
for( var a = 0; a <= 35; a++){
var b = 35 - a;
if(a*2+b*4 == 94){
console.log('小鸡有'+a+'只,兔子有'+b+'只');
}
}
7.寻找100以内的质数
// 穷举法
outer: for(var i = 2; i <=100; i++){
//内层循环从2开始尝试除i,如果能够整除,说明它不是质数,就可以筛选下一个数字了
for(var j = 2 ;j < i; j++){
if(i%j == 0){
// 说明数字i不是质数
//要给外层循环加上label,然后在continue的后面加上label,这样就表示立即开始迭代外层for循环的下一个数字了,而不是内层for循环
continue outer;
}
}
// 能够遇见这条语句的数字i,一定是质数,否则就被continue略过了
console.log(i)
}
8.do while[dx,dy]
// 题目:随机两个变量,dx和dy,它们都在[-4,4]之间随机取值,但是不能都为0
do{
//得到[a,b]区间的整数,公式: parseInt(Math.random()*(b-a+1))+a
var dx = parseInt(Math.random()*9)-4;
var dy = parseInt(Math.random()*9)-4;
}while(dx==0 && dy==0)
// 只要出循环,就保证dx和dy不都是0
console.log(dx,dy);
9.计算圆周率
// 用莱布尼茨级数估算圆周率
// Π = 2 * (1 + 1/3 + (1*2)/(3*5) + (1*2*3)/(3*5*7) +(1*2*3*4)/(3*5*7*9)+(1*...n)/(3*5...*(2n+1))
//累加器,就是最后的答案
var sum = 0;
// 累乘器,用来制作每一项,制作出来了的那个项,要往累加器中累加
var item = 1;
// 让用户输入n
var n = Number(prompt('请输入数字n'));
for(var i = 1; i < n+1; i++){
// 要先制作出这一项,这一项怎么制作?要使用累乘器,item就是小车箱
item *= i/(2 * i +1);
console.log(item);
sum += item;
}
//显示结果
alert((1+sum)*2);
10.递归求阶乘
// 用莱布尼茨级数估算圆周率
// Π = 2 * (1 + 1/3 + (1*2)/(3*5) + (1*2*3)/(3*5*7) +(1*2*3*4)/(3*5*7*9)+(1*...n)/(3*5...*(2n+1))
//累加器,就是最后的答案
var sum = 0;
// 累乘器,用来制作每一项,制作出来了的那个项,要往累加器中累加
var item = 1;
// 让用户输入n
var n = Number(prompt('请输入数字n'));
for(var i = 1; i < n+1; i++){
// 要先制作出这一项,这一项怎么制作?要使用累乘器,item就是小车箱
item *= i/(2 * i +1);
console.log(item);
sum += item;
}
//显示结果
alert((1+sum)*2);
11.随机样本
var arr = [3,4,5,2,5,,2,89];
// 结果数组
var result = [];
//遍历原数组
for(var i=0;i<arr.length;i++){
//随机选择一项的下标,数组的下标0~arr.length-1;
//random公式: [a,b]区间的随机数是parseInt(Math.random()*b-a+1)+a
var n = parseInt(Math.random()* arr.length);
//把这项推入数组
result.push(arr[n]);
//删除这项,防止重复被随机到
arr.splice(n,1);
}
console.log(result);
12.数组去重
var arr = [1,23,2,3,1,11,111,11,2,2,2,]
// 结果数组
var result = [];
//遍历数组
for(var i=0;i<arr.length;i++){
//判断遍历的这项是否在结果数组中,如果不在就推入
//includes方法用来判断某项是否在数组中
if(!result.includes(arr[i])){
result.push(arr[i]);
}
}
console.log(result);
13.浅克隆
var arr = [1,2,3,4,5,[3,6,8]];
//结果数组
var result = [];
// 如果使用 result = arr;则将arr的内存地址传给了result,而数值无法克隆
//遍历原数组的每一项,把遍历到的项推入到结果数组中
for(var i =0 ;i< arr.length;i++){
result.push(arr[i])
}
console.log(result);
console.log(result == arr);//期望false,因为引用类型值进行传递比较的时候,==比较的是内存地址
console.log(result[4] == arr[4]); //藕断丝连
14.冒泡排序
// 冒泡排序
var arr = [1,23,4,5356,3,2,65,5];
//一趟趟 比较,趟数序号就是i
for(var i =1;i < arr.length;i++){
//内层循环负责两两相比较
for(var j = arr.length-1;j>=i;j--){
//判断项的大小
if(a[j]<a[j-1]){
var temp = arr[j];
arr[j] = arr[j-1];
arr [j-1] = temp;
}
}
console.log(arr);
}
console.log(arr);
15.sort()方法排序
// 函数中的a、b分别表示数组中靠前和靠后的项,
//如果需要将它们交换位置,则返回任意正数;否则就返回负数。
var arr =[33,55,22,11,66];
//排序
arr.sort(function (a,b){
return a-b;
});
console.log(arr);
16.斐波那契数列
//斐波那契数列是这样的数列:1、1、2、3、5、8、13、21
// 编写一个函数,这个函数的功能是返回斐波那契数列中下标为n的那项
function fib(n){
// 数列下标为0的项目,和下标为1的项值都是1
if(n ==0 || n==1) return 1;
//斐波那契数列的本质特征就是每一项,都等于前面两项的和
return fib(n-1) + fib(n-1);
}
// 书写一个循环语句,计算斐波那契数列的前15项
for(var i =0;i<15;i++){
console.log(fib(i));
}
17.喇叭花数
// 题目:刺叭花数是这样的三位数:其每一位数字的阶乘之和恰好等于它本身。即abc=a!+b!+c!,其中abc表示一个三位数。试寻找所有喇叭花数。
// 思路:将计算某个数字的阶乘封装成函数,这样可以让问题简化。
//阶乘函数,计算一个数字的阶乘
function factorial(n){
//累乘器
var result = 1;
for( var i=1 ; i<= n; i++){
result *= i;
}
return result;
}
//穷举法,从100-999寻找喇叭花数
for(var i = 100; i<= 999;i++){
// 把数字i转成字符串
var i_str = i.toString();
// abc分别表示百位\十位\个位
var a = Number(i_str[0]);
var b = Number(i_str[1]);
var c = Number(i_str[2]);
// 根据喇叭花数的条件,来判断
if(factorial(a) + factorial(b) + factorial(c) == i){
console.log(i);
}
}
18.实现深克隆
//使用递归思想,整体思路和浅克隆类似,但稍微进行一些改动:如果遍历到项是基本类型值,则直接推入结果数组;如果遍历到的项是又是数组,则重复执行浅克隆的操作。
//原数组
var arr1 = [33,44,11,22,[66,88]];
//函数,这个函数会被递归
function deepClonw(arr){
// 结果数组,每一层都有一个结果数组
var result = [];
// 遍历数组中的每一项
for(var i=0;i<arr.length;i++){
//类型判断,如果遍历到的项是数组
if(Array.isArray(arr[i])){
// 递归
result.push(deepClonw(arr[i]));
}else {
//如果遍历到的项不是数组,是基本类型值,就直接推入到结果数组中
result.push(arr[i]);
}
}
//返回结果数组
return result;
}
// 测试一下
var arr2 = deepClonw(arr1);
console.log(arr2);
//是否'藕断丝连'
console.log(arr1[4][2] == arr2[4][2]);
arr1[4].push(99);
console.log(arr1);
console.log(arr2);
19.用闭包模拟私有变量
//题目:请定义一个变量a,要求是能保证这个a只能被进行指定操作(如加1、乘2),而不能进行其他操作,应该怎么编程呢?
//封装一个函数,这个函数的功能就是私有化变量
function fun(){
//定义一个局部变量a
var a= 0;
return{
getA:function (){
return a;
},
add:function (){
a++;
},
pow:function (){
a *=2;
}
};
}
var obj = fun();
// 如果想在fun函数外面使用变量a,唯一的方法就是调用getA()方法
console.log(obj.getA());
// 想让变量a进行+1操作
obj.add();
obj.add();
obj.add();
console.log(obj.getA());
obj.pow();
console.log(obj.getA());
20.IIFE的作用
//IIFE:立即执行函数(使语法更紧凑)
// 作用一:为变量赋值 (if语句中)
var age = 12;
var sex = '男'
var title = (function(){
if (age < 18){
return '小朋友'
}else{
if(sex == '男'){
return '先生';
}else{
return '女士';
}
}
})();
alert(title);
//作用二:将全局变量变为局部变量(for语句中)
var arr = [];
for (var i = 0; i<5;i++){
(function (i){
arr.push(function (){
alert(i);
});
})(i);
}
arr[0]();
arr[1]();
arr[2]();
arr[3]();
arr[4]();