JavaScript 函数
-
了解函数
- 函数是我们的一个数据类型,和数学的一次函数之类的没有关系
- 函数是我们接触的第一个复杂数据类型
- 函数的作用:就是把一段功能性代码放在一个“盒子”里面
- 想用的时候,直接把“盒子”里面的代码拿出来用就可以了
-
例子:微波炉
- 把一个加热功能放在一个铁盒子里面
- 只要你想加热东西的时候,把微波炉打开,就能把你放进去的内容加热
-
代码:
- 就是我们在内存中开辟一个小空间,把我们的代码放进去
- 然后给这个空间起一个名字,比如:叫做abc
- 我以后只需要用到这个空间里面的代码的时候
- 我就告诉我的程序,把abc空间里面的代码执行一下
函数的创建
一、声明式函数
-
语法: function 函数名( ) { }
- function:就是一个声明函数的关键字
- 函数名:就是你给这一段空间自定义的一个名字(命名规则和命名规范同变量名)
- () :存放参数(形参)的位置,必须写
- {} :就是你存放的一段代码
// 定义一个函数 - 声明式
function weibolu() {
console.log('呜呜呜')
console.log('叮 ~ ')
}
二、赋值式函数(匿名函数)
-
语法1: var 函数名 = function ( ) { }
- var:定义变量的关键字
- 函数名:就是我们给这个函数起的一个自定义名称
- =:赋值
- function:表示一个函数
- () :写参数(参数)的位置,必须写
- {} :存放一段代码
// 定义一个函数 - 赋值式
var weibolu = function () {
console.log('呜呜呜')
console.log('叮 ~')
}
// 函数功能:求和
// @param number a 传入的第一个参数
// @param number b 传入的第二个参数
函数的调用
- 不管用哪种方式定义的函数,他们的调用方式是一样的
一、直接调用
// 准备两个函数
function weibolu() {
console.log('呜呜呜')
console.log('叮~')
}
var bingxiang = function () {
console.log('嗡嗡嗡')
}
// 调用函数,直接调用
// weibolu()
// bingxiang()
二、依赖行为调用
-
需要给一个元素绑定一个行为
-
在js里面获取一个页面元素有一个简单的办法,就是直接使用id名称
- 在一个页面里面,一个元素的id名称可以直接被当做一个js的变量来使用
- 这个id名表示的就是这个元素
-
暂时添加一个点击行为,就是 onclick
-
元素.onclick ->表示当点击这个元素的时候
-
给他赋值为一个函数名称就可以了
- 不需要写小括号,因为我不是让你直接执行,而是当你点击的时候执行
<body>
<div id="box"></div>
<script>
// 准备一个函数
function weibolu() {
console.log('呜呜呜')
console.log('叮~')
}
// 依赖行为调用
// box 是页面中一个 div 的 id 名称, 在 js 里面可以直接使用
// onclick 当点击在某一个元素身上的时候
// 等于号后面是一个函数名称, 不需要小括号
// 当你点击这个 box 元素的时候, 会调用后面的这一个函数
// 点击多少次调用多少次
box.onclick = weibolu
</script>
</body>
函数调用时机上的区别
-
当你调用一个函数的时候,方式都是一样的
-
但是调用的时机取决于你使用哪一种方式定义函数
一、声明式函数(function fn( ) { })的调用时机
- 声明式函数可以再声明之前调用,也可以在声明之后调用
// 声明式函数之前调用
weibolu()
// 声明式函数
function weibolu() {
console.log('呜呜呜')
console.log('叮~')
}
// 声明式函数之后调用
weibolu()
二、赋值式函数(var fn = function( ) { })的调用时机
- 赋值式函数只能在声明之后调用,声明之前调用会报错
// 赋值式函数
var bingxiang = function () {
console.log('嗡嗡嗡')
}
// 赋值式函数调用
bingxiang()
- 注:赋值式函数之前调用会报错: bingxiang is not a function,即:bingxiang 不是一个函数
函数的参数
- 函数里面的参分两种,一种是形参,一种是实参
一、形参
-
就是写在函数定义的时候的 () 里面的
-
可以写多个,多个的时候中间用逗号(,)分隔
-
形参:就是一个只能在函数内部使用的变量
-
至于这个变量(形参)的值是什么,由函数的实参决定
二、实参
-
就是写在函数调用的时候的 () 里面的
-
可以写多个,多个之间使用逗号(,)分隔
-
实参:是一个准确的数值,用来给对用的形参赋值的
// 形参和实参
function bingxiang(a) {
// a 就是一个只能在函数内部使用的变量名
console.log('嗡嗡嗡')
console.log('冰冰冰 ' + a + ' 冰冰冰')
}
// 这个位置的小括号里面的内容叫做实参, 式用来给形参赋值的
bingxiang('大熊猫')
bingxiang('大象')
// 多个参数
function bingxiang(a, b, c) {
// a b c就是一个只能在函数内部使用的变量名
console.log(a)
console.log(b)
console.log(c)
}
// 这个位置的小括号里面的内容叫做实参, 式用来给形参赋值的
bingxiang('大象', 100, true)
三、函数参数的个数关系
1、形参和实参一样多
- 按照从左到右的顺序一一进行赋值
// 参数一样多
function fn(a, b, c) {
console.log(a)
console.log(b)
console.log(c)
}
fn('hello world', true, 100)
2、形参比实参多
-
前面的一一对用,多出来的形参,没有实参进行赋值
-
在函数内部使用的时候,就是没有赋值的变量,就是undefined
// 形参多
function fn(a, b, c) {
// 在这个函数里面, c 就相当于定义了一个变量, 但是没有被赋值
// c 在使用的时候就是 undefined
console.log(a)
console.log(b)
console.log(c)
}
fn('hello world', 100)
3、实参比形参多
-
前面的一 一对用,多出来的实参,在函数内部没有形参接收
-
相当于你白写了多出来的实参,在函数内部不能直接使用
// 实参多
function fn(a, b, c) {
// 我在这里不能直接使用传递进来的 200 300 400 500
// 但是不会报错
console.log(a)
console.log(b)
console.log(c)
}
fn('hello world', true, 100, 200, 300, 400, 500)
函数内部的arguments
一、arguments 的概念
-
是函数内部的一个特殊的变量
-
在每一个函数内部,都有一个天生的变量,不需要我们定义,直接就能拿来使用,这个变量叫做 arguments,表示:所有实参的集合(实参的数组)
二、arguments 详解
-
会把所有的实参按照从左到右的顺序依次拍好
-
给每一个实参一个编号(索引 / 下标)
-
这个编号(索引 / 下标)是从 0 开始的,依次递增
-
当你需要获取到这里面的某一个实参值的时候
-
你只需要写 arguments[编号] 就能得到某一个准确的实参值
// 简单演示 arguments
function fn() {
// arguments 不需要定义, 直接使用就行
console.log(arguments)
console.log(arguments[0]) // 我要获取 arguments 里面索引为 0 的那一项 hello
console.log(arguments[1]) // 我要获取 arguments 里面索引为 1 的那一项 world
console.log(arguments[2]) // 我要获取 arguments 里面索引为 2 的那一项 100
console.log(arguments[3]) // 我要获取 arguments 里面索引为 3 的那一项 200
// 我要获取 arguments 这个集合的长度
console.log(arguments.length) // 4
}
fn('hello', 'world', 100, 200)
三、arguments 的 length 属性
-
length 表示arguments 这个集合的长度,也就是里面有多少个数据
-
值是一个 number 数据类型的数字
-
如果你想得到这个属性就写 arguments.length
四、循环遍历整个 arguments 获取到实参的每一个数据
-
因为 arguments 的索引(下标)是按照从 0 开始一次叠加的顺序
-
我们的循环刚好也可以给我提供一组有规律的数字
-
所以我们可以使用循环的方式来遍历 arguments
// 循环遍历 arguments
function fn() {
// 五个数据的索引分别是 0 1 2 3 4
console.log(arguments)
// 使用循环提供一个从 0 ~ 4 的数字
// 循环的结束位我们应该根据 arguments 的 length 来决定
for (var i = 0; i < arguments.length; i++) {
// console.log(i) // i 得到的就分别时 0 1 2 3 4
// 当 i === 0 的时候, 使用 arguments[0]
// 当 i === 1 的时候, 使用 arguments[1]
// 当 i === 2 的时候, 使用 arguments[2]
// ......
console.log(arguments[i])
}
}
fn('hello', 'world', true, 100, 500, false, '你好')
五、循环遍历的时候为什么要小于length
-
当 length === 5 的时候,我们要的索引数字是 0 1 2 3 4
- for (var i = 0; i < 5; i++) { } 得到的 i 分别是 0 1 2 3 4
-
当 length === 7 的时候,我们要的索引数字是 0 1 2 3 4 5 6
- for (var i = 0; i < 5; i++) { } 得到的 i 分别是 0 1 2 3 4 5 6
六、什么时候使用 arguments 什么时候使用形参
- 因为函数使我们提前定义好的,为了更好的书写代码,为了以后多次调用
使用形参:
-
当我们定义函数的时候,如果能确定以后使用的实参的个数
-
我们推荐使用形参的方式来获取实参的每一个数据,因为方便
使用 arguments :
-
当我们定义函数的时候,如果不能确定以后使用的实参的个数
-
我们推荐使用 arguments 的形式来获取实参的每一个数据,因为灵活
七、案列
求和小案例1:
/*
需求: 利用一个函数计算两个数字的和
我们就定义一个函数, 接收两个形参
以后使用的时候也是传递两个实参
*/
// 我定义的函数就是为了计算两个数字的和
function fn(a, b) {
var result = a + b
console.log(result)
}
fn(10, 20) // 能在控制台打印出 10 + 20 的结果
fn(50, 70) // 能在控制台打印出 50 + 70 的结果
求和小案例2:
/*
需求: 利用一个函数计算三个数字的和
我们就顶一个函数, 接收三个形参
以后使用的时候也是传递三个实参
*/
// 我定义的函数就是为了计算三个数字的和
function fn(a, b, c) {
var result = a + b + c
console.log(result)
}
fn(10, 20, 30) // 能在控制台打印 10 + 20 + 30 的结果
fn(100, 300, 500) // 能在控制台打印 100 + 300 + 500 的结果
求和小案例3:
/*
需求三: 利用一个函数计算若干个数字的和
我们定义一个函数, 不一定接收几个形参
因为求的是若干个数字的和, 我不确定将来传递几个实参
我如果传递 3 个实参, 那么就把 3 个实参加在一起
我如果传递 5 个实参, 那么就把 5 个实参加在一起
这个时候就使用 arguments 比较好 灵活
*/
// 我定义的函数就是为了计算若干个数字的总和
function fn() {
console.log(arguments)
// 准备一个初始变量, 用来接收计算结果
var result = 0
// 循环遍历 arguments 的每一个
for (var i = 0; i < arguments.length; i++) {
// 把 arguments 里面的每一个都叠加到 result 变量身上
result += arguments[i]
}
// 循环结束以后, result 就是 arguments 里面每一个数字的和
console.log(result)
}
fn(10, 20, 30, 40) // 计算的就是四个数字的和
fn(10, 20, 30, 40, 50) // 计算的就是五个数字的和
fn(10, 20, 30, 40, 50, 60) // 计算的就是六个数字的和
函数的返回值
- 返回值:就是一个函数的结果
例子1:冰箱
-
函数:封装一个功能,即:冰箱就是封装了一个制冷功能
-
参数:在调用的时候传递,即:你向冰箱里面放了什么东西
-
返回值:调用完函数得到的结果,即:冰箱能给你的是一个什么
例子2:parseInt( )
-
parseInt 是一个 js 内置(自带)的函数
-
parseInt( ) 就是在调用这个函数
-
parseInt( 10.234 ) 就是传递一个参数
-
var result = parseInt( 10.234 )
- result 接收的就是 parseInt( ) 这个函数的返回值
-
这个函数里面做的就是 把你传递进来的数据转换成数值类型
- 然后再返回给你
一、return 关键字
return 关键字有两个作用
-
作为函数的返回值使用
- 让外部程序可以调用函数内部的值即:让全局变量可以访问局部变量
-
打断函数不在继续执行 - 类似于 break
- 当你在函数内部的代码执行到 return 关键字以后
- return 后面的代码就不带执行了,直接结束函数
- 和 break 的功能比较相似
- break 是结束循环的关键字
- return 是结束函数的关键字
function fn() {
console.log('第一行代码执行')
console.log('第二行代码执行')
return
// 这里开始向后的代码就不在执行了
console.log('第三行代码执行')
}
fn()
在函数里面添加返回值使用 return 关键字
- 你想让这个函数返回什么,就写 return 你要返回的数据
// 函数的返回值
function bingxiang(a) {
// 形参 a 就是你传递进来的实参
// 我是一个功能性的东西, 我的功能就是把你传递进来的东西冻起来
var str = '冰冰冰 ' + a + ' 冰冰冰'
// 把冻好以后的结果给你
// return str 这句代码就是 bingxiang
// 这个函数将来被调用以后能得到的结果
return str
}
// 现在开始, 只要我调用 bingxiang 这个函数, 那么我就能得到一个 str
var res = bingxiang('大象')
console.log(res) // 冰冰冰 大象 冰冰冰
var res2 = bingxiang('八爪鱼')
console.log(res2) // 冰冰冰 八爪鱼 冰冰冰
二、案例
/*
我要自己封装一个函数, 用来判断一个数字是不是质数
我就自己写一个函数, 接收一个参数
返回值, 就是一个 布尔值
如果返回 true, 表示我传递进去的参数是一个质数
如果返回 false, 表示我传递进去的参数不是一个质数
*/
function isZhishu(num) {
// 判断 num 是不是一个质数
// num 是你将来调用的时候传递进来的一个数字
// 假设是质数
var flag = true
for (var i = 2; i < num; i++) {
if (num % i === 0) {
flag = false
break
}
}
// 因为 flag 是 true 就返回 true
// falg 是 false 就返回 false
return flag
}
var result1 = isZhishu(10) // 为了得到一个结果告诉我 10 是不是质数
console.log(result1) // false
var result2 = isZhishu(7) // 为了得到一个结果告诉我 7 是不是质数
console.log(result2) // true
三、案例
/*
作业一: 自己书写一个函数
求任意个数字的和
写一个函数, 可以接收任意个数字(任意个参数)
在函数内部计算传递进来的所有参数的和
把结果当作返回值返回函数外面
*/
function and() {
// 计算所有参数的和
// 1. 准备一个变量接收叠加结果
var result = 0
// 2. 循环遍历 arguments, 把 arguments 里面的每一个叠加到 result 身上
for (var i = 0; i < arguments.length; i++) {
result += arguments[i]
}
// 循环结束, result 就是所有数字的和
// 3. 把求和的结果作为返回值返回
return result
}
// 使用一下我写的函数求和
// 我想求 10 20 30 40 的和
// res 接收的就是 and 函数的 return
var res = and(10, 20, 30, 40)
console.log(res)
// 我想求 100 200 的和
// res 接收的就是 and 函数的返回值
var res2 = and(100, 200)
console.log(res2)
四、案例
/*
若干个数字求乘积
1. 准备一个函数, 接收任意个参数
2. 求所有参数的乘积
3. 把计算结果返回
*/
// 1. 准备一个函数, 接收任意个参数, 那么就不接受了, 使用 arguments 就可以
function cheng() {
// 2. 开始求所有参数的乘积
// 2-1. 准备一个初始变量, 用来叠加内容
var result = 1
// 2-2. 遍历 arguments 里面的每一个
for (var i = 0; i < arguments.length; i++) {
// 2-3. 把 arguments 里面的每一个乘到 result 身上
result *= arguments[i]
}
// 在这里, 循环结束, result 就是所有实参的乘积
// 3. 返回计算结果
return result
}
// 使用的
var res = cheng(1, 2, 3, 4, 5)
console.log(res)
系统函数
一、普通弹窗 alert(’ ')
alert('你要输入的内容')
二、确认弹窗 confirm(’ ')
-
点击确认返回**true **
-
点击取消就返回false
var a = confirm('确定删除吗?')
console.log(a)
三、输入框弹窗 prompt(’ ')
-
点击确认返回**输入框的值 **
-
点击取消就返回 null
var res = prompt('请输入姓名');
console.log(res)
四、非数字判断 isNaN( )
var a = 3;
console.log(!isNaN(0)) // true
五、计算 eval( )
console.log(eval(3+'+'+2)) // 5
console.log(eval(3+'-'+2)) // 1
console.log(eval(3+'*'+2)) // 6
console.log(eval(3+'/'+2)) // 1.5
console.log(eval(3+'%'+2)) // 1
递归函数(了解)
-
递归:就是一个函数的高级使用方法 — 自己调用自己
-
本质:
- 自己调用自己
- 递进去,归回来
一、递归函数基础
-
就是一个函数自己调用自己,把一个有规律的问题,转换成自己和自己的前一步
-
注意:一定要设置一个尽头,不能一直递进去(死递归)
-
写递归函数:先写停的条件
// 死递归!!!死递归!!!死递归!!!
function fn() {
console.log('fn 函数执行了一下')
// 自己调用自己
fn() // 打开变成死递归了
}
fn()
二、递归函数使用
-
90%~95% 递归函数能做的事情,循环也能做,因此推荐使用循环,不推荐使用递归
-
慎用!!!慎用!!!慎用!!!
/*
递归求阶乘
*/
function fn(n) {
// 当 数字 === 1 的时候, 结束我们递进来的过程
// 开始准备归回去
if (n === 1) {
return 1
}
// 把每一个数字的阶乘转换为求 数字 * 数字-1 的阶乘
return n * fn(n - 1)
}
var res = fn(4)
console.log(res) // 24
/*
斐波那契 数列
数列: 1 1 2 3 5 8 13 21 34 55 ...
第一个和第二个永远是 1
第三个数字开始, 是前两个数字的和
使用递归来计算
比如: 我要求 数列的 第 6 位是什么 ? 8
求 fn(6) -> 求 fn(5) + fn(4)
停的条件: 当 n === 1 或者 n === 2 的时候
当你要求 第一位 或者 第二位 的时候
停掉开始归回去, 直接返回 1
*/
function fn(n) {
// n 表示我要求的是这个数列的 第几位 数字
// 1. 先写停的条件
if (n === 1 || n === 2) {
// 表示要得到前两位, 一定是 1
return 1
}
// 2. 写一个递归进去的条件
return fn(n - 1) + fn(n - 2)
}
var res = fn(6)
console.log(res) // 8
var res2 = fn(10)
console.log(res2) // 55
/*
用递归函数求和
*/
function fn(n) {
// 当 数字 === 1 的时候, 结束我们递进来的过程
// 开始准备归回去
if (n == 1) {
return 1
}
return fn(n-1) + n
}
var res = fn(100)
console.log(res) // 5050
var res2 = fn(50)
console.log(res2) // 1275
var res3 = fn(10)
console.log(res3) // 55
页面交互小案列
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!--
type = number
表示的是这个文本框能输入什么东西
并不能表示你 js 拿到的也是什么东西
-->
<input type="text" id="first">
+
<input type="text" id="second">
<input type="button" id="btn" value="=">
<input type="text" id="result" disabled>
<script>
/*
页面交互小案例
页面上有两个文本框
分别让用户输入数字
有一个 button 按钮
点击的时候计算两个数字的和, 填充到第三个文本框里面
1. 要给 button 按钮添加一个点击行为
当点击的时候要做一些事情
2. 在点击的函数里面
当点击的时候再去获取两个文本框的内容
只要 JS 从页面文本框里面获取内容, 那么一定是 string
prompt 获取到的是 string
input 获取到的也是 string
3. 在点击的函数里面
计算两个数字的和
把结果填充到第三个文本框里面
*/
// 1. 给 btn 添加一个点击行为
// 当你点击哪个 button 按钮的时候执行 andHandler 函数
btn.onclick = andHandler
function andHandler() {
// 2. 拿到两个文本框 first 和 second 的输入内容
// 获取文本框的内容就是获取文本框 的 value
// first 表示这个文本框
// . 表示 的
// value 就是元素的 value 属性, 也就用用户输入的内容
// 一个文本框 的 用户输入内容
var a = first.value - 0
var b = second.value - 0
// 3-1. 计算结果
var res = a + b
// 3-2. 把结果填充到第三个文本框里面
// 第三个文本框 的 value 值为 res
result.value = res
}
</script>
</body>
</html>
回调函数(了解)
一 、立即执行函数
- 只执行一次,一般写作匿名函数
(function (n) {
console.log(n)
})(5) // 5
二、回调函数
// 打印随机数的结果
function fn(callback) {
var res = callback
console.log(res)
}
// 回调
fn (function(n) {
return parseInt(Math.random() * n)
}(6))