首页 > 编程语言 >前端基础之JavaScript函数

前端基础之JavaScript函数

时间:2024-03-21 16:22:49浏览次数:16  
标签:function city return 函数 作用域 前端 JavaScript var

一、什么是函数

在 JavaScript 中,函数是一种可重复使用的代码块,用于执行特定任务或计算。函数可以接受参数,执行特定的操作,并返回一个值。

二、函数的声明

函数可以通过函数声明、函数表达式或箭头函数来定义。

  • 函数声明
function functionName(parameters) {
    // 函数体
    return value; // 可选
}
  • 函数表达式
const functionName = function(parameters) {
    // 函数体
    return value; // 可选
};
  • 箭头函数(ES6+):
const functionName = (parameters) => {
    // 函数体
    return value; // 可选
};

三、函数的调用

  • 一般调用: 函数名([参数])

  • 事件调用 触发事件后调用函数

  • 可以多次调用,每次调用是独立不相关的

  • 函数的执行跟定义位置无关,只与调用的位置有关

(1)无参函数

function func1(){
    console.log('hello world')
}
func1()  // 调用,加括号调用,跟Python是一样的

(2)有参函数

function func2(a,b){
    console.log(a,b)
}

func2(1,2)
VM3223:2 1 2

func2(1,2,3,4,5,6,7) # 传多了没关系,只要对应的数据
VM3223:2 1 2

func2(1)  # 少了也没关系 
VM3223:2 1 undefined

四、函数参数

  • 形式参数(形参):定义函数时所传递的参数
  • 实际参数(实参): 调用函数时所传递的参数

注意:

  • 参数传递: 只能将实参 传递给形参,即单向传递

  • 形参只能是变量; 实参可以是变量,常量,表达式

  • 实参的数量 小于形参的数量时,多余的形参值为undefined

  • 实参的数量 大于形参的数量时,自动忽略多余的实参

函数可以接受零个或多个参数,参数在函数定义中的括号中指定。参数可以是任何有效的 JavaScript 表达式。

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

五、函数返回值

在 JavaScript 中,函数可以通过 return 语句返回一个值。函数的返回值可以是任何数据类型,包括数字、字符串、布尔值、对象、数组等。

(1)返回单个值

函数可以返回单个值,该值可以是任何 JavaScript 数据类型。

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

let result = add(3, 4); // result 等于 7

(2)返回对象

函数可以返回一个对象,对象可以包含多个属性。

function createPerson(name, age) {
    return {
        name: name,
        age: age
    };
}

let person = createPerson("Alice", 30);
console.log(person.name); // 输出 "Alice"
console.log(person.age); // 输出 30

(3)返回数组

函数可以返回一个数组。

function getNumbers() {
    return [1, 2, 3, 4, 5];
}

let numbers = getNumbers();
console.log(numbers); // 输出 [1, 2, 3, 4, 5]

(4)返回函数

函数可以返回另一个函数。

function createMultiplier(multiplier) {
    return function(x) {
        return x * multiplier;
    };
}

let double = createMultiplier(2);
let result = double(5); // result 等于 10

(5)返回 undefined

如果函数没有明确的 return 语句,它将返回 undefined

function noReturn() {
    // 没有 return 语句
}

let value = noReturn(); // value 等于 undefined

(6)多个返回值

JavaScript 中函数只能返回一个值。如果需要返回多个值,可以使用对象、数组或其他数据结构来封装这些值。

function calculateCircle(radius) {
    let circumference = 2 * Math.PI * radius;
    let area = Math.PI * radius * radius;

    return { circumference: circumference, area: area };
}

let circleInfo = calculateCircle(5);
console.log(circleInfo.circumference); // 输出圆周长
console.log(circleInfo.area); // 输出面积

(7)函数返回值的应用

  • 函数的返回值不仅可以用作其他变量的赋值,还可以作为其他函数的参数。
  • 例如,在排序算法中,我们可以创建一个比较函数并将其作为Array.prototype.sort()方法的参数,利用其返回值来决定数组元素的排列顺序
function compareNumbers(a, b) {
    return a - b; // 返回a与b的差值,sort()会根据这个差值决定元素的顺序
}

