JavaScript函数

JavaScript 函数定义

JavaScript 使用关键字 function 定义函数。

函数可以通过声明定义,也可以是一个表达式。

函数声明

在之前的教程中,你已经了解了函数声明的语法 :

function functionName(parameters) {执行的代码
}

函数声明后不会立即执行,会在我们需要的时候调用到。

function myFunction(a, b) {return a * b;
}

分号是用来分隔可执行JavaScript语句。
由于函数声明不是一个可执行语句,所以不以分号结束。

函数表达式

JavaScript 函数可以通过一个表达式定义。

函数表达式可以存储在变量中:

var x = function (a, b) {return a * b};

在函数表达式存储在变量后,变量也可作为一个函数使用:

var x = function (a, b) {return a * b};
var z = x(4, 3);

以上函数实际上是一个 匿名函数 (函数没有名称)。

函数存储在变量中,不需要函数名称,通常通过变量名来调用。

上述函数以分号结尾,因为它是一个执行语句。

Function() 构造函数

在以上实例中,我们了解到函数通过关键字 function 定义。

函数同样可以通过内置的 JavaScript 函数构造器(Function())定义。

var myFunction = new Function("a", "b", "return a * b");var x = myFunction(4, 3);

实际上,你不必使用构造函数。上面实例可以写成:

var myFunction = function (a, b) {return a * b};var x = myFunction(4, 3);

在 JavaScript 中,很多时候,你需要避免使用 new 关键字。

函数提升(Hoisting)

在之前的教程中我们已经了解了 “hoisting(提升)”。

提升(Hoisting)是 JavaScript 默认将当前作用域提升到前面去的行为。

提升(Hoisting)应用在变量的声明与函数的声明。

因此,函数可以在声明之前调用:

myFunction(5);function myFunction(y) {return y * y;
}

使用表达式定义函数时无法提升。

自调用函数

函数表达式可以 “自调用”。

自调用表达式会自动调用。

如果表达式后面紧跟 () ,则会自动调用。

不能自调用声明的函数。

通过添加括号,来说明它是一个函数表达式:

(function () {var x = "Hello!!";      // 我将调用自己
})();

以上函数实际上是一个 匿名自我调用的函数 (没有函数名)。

函数可作为一个值使用

JavaScript 函数作为一个值使用:

function myFunction(a, b) {return a * b;
}var x = myFunction(4, 3);

JavaScript 函数可作为表达式使用:

function myFunction(a, b) {return a * b;
}var x = myFunction(4, 3) * 2;

函数是对象

在 JavaScript 中使用 typeof 操作符判断函数类型将返回 “function” 。

但是JavaScript 函数描述为一个对象更加准确。

JavaScript 函数有 属性 和 方法。

arguments.length 属性返回函数调用过程接收到的参数个数:

function myFunction(a, b) {return arguments.length;
}

toString() 方法将函数作为一个字符串返回:

function myFunction(a, b) {return a * b;
}var txt = myFunction.toString();

箭头函数

ES6 新增了箭头函数。

箭头函数表达式的语法比普通函数表达式更简洁。

(参数1, 参数2, …, 参数N) => { 函数声明 }

(参数1, 参数2, …, 参数N) => 表达式(单一)
// 相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }

当只有一个参数时,圆括号是可选的:

(单一参数) => {函数声明}
单一参数 => {函数声明}

没有参数的函数应该写成一对圆括号:

() => {函数声明}

// ES5
var x = function(x, y) {return x * y;
}// ES6
const x = (x, y) => x * y;

有的箭头函数都没有自己的 this。 不适合定义一个 对象的方法。

当我们使用箭头函数的时候,箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的。

箭头函数是不能提升的,所以需要在使用之前定义。

使用 const 比使用 var 更安全,因为函数表达式始终是一个常量。

如果函数部分只是一个语句,则可以省略 return 关键字和大括号 {},这样做是一个比较好的习惯:

const x = (x, y) => { return x * y };

注意:IE11 及更早 IE 版本不支持箭头函数。

JavaScript 函数参数

JavaScript 函数对参数的值没有进行任何的检查。

函数显式参数(Parameters)与隐式参数(Arguments)

在先前的教程中,我们已经学习了函数的显式参数:

functionName(parameter1, parameter2, parameter3) {// 要执行的代码……
}

函数显式参数在函数定义时列出。

