04-JS函数

一、函数类型的认识

  1. 函数的概念

    函数是具备某个功能的工具,是完成某个功能的一段代码

  2. 函数的定义及调用

    // 第一种定义方法:声明式/直接创建式
    // 定义
    function 函数名(){
        代码段
    }
    //调用
    函数名()
    
    // 第二种定义方法:赋值式/匿名函数
    // 定义
    var 变量名 = function(){
        代码段
    }
    // 调用
    变量名()
    
    // 案例:定义函数求两个数字的和
    function fn(){
        var a = 1,b = 2,c = a+b;
        console.log(c);
    }
    fn();
    

    函数的注意点:

    1. 注意单词、格式、语法、符号
    2. 注意执行函数的位置,不同的创建方式可执行的位置不同
    3. 注意行为调用函数时的格式
    4. 注意不要概念复杂化

    函数的优点:

    1. 实现了代码的可重用性
    2. 实现了模块化编程

    函数的调用执行拓展:通过事件来调用匿名函数(绑定事件)

    <body>
        <button onclick="fn()">点击调用函数</button>
    </body>
    <script>
        function fn(){
            var a = Number(prompt("输入第一个数"))
            var b = Number(prompt("输入第二个数"))
            alert(a+b)
        }
    </script>
    

二、函数的参数应用

function fn(a){//形参:形式上的参数
    console.log(a)
}
fn(10)//实参:实际上的参数

//需要根据参数不同显示不同的乘法表
function fn(num) {
    document.write('<table>')
    for (var i = 1; i <= num; i++) {
        document.write('<tr>')
        for (var j = 1; j <= i; j++) {
            document.write('<td>' + j + 'x' + i + "=" + i * j + '</td>')
        }
        document.write('</tr>')
    }
    document.write('</table>')
}
var num=Number(prompt("输入一个数字"))
fn(num)

参数的概念

  1. 形参:形式参数,作用是接收参数
  2. 实参:真实参数,作用是传递参数

参数使用注意点

  1. 函数的参数有多个的时候,需要用逗号隔开
  2. 在形参中书写变量,表示在函数体内声明了变量,此时还没有赋值,数据类型为undefined
  3. 当传入实参的时候,表示给形参的变量赋值

参数数量的问题

  1. 原则上,函数的形参和实参的个数和数据类型都要一一对应的
  2. 如果实参的数量比形参数量少,从左到右依次选择,没有参数的形参默认是undefined
  3. 如果实参的数量比形参数量多,从左到右依次选择,多的形参对函数体内的结果不产生任意的影响,但此时函数会默认提供一个内置对象

函数内置对象的拓展【类数组/伪数组】

  1. arguments.length 获取传入参数的个数
  2. arguments[索引] 通过索引获取对应的参数

三、函数的返回值

函数运行结束之后得到的结果

// 案例1:定义一个让指定数字递增的函数
function increase(x){
    var y = x+1;
    return y//return 程序结束后,后面的代码就不会执行了
}

// 1. 让2递增,得到的结果赋值给变量a
var a = increase(2);
document.write(a%2);//1

// 2. 让5递增,得到的结果赋值给变量b
var b = increase(5);
alert(b/2);//3

// 3. 让10递增,得到的结果赋值给c
var c = increase(10);
console.log(c+a-b);//11+3-6 = 8

// 案例2:定义三个变量,先求和再求平均数
var a = Number(prompt("输入第一个数"))
var b = Number(prompt("输入第二个数"))
var c = Number(prompt("输入第三个数"))

function sum(a,b,c){
    return (a+b+c)
}
function avg(a,b,c){
    return (a+b+c)/3
}

document.write('和是'+sum(a,b,c)+'<br>')
document.write('平均是'+avg(a,b,c))

函数的嵌套【拓展】

  1. 由于函数的调用可以写在页面的任意地方,在另外一个函数里面调用执行一个函数也可行

  2. 用函数嵌套的方式实现上面的案例2

    function sum(a,b,c){
        return (a+b+c)
    }
    function avg(a,b,c){
        document.write('和是'+sum(a,b,c)+'<br>')
        return (a+b+c)/3
    }
    document.write('平均是'+avg(a,b,c))
    

四、函数的预解析

  1. 浏览器中有一段程序专门用来解析JS代码,叫做JS解析器。js解析器在执行js代码的时候,分两步进行:

    1. 预解析js代码:预解析的过程,就是查找代码中的var和function这两个关键字,找到以后,将变量和函数存在了一个地方,并给他们赋一个初始值,变量的初始值为undefined,函数的初始值为代码段
    2. 开始按顺序一行一行解读代码:解读代码的时候,仓库中的变量和函数的值会随着代码的解读而发生变化,也就是变量的赋值和函数的调用
  2. 预解析分为变量的预解析和函数的预解析,也就是代码在执行之前先进行解析,这步是不可见的

  3. 总结:预解析就是将变量和函数的声明放在当前作用域的最前面,也可以叫做变量提升

    变量赋值和函数的调用保留在原来的位置不动,其他代码正常运行