const numbers = [5, 2, 9, 1, 5, 6];
numbers.sort(compareNumbers); // 排序后的数组:[1, 2, 5, 5, 6, 9]

(8)特殊之处

  • 返回多个值且用逗号连接,则只能拿到最后一个
function index(){
    return 666,777,888
}
res = index()
888
res
888 # 只能拿到最后一个
  • JavaScript不支持解压赋值
function index(){
    return [666,667,888]
}
res = index()
(3) [666, 667, 888]
res
(3) [666, 667, 888]

六、函数作用域

  • 函数内部定义的变量在函数外部不可访问,这被称为函数作用域。
  • 函数的参数是局部变量,只能在函数内部使用
  • 函数内部的变量也是局部变量
  • 可以理解为函数体本身是一个独立的作用域

(1)作用域的范围

全局作用域:在函数外定义的变量,在页面的任何一个地方都可以使用
局部作用域: 在函数内定义的变量,只能在函数内使用

注意:

  • 在函数中声明变量不加var 就是全局变量;形参 是局部作用域
  • 变量退出作用域会自动销毁,全局变量只有关闭浏览器才会销毁

(2)作用域链

作用域链:如果当前作用域没有,则向父级作用域查找,如果还没有,继续向父级作用域,一直找到全局作用域,如果还没有,报错

一旦进入作用域,就启动js解析器

1、预解析 var function 形参

找到var后,将var后的变量名提取出来,并给他初始化一个值undefined
变量和函数同名,丢变量,保函数
当有多个script标签时,从上到下依次解析并执行第一个script标签,然后下一个,依次类推
在上面script标签中声明的东西,下面script标签中都可以使用
下面script标签中声明的东西,上面使用会报错

2、逐行解读代码

执行表达式
函数调用

(3)遮蔽效应

  • 程序在遇到一个变量时,使用时作用域查找顺序,不同层次的函数内都有可能定义相同名字的变量名,会优先从自己所在的层查找,如果没有查找到会依次往上找。
  • 整个过程中会发生内层变量遮蔽外层变量的效果叫做遮蔽效应

七、匿名函数

匿名函数是没有函数名的函数,通常用作回调函数或立即执行函数。

  • 匿名函数,也称为无名函数或者lambda表达式,是一种没有名称的JavaScript函数定义形式。
  • 它们在编程中广泛用于一次性使用的简单计算、事件处理、高阶函数等场景,因为它们不需要被赋给特定变量或者作为对象的方法来使用。

(1)将匿名函数赋值给一个变量

var fn = function () {
         alert('hello')
      }
fn()

(2)将匿名函数绑定给一个事件

document.onclick = function () {
         alert(0)
       }

(3)匿名函数自我执行

(function () {
         alert(123)
       })()

(4)匿名函数的返回值(将一个匿名函数的返回值赋值给一个变量)

var num = (function () {
         return 3
       })()
       alert(num)

(5)输出一个匿名函数的返回值

alert(
         (function () {
           return 'hello'
         })()
       )

(7)给匿名函数传递参数

var sum = (function (a, b) {
        return a + b
      })(3, 4)
      alert(sum)

(8)特点与用途

  • 无名/临时作用域:由于匿名函数没有名字,它们的变量和参数只在其所在的作用域内有效,不会污染全局命名空间,适用于一些短暂或局部需求的场合。

  • 闭包:当匿名函数作为另一个函数的参数传递或返回时,它可以访问到外部函数的局部变量和参数,形成闭包。这对于实现数据封装、模块化编程以及实现私有变量等功能非常有用。

  • 简便的函数创建:在某些情况下,如高阶函数应用、事件处理器等,使用匿名函数可以避免额外的声明和赋值操作,使得代码更加简洁高效。

  • 立即执行:通过使用立即调用表达式(IIFE),匿名函数可以在定义的同时就被执行,比如初始化数据、防止变量污染全局命名空间等场景。

八、函数的递归

(1)递归介绍

函数自己调用自己,跟循环类似,他为了避免无限的递归,需要设置一个递归终止点

本质 :实现循环

  • 函数的内部可以调用函数自己本身,这种做法称为递归
  • 函数递归需要考虑好结束递归的条件,不然会出现死递归导致程序崩溃,内存溢出