函数隐式参数在函数调用时传递给函数真正的值。

参数规则

JavaScript 函数定义显式参数时没有指定数据类型。

JavaScript 函数对隐式参数没有进行类型检测。

JavaScript 函数对隐式参数的个数没有进行检测。

默认参数

ES5 中如果函数在调用时未提供隐式参数,参数会默认设置为: undefined

有时这是可以接受的,但是建议最好为参数设置一个默认值:

function myFunction(x, y) {if (y === undefined) {y = 0;} 
}

或者,更简单的方式:

function myFunction(x, y) {y = y || 0;
}

如果 y 已经定义,y || 0 返回 y,因为 y 是 true,否则返回 0,因为 undefined 为 false。

如果函数调用时设置了过多的参数,参数将无法被引用,因为无法找到对应的参数名。 只能使用 arguments 对象来调用。

ES6 函数可以自带参数

ES6 支持函数带有默认参数,就判断 undefined 和 || 的操作:

function myFunction(x, y = 10) {// y is 10 if not passed or undefinedreturn x + y;
}myFunction(0, 2) // 输出 2
myFunction(5); // 输出 15, y 参数的默认值

arguments 对象

JavaScript 函数有个内置的对象 arguments 对象。

arguments 对象包含了函数调用的参数数组。

通过这种方式你可以很方便的找到最大的一个参数的值:

x = findMax(1, 123, 500, 115, 44, 88);function findMax() {var i, max = arguments[0];if(arguments.length < 2) return max;for (i = 0; i < arguments.length; i++) {if (arguments[i] > max) {max = arguments[i];}}return max;
}

或者创建一个函数用来统计所有数值的和:

x = sumAll(1, 123, 500, 115, 44, 88);function sumAll() {var i, sum = 0;for (i = 0; i < arguments.length; i++) {sum += arguments[i];}return sum;
}

通过值传递参数

在函数中调用的参数是函数的隐式参数。

JavaScript 隐式参数通过值来传递:函数仅仅只是获取值。

如果函数修改参数的值,不会修改显式参数的初始值(在函数外定义)。

隐式参数的改变在函数外是不可见的。

通过对象传递参数

在JavaScript中,可以引用对象的值。

因此我们在函数内部修改对象的属性就会修改其初始的值。

修改对象属性可作用于函数外部(全局变量)。

修改对象属性在函数外是可见的。

JavaScript 函数调用

JavaScript 函数有 4 种调用方式。

每种方式的不同在于 this 的初始化。

this 关键字

一般而言,在Javascript中,this指向函数执行时的当前对象。

注意 this 是保留关键字,你不能修改 this 的值。

调用 JavaScript 函数

在之前的章节中我们已经学会了如何创建函数。

函数中的代码在函数被调用后执行。

作为一个函数调用

function myFunction(a, b) {return a * b;
}
myFunction(10, 2);           // myFunction(10, 2) 返回 20

以上函数不属于任何对象。但是在 JavaScript 中它始终是默认的全局对象。

在 HTML 中默认的全局对象是 HTML 页面本身,所以函数是属于 HTML 页面。

在浏览器中的页面对象是浏览器窗口(window 对象)。以上函数会自动变为 window 对象的函数。

myFunction() 和 window.myFunction() 是一样的:

function myFunction(a, b) {return a * b;
}
window.myFunction(10, 2);    // window.myFunction(10, 2) 返回 20

这是调用 JavaScript 函数常用的方法, 但不是良好的编程习惯
全局变量,方法或函数容易造成命名冲突的bug。

全局对象

当函数没有被自身的对象调用时 this 的值就会变成全局对象。

在 web 浏览器中全局对象是浏览器窗口(window 对象)。

该实例返回 this 的值是 window 对象:

function myFunction() {return this;
}
myFunction();                // 返回 window 对象

函数作为全局对象调用,会使 this 的值成为全局对象。
使用 window 对象作为一个变量容易造成程序崩溃。

函数作为方法调用

在 JavaScript 中你可以将函数定义为对象的方法。

以下实例创建了一个对象 (myObject), 对象有两个属性 (firstName 和 lastName), 及一个方法 (fullName):

var myObject = {firstName:"John",lastName: "Doe",fullName: function () {return this.firstName + " " + this.lastName;}
}
myObject.fullName();         // 返回 "John Doe"

fullName 方法是一个函数。函数属于对象。 myObject 是函数的所有者。

