函数
/**
* 使用函数:
* 创建函数, 调用函数
*
* 创建函数的语法:
* 1. 声明式定义 function 函数名/变量名 (参数: 可以不写, 所以暂时不讲) { 书写函数被调用的时候需要执行的代码 }
*
* 2. 赋值式定义 var 函数名/变量名 = function (参数: 可以不写, 所以暂时不讲) { 书写函数被调用的时候需要执行的代码 }
1. 声明式定义 命名函数创建
function fn1() {
console.log('我是FN1函数')
}
2. 赋值式定义 匿名函数创建
var fn2 = function () {
console.log('我是FN2函数')
}
3)、自执行函
一般自执行函数因为没有名字,
所以会在开始执行一次,不会被再次调用
(function(){
})()
/**
* 两种定义函数的区别
*
* 1. 书写的区别
* 2. 调用的区别
* 声明式定义: 在函数定义前或者后都可以调用函数
* 赋值式定义: 在函数定义后可以调用, 函数定义前不能调用, 因为此时变量内部的值为 undefined
*/
为什么要有参数
/**
* 为什么要有参数?
* 如果没有参数, 那么函数的功能比较单一
* 比如下边的案例中, 当前函数只能计算 1 + 1
* 如果我们需要 计算 1 + 2, 那么我们需要重新封装一个函数
*
* 如果想要让函数的功能更加灵活, 或者说代码的复用性更高, 那就需要函数的参数
*
*
* 函数的参数分为两种
* 1. 形参
* 形参书写在函数名后边或者function关键字后边的小括号中
* function fn(形参书写的位置) {}
* 形参的书写就相当于在函数中创建了一个变量, 能够在函数内部使用, 形参的命名规则参考变量的命名规则
*
* 2. 实参
* 实参书写在调用函数时函数名后的小括号中
* fn(实参书写的位置)
* 实参需要我们具体的传入一个数据或者变量, 然后这个数据或者变量会对应的赋值给形参
*
*
*
* 函数的参数中, 形参和实参的数量可以有多个
* 推荐(强烈建议) 形参和实参数量一一对应
* 但是可以数量不一致
*
*
*
*/
// 基础示例
function fn (a) {
console.log(a)
}
fn('QF001')
fn(10086)
/**
* 调用 fn 函数的时候, 书写了一个 实参 就是 'QF001'
* 然后 会按照规则 传递给对应的 形参 就是 形参 a
*
* 那么也就相当于 在 fn 函数中 创建了 一个 变量 并 赋值为 'QF001'
*
* 这样的好处时 函数中 的形参a, 值不固定, 那么函数的功能也就更加灵活
*/
// 多个形参与实参
var fn = function (a, b, c) {
console.log(a)
console.log(b)
console.log(c)
}
fn(100, 200, 300)
/**
* 调用 fn 函数的时候, 书写了 三个 实参 分别是 100 200 300
*
* 然后会按照规则 传递给 对应的 形参 (按照书写的顺序)
* 第一个实参 -> 第一个形参
* 第二个实参 -> 第二个形参
* 以此类推
*
* 所以当前案例中
* a === 100
* b === 200
* c === 300
*/
/**
* 实参多, 形参少
*
* 第一个实参会按照顺序给第一个形参, 并以此类推
*
* 多写的实参, 没有对应的形参接收, 那么在函数中暂时无法使用
*/
function fn1(a) {
console.log(a)
}
fn1(100, 200, 300)
/**
* 实参少, 形参多
*
* 第一个实参会按照顺序给第一个形参, 并以此类推
*
* 但是 多写的 形参没有对应的实参传递具体的值
* 所以相当于 变量定义了但没有赋值, 所以他们的值都是 undefined
*/
var fn2 = function (a, b, c, q, w, e, r, t) {
console.log(a, b, c, q, w, e, r, t)
}
fn2('QF001')
* 形参的默认值
*
* 在书写形参的时候, 后续跟一个赋值号, 加上默认值
* 默认值只有在我们调用函数并且没有给形参传递对应的实参的时候才会生效
* 如果你传递了对应的实参, 那么这个形参的值就是你传入的数据
function fn(a, b = 'B的默认值', c = 'C的默认值') {
console.log(a, b, c)
}
fn('QF001', '传递给形参B的字符串')
参数种类
/*
参数的种类
1、必填参数项 如果不填写这个参数的会造成不是预期的结果
要求这个参数对应的实参必须填写
2、初始值参数项 这个参数介于必填参数项和选填参数项之间,
并且设置了默认参数值,如果不填,这个参数有默认的值,
如果填写实参就是实参值
3、可选参数项 因为有判断,这个参数如果没有填写,
不会造成预期之外的情况,这个参数可填可不填
4、剩余参数项 如果填入的实参的数量远远大于形参的数量,
多出来的实参就会放在剩余参数项,剩余参数项是个伪数组
*/
// 形参是在函数定义时的参数命名
// a b c d 参数名 形参
// c=10 叫做初始值参数,如果对应的实参没有填写,
这个初始值参数就是对应的10,如果填写了对应的实参,
这个参数值就是填入实参值
// ...arg 就是剩余参数
function fn(a,b,c=10,d,...arg){
var s=a+b+c;
if(d!==undefined){
s+=d;
}
console.log(arg);// 数组: [5, 6, 7, 8, 9]
console.log(arg.length); // 长度为5
console.log(s);// 10
}
// 执行函数时赋值给参数名的值 实参
fn(1,2,3,4,5,6,7,8,9);
// 迭代器
// 函数内才有arguments
// 没有形参
function fn(){
console.log(arguments.length); //相当于数组长度代表了
实参的数量 是4
console.log(arguments[0]) //下标0是 1
console.log(arguments[3]) //下标3是 a
for(var i=0;i<arguments.length;i++){
// 循环获取每个实参的值
console.log(arguments[i]); //1,2,3,a
}
}
// 实参
fn(1,2,3,'a');
function fn1(a,b,c){
console.log(arguments.length);//实参的数量 2
console.log(fn1.length);//形参的数量 3
}
fn1(1,2);
函数的返回值
/**
* 函数的返回值 (函数的执行结果)
*
* 在 JS 中, 所有的函数都会有执行结果, 也就是返回值
* 每一个函数中, 默认具有一个 return, 在函数内部的最后一行, 默认为 return undefined
*
* 如果我们想要修改函数的执行结果, 我们需要在函数的内部手动书写一个返回值
* return 你需要返回的内容
*
*
* 注意:
* 1. 函数内部的 return 只会有一个生效, 谁先执行, 谁生效
* 2. return 具有中断函数的功能, 所以推荐写在函数的末尾, 如果必须书写在函数的开头, 那么一定要结合分支语句
*
*
* JS 中的一个规则
* 在函数的外部, 无法使用函数内部的变量或者形参, 如果我们需要函数内部的数据, 就需要借助 返回值
*
*
* 返回值的功能:
* 1. 中断函数
* 2. 将函数内部的一些值, 返回给函数外部, 让函数外部可以使用
*/
function fn (a, b) {
var num = a + b
console.log('在函数内部使用 num: ', num)
return num
}
/**
* 当前 fn 函数接收两个形参, 就是 a 和 b
* 函数内部会创建一个变量 num, num 的值就是两个形参相加的和
*
* 因为 JS 的规则, 我们无法在函数外部使用函数内创建的变量, 所以我们无法直接使用 num
*
* 但是我们可以借助 函数的 返回值 将 num 的值返回出来, 然后存储到 外边的变量 res 中
*
* 后续我们的 变量 res 就相当于 函数内部的变量 num
*/
var res = fn(1, 2)
console.log('在函数外部使用 num: ', res)
continue,return与break 的区别
// break仅跳出循环,并不会跳出函数,还会执行循环后面的语句
// return直接跳出函数,不会执行后面的所有内容
//continue; 跳出本次循环,重新判断条件继续循环,还会执行循环后面的语句
function fn(){
for(var i=0;i<10;i++){
if(i===5) continue;//i===5时跳出5的循环,运行5以后的循环,运行循环后的代码
console.log(i);
}
console.log("ccds");
}
fn()
function fn1(){
for(var i=0;i<10;i++){
if(i===5) break; //i===5时循环结束,运行循环后的代码: console.log("dscsc");
console.log(i);
}
console.log("dscsc");
}
fn1();
// return直接跳出函数,不会执行后面的所有内容
function fn1(){
for(var i=0;i<10;i++){
if(i===5) return;
console.log(i)
}
console.log("aaa")
}
fn1();
作用域
*
* 简单来说: 就是一个变量生效的范围
*
* 全局作用域
* 整个 script 标签内部的区域声明的变量就是在全局作用域创建
* 如果是在全局作用域创建的变量或者函数我们统称为 全局变量或者全局函数
* 那么能在当前代码的所有位置去使用
*
* JS 在全局作用域中提供了一个 对象, 叫做 window, 我们书写的所有的 全局变量或者全局函数, 都在 window 对象内部存放
*
* 局部作用域(函数作用域)
* 在一个函数内部生成的变量就是存在于局部作用域的,
* 在局部作用域创建的变量只能在当前作用域内使用
函数或者使用var定义的全局变量会在script标签开始就会预解析(就是运行)
全局变量 可以在任意位置使用
var a=1;
console.log(a,"1");
function fn(){
// 局部变量 这个变量只能在函数内部使用,并且当函数执行完成后会自动销毁,
他不能长期保留
// 在函数中使用function创建的函数,也是局部函数
// 使用var定义的局部变量,或者在函数中使用function创建函数,
都会在函数刚开始执行的时候做预解析,
// 使用function定义的局部函数也会在函数开始的时候做预赋值
console.log(b);//undefined
console.log(fn1); // 函数fn1
var b=2;
console.log(a,"2");
function fn1(){
}
}
console.log(a,"3");
fn();
局部变量
// 全局变量
var a=1;
// 全局函数
function fn(){
console.log(a);
}
fn();
// 全局变量可以在任意位置调用
// 局部变量只能在函数内调用,函数执行完成后会销毁
-------
当把变量定义在函数体内的时候,这个变量只能在函数内使用,函数执行完成后会立即销毁
函数外是无法使用函数内定义的变量的
函数内定义的变量叫做局部变量
console.log(a); //报错 函数外无法调用函数内的变量
function fn(){
console.log(a);//预解析 当函数执行刚开始执行时就会预解析当前函数内使用var定义的所有变量
var a=1;
console.log(a);
console.log(getSum);
// 在函数内定义的局部函数,也会在函数执行开始的时候预解析预赋值
function getSum(){
}
// 在函数内使用var定义的变量或者使用function定义的函数,都是局部变量或者局部函数
}
fn();
console.log(a);
---------
如果函数内的局部变量与函数外的变量同名时,外部的变量将无法与函数内同名变量产生影响,
不能再函数中使用
var a=1;
function fn(){
console.log(a);
var a=3; var a 变量提升 值留在原地
}
fn();
console.log(a);// 1 //局部变量用完销毁,全局变量并没有在函数中起作用
下面没有局部变量
var a=1;
function fn(){
a=3;//没有var 全局变量
console.log(a);//3
}
fn();
console.log(a);//3
-------参数也是局部变量
var a=1;
function fn(a){
console.log(a); //2
a=3;//修改的是局部变量,参数也是局部变量,所以全局变量a在这里没有用处
}
fn(2)
console.log(a);//1
-----特殊的局部变量
var a=1;
// 匿名函数起别名a 也是局部变量
var fn=function a(){
console.log(a); // 就是函数自身
a=3;
}
fn();
console.log(a);
匿名函数起别名的局部变量优先赋值,实参赋值形参在其后
参数的值覆盖别名的局部变量值
var a=1;
var fn=function a(a){
console.log(a);// 2
}
fn(2);
var a=1;
var fn=function a(a){
console.log(a);//3
var a=2;
console.log(a);//2
}
fn(3);
/*
局部变量有3个
1、函数内使用var或者 function定义的变量
2、形参
3、匿名函数的别名
匿名函数的别名 --- 实参赋值形参 --- 使用var或者function定义的变量或者函数
*/
变量和函数的预解析
*
* 预解析: (预解释)
*
* 变量的规则: 也有人说是变量提升
* JS 在运行我们的代码的时候, 会先整体阅读一次我们的 JS 代码,
* 此时读取完毕后, 会将变量的声明(定义) 提取到页面的最顶部(JS代码的最顶部)
* 但是注意: 提升的只有变量的声明, 没有变量的赋值
* 那么换句话说, 也就是在变量定义前去使用, 得到的是一个 undefined
*
* 函数的规则: 也有人说是函数提升
* JS 在运行我们的代码的时候, 会先整体阅读一次我们的 JS 代码,
* 此时读取完毕后, 会将声明式定义的函数 提取到页面的最顶部(JS代码的最顶部)
* 所以我们在书写代码的时候, 可以在 声明式定义的函数前, 去调用函数
*/
1. 声明式定义的函数 命名函数
可以做预解析预赋值
当执行到script标签时,如果有使用function定义的命名函数时,
首先先在script开始时做预解析,开辟
空间起名为函数名,并且直接将这个函数赋值给这个空间中
console.log(a) // ƒ a () { console.log('我是a函数') }
function a() {
console.log('我是a函数')
}
console.log(a) // ƒ a () { console.log('我是a函数') }
/**
* 原本的代码
* console.log(a) // ƒ a () { console.log('我是a函数') }
* function a () { console.log('我是a函数') }
* console.log(a) // ƒ a () { console.log('我是a函数') }
*
* 浏览器读取我们的JS整体代码, 开始函数提升
*
* 提升后的代码:
* function a () { console.log('我是a函数') }
* console.log(a)
* console.log(a)
*/
变量的规则====================================================
console.log(a) // undefined
var a = 100
console.log(a) // 100
/*
原本的代码
console.log(a)
var a = 100
console.log(a)
开始运行代码, 浏览器读取我们JS整体代码
读取完毕, 预解析 后的代码
var a
console.log(a) // undefined
a = 100
console.log(a) // 100
*/
2. 匿名函数,只是做变量的预解析,没有预赋值
console.log(a) // undefined
var a = function () { console.log('我是a函数') }
console.log(a) // ƒ () { console.log('我是a函数') }
/*
原本的代码
console.log(a)
var a = function () { console.log('我是a函数') }
console.log(a)
开始运行代码, 浏览器读取我们JS整体代码
读取完毕, 预解析 后的代码
var a
console.log(a) // undefined
a = function () { console.log('我是a函数') }
console.log(a) // ƒ () { console.log('我是a函数') }
*/
3. console.log(a);//函数 因为变量提升先将var a 提升到最前面
但 函数 a与变量a重名 会预解析预赋值,
函数a优先级比变 量a要 高
所以会覆盖变量a 所以打印结果是函数a
var a=1; // a在这里重新赋值但不会提升
function a(){
}
console.log(a);//1
4.//同名的变量,服了两次值,后面的值把前面的覆盖了 所以打印函数
console.log(a)//undefined
var a=1;
var a=function (){
}
console.log(a);//函数
5. // var a;//如果没有赋值,因为函数原本已经预解析预赋值了,
所以a变量存储的是函数,这里并没有重新赋值,所以a还是函数
// var a=undefined;//如果赋值,表示将a变量的值替换了undefined,
那么undefined 就是函数a与var a预解析后 再赋值的值 就会打印出 undefined
// function a(){
// }
// console.log(a);
封装一个函数, 计算出两个数字的最大公约数
/**
* 逻辑:
* 书写一个函数
* 需要两个参数
* 需要返回值, 返回最大的公约数
*/
function fn(x, y) {
var min = x > y ? y : x
// 拿到 1~较小值 之间的所有数字
for (var i = min; i >= 1; i--) {
// 寻找 x 和 y 的公约数
if (x % i === 0 && y % i === 0) {
// console.log(i)
/**
* 当前 分支语句执行的时候, 代表找到了 公约数
* 顺序中 第一个就是最大公约数, 所以当前分支第一次执行完毕后, 就等于找到了最大公约数
*
* 然后直接 return i 将最大公约数返回给外部, 并且函数到此结束, 后续不在运行
*/
return i
}
}
}
封装一个函数, 用于判断一个 数字是不是质数
/**
* 逻辑:
* 1. 封装一个函数
* 2. 需要一个参数
* 3. 需要返回值, 并且是布尔值
*/
function fn(n) {
var num = 0 // 用于作为一个计数器
// 1. 找到 2~n-1 之间的所有数字
for (var i = 2; i < n; i++) {
// 2. 找有没有能够和 形参n 完整整除的数字
if (n % i === 0) {
// 3. 当分支执行的时候, 修改 num 的值 (修改计数器)
num++
}
}
//方法1
// if (num === 0) {
// // 是质数
// return true
// } else {
// // 不是质数
// return false
// }
//方法2 num === 0 是个表达式做判断,当成立时 为真也就是 true ,条件不成立输出假 false
return num === 0
}
var res = fn(11)
console.log(res)
* 递归函数
*
* 在编程中就是指 自己调用自己的一种手段
*
* 递归函数就是在一个函数内部调用了自身. 循环执行
// 最简单的一个递归函数(错误版)
// function fn() {
// console.log(1)
// fn()
// }
/**
* 递归函数有点类似于循环
* 也需要有初始化, 自增, 执行的代码, 判断条件
* 如果上述的内容缺少, 那么就是一个死递归, 永远不会结束
*/
/**
* 求 1~5 的所有数字相加的和
*
* 1 + 2 === 3
* 3 + 3 === 6
* 6 + 4 === 10
* 10 + 5 === 15
*/
function fn(n) {
// 1. 先书写结束条件
if (n === 5) {
return 5
}
/**
* 2. 开始递归
*
* 假设我们的 fn 函数能够帮我们计算 某一个数字到5的所有数字相加的和
*
* 1~5 fn(1)
* 2~5 fn(2)
* 3~5 fn(3)
* 4~5 fn(4)
* 5~5 fn(5)
*
*
* 目前是要计算 1~5的所有数字相加
* 所以等式可以理解为 1 + 2~5
* 1 + fn(2)
*/
return n + fn(n + 1)
}
var res = fn(1)
console.log(res)
/**
* 第一次执行 n === 1
*
* 函数开始执行
* 1. 分支语句 条件 n === 5 条件失败, 不执行
*
* 2. return n + fn(n + 1)
* return 1 + fn(2)
* return 1 + 14
*
*
* 第二次执行 n === 2
* 函数开始执行
* 1. 分支语句 条件 n === 5 条件失败, 不执行
* 2. return n + fn(n + 1)
* return 2 + fn(3)
* return 2 + 12
*
*
* 第三次执行 n === 3
* 函数开始执行
* 1. 分支语句 条件 n === 5 条件失败, 不执行
* 2. return n + fn(n + 1)
* return 3 + fn(4)
* return 3 + 9
*
*
* 第四次执行 n === 4
* 函数开始执行
* 1. 分支语句 条件 n === 5 条件失败, 不执行
* 2. return n + fn(n + 1)
* return 4 + fn(5)
* return 4 + 5
*
*
* 第五次执行 n === 5
* 函数开始执行
* 1. 分支语句 条件 n === 5 条件成立, 直接 return 5 并且中断函数 并将fn(5)===5的结果带入 fn (1),fn (2),fn(3),fn (4) 的计算中
*/
/**
* 斐波那契数列是一个特殊的数列
*
* 该数列中, 前两位的数字固定为 1, 从第三位开始, 每个位置上的数字是前两位的和
*
* 数列: 1,1,2,3,5,8,13,21,34,55,89......
*
* 要求: 封装一个函数, 能够计算出斐波那契数列中某一个位置的具体数值
*
* 请使用递归函数解决
*/
function fn(n) {
if (n === 1 || n === 2) {
return 1
}
/**
* 假设 fn 函数能够帮我们计算数列中某一个位置的数值
*
* 如果求 第 4 位: fn(4)
* 如果求 第 3 位: fn(3)
* 如果求 第 2 位: fn(2)
* 如果求 第 1 位: fn(1)
* 如果求 第 100 位: fn(100)
*/
// return '数列中第 n 位的值'
return fn(n - 1) + fn(n - 2)
}
// var res1 = fn(4)
// console.log(res1)
// var res2 = fn(7)
// console.log(res2)
// var res3 = fn(8)
// console.log(res3)
// fn(49)