03-JavaScript-函数

2023-09-21 10:23:47

函数就是将特定功能的代码封装起来,当需要实现特定功能时,直接调用函数实现即可。

一、函数定义和使用

1.1 定义函数
  • 语句式命名函数
function sum(a,b) {
	return a + b;
}
  • 变量函数表达式
    是一个匿名函数,它没有函数名
let sum = function(a,b) {
	return a + b;
};
1.2 调用函数

按顺序传入参数

console.log(sum(1,3))
function sum(a,b) {
	return a + b;
}

let sum2 = function(a,b) {
	return a + b;
};
console.log(sum2(1,3))

函数声明提升:
语句式命名函数存在函数声明提升,函数表达式声明函数函数时,不可以把函数调用写在函数声明前面,因为变量赋值不存在提升。

1.3 arguments
  • 在函数内部起作用,指向当前函数的调用者传入的所有参数,它是一个类似数组的对象
let sum2 = function(a,b) {
    console.log(arguments)  //[1, 3, 4, 5, callee: ƒ, Symbol(Symbol.iterator): ƒ]
    console.log(arguments[2]) //4

	return a + b;
};
sum2(1,3,4,5)
  • arguments…length 获取调用函数实参个数
  • callee 属性,指向拥有这个arguments对象的函数
1.4 rest参数

剩余参数,遵循传入顺序后的,所有参数。rest参数只能写在最后,前面用…标识

let sum = function(a,b,...rest) {
    console.log(rest) //[4, 5]

	return a + b;
};
sum2(1,3,4,5)

二、变量作用域与解构赋值

2.1 变量提升

JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部

2.2 全局作用域

函数之外声明的变量,会成为全局变量,就具有全局作用域

2.3 局部作用域

函数中声明的变量,会成为函数的局部变量,在函数外部不能访问

2.4 作用域链

当前作用域没有定义的变量,会顺着作用域向父作用域找,如果父级也没有找到,再一层一层向上寻找,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链 。

2.5 名字空间

用于解决相同的全局变量、相同名字的函数,造成命名冲突。

var NAME_SPACE = {}
NAME_SPACE.name = ''
NAME_SPACE.sum = function() {}
2.6 常量

let、var声明的是变量,要声明一个常量,使用ES6的const声明,通常用全部大写的变量来表示“这是一个常量,不要修改它的值”,谷歌浏览器下,赋值会报错。

const PI = 3.14
PI = 3 // index.html:53 Uncaught TypeError: Assignment to constant variable.
console.log(PI)
2.7 解构赋值

可以同时对一组变量进行赋值

  • 数组解构(注意嵌套层次和位置要保持一致)
let arr = ['诸葛亮', ['刘备', '关羽'], '张飞'];
let [o, [p,q], r] = arr;
console.log(o,p,q,r) //诸葛亮 刘备 关羽 张飞
  • 对象进行解构赋值
    正常解构
let obj = {
    name: '张飞',
    age: 37,
    country: '蜀国'
}
let {name, age, country} = obj
console.log(name,age,country) //张飞 37 蜀国

嵌套的对象属性进行赋值:
address不是变量,而是为了让country、province、city获得嵌套的address对象的属性

let obj = {
    name: '张飞',
    age: 37,
    address: {
        country: '蜀国',
        province: '四川',
        city: '成都'
    }
}
let {name, age, address:{country,province,city}} = obj
console.log(name,country,province,city) //张飞 蜀国 四川 成都   
console.log(address) //Uncaught ReferenceError: address is not defined

当解构赋值,对应属性不存在时,会赋值undefined

let obj = {
    key: '004',
    name: '张飞',
    age: 37,
    address: {
        country: '蜀国',
        province: '四川',
        city: '成都'
    }
}
// key不是变量,而是为了让变量code获得key属性
let {name,id,key:code} = obj
console.log(name) //张飞
console.log(id) // undefined
console.log(code) // 004

解构赋值还可以使用默认值

let obj = {
    key: '004',
    name: '张飞',
    age: 37,
    address: {
        country: '蜀国',
        province: '四川',
        city: '成都'
    }
}
let {name,id='001'} = obj
console.log(name) //张飞
console.log(id) // 001
  • 使用场景
    1、直接获取某对象里面的属性值,不需要通过.来获取和连续声明变量
let {x,y,z} = {}

2、函数接收一个对象作为参数,可以直接用解构把对象属性赋值给变量

sum({x,y,z=0}){
	return x+y+z
}

3、变量值替换,减少中间变量

let x=1, y=2;
[x,y] = [y,x]
console.log(x,y) //2 1

三、方法

一个对象中绑定函数,这个函数称为这个对象的方法。

let obj = {
    name: '张飞',
    birth: 1993,
    age: function() {
        let y = new Date().getFullYear()
        return y - this.birth
    }
}
console.log(obj.age()) //30
3.1 this指向

this是执行环境上下文对象 ,它指向谁取决于它什么时候被调用、被谁调用。

  • 在方法中,this表示该方法所属的对象
    也就是说,谁执行这个方法,this就指向谁