this对象,拥有 JavaScript 代码。实例中 this 的值为 myObject 对象。

测试以下!修改 fullName 方法并返回 this 值:

var myObject = {firstName:"John",lastName: "Doe",fullName: function () {return this;}
}
myObject.fullName();          // 返回 [object Object] (所有者对象)

函数作为对象方法调用,会使得 this 的值成为对象本身。
使用构造函数调用函数

如果函数调用前使用了 new 关键字, 则是调用了构造函数。

这看起来就像创建了新的函数,但实际上 JavaScript 函数是重新创建的对象:

// 构造函数:
function myFunction(arg1, arg2) {this.firstName = arg1;this.lastName  = arg2;
}// This    creates a new object
var x = new myFunction("John","Doe");
x.firstName;                             // 返回 "John"

构造函数的调用会创建一个新的对象。新对象会继承构造函数的属性和方法。

构造函数中 this 关键字没有任何的值。
this 的值在函数调用实例化对象(new object)时创建。

作为函数方法调用函数

在 JavaScript 中, 函数是对象。JavaScript 函数有它的属性和方法。

call() 和 apply() 是预定义的函数方法。 两个方法可用于调用函数,两个方法的第一个参数必须是对象本身。

function myFunction(a, b) {return a * b;
}
myObject = myFunction.call(myObject, 10, 2);     // 返回 20
function myFunction(a, b) {return a * b;
}
myArray = [10, 2];
myObject = myFunction.apply(myObject, myArray);  // 返回 20

两个方法都使用了对象本身作为第一个参数。 两者的区别在于第二个参数: apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。

在 JavaScript 严格模式(strict mode)下, 在调用函数时第一个参数会成为 this 的值, 即使该参数不是一个对象。

在 JavaScript 非严格模式(non-strict mode)下, 如果第一个参数的值是 null 或 undefined, 它将使用全局对象替代。

通过 call() 或 apply() 方法你可以设置 this 的值, 且作为已存在对象的新方法调用。

JavaScript 闭包

JavaScript 变量可以是局部变量或全局变量。

私有变量可以用到闭包。

全局变量

函数可以访问由函数内部定义的变量,如:

function myFunction() {var a = 4;return a * a;
}

函数也可以访问函数外部定义的变量,如:

var a = 4;
function myFunction() {return a * a;
}

后面一个实例中, a 是一个 全局 变量。

在web页面中全局变量属于 window 对象。

全局变量可应用于页面上的所有脚本。

在第一个实例中, a 是一个 局部 变量。

局部变量只能用于定义它函数内部。对于其他的函数或脚本代码是不可用的。

全局和局部变量即便名称相同,它们也是两个不同的变量。修改其中一个,不会影响另一个的值。

变量声明时如果不使用 var 关键字,那么它就是一个全局变量,即便它在函数内定义。

变量生命周期

全局变量的作用域是全局性的,即在整个JavaScript程序中,全局变量处处都在。

而在函数内部声明的变量,只在函数内部起作用。这些变量是局部变量,作用域是局部性的;函数的参数也是局部性的,只在函数内部起作用。

计数器困境

设想下如果你想统计一些数值,且该计数器在所有函数中都是可用的。

你可以使用全局变量,函数设置计数器递增:

var counter = 0;function add() {return counter += 1;
}add();
add();
add();// 计数器现在为 3

计数器数值在执行 add() 函数时发生变化。

但问题来了,页面上的任何脚本都能改变计数器,即便没有调用 add() 函数。

如果我在函数内声明计数器,如果没有调用函数将无法修改计数器的值:

function add() {var counter = 0;return counter += 1;
}add();
add();
add();// 本意是想输出 3, 但事与愿违,输出的都是 1 !

以上代码将无法正确输出,每次我调用 add() 函数,计数器都会设置为 1。

JavaScript 内嵌函数可以解决该问题。

JavaScript 内嵌函数

所有函数都能访问全局变量。

实际上,在 JavaScript 中,所有函数都能访问它们上一层的作用域。

JavaScript 支持嵌套函数。嵌套函数可以访问上一层的函数变量。

该实例中,内嵌函数 plus() 可以访问父函数的 counter 变量:

function add() {var counter = 0;function plus() {counter += 1;}plus();    return counter; 
}

如果我们能在外部访问 plus() 函数,这样就能解决计数器的困境。