// 案例1:变量提升
console.log(a)
var a = 10
//undefined  提升的是定义,不是赋值

// 案例2:声明式函数提升
fn()
function fn(){
    console.log('Hello')
}
//正常输出

// 案例3:赋值式函数提升
fn()
var fn = function(){
    console.log('Hello')
}
//报错 提升的是var fn,没有赋值。调用fn()的时候报错,不会再往下运行

变量提升总结:

  1. 用var关键字声明的变量,将变量的声明提升到当前作用域的最前面,赋值不提升
  2. 自定义的函数整体提升到当前作用域的最前面
  3. 函数同名,后面的会覆盖前面的
  4. 变量和函数同名,函数优先提升
// 第一题
console.log(fn)
fn()
var fn = function(){
	console.log(123)
}
// undefined,报错
// 赋值函数,提升的是var fn,此时fn是undefined

// 第二题
fun()
var fn = function(){
    console.log('我是fn')
}
function fun(){
    console.log('我是fun')
}
fn()
fn = 100
fn()
// 我是fun,我是fn,报错(fn赋值了100,不是函数了)

五、函数的作用域

  1. 全局作用域:不在任何函数中
  2. 局部作用域:在函数的内部中
1. 局部作用域
function fn(){
    var a = 1//局部变量
    console.log(a) //1
}
fn()
console.log(a)//在函数体的外面,并没有声明定义a变量,所以得到的结果就是报错

局部作用域总结:

  1. 在函数内部定义的变量叫做局部变量,只能在函数内部使用
  2. 全局变量不可以访问局部变量
2. 全局作用域
var b = 1 //全局变量
function fn(){
    var b = 2 //局部变量
}
fn()
console.log(b)

全局作用域总结:

  1. 在函数外面定义的变量叫做全局变量
  2. 当函数内部声明变量没有使用var的时候,情况比较特殊
  1. 特殊情况

    //特殊情况:函数内部声明的变量也是全局变量
    var c=1
    function fn(){
        c=2//当函数内部声明没有使用关键字的时候是会往上级查找的,可以理解为是全局变量
        //这种声明方式是“隐式声明”
    }
    console.log(c)
    

六、函数访问赋值规则

1. 访问规则
function f1(){
    var a = 2
    function f2(){
        console.log(a)//2
    }
    f2()
}
f1()

访问规则的总结

  1. 如果当前作用域有定义的时候就会直接使用当前作用域的变量
  2. 如果当前作用域没有定义就会往上级查找,上级没有就继续往上,直到找到全局
  3. 如果全局有就输出,没有就报错
2. 赋值规则
//案例1
function f1(){
    function f2(){
        a = 1
        //1. 加了var关键字代表局部变量
        //2,不加var关键字 隐式声明,全局作用域
    }
    f2()
}
f1()
console.log(a)

//案例2
function f1(){
    var a = 1
    function f2(){
        a = 2
        console.log(a)
    }
    f2()
    console.log(a)
}
f1()
console.log(a)
// 2 2 报错

赋值规则总结

  1. 往上级查找,上级存在可以重新赋值,上级没有就一直往全局查找
  2. 如果全局有就使用全局中的变量
  3. 如果全局没有定义变量,查找到全局的时候,会自己在全局定义一个变量
// 练习1
var num = 10;
fn1();
function fn1(){
    console.log(num);
    var num = 20;
}
//undefined

// 练习2
fn2();
var a = 18;
function fn2(){
    var b = 9;
    console.log(a);
    console.log(b);
}
// 练习2 预解析过程
var a
function fn2(){
    var b 
    b=9
    console.log(a)
    console.log(b)
}
fn2()
a=18
//undefined 9
// 如果a的赋值在函数调用之前,就会是18

// 练习3
fn3();
console.log(c);
console.log(b);
console.log(a);
function fn3(){
    var a=b=c=9;
    console.log(a);
    console.log(b);
    console.log(c);
}
// var a=b=c=9; 理解为 var a=9;b=9;c=9 所以a是局部作用域
//9 9 9 9 9 报错

// 练习4
var a = 4;
console.log(a);
a = 6
console.log(a);

function a(){
    console.log('哈');
}
a();
a = 10;
console.log(a);
// 练习4 预解析 
var a
function a(){
    console.log('哈')
}
a=4
console.log(a);
a=6
console.log(a);
a();//此时a是6 以函数的方式调用,会报错
a=10;
console.log(a)
// 4 6 报错

七、递归函数

  1. 在函数中调用自己
  2. 类似于循环
  3. 使用递归函数要注意返回,否则会造成死递归
  4. 递归比较消耗性能,尽量少用
//递归的语法
function fn(){
    fn()
}
fn()

