JavaScript函数基础

本文最后更新于:1 个月前

定义函数

函数声明

一个函数定义(也称为函数声明,或者函数语句)由function关键字,并跟随以下部分组成

  • 函数名称
  • 函数参数列表
  • 定义函数的JavaScript语句
1
2
3
function fn(number){
return number + 1;
}
  • typeof检测类型为function

参数本质上是按照值传递给函数的——因此即使函数体的代码为传递函数的参数赋了新值,这个改变也不会反映到全局或调用该函数的代码中。

如果你将对象作为参数传递,而参数改变了这个对象的属性,这样的改变对函数外部是可见的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function myFunc(theObject) {
theObject.make = "Toyota";
}

const mycar = {
make: "Honda",
model: "Accord",
year: 1998,
};

console.log(mycar.make); // "Honda"
myFunc(mycar);
console.log(mycar.make); // "Toyota"

如果你将数组作为参数传递,而参数改变了这个数组的值,这样的改变对函数外部也同样可见

1
2
3
4
5
6
7
8
9
10
function myFunc(theArr) {
theArr[0] = 30;
}

const arr = [45];

console.log(arr[0]); // 45
myFunc(arr);
console.log(arr[0]); // 30

函数表达式

由函数表达式创建的函数是匿名的,它不必有一个名称,例如

1
2
3
const square = function (number){
return number*number;
};

也可以为函数表达式提供名称

1
2
3
4
5
const factorial = function fac(n) {
return n < 2 ? 1 : n * fac(n - 1);
};

console.log(factorial(3)); // 6

我们也可以将函数作为函数的参数,函数也是对象的一种

1
2
3
4
5
6
7
8
9
10
function fn1(a){
a()
}

let obj = {name:'Tom'}

function fn2(){
console.log("我是fn2")
}
fn(fn2)//我是fn2

函数参数

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

  • 在函数中定义形参相当于在函数内部生成了一个没有赋值的变量

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

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

  • 参数

    1. 如果实参和形参数量相同,则对应的实参复制给对应的形参
    2. 如果实参多于形参,则多余的实参不会使用
    3. 如果形参多于实参 ,则多余的形参为Undefined
  • 任何值都可以作为参数,注意:函数不会检查类型

函数重载

  • 函数通过参数的个数和类型不同来创建不同的函数签名,通过调用函数时传入的参数个数和参数类型来进行命名参数的验证。

  • 但是,在js后声明的同名函数会对前面声明的一个函数进行一个覆盖,所以JavaScript没有真正意义上的函数重载

类数组arguments对象

  • 如果一个函数传递了三个参数,你可以以如下方式引用他们

1
2
3
arguments[0]
arguments[1]
arguments[2]

回调函数

回调函数是一个函数,将会在另一个函数完成执行后立即执行。回调函数是一个作为参数传给另一个 JavaScript 函数的函数。这个回调函数会在传给的函数内部执行。

函数提升

1
2
3
console.log(a)//Undefined

var a = 10

变量的提升

  • 使用var声明的变量,它会在所有代码执行前被声明但是不会被赋值

    • 我们可以在变量声明前就访问变量
1
2
3
4
5
fn()//可以成功执行

function fn(){
alert("111")
}

函数的提升

  • 使用**函数声明(function开头)**创建的函数,会在其他代码执行前被创建

  • 我们可以在函数声明前调用函数

  • JavaScript解释器会将整个函数声明提升到当前作用域的顶部

let声明的变量

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

函数作用域

在函数内定义的变量不能在函数之外的任何地方访问,因为变量仅仅在该函数的作用域内定义。相对应的,一个函数可以访问定义在其范围内的任何变量和函数

  • 函数作用域在函数调用时产生,调用结束后销毁

  • 函数每次调用都会产生一个全新的函数作用域

  • 在函数中定义的变量是局部变量,只能在函数内部访问,外部无法访问

闭包

闭包是 JavaScript 中最强大的特性之一。JavaScript 允许函数嵌套,并且内部函数具有定义在外部函数中的所有变量和函数(以及外部函数能访问的所有变量和函数)的完全访问权限。

但是,外部函数却不能访问定义在内部函数中的变量和函数。这给内部函数的变量提供了一种封装。

此外,由于内部函数可以访问外部函数的作用域,因此当内部函数生存周期大于外部函数时,外部函数中定义的变量和函数的生存周期将比内部函数执行的持续时间要长。当内部函数以某一种方式被任何一个外部函数之外的任何作用域访问时,就会创建闭包

注意:使用闭包时需要注意许多陷阱!