我们同样需要确保 counter = 0 只执行一次。

我们需要闭包。

JavaScript 闭包

还记得函数自我调用吗?该函数会做什么?

var add = (function () {var counter = 0;return function () {return counter += 1;}
})();add();
add();
add();// 计数器为 3

实例解析

变量 add 指定了函数自我调用的返回字值。

自我调用函数只执行一次。设置计数器为 0。并返回函数表达式。

add变量可以作为一个函数使用。非常棒的部分是它可以访问函数上一层作用域的计数器。

这个叫作 JavaScript 闭包。它使得函数拥有私有变量变成可能。

计数器受匿名函数的作用域保护,只能通过 add 方法修改。

闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。
直观的说就是形成一个不销毁的栈环境。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/33699.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uni-app实现图片上传功能

效果 代码 <uni-forms-item name"ViolationImg" label"三违照片 :"><uni-file-picker ref"image" limit"1" title"" fileMediatype"image" :listStyles"listStyles" :value"filePathsL…

测试开发环境安装

安装python运行环境 下载地址&#xff1a;链接 http://python.p2hp.com/downloads/windows/index.html 选择合适自己的版&#xff0c;我下载的3.8.10的进行安装 安装码编辑器Pychrom 下载地址&#xff1a;链接 https://www.jetbrains.com/pycharm/ 拉到最下面可以下载社区版…

企业举办活动邀请媒体的意义和重要性

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 企业举办活动并邀请媒体的意义和重要性是多方面的&#xff0c;主要有以下一些&#xff1a; 1. 品牌曝光与宣传&#xff1a;邀请媒体参与企业活动可以提高企业的品牌曝光度。媒体报道能够…

kubernetes的存储卷使用

目录 一、为什么使用存储卷 二、emptyDir存储卷 1.概念 2.创建Pod emptyDir 3. 验证emptyDir存储卷 三、hostPath存储卷 1.概念 2.创建Pod hostPath 3.验证hostPath存储卷 三、nfs共享存储卷 1.概念 2.安装nfs&#xff0c;配置nfs服务 3.创建Pod 4.验证nfs存储卷 一、…

论文笔记--Llama 2: Open Foundation and Fine-Tuned Chat Models

