JavaScript函数揭秘:提升编程技能的必学秘籍!

JavaScript是一种基于原型的高级、解释型编程语言,它是一门动态语言,常用于网页交互和前后端开发。在JavaScript中,函数是一段可重复使用的代码块,它可以执行一个特定的任务。

函数的使用可以增加代码的模块性和重用性,使得代码更加简洁明了。本文将介绍如何在JavaScript中创建和使用函数。

一、函数的介绍

简单来说,函数就是一段可重复使用的代码块,它可以接收输入参数,执行特定的操作,并返回结果。在JavaScript中,我们可以使用关键字function来定义一个函数。例如:

function add(a, b) {
  return a + b;
}

这个简单的函数接受两个参数a和b,然后返回它们的和。

函数的作用:

  • 实现功能的封装,提高代码复用率
  • 用于构建对象的模板(构造函数)

函数实际上是对象,每个函数都是Function类型的实例,并且都与其他引用类型一样具有属性和方法,由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。

二、函数的创建

在JavaScript中,有多种方式可以用来创建函数,其中最常用的有以下几种:

1、函数声明(Function Declaration)

函数声明是最常见的创建函数的方式,它使用function关键字,后跟函数名和参数列表,然后是函数体。

例如,我们可以创建一个名为greet的函数,该函数接收一个参数name,并打印出一条问候消息:

function greet(name) {
  console.log("Hello, " + name + "!");
}

2、函数表达式(Function Expression)

函数表达式是将函数定义为一个表达式的值。它与函数声明类似,但是没有函数名。

例如,我们可以创建一个匿名函数,并将其赋值给一个变量greet:


var greet = function(name) {
  console.log("Hello, " + name + "!");
};

3、箭头函数(Arrow Function)

箭头函数是ES6引入的新特性,它提供了一种更简洁的方式来创建函数。箭头函数使用=>符号,左侧是参数列表,右侧是函数体。

例如,我们可以创建一个箭头函数,并将其赋值给一个变量greet:

var greet = (name) => {
  console.log("Hello, " + name + "!");
};

三、函数内部属性

函数内部属性只能在函数内部才能访问。

arguments

arguments是一个类数组对象,包含着传入函数中的所有参数。arguments主要用途是保存函数参数。

function foo(){
  console.log(arguments) // [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 }
  console.log(arguments[1]) // 2
}
// 当传递的实参个数超过形参的个数的时候不会报错,所有的实参都会保存在arguments里
foo(1,2,3,4)

注意:arguments 中存的是实参,而不会存形参。

function foo(a,b = 2,c = 3){
  console.log(arguments) // [Arguments] { '0': 1 }
  console.log(b) //2
  console.log(c) //3
}
//只传了一个实参,那么arguments中就只有一个值
foo(1)

callee 属性

arguments 对象有一个名为callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。

arguments.callee 实际上就是函数名。
// 递归求n的阶乘
function factorial(n) {
  if (n == 1) {
    return 1
  }
  return arguments.callee(n - 1) * n //arguments.callee 相当于函数名factorial
}
console.log(factorial(10));

length 属性

arguments 对象的 length 属性返回实参个数。

function foo(){
  console.log(arguments.length) //5
}
foo(1,2,3,4,5)

四、函数的使用

创建了函数后,我们就可以在代码中调用它来执行特定的任务。函数调用是通过函数名和括号内的参数列表来实现的。

例如,我们可以调用上面创建的greet函数:

greet("Alice"); // 输出:Hello, Alice!

此外,JavaScript还支持以下高级功能:

1、默认参数:

可以为函数的参数设置默认值,当调用函数时未提供该参数时,将使用默认值。

function greet(name = "World") {
  console.log("Hello, " + name + "!");
}

greet(); // 输出:Hello, World!
greet(“Alice”); // 输出:Hello, Alice!

2、回调函数:

可以将函数作为参数传递给其他函数,以便在特定时机执行

setTimeout(function() {
  console.log("This message will be displayed after 1 second.");
}, 1000);

3、递归函数:

函数可以直接或间接地调用自身,以实现复杂的逻辑。

function factorial(n) {
  if (n === 0) {
    return 1;
  } else {
    return n * factorial(n - 1);
  }
}

console.log(factorial(5)); // 输出:120

五、函数的作用域

JavaScript函数的作用域分为全局作用域和局部作用域。

全局作用域:  全局作用域中的变量是在任何函数之外声明的,或者是使用var关键字在一个函数内部声明但没有使用let或const的变量。这些变量在整个程序中都是可用的。

局部作用域:  局部作用域中的变量是在函数体内声明的,包括使用let、const或var关键字声明的变量。这些变量只在函数内部及其子函数中可用,函数外部无法访问。

此外,JavaScript还有块级作用域,由{}包围的代码块也可以创建新的作用域,通常用于控制流语句(如if、for等)中。在块级作用域中声明的变量,在块外部是不可见的。

六、什么是闭包

1、什么是闭包

简单讲,闭包就是指有权访问另一个函数作用域中的变量的函数。

闭包是一种特殊的对象。它由两部分构成:函数以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。

2、闭包形成的条件

闭包的生成有三个必要条件:

  • 函数嵌套函数
  • 内部函数引用了外部函数中的数据(属性、函数)
  • 参数和变量不会被回收

这样就形成了一个不会销毁的函数空间。

下面例子中的 closure 就是一个闭包:

function func() {
  var a = 1, b = 2;

function closure() {
return a + b;
}
return closure;
}
console.log(func()()); // 3

闭包的作用域链包含着它自己的作用域,以及包含它的函数的作用域和全局作用域。

在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成 " 定义在一个函数内部的函数 " 。

所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

3、闭包的作用

闭包可以用在许多地方。它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

function f1() {
  var n = 999;
  nAdd = function () {
   n += 1
  }
  function f2() {
    console.log(n);
  }
  return f2;
}
var result = f1();
result(); // 999
nAdd();
result(); // 1000

在这段代码中,result 实际上就是闭包 f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。

其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

4、使用闭包的注意点

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露,这是IE的BUG。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
  • 闭包会在父函数外部改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。多个子函数的scope都是同时指向父级,是完全共享的。因此当父级的变量对象被修改时,所有子函数都受到影响。

JavaScript函数的创建和使用是编程过程中的基本技能,掌握好函数的使用可以使代码更加模块化、简洁和高效。它们不仅可以帮助我们将代码模块化、复用,还可以实现更复杂的逻辑和功能。

希望这篇文章能帮助你更好地理解和应用JavaScript函数,让你的编程之旅更加精彩!


这是一个从 https://juejin.cn/post/7368320489889890340 下的原始话题分离的讨论话题