(2)案例介绍

  • 1~n之间所有数的和
function fnSum(n) {
    // 结束条件
    if (n === 1) {
        return 1
    }
    return n + fnSum(n - 1)
}
console.log(fnSum(100))
  • 计算斐波那契数列的第N项

斐波那契数列(Fibonacci sequence),又称黄金分割数列。因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”。指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上, 斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3)。

 function fibonacci(n){
            if(n==1){
                return 1;
            }
            if(n==2){
                return 1;
            }
            //n = 3 :return fibonacci(2)+fibonacci(1)
            //n = 4 :return fibonacci(3)+fibonacci(2)
            return fibonacci(n-1)+fibonacci(n-2);
        }
 fibonacci(10)

(3)递归函数的优缺点

优点:结构清晰, 可读性强, 而且容易用数学归纳法来证明算法的正确性, 因此它为设计算法、 调试程序带来很大方便。

缺点:递归算法的运行效率较低, 无论是耗费的计算时间还是占用的存储空间都比非递归算法要多。

九、箭头函数

在 JavaScript 中,箭头函数是 ES6 引入的一种新的函数语法,提供了一种更简洁的函数定义方式。下面是关于 JavaScript 箭头函数的详细介绍:

(1)基本语法

箭头函数使用 => 符号来定义,语法如下:

// 无参数箭头函数
const greet = () => {
    return "Hello!";
};

// 单个参数箭头函数
var func1 = (v) => v;
// 或者等价的传统写法
var func1 = function(v) {
  return v;
};

// 多个参数箭头函数
var func2 = (args1, args2) => args1 + args2;
// 或者等价的传统写法
var func2 = function(args1, args2) {
  return args1 + args2;
};

(2)省略括号和 return

当函数只有一个参数时,可以省略参数的括号;如果函数体只有一条语句且是返回语句,可以省略花括号和 return 关键字。

const double = x => x * 2; // 省略括号和 return

const sayHello = name => `Hello, ${name}!`; // 省略花括号和 return

(3)箭头函数和 this

箭头函数没有自己的 this 上下文,它会继承外部作用域的 this 值。

const person = {
    name: "Alice",
    greet: function() {
        setTimeout(() => {
            console.log(`Hello, my name is ${this.name}`);
        }, 1000);
    }
};

person.greet(); // 输出 "Hello, my name is Alice"

(4)适用场景

  • 简洁性:箭头函数通常比传统函数更简洁,特别是在回调函数或只包含一行代码的函数中。
  • this 上下文:箭头函数的 this 绑定在定义时的上下文,避免了传统函数中 this 指向的困扰。

(5)注意事项

  • 箭头函数不能用作构造函数,不能使用 new 关键字实例化。
  • 箭头函数没有自己的 arguments 对象,可以使用剩余参数(rest parameters)或展开语法替代。

(6)与传统函数的区别

  • 没有 thisargumentssupernew.target 绑定。
  • 不能用作构造函数。
  • 没有原型(prototype)属性。

箭头函数在简化代码、处理 this 上下文等方面提供了便利,但也需要注意其适用场景和限制。

补充:函数的全局变量与局部变量

  • 跟python查找变量顺序一样
var city = "BeiJing";
function f() {
  var city = "ShangHai";
  function inner(){
    var city = "ShenZhen";
    console.log(city);
  }
  inner();
}

f();  //输出结果是?
  • 输出结果是:"ShenZhen"。

  • 在函数 inner 内部,变量 city 的值被赋为 "ShenZhen"

    • 因此 console.log(city) 会输出 "ShenZhen"。
  • 由于函数 inner 是在函数 f 中定义的,并且在函数 f 中调用了 inner

    • 所以同样的规则适用于函数 f
  • 即使在函数 f 内部有一个与全局变量 city 同名的局部变量 city

    • 但在执行 console.log(city) 时,它将访问最内层的局部变量 city 的值
    • 也就是 "ShenZhen"。