//常见的递归
function fn(a){
    if(a==1){
        return 1
    }
    return fn(a-1)
}
var res = fn(10)
console.log(res)
//案例1:使用递归求1-5的和
function fn(a){
    if(a==1){
        return 1
    }
    return (a+fn(a-1))
}
var res = fn(5)
console.log(res)
// 案例2:斐波那契数列
function fn(a){
    if(a==1||a==2){
        return 1;
    }
    return fn(a-1) + fn(a-2)
}
var res=fn(10)
console.log(res)

八、事件的了解

事件触发函数

<body>
    <button id="btn1">
        声明式函数触发
    </button>
    <button id="btn2">
        赋值式函数触发
    </button>
</body>
<script>
    //声明式函数触发
    function fn(){
        alert("我是声明式函数")
    }
    btn1.onclick=fn
    //赋值式函数触发
    btn2.onclick=fn(){
        alert("我是赋值式函数")
    }
</script>

<body>
    <input type="text" placeholder="请输入一个数">
    <button>
        计算
    </button>
    
</body>
<script>
// 案例:触发事件求阶乘
// 1. 计算阶乘;2. 给标签绑定id,事件方式触发函数;3. 获取用户输入的值 DOM.value
    function fn(num){
        if(num == 1){
            return 1
        }
        return num * fn(num-1)
    }
	fn(5)
</script>

九、对象数据类型

对象的概念:用来储存数据,可以理解为容器

1. 对象的表现形式
var obj = {} //对象
var arr = [] //数组
var none = null //空
2. 对象的创建方式
//第一种:声明式/直接创建式
var obj = {}

//第二种:构造函数方式/new方式
var obj = new Object()
3. 对象存储数据的格式
//第一种:声明式/直接创建式
var obj = {1:1,2:2,
    ...
}

//第二种:构造函数方式
var obj = new Object({1:1,2:2,
    ...
})

对象存储数据的语法规则:键值对

  1. 键(key):也叫做对象的属性,必须是字符串,但是这个字符串在不包含连字符的时候可以不加引号,建议加上。如果键有多个单词有连接符的时候,要注意加上“”,或者使用驼峰命名
  2. 值(value):可以是任意类型,包括:数字、字符串、布尔、对象、函数
  3. 键值对之间使用逗号隔开
  4. 键值对之间没有顺序,是一个无序的数据集合
4. 对象的访问
var person = {
    "name":"小明",
    "age":10,
    "background-color":"green"
}
console.log(person.name,person.age)
console.log(person['name'],person['age'],person['background-color'])
console.log(preson['aa'])

对象访问的注意点

  1. 两种方式都可以访问对象中的数据,通过键获取得到的就是键值对的值
  2. 对象.键 这种的获取方式,如果键有连接符号的时候会报错,获取不到
  3. 如果键有连接符号的时候,需要使用 对象[“键”] 这种方法获取
  4. 对象中没有这个键的时候,返回的是undefined
5. 对象的遍历
for(var attr in obj){
    //输出需要的数据
    console.log(attr)
    console.log(attr[i])
}
//attr表示对象属性名,obj表示这个对象

对象遍历的注意点

  1. 遍历出来的数据是一个变量 对象.键 这种方式获取不到
  2. 遍历出来的数据是一个变量 对象[键] 这种方式是可以获取得到的,并且引号不用添加
  3. 如果使用for in这种方式遍历对象的话,建议使用 对象[键] 这种方式进行获取数据
6. 对象的方法
  1. 对象中的值可以是任意的数据类型
  2. 值可以是一个对象,还可以是一个函数(function=>方法)
//1.定义一个对象
var person = {
    name:'小明',
    age:26,
    sex:'男',
    // 2. 在对象中再嵌套一个对象
    brother:{
        name:'大明',
        age:30,
    }
    // 3. 对象中的值还可以是一个函数,可以理解为对象中的方法
    eat:function(){
        console.log("吃饭")
    }
    
}

console.log(person.brother.name)
//1. 之前调用函数的方法是:函数名称()
//2. 在对象中调用函数方法:键值对中的键()
person.eat()
7. 对象的操作
  1. 增加 对象.键的名字 = 值
  2. 修改 对象.键的名字 = 值
  3. 删除 delete 对象.键的名字
  4. 查询 对象.键的名字 || 对象[‘键的名字’]
var obj = {
    name = '小明',
    age = 18,
    gender = '女'
}
obj.money = 0 //增加
obj.money = 2000 //修改
delete obj.money //删除
obj = {} //置空
for(var i in obj){ //置空
    delete obj[i]
}
console.log(obj)
8. 构造函数【拓展】

只要使用了new方式创建的方法都是构造函数。任意的数据类型都可以用构造函数去创建

//1.使用构造函数创建对象
var obj = new Object()
console.log(obj)

//2.利用构造函数方式创建函数
function Fn(){}
var a = new Fn()
console.log(a)

//3.利用构造函数创建数据类型
var num = new Number()
console.log(num)

//指向window 默认情况下,this指向全局
console.log(this)

总结:关于构造函数和普通函数的区别

  1. 有没有new关键字
  2. 关于this指向的问题
  3. 首字母是否大写是区别构造函数和普通函数的最直观的表现
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

echozzi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值