JavaScript -- 06.函数知识汇总
创始人
2024-03-12 06:41:35
0

文章目录

  • 函数
    • 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
      
  • 在开发中,应该尽量使用严格模式,这样可以将一些隐藏的问题消灭在萌芽阶段,同时也能提升代码的运行性能

相关内容

热门资讯

团拜会主持词 -主持词 团拜会主持词 -主持词大家下午好!腊梅催春至,瑞雪兆丰年!此时窗外虽然大雪纷飞、寒意袭人,但这里却热...
最新三八妇女节活动的主持词 最新三八妇女节活动的主持词(精选10篇)  主持词的写作需要将主题贯穿于所有节目之中。在现在的社会生...
小学师德报告会的主持词 小学师德报告会的主持词各位领导,各位老师:  大家下午好!采撷着金秋十月的累累硕果,收藏着金秋十月的...
《像小强一样儿活着》的经典台... 《像小强一样儿活着》的经典台词  《像小强一样活着》改编自同名网络小说,是难得的本土电影。曾有影评家...
汇演主持词 汇演主持词  主持词要根据活动对象的不同去设置不同的主持词。在人们积极参与各种活动的今天,主持人在各...
联欢会主持词结束语 联欢会主持词结束语(通用6篇)  晚会开得就是否成功圆满与主持人的讲话有很大关系。下面小编整理的联欢...
幼儿园毕业晚会主持词 幼儿园毕业晚会主持词  主持人在台上表演的灵魂就表现在主持词中。时代不断在进步,司仪等是很多场合都需...
美剧经典台词 美剧精选经典台词  在快速变化和不断变革的今天,能够利用到台词的场合越来越多,台词是一种特殊的,也是...
朗诵会主持词 关于朗诵会主持词4篇  主持词要根据活动对象的不同去设置不同的主持词。在当下这个社会中,很多场合都需...
记者节晚会主持词 记者节晚会主持词  主持词是主持人在台上表演的灵魂之所在。随着社会一步步向前发展,主持词的实用频率越...
婚礼父亲致辞 婚礼父亲致辞(精选15篇)  在平凡的学习、工作、生活中,大家肯定对各类致辞都很熟悉吧,致辞具有“礼...
校园红歌赛的主持词 校园红歌赛的主持词  主持词是主持人在节目进行过程中用于串联节目的串联词。在现今人们越来越重视活动氛...
开业主持词开场白 开业主持词开场白  根据活动对象的不同,需要设置不同的主持词。在当今社会生活中,活动集会越来越多,主...
关于唱歌比赛主持词   主持词是指主持人在主持节目的过程中进行节目串联的串联词,一般由开场白、中间部分与结束语组成。以下...
动漫感人台词 动漫感人台词(通用175句)  台词可以刻画人物的性格,表现人物的感情,加强剧情的表现力。那些广为流...
最新年会主持词 最新年会主持词(精选11篇)  契合现场环境的主持词能给集会带来双倍的效果。在如今这个时代,主持人的...
新生文艺汇演主持词 新生文艺汇演主持词  主持词要根据活动对象的不同去设置不同的主持词。在当今社会生活中,各种集会的节目...
家长代表幼儿园毕业典礼主持词 家长代表幼儿园毕业典礼主持词  主持词是各种演出活动和集会中主持人串联节目的串联词。在人们积极参与各...
学校元旦晚会主持词开场白和结... 学校元旦晚会主持词开场白和结束语  2017年元旦晚会主持词怎么写?怎么开场比较好呢?结束语又该怎么...
毕业晚会致辞 毕业晚会致辞(精选18篇)  在学习、工作或生活中,大家都写过致辞吧,致辞要求风格的雅、俗、庄、谐要...