let person = {
    name: '张飞',
    birth: 1993,
    say: function() {
        console.log(this)
        console.log(this.name)
    }
}
// person对象调用了方法,this指向person
person.say()
let person = {
    name: '张飞',
    birth: 1993,
    say: function() {
        console.log(this)
        console.log(this.name)
    }
}
// 全局对象调用say方法,因此this指向window
let say = person.say
say()
  • 单独使用,this 表示全局对象,浏览器中指向向window
  • 函数中使用this,表示全局对象,浏览器中指向向window
  • 事件中的this,指向了接收事件的 HTML 元素
  • 显示函数绑定
    js中函数也是对象,对象中则有方法,apply 和 call 就是函数对象的方法,他们允许切换函数执行的上下文环境(context),即 this 绑定的对象。
let personBase = {
    name: '张飞',
    birth: 1993,
    say: function() {
        console.log(this)
        console.log(this.name)
    }
}
let person = {
    name: '刘备',
}
let say = personBase.say
say.call(person) //指向person
say.apply(personBase) //personBase

1.2 调用函数扩展

  • 函数名(实参列表):say(a,b)
  • 函数名.call(执行环境对象,实参列表):say.call(person,a,b,c…)
    call 方法用来改变 this 的指向,第一个参数表示 this 的指向,后面的参数表示传递的实参,可以零个、一个或多个
  • 函数名.apply(执行环境对象,实参列表数组):say.apply(person,[a,b,c])
    apply 方法和 call 方法基本相同,也是用来改变 this 的指向,不同点在于,apply 传递实参时,采用数组的形式。
  • 函数名.bind(执行环境对象)(实参列表):say.bind(person).(a,b,c)
    bind 也是用来改变 this 的指向的,但是 bind(执行环境对象) 返回的是函数,还没有被执行,需要再使用()执行函数。
  • 回调函数
    主函数的事先做完,回头再调用传进来的那个函数

四、立即执行函数

声明函数的同时立即调用这个函数,立即执行函数是一个匿名函数

4.1 作用

1、页面加载完成后只执行一次的设置函数。
2、将设置函数中的变量包裹在局部作用域中,不会泄露成全局变量

4.2 书写方式
// a b 形参
(function(a,b){
	//函数体内容
    console.log(a,b)
}(1,2));//形参  立即执行函数结束要加分号

(function(a,b){
    console.log(a,b)
})(3,4)
4.3 用例

由于log调用前,循环执行结束,所以输出5

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

使用立即执行函数

for(let i=0; i<6; i++) {
    (function(j) {
        console.log(i)  //0,1,2,3,4,5
    })(i)
}

五、闭包

闭包是指有权访问另一个函数中的变量函数。(定义一个在函数内部的函数,内部函数可以访问外包函数的局部变量)

5.1 闭包形成条件
  • 函数嵌套
  • 内部函数使用到了外部函数内容(变量、函数等)
  • 参数和变量不会被回收
function fun () {
    var x = 0, y = 1;
    function sum() {
        console.log(x + y)
        return x + y
    }
    return sum
}
let f = fun()
f()
5.2 闭包的作用
  • 可以读取函数内部的变量
  • 让这些变量的值始终保持在内存中
function fun () {
    var n = 0;
    function sum() {
        n += 1
        console.log(n)
    }
    return sum
}
var f = fun()
f() //1
f() //2

fun执行两次, n分别输出1 和 2,说明fun中的局部变量n一直存在内存中,没有在fun调用后,自动清除。
未清除的原因:sum函数是fun的子函数,fun执行后,把sum函数赋值给了全局变量f,全局变量f一直存在,所以sum函数一直存在,而sum又依赖于fun函数,所以fun一直存在内存中,未在调用结束后被垃圾回收机制回收。

5.3 闭包的注意事项
  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题
  • 闭包会在父函数外部改变父函数内部变量的值。所以,如果你把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,不要随便改变父函数内部变量的值。多个子函数都是同时指向父级,是完全共享的。因此当父级的变量对象被修改时,所有子函数都受到影响

六、箭头函数

箭头函数相当于匿名函数,简化了函数定义

6.1 定义方式
// 省略简写方式
let fun1 = (x,y)=> x+y;

// 多条语句
let fun2 = (x,y)=> {
    console.log(x)
    return x+y
}
fun1(1,2)
fun2(2,3)
6.2 this问题

箭头函数内部的this是词法作用域,由上下文确定。
箭头函数的外层如果有普通函数,那么箭头函数的this就是这个外层的普通函数的this,箭头函数的外层如果没有普通函数,那么箭头函数的this就是全局变量。

  • 箭头函数是匿名函数,不能作为构造函数,不可以使用new命令,否则后抛出错误
    箭头函数没有原型对象prototype这个属性
  • 箭头函数不绑定arguments,取而代之用rest参数解决,同时没有super和new.target
  • 箭头函数可以通过拓展运算符获取传入的参数。
