JavaScript -- 06.函数知识汇总

news/2024/10/19 7:31:10/

文章目录

  • 函数
    • 1 函数介绍
    • 2 函数的创建方式
      • 2.1 函数声明
      • 2.2 函数表达式
      • 2.3
    • 3 参数
      • 3.1 函数声明
      • 3.2.函数表达式
      • 3.3 箭头函数
      • 3.4 默认参数
      • 3.5 使用对象作为参数
      • 3.6 函数作为参数
    • 4 函数的返回值
    • 5 作用域
      • 5.1 函数作用域
      • 5.2 作用域链
      • 5.3 练习
    • 6 window对象
    • 7 提升
      • 7.1 变量的提升
      • 7.2 函数的提升
      • 7.3 为什么要有提升
    • 8 debug
    • 9 立即执行函数
    • 10 函数中的this
      • 10.1 普通函数的this
      • 10.2 箭头函数的this
    • 11 严格模式

函数

1 函数介绍

函数(Function)

  • 函数也是一个对象
  • 它具有其他对象所有的功能
  • 函数中可以存储代码,且可以在需要时调用这些代码

语法:

function 函数名(){语句...
}

调用函数:

  • 调用函数就是执行函数中存储的代码
  • 语法:函数对象()

使用 typeof 检查函数对象时会返回 function

image-20221201140842209

2 函数的创建方式

三种方式

  • 函数声明
  • 函数表达式
  • 箭头函数

2.1 函数声明

function 函数名(){语句...
}

示例:

function fn(){console.log("函数声明所定义的函数~")
}

2.2 函数表达式

const 变量 = function(){语句...
}

示例:

const fn2 = function(){console.log("函数表达式")
}

2.3

() => {语句...
}

示例:

const fn3 = () => {console.log("箭头函数")
}const fn4 = () => console.log("箭头函数")