var city = "BeiJing";
function Bar() {
  console.log(city);
}
function f() {
  var city = "ShangHai";
  return Bar;
}
var ret = f();
ret();  // 打印结果是?
  • 在你提供的代码中,输出结果将是:"BeiJing"。

  • 首先,全局变量 city 被赋值为 "BeiJing"。

  • 然后,在函数 f 内部定义了一个名为 city 的局部变量,将其值设置为 "ShangHai"。

  • 接下来,f 函数返回了函数 Bar 的引用。

  • 在执行 var ret = f() 后,ret 变量被赋值为 Bar 函数的引用

    • ret 现在指向函数 Bar
  • 最后,执行 ret()

    此时由于 ret 指向 Bar 函数

    • 所以会执行 Bar 函数,并打印当前作用域内的变量 city 的值,即输出 "BeiJing"。
  • 注意,这是因为 Bar 函数在执行时访问的是其定义时所处的作用域,即全局作用域,而非 f 函数内部的作用域。

var city = "BeiJing";
function f(){
    var city = "ShangHai";
    function inner(){
        console.log(city);
    }
    return inner;
}
var ret = f();
ret();
  • 输出结果将是:"ShangHai"。

  • 首先,全局变量 city 被赋值为 "BeiJing"。

    • 然后,在函数 f 内部定义了一个名为 city 的局部变量,将其值设置为 "ShangHai"。
    • 接下来,f 函数返回了函数 inner 的引用。
  • 在执行 var ret = f() 后,

    • ret 变量被赋值为 inner 函数的引用
    • ret 现在指向函数 inner
  • 最后,执行 ret()

    • 此时由于 ret 指向 inner 函数,所以会执行 inner 函数,并打印当前作用域内的变量 city 的值,即输出 "ShangHai"。
  • 注意,这是因为 inner 函数在执行时访问的是其定义时所处的作用域,即函数 f 内部的作用域。在该作用域中,变量 city 的值为 "ShangHai"。

补充:arguments对象

在 JavaScript 中,arguments 是一个类数组对象(array-like object),它包含了函数调用时传递给函数的所有参数。下面是关于 arguments 关键字的详细解释:

1. 访问参数

通过 arguments 可以访问函数接收到的所有参数,即使在函数定义时没有明确指定参数名。

普通用法

function func4(a,b){
    if(arguments.length<2){
        console.log('传少了')
    }else if(arguments.length>2){
        console.log('传多了')
    }else{
        console.log('正常执行')
    } 
}

func4(1)
VM3378:3 传少了

func4(1,2,34,8)
VM3378:5 传多了

func4(3,2)
VM3378:7 正常执行

进阶用法

function sum() {
    let total = 0;
    for (let i = 0; i < arguments.length; i++) {
        total += arguments[i];
    }
    return total;
}

console.log(sum(1, 2, 3)); // 输出 6

2. 类数组对象

arguments 是一个类数组对象,它有一个 length 属性和按索引访问参数的能力,但它并不是一个真正的数组,因此不具备数组的方法(如 pushpop 等)。

3. 与剩余参数语法的区别

在 ES6 中引入了剩余参数(rest parameters)语法,它更灵活且易于使用,与 arguments 相比具有以下优势:

  • 剩余参数语法创建的是一个真正的数组,可以直接使用数组的方法。
  • 剩余参数只包含函数定义时未命名的参数,而 arguments 包含所有传递给函数的参数。

4. 限制

  • arguments 对象是一个类数组对象,不是真正的数组,因此不能直接调用数组的方法。
  • 在严格模式下,无法对 arguments 对象进行赋值,即使对 arguments 的修改也不会影响到对应的函数参数。

5. 使用建议

  • 在 ES6 中,推荐使用剩余参数语法来代替 arguments 对象,因为它更灵活、易读且具有更好的数组支持。
  • 如果需要访问所有传递给函数的参数,包括未命名的参数,可以继续使用 arguments 对象。

总的来说,arguments 对象提供了一种访问函数参数的方式,但随着 ES6 的引入,剩余参数语法通常被认为是更好的选择,因为它更灵活、易用且更符合现代 JavaScript 的发展趋势。

标签:function,city,return,函数,作用域,前端,JavaScript,var
From: https://www.cnblogs.com/xiao01/p/18087639