论文笔记--Llama 2: Open Foundation and Fine-Tuned Chat Models 1. 文章简介2. 文章概括3 文章重点技术3.1 预训练Pretraining3.1.1 预训练细节3.1.2 Llama2模型评估 3.2 微调Fine-tuning3.2.1 Supervised Fine-Tuning(FT)3.2.2 Reinforcement Learning with Human Feedback(…

(二)结构型模式:2、桥接模式(Bridge Pattern)(C++实现示例)

目录 1、桥接模式&#xff08;Bridge Pattern&#xff09;含义 2、桥接模式应用场景 3、桥接模式的UML图学习 4、C实现桥接模式的示例 1、桥接模式&#xff08;Bridge Pattern&#xff09;含义 桥接模式是一种结构型设计模式&#xff0c;它将抽象部分与实现部分分离&#…

构建Docker容器监控系统(Cadvisor +Prometheus+Grafana)

Cadvisor PrometheusGrafana 1.1、Cadvisor产品简介 Cadvisor是Google开源的一款用于展示和分析容器运行状态的可视化工具。通过在主机上运行Cadvisor用户可以轻松的获取到当前主机上容器的运行统计信息&#xff0c;并以图表的形式向用户展示。 1.2、安装docker-ce [rootloc…

arcgis栅格数据之最佳路径分析

1、打开arcmap&#xff0c;加载数据&#xff0c;需要对影像进行监督分类&#xff0c;如下&#xff1a; 这里任选一种监督分类的方法&#xff08;最大似然法&#xff09;&#xff0c;如下&#xff1a; 这里会先生成一个.ecd文件&#xff0c;然后再利用.ecd文件对影像进行分类。如…

rust关于项目结构包,Crate和mod和目录的组织

rust 最近开始学习rust语言。感觉这门语言相对java确实是难上很多。开几个文章把遇到的问题记录一下 rust关于包&#xff0c;Crate 关于包&#xff0c;Crate这块先看看官方书籍怎么说的 crate 是 Rust 在编译时最小的代码单位。如果你用 rustc 而不是 cargo 来编译一个文件…

C++使用mutex保护临界区时总是某一个线程在多次执行后才轮到下一个线程

记录一个有意思的现象&#xff0c; 假设我们启动N个线程&#xff0c;同时执行一个线程函数&#xff0c;线程函数先是进入循环进行抢占锁&#xff0c;然后睡眠一段时间模拟处理业务的过程&#xff0c;然后释放锁&#xff0c;然后进行下一次循环&#xff0c;代码如下&#xff1a…

阿里云服务器安装部署Docker使用教程

本文阿里云百科分享如何在云服务ECS实例上&#xff0c;部署并使用Docker。Docker是一款开源的应用容器引擎&#xff0c;具有可移植性、可扩展性、高安全性和可管理性等优势。开发者可将应用程序和依赖项打包到一个可移植的容器中&#xff0c;快速发布到Linux机器上并实现虚拟化…

selenium常见等待机制及其特点和使用方法

目录 1、强制等待 2、隐式等待 3、显示等待 1、强制等待 强制等待是在程序中直接调用Thread.sleep(timeout) ,来完成的&#xff0c;该用法的优点是使用起来方便&#xff0c;语法也比较简单&#xff0c;缺点就是需要强制等待固定的时间&#xff0c;可能会造成测试的时间过…

Nim游戏:取石头

&#xff08;一&#xff09;一堆取石头 背景&#xff1a; 在博弈论中&#xff0c;有一种称为Nim游戏的经典问题&#xff0c;它涉及到取石子的问题&#xff0c;其中有许多变种。Nim游戏是一种零和博弈&#xff0c;即两名玩家交替行动&#xff0c;每次只能从一堆物品中取走一定数…

【数据结构】链表(一)

链表&#xff08;一&#xff09; 文章目录 链表&#xff08;一&#xff09;01 引入02 概念及结构03 单向不带头不循环链表实现3.1 创建节点类型3.2 简易创建一个链表3.3 遍历链表每个节点3.4 获取链表长度3.5 查找是否包含关键字key是否在单链表当中3.6 头插法3.7 尾插法3.8 任…

JVM之内存模型

1. Java内存模型 很多人将Java 内存结构与java 内存模型傻傻分不清&#xff0c;java 内存模型是 Java Memory Model&#xff08;JMM&#xff09;的意思。 简单的说&#xff0c;JMM 定义了一套在多线程读写共享数据时&#xff08;成员变量、数组&#xff09;时&#xff0c;对数据…

2023-08-08 Ubuntu 挂载U盘 fdisk -l 、sudo mount /dev/sdb1 /mnt/mydisk

一、基本命令 1、插入U盘&#xff0c;查看U盘是否被系统识别&#xff1a; 打开终端&#xff0c;输入&#xff1a; sudo fdisk -l 查看系统是否识别U盘&#xff0c;如果识别&#xff0c;会显示U盘的相关信息&#xff0c;如果没有识别&#xff0c;则说明系统没有识别U盘。 2…

ESP 32 蓝牙虚拟键盘链接笔记本电脑的键值问题

由于打算利用esp32 通过蓝牙链接电脑后实现一些特俗的键盘功能&#xff0c;所以就折腾了一下&#xff0c;折腾最耗费时间的却是键值问题&#xff0c;让一个20多年的老司机重新补充了知识 过程曲折就不说了&#xff0c;直接说结果。 我们通过网络搜索获取的键值和蓝牙模拟键盘传…

Qt5.9.4搭建安卓环境-Qt for Android

目录 需要安装以下内容&#xff1a;安装JDK设置环境变量安装剩余文件 使用新建文件 可能出现的问题第一种解决方法&#xff1a; 第二种解决方法 需要安装以下内容&#xff1a; 下载地址&#xff1a; https://www.qter.org/portal.php?modview&aid10 很多Qt开发会用到的环…

2.0 Maven基础

1. Maven概述 Maven概念 Apache Maven是一个软件项目管理工具&#xff0c;将项目开发和管理过程抽象程一个项目对象模型&#xff08;POM&#xff0c;Project Object Model&#xff09;。 Maven作用 项目构建 提供标准的、跨平台的自动化项目构建方式。 依赖管理 方便快捷…

LeetCode 1572. 矩阵对角线元素的和

【LetMeFly】1572.矩阵对角线元素的和 力扣题目链接&#xff1a;https://leetcode.cn/problems/matrix-diagonal-sum/ 给你一个正方形矩阵 mat&#xff0c;请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1&…