3 参数

  • 形式参数

    • 在定义函数时,可以在函数中指定数量不等的形式参数(形参

    • 在函数中定义形参,就相当于在函数内部声明了对应的变量但是没有赋值

  • 实际参数

    • 在调用函数时,可以在函数的()传递数量不等的实参

    • 实参会赋值给其对应的形参

    • 参数:

      • 如果实参和形参数量相同,则对应的实参赋值给对应的形参

      • 如果实参多于形参,则多余的实参不会使用

      • 如果形参多于实参,则多余的形参为undefined

    • 参数的类型

      • JS中不会检查参数的类型,可以传递任何类型的值作为参数
// 计算两数之和
function sum(a, b){console.log(a + b)
}

3.1 函数声明

function 函数名([参数]){语句...
}

3.2.函数表达式

const 变量 = function([参数]){语句...
}

3.3 箭头函数

([参数]) => {语句...
}

例子:

const fn = (a, b) => {console.log("a =", a);console.log("b =", b);
}fn(123, 456)

箭头函数只有一个参数的时候可以省略()

const fn2 = a => {console.log("a =", a);
}

3.4 默认参数

定义参数的时候,可以指定默认值,默认值会在没有对应实参时候生效

const fn3 = (a=10, b=20, c=30) => {console.log("a =", a);console.log("b =", b);console.log("c =", c);
}fn3(1, 2) // a = 1, b = 2, c = 30

3.5 使用对象作为参数

对象可以作为参数传递,传递的是变量中存储的值,也就是对象的内存地址,所以形参和实参指向的就是同一个对象

  • 当修改形参指向的时候,只会影响当前变量(形参指向改变)
  • 当修改对象中的参数的时候,如果有其他变量指向该对象则所有指向该对象的变量都会受到影响

这个必须得区分清楚

function fn(a){console.log("a =", a)// a = {} // 修改变量时,只会影响当前的变量a.name = "猪八戒" // 修改对象时,如果有其他变量指向该对象则所有指向该对象的变量都会受到影响console.log(a)
}let obj = {name:"孙悟空"}
fn(obj)

如果形参的默认值是一个对象的话,那么在函数每次调用的时候,都会重新创建新对象

// 函数每次调用,都会重新创建默认值
function fn2(a = {name:"沙和尚"}){console.log("a =", a)a.name = "唐僧"console.log("a =", a)
}fn2() // 沙和尚 唐僧
fn2() // 沙和尚 唐僧

但是如果是下面这个样子的话,每次指向的都是obj2,不会重新创建对象

let obj2 = {name:"沙和尚"}// 函数每次调用,都会重新创建默认值
function fn2(a = obj2){console.log("a =", a)a.name = "唐僧"console.log("a =", a)
}fn2() // 沙和尚 唐僧
fn2() // 唐僧 唐僧

3.6 函数作为参数

在JS中,函数也是一个对象(一等函数),别的对象能做的事情函数也可以

所以别的对象可以作为参数传递到函数中,函数也可以作为参数传递到函数中

function fn(a) {console.log("a =", a)a()
}function fn2() {console.log("fn2被调用了")
}fn(fn2) 

image-20221201143035741

有了这种功能之后,我们就可以动态的执行代码

fn(() => console.log("我是箭头函数"))function exec(desc, fn, arg1, arg2) {console.log(desc + " = " + fn(arg1, arg2))
}function add(arg1, arg2) {return arg1 + arg2
}function sub(arg1, arg2) {return arg1 - arg2
}exec("3 + 5", add, 3, 5)
exec("5 - 3", sub, 5, 3)
exec("5 * 3", (arg1, arg2) => {return arg1 * arg2}, 5, 3) // 使用匿名函数作为参数

image-20221201143416306

4 函数的返回值

在函数中,可以通过return关键字来指定函数的返回值,返回值就是函数的执行结果,函数调用完毕返回值便会作为结果返回

任何值都可以作为返回值使用(包括对象和函数之类),如果return后不跟任何值,则相当于返回undefined,如果不写return,那么函数的返回值依然是undefined

function sum(a, b) {// console.log(a + b)// 计算完成后,将计算的结果返回而不是直接打印return a + b
}let result = sum(2,3) // 5

return一执行函数立即结束

function sum(a, b) {return a + bconsole.log(a + b) // 不会执行这条语句
}

箭头函数的返回值可以直接写在箭头后,如果直接在箭头后设置对象字面量为返回值时,对象字面量必须使用()括起来

const sum = (a, b) => {return a + b
}const sum1 = (a, b) => a + b
const fn = () => ({name: "张三"})

5 作用域

  • 作用域指的是一个变量的可见区域
  • 作用域有两种:
    • 全局作用域
      • 全局作用域在网页运行时创建,在网页关闭时销毁
      • 所有直接编写到script标签中的代码都位于全局作用域中
      • 全局作用域中的变量是全局变量,可以在任意位置访问
    • 局部作用域
      • 块作用域
        • 块作用域是一种局部作用域
        • 块作用域在代码块执行时创建,代码块执行完毕它就销毁
        • 在块作用域中声明的变量是局部变量,只能在块内部访问,外部无法访问

5.1 函数作用域

  • 函数作用域也是一种局部作用域
  • 函数作用域在函数调用时产生,调用结束后销毁
  • 函数每次调用都会产生一个全新的函数作用域
  • 在函数中定义的变量是局部变量,只能在函数内部访问,外部无法访问
function fn(){let a = "fn中的变量a"console.log(a)
}fn()
console.log(a) // undefined 

5.2 作用域链

当我们使用一个变量时,JS解释器会优先在当前作用域中寻找变量,就近原则

  • 如果找到了则直接使用
  • 如果没找到,则去上一层作用域中寻找,找到了则使用
  • 如果没找到,则继续去上一层寻找,以此类推
  • 如果一直到全局作用域都没找到,则报错 xxx is not defined

参照下面的例子理解

报错的原因看下一节提升相关概念

let a = 10{console.log(a) // 报错,Cannot access 'a' before initializationlet a = "第一代码块中的a"console.log(a) // "第一代码块中的a"{console.log(a) // 报错,Cannot access 'a' before initializationlet a = "第二代码块中的a"console.log(a) // "第二代码块中的a"}console.log(a) // "第一代码块中的a"
}
console.log(a) // 10
let b = 33function fn() {console.log(b) // 报错,Cannot access 'b' before initializationlet b = 44console.log(b) // 44function f1() {console.log(b) // 报错,Cannot access 'b' before initializationlet b = 55console.log(b) // 55}f1()
}fn()

5.3 练习

练习1

var a = 1function fn() {a = 2console.log(a) // 2
}fn()
console.log(a) // 2

练习2

var a = 1function fn() {console.log(a) //undefinedvar a = 2console.log(a) // 2
}fn()
console.log(a) // 1

练习3

var a = 1function fn(a) {console.log(a) //undefineda = 2 	// 修改的是形参console.log(a) // 2
}fn()
console.log(a) // 1

练习4

var a = 1function fn(a) {console.log(a) //10a = 2console.log(a) // 2
}fn(10)
console.log(a) // 1

练习5

var a = 1function fn(a) {console.log(a) //1a = 2console.log(a) // 2
}fn(a)
console.log(a) // 1

练习6

console.log(a)  // a指向的是第五行的函数,var只是声明,并不赋值,如果声明过不会重复赋值var a = 1
console.log(a) // 1
function a() {alert(2)
}
console.log(a) // 1
var a = 3
console.log(a) // 3
var a = function () {alert(4)
}
console.log(a) // 打印11行函数
var a	// 已经声明过了,根本不执行
console.log(a) // 打印11行函数

6 window对象

  • 在浏览器中,浏览器为我们提供了一个window对象,可以直接访问
  • window对象代表的是浏览器窗口,通过该对象可以对浏览器窗口进行各种操作,除此之外window对象还负责存储JS中的内置对象和浏览器的宿主对象
  • window对象的属性可以通过window.对象名访问,也可以直接通过对象名访问
  • 函数就可以认为是window对象的方法
  • 向window对象中添加的属性会自动成为全局变量
window.a = 50
console.log(window.a) // 50
console.log(a) // 50

var:用来声明变量,作用和let相同,但是var不具有块作用域

  • 在全局中使用var声明的变量,都会作为window对象的属性保存

    • var b = 20 等价于 window.b = 20
  • 使用function声明的函数,都会作为window的方法保存

    function fn(){alert('我是fn')
    } 
    fn() // 等价于window.fn()
    
  • 使用let声明的变量不会存储在window对象中,而存在一个秘密的小地方(无法访问)

  • var虽然没有块作用域,但有函数作用域

    function fn2(){var d = 10 // var虽然没有块作用域,但有函数作用域m = 10 // 在局部作用域中,如果没有使用var或let声明变量,则变量会自动成为window对象的属性 也就是全局变量
    }fn2()console.log(d) // d is not defined 
    console.log(m) // 10
    

7 提升

7.1 变量的提升

使用var声明的变量,会在所有代码执行前被声明(而不是赋值),所以就可以在变量声明前就访问变量

let声明的变量实际也会提升,但是在赋值之前解释器禁止对该变量的访问

console.log(a) // 打印undefined
var a = 10 console.log(b) // 报错 b is not defined
b = 10console.log(c) // Cannot access 'c' before initialization
let c = 10

7.2 函数的提升

使用函数声明创建的函数,会在其他代码执行前被创建,所以我们可以在函数声明前调用函数

只有以var, let或者function开头的变量才会提升

fn() // 可以正常调用执行
fn2() // 报错fn2 is not a functionfunction fn(){alert("我是fn函数~")
}
var fn2 = function(){alert("我是fn函数~")
}