相关文章

  • C++ 纯虚函数
    纯虚函数优点防止派生类忘记实现虚函数,纯虚函数使得派生类必须实现基类的虚函数。在某些场景下,创建基类对象是不合理的,含有纯虚拟函数的类称为抽象类,它不能直接生成对象。声明方法:在基类中纯虚函数的方法的后面加=0。virtualvoidfuntion()=0;virtualstd::stringGetN......
  • JavaScript 实现通过 id 数组获取可展示的 name 拼接字符串
    JavaScript实现通过id数组获取可展示的name拼接字符串场景有一个包含许多对象的数组,每个对象都包含了一个标识(id)和一个名称(name)。想要从这个数组中选出特定的一些对象,这些对象的标识(id)在另一个数组中已经给出。然后,想把这些选出来的对象的名称(name)连接成一个字符串,用逗号分......
  • 测试面经 之 如何判断 Bug 是前端问题还是后端问题
    常见软件测试问题,如何判断一个Bug是Bug,并且如何判断该bug是前端问题还是后端问题。1.记录Bug:首先,作为一个专业的测试,无论该bug怎样,是否太浅显,都需要及时记录该问题。将该问题的标题,问题描述,复现步骤,环境版本,测试环境,测试账号,服务器端日志,报错截图,接口入参和......
  • SQL Server分页的内置函数
    SQLServer提供了几个内置函数来实现数据分页,以下是其中常用的函数:ROW_NUMBER():ROW_NUMBER()函数可以为结果集中的每一行添加一个序号,按照指定的排序方式进行排序。结合ROW_NUMBER()函数和WHERE子句,可以实现简单的分页效果。示例:SELECT*FROM(SELECT*,ROW_NUMBER()OV......
  • python 函数(解包、互相调用、作用域、函数的封装、内置函数:eval()、zip()、open())
    函数解包"""1、函数的注释:参数和返回值在注释里可以自动添加显示,只需手动加说明。2、函数的解包【拆包】:函数的参数要传递数据有多个值的时候,中间步骤拿到数据保存在元组或者列表或者字典里。-传递参数的时候加一个*或者**解包-一次拿到元组列表字典的......
  • [踩坑回顾]前端项目打包编译之后,页面访问异常:exports is not defined.
    周一遇到的问题,是qiankun框架的一个vue2子项目,使用yudao开源框架二次开发。 排查:1.开发环境正常。2.项目迁移前使用的babel、package配置均未被使用过。3.考虑打包使用的命令错误。发现未按照官方教程使用npm进行编译,后端同事用pom.xml文件使用pnpm命令进行编译。 解......
  • C++ 合成默认构造函数
    问题:C++面向对象编程时,如果我们没有声明任何构造函数constructor,按照以前最初学习,说编译器会自动合成一个默认的无参构造函数defaultconstructor,但是事实确实是这样吗,存不存在例外呢,即使有合成构造函数,那么它又将对类数据进行怎样的初始化呢?1.问题一如果我们没有声明任何构造......
  • 复变函数,启动!计应数番外编~对冒险主义的姚发起华丽叛逆的说!!!
    \[\newcommand{\Arg}{\operatorname{Arg}}\newcommand{\i}{\mathbfi}\newcommand{\d}{\mathrmd}\newcommand{\p}{\part}\newcommand{\Ln}{\operatorname{Ln}}\newcommand{\Arccos}{\operatorname{Arccos}}\newcommand{\Arcsin}{\operatorname{A......
  • 前端学习-vue学习009-侦听器
    官方教程链接侦听器:import{ref,watch}from'vue'constcount=ref(0)watch(count,(newCount)=>{console.log(`newcountis:${newCount}`)})pre标签:识别json对象中的\n\t等转义字符,展示原始的JSON对象结构。v-ifv-elseasyncawaitfetch<template>......
  • gin框架函数语法
    引擎类:gin.Default()带中间件路由引擎gin.New()不带中间件路由引擎路由类:r.GET()查询接口r.POST()创建接口r.PUT()更新接口r.DELETE()删除接口r.Run()运行端口r.Static()模糊路径配置静态文件r.StaticFS()API路径配置静态文件r.StaticFile()精确路径配置静态文件r.L......