let fun2 = (...args)=> {
    console.log(args)
    return args[0]+args[1]
}
fun2(2,3)
  • 使用call,apply,bind并不会改变箭头函数中的this指向
    当对箭头函数使用call或apply方法时,只会传入参数并调用函数,并不会改变箭头函数中this的指向。
    当对箭头函数使用bind方法时,只会返回一个预设参数的新函数,并不会改变这个新函数的this指向。
更多推荐

创建第一个MyBatis框架--保姆级教学

文章目录前言一、创建一个空的mybatis项目二、创建一个Maven模块三、各个文件的配置四、总结前言在idea上创建我的第一个MyBatis框架一、创建一个空的mybatis项目1、new一个新的项目2、选择最下面,创建一个空项目3、为空项目取一个名字,位置可以自己选4、点击完成后,开始配置以下版本,两个版本得一样,

HDMI字符显示实验

FPGA教程学习第十五章HDMI字符显示实验文章目录FPGA教程学习前言实验原理程序设计像素点坐标模块字符叠加模块实验结果知识点总结前言在HDMI输出彩条的基础上输出osd叠加信息。实验原理实验通过字符转换工具将字符转换为16进制coe文件存放到单端口的ROMIP核中,再从ROM中把转换后的数据读取出来显示到HDMI上

高云FPGA系列教程(9):cmd-parser串口命令解析器移植

文章目录@[toc]cmd-parser库简介cmd-parser库源码获取GW1NSR-4C移植cmd-parser实际测试cmd-parse命令解析器优化本文是高云FPGA系列教程的第9篇文章。上一篇文章介绍片上ARMCortex-M3硬核处理器串口外设的使用,演示轮询方式和中断方式接收串口数据,并进行回环测试。本

千兆以太网硬件设计及链路层 MAC 协议格式

以太网系列文章:(1)千兆以太网硬件设计及链路层MAC协议格式(2)千兆以太网网络层ARP协议的原理与FPGA实现(3)CRC校验代码原理文章目录前言一、以太网MAC层接口介绍1.MII接口2.GMII接口3.RGMII接口二、以太网(MAC)帧协议介绍前言从本章开始,将分享千兆以太网设计的相关知识。将为大家分享千兆以

基于FPGA的图像sobel锐化实现,包括tb测试文件和MATLAB辅助验证

目录1.算法运行效果图预览2.算法运行软件版本3.部分核心程序4.算法理论概述5.算法完整程序工程1.算法运行效果图预览将FPGA的仿真结果导入到matlab显示图像效果2.算法运行软件版本MATLAB2022a,vivado2019.23.部分核心程序.................................

【洛谷 P1364】医院设置 题解(图论+深度优先搜索)

医院设置题目描述设有一棵二叉树,如图:其中,圈中的数字表示结点中居民的人口。圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为111。如上图中,若医院建在111处,则距离和=4+12+2×20+2×40=136=4+12+2\times20+2\ti

分布式调度 Elastic-job

分布式调度Elastic-job1.概述1.1什么是任务调度我们可以思考一下下面业务场景的解决方案:某电商平台需要每天上午10点,下午3点,晚上8点发放一批优惠券某银行系统需要在信用卡到期还款日的前三天进行短信提醒某财务系统需要在每天凌晨0:10分结算前一天的财务数据,统计汇总以上场景就是任务调度所需要解决的问题任务调

“混合”引擎为通用子模块提供动力,实现嵌入式I / O灵活性

现成的组件对于嵌入式开发工程师而言并不是什么新鲜事物。但是,实际上没有人期望一种“一刀切”的解决方案,尤其是在涉及复杂的I/O要求的情况下。但是,基于流行的夹层卡格式的具有成本效益的现场可编程门阵列(FPGA)技术的新实现正在“针对VME,PCI,CompactPCI和VME提供可承受的标准硬件和自由形式的I/O多功能

Mac FoneLab for Mac:轻松恢复iOS数据,专业工具助力生活

如果你曾经不小心删除了重要的iOS数据,或者因为各种原因丢失了这些数据,那么你一定知道这种痛苦。现在,有一个名为MacFoneLab的Mac应用程序,它专门设计用于恢复iOS数据,这可能是你的救星。MacFoneLabforMac是一种强大的工具,它可以帮助你恢复各种类型的iOS数据,包括照片、视频、联系人、短信、通话

Vulnhub实战-DC9

前言本次的实验靶场是Vulnhub上面的DC-9,其中的渗透测试过程比较多,最终的目的是要找到其中的flag。一、信息收集对目标网络进行扫描arp-scan-l对目标进行端口扫描nmap-sC-sV-oAdc-9192.168.1.131扫描出目标开放了22和80两个端口,访问目标的80端口。对目标进行目录扫描与分析。

大数据课程M2——ELK的ELASTICSEARCH概述

文章作者邮箱:yugongshiye@sina.cn地址:广东惠州▲本章节目的⚪了解ELK的ELASTICSEARCH概括;⚪掌握ELK的ELASTICSEARCH核心概念;一、ELASTICSEARCH概括1.ES安装和启动注意:如果使用达内云主机,无需安装es,直接配置yml启动即可。1.安装下载ES最新版本的安装

热文推荐