7.3 为什么要有提升

是为了解决性能问题,要根据变量和函数的数量决定开多大的内存空间

8 debug

两种方式

https://www.bilibili.com/video/BV1mG411h7aD?p=67

  1. 直接在代码中写debugger,在浏览器中打开控制台刷新页面后会停到这个位置

    debugger // 在代码中打了一个断点console.log(a) // 2
    var a = 1
    console.log(a) // 1
    
  2. 直接打开浏览器,F12 -> 源代码 -> 指定位置点击行号 -> 刷新

    image-20221201185036935

9 立即执行函数

在开发中应该尽量减少直接在全局作用域中编写代码

所以我们的代码要尽量编写的局部作用域

如果使用let声明的变量,可以使用{}来创建块作用域,这样也不会相互干扰

{let a = 10
}
{let a = 20
}

而如果现在函数里面,哪怕是使用var声明的变量也不会相互干扰,但是使用函数的时候需要再单独调用一下

function fn(){var a = 10}fn()function fn2(){var a = 20}fn2()

我们希望可以创建一个只执行一次的匿名函数

立即执行函数(IIFE)

  • 立即是一个匿名的函数,并它只会调用一次
  • 可以利用IIFE来创建一个一次性的函数作用域,避免变量冲突的问题
  • 外面包裹的括号不能少,是用来防止这个函数被提升;另外,最后的分号也不能省略,不然js会解析成(xxx)(xxx)
(function(){let a = 10console.log(111)
}());(function(){let a = 20console.log(222)
}());

10 函数中的this

10.1 普通函数的this

  • 函数在执行时,JS解析器每次都会传递进一个隐含的参数,这个参数就叫做 this
  • this会指向一个对象
    • this所指向的对象会根据函数调用方式的不同而不同
      • 1.以函数形式调用时,this指向的是window

      • 2.以方法的形式调用时,this指向的是调用方法的对象

      • 但实际上都是指向调用的对象,以函数方式调用的时候默认调用的对象是window

  • 通过this可以在方法中引用调用方法的对象
function fn() {// console.log(this === window)console.log("fn打印", this)
}
fn()	// 就相当于 window.fn()const obj = { name: "孙悟空" }
obj.test = fn
obj.test()

案例:为两个对象添加一个方法,可以打印自己的名字

方法一:

const obj3 = {name: "沙和尚",sayHello: function () {console.log(this.name)},
}
const obj4 = { name: "唐僧",sayHello: function(){console.log(this.name)}
}// 为两个对象添加一个方法,可以打印自己的名字
obj3.sayHello()
obj4.sayHello()

方法二:

const sayHello = function(){console.log(this.name)
}const obj3 = {name: "沙和尚",sayHello: sayHello
}
const obj4 = { name: "唐僧",sayHello: sayHello
}// 为两个对象添加一个方法,可以打印自己的名字
obj3.sayHello()
obj4.sayHello()