如果一个闭包的函数定义了一个和外部的某个变量名称相同的变量,那么这个闭包将无法引用外部作用域中的这个变量。(内部作用域的变量“覆盖”外部作用域,直至程序退出内部作用域。可以将其视作命名冲突。

作用域链

1
2
3
4
5
6
7
8
let a = 10
{
let a = "第一代码块中的a"
{
let a = "第二代码块中的a"
console.log(a)//第二代码块中的a
}
}

作用域链

  • 当我们使用一个变量时,JavaScript解释器会优先在当前作用域中寻找变量,如果找到了则直接使用,如果没找到,则去上一层作用域中寻找,如果一直没找到,就会报错!

window对象

  • 浏览器为我们提供了一个window对象,可以直接访问

  • window对象代表的是浏览器窗口,通过该对象可以对浏览器窗口进行各种操作,除此之外,window对象还负责存储JS中的内置对象和浏览器的宿主对象

  • window对象的属性可以通过window对象访问,也可以直接访问

  • 函数可以认为是window对象的方法

1
2
3
window.alert(123)
window.console.log()//window可省略
window.a = 10//向window中添加的属性会自动成为全局变量
  • var用来声明变量,作用和let相同,但是var不具有块作用域

    • 全局中使用var声明的变量,都会作为window对象的属性保存
    • 使用function声明的函数,都会作为window的方法保存
    • 使用let声明的变量不会存储在window
      综合例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var b = 20 //window.b = 20

function fn(){
alert('我是fn')
}

fn()
window.fn()

let c = 33
window.c()//Undefined

let c = 33
window.c = 44

console.log(c)//33 首先在秘密的地方找

function fn2(){
//var d = 10 var虽然没有块作用域,但是有函数作用域
d = 10//在局部作用域中,如果没有使用var或者let声明变量,则变量自动称为window对象的属性也就是全局变量,不要这样!!
}

fn2()

console.log(d)//可以访问

函数中的this

  • 环境对象this

  • 函数在执行时,JavaScript解释器每次都会传递一个隐含的参数,这个参数叫this

  • this会指向一个对象

    • this指向的对象会根据函数调用方式的不同而不同
      1. 以函数形式调用,this指向window
      2. 以方法的方式调用,this指向的是调用方法的对象
      3. 谁调用指向谁例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
function fn(){
console.log(this)//this === window
}

const obj = {name:'Tom'}

obj.test = fn

const obj2 = {name:'Bob',test:fn}

fn()//window window.fn()
obj.test()//name:'Tom'
obj2.test()//name:'Bob',test:fn

箭头函数

箭头函数表达式相比函数表达式有着更简洁的语法。箭头函数总是匿名的

有两个因素会影响箭头函数的引入:更简洁的函数和this的无绑定性

定义:

1
2
3
4
const 变量 = () =>{
...
}
const 变量 = () => ...

箭头函数参数和返回值

箭头函数只有一个参数时,可以省略括号

1
2
3
const fn2 = a =>{
console.log(a)
}

箭头函数的返回值可以写在箭头后

1
2
3
4
5
const sum = (a,b) => {
return a+b
}
简化为
const sum = (a,b) => a+b

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

1
const fn = () => ({name:'TOM'})

箭头函数的this

  • 箭头函数没有自己的this,它的this由外层作用域决定,和他的this无关举例说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function fn(){
console.log("fn -->",this)
}

const fn2 = () => ("fn2 -->",this)

fn()//-->window
fn2()//--window

const obj={
name:'Tom',
fn:fn,
fn2,//属性名和属性值相同可以省略
sayHello/*:function(可以省略)*/(){
console.log(this.name)

function t(){
console.log("t-->",this)
}

t()//-->window 由函数调用

const t2 = () => {
console.log("t2 -->",this)
}

t2()//-->obj 箭头函数this由外层作用域决定
}
}
obj.fn()//-->obj
obj.fn2()//-->window

立即执行函数

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

我们的代码要尽量编写在局部作用域中,如果使用let声明的变量,可以利用{}来创建块作用域

立即执行函数是一个匿名的函数,并且只会调用一次

可以利用立即执行函数来创建一个一次性的函数作用域,避免变量冲突

1
2
3
4
5
6
7
( function(){
let a = 10
}() );//两个在一起要加分号

( function(){
let a = 10
}() )

JavaScript函数基础
https://blog.seasalt-haiyan.top/2024/03/04/JavaScript函数基础/
作者
Xu Haoyang
发布于
2024年3月4日
更新于
2024年3月5日
许可协议