10.2 箭头函数的this

箭头函数:([参数]) => 返回值

例子:

  • 无参箭头函数:() => 返回值

  • 一个参数的:a => 返回值

  • 多个参数的:(a, b) => 返回值

  • 只有一个语句的函数:() => 返回值

  • 只返回一个对象的函数:() => ({...})

  • 有多行语句的函数:

    () => {....    return 返回值
    }
    

箭头函数没有自己的this,它的this由外层作用域决定,箭头函数的this和它的调用方式无关,外面的this是谁,它就是,如果在最外层,那么它的this就是window

function fn() {console.log("fn -->", this)
}const fn2 = () => {console.log("fn2 -->", this) // 总是window
}fn() // window
fn2() // windowconst obj = {name:"孙悟空",fn, // fn:fn的简写fn2,sayHello(){console.log(this.name)function t(){console.log("t -->", this)}t() // windowconst t2 = () => {console.log("t2 -->", this)}t2() // obj}
}obj.fn() // obj
obj.fn2() // windowobj.sayHello()// 

11 严格模式

JS运行代码的模式有两种:

  • 正常模式

    • 默认情况下代码都运行在正常模式中,
      • 在正常模式,语法检查并不严格,它的原则是:能不报错的地方尽量不报错
    • 这种处理方式导致代码的运行性能较差
  • 严格模式

    • 在严格模式下,语法检查变得严格

      • 禁止一些语法
      • 更容易报错
      • 提升了性能
    • 使用方式

      "use strict" // 全局的严格模式
      let a = 10function fn(){"use strict" // 函数的严格的模式
      }// 如果在严格模式下定义如:
      a = 10 // 则会报错a is not defined
      
  • 在开发中,应该尽量使用严格模式,这样可以将一些隐藏的问题消灭在萌芽阶段,同时也能提升代码的运行性能


http://www.ppmy.cn/news/624.html

相关文章

redux 中间件

//actions.js import { FILEMS } from ./constants; // 利用action来修改状态 export const Filems (filems)>{ return {type:FILEMS,filems}; }; //constants.js export const FILEMS FILEMS; reducer.js import {combineReducers } from redux import reducerFilem from…

java毕业设计的大学宿舍管理系统mybatis+源码+调试部署+系统+数据库+lw

java毕业设计的大学宿舍管理系统mybatis源码调试部署系统数据库lw java毕业设计的大学宿舍管理系统mybatis源码调试部署系统数据库lw本源码技术栈: 项目架构:B/S架构 开发语言:Java语言 开发软件:idea eclipse 前端技术&#…

基于yolov5n的轻量级MSTAR遥感影像目标检测系统设计开发实战

做过很多目标检测类的项目了,最近看到一个很早之前用过的数据集MSTAR,之前老师给的任务是基于这个数据集来搭建图像识别模型,殊不知他也是可以用来做目标检测的,今天正好有点时间就想着基于这个数据集来做一下目标检测实践。 首先…

keychron矮轴无线机械键盘简直就是yyds

一、背景 日常生活中,我们都离不开键盘,游戏党打游戏要键盘, 工作人员无论使用台式电脑还是笔记本操作都离不开键盘,尤其程序员这一族,键盘简直就是敲代码的灵魂。随着经济和科技飞速发展,我们早已不在像从…

【读论文】GANMcC

GANMcC简单介绍网络结构生成器辨别器损失函数生成器损失函数辨别器tips总结参考论文:https://ieeexplore.ieee.org/document/9274337 如有侵权请联系博主 这几天又读了一篇关于GAN实现红外融合的论文,不出意外,还是FusionGAN作者团队的人写…

下班路上捡了一部手机,我用8年开发知识主动找到了失主

☆ 我们学习开发知识,其实并不是只解决当前用户的需求,实现UI原型的还原,不应该只把自己嵌套在当前的浏览器或者某个docker的环境下。 因为不管你在tomcat或者数据库里折腾得再欢,可能到了生活中发现自己失去了超能力。 ☆ 记得有…

你是否还记得有一款游戏叫做“魔塔”?

目录 前言 正文 游戏介绍: 游戏开发制作流程 1.收集素材 2.创建攻击函数 2.1 定义两个对象(主角和怪物) 2.2 函数输出为【0】表示打不过,胜利输出受损生命值,设置 cancel 可以撤销本次战斗即回到之前状态 2.…

星起航跨境:跨境卖家可以这样获得好评,稳步提升销量

据调查数据显示,90%以上的消费者购物之前会先查看产品评论,好的评价能促进产品更快地转化,不好的评价还会对产品listing权重产生一定的影响。例如:在产品listing的评价较少的情况下,一个1星差评在listing展示超过3天之…