第7集丨JavaScript 中函数——概述

news2024/12/23 13:33:21

目录

  • 一、函数概览
  • 二、函数定义
    • 2.1 函数声明 (函数语句)
    • 2.2 函数表达式 (function expression)
    • 2.3 匿名函数立即执行
    • 2.4 函数生成器声明 (function* 语句)
    • 2.5 函数生成器表达式 (function*表达式)
    • 2.6 箭头函数表达式 (=>)
    • 2.7 Function构造函数
    • 2.8 生成器函数的构造函数
  • 三、函数参数
    • 3.1 默认参数
    • 3.2 剩余参数
  • 四、arguments对象
  • 五、方法函数定义
    • 5.1 Getter 和 setter 函数
    • 5.2 方法定义语法
  • 六、构造函数 vs 函数声明 vs 函数表达式
    • 6.1 对比案例
    • 6.2 差别
      • 6.2.1 函数名和函数的变量
      • 6.2.2 new Function 定义的函数
      • 6.2.3 函数声明的优势
      • 6.2.4 三者对比
    • 6.3 举例
  • 七、块级函数
    • 7.1 非严格模式下的块级函数
  • 八、示例
    • 8.1 返回格式化数字
    • 8.2 检测函数是否存在

一般来说,一个函数是可以通过外部代码调用的一个“子程序”(或在递归的情况下由内部函数调用)。像程序本身一样,一个函数由称为函数体的一系列语句组成。值可以传递给一个函数,函数将返回一个值。在 JavaScript 中,函数是头等 ( first-class)对象,因为它们可以像任何其他对象一样具有属性和方法。它们与其他对象的区别在于函数可以被调用。简而言之,它们是 Function对象。

一、函数概览

JavaScript 中,每个函数其实都是一个Function对象。

  • 如果一个函数中没有使用 return 语句,则它默认返回undefined
  • 要想返回一个特定的值,则函数必须使用 return 语句来指定一个要返回的值。(使用new关键字调用一个构造函数除外)。

调用函数时,传递给函数的值被称为函数的实参(值传递),对应位置的函数参数名叫作形参

  • 如果实参是一个包含原始值 (数字,字符串,布尔值) 的变量,则就算函数在内部改变了对应形参的值,返回后,该实参变量的值也不会改变。
  • 如果实参是一个对象引用,则对应形参会和该实参指向同一个对象。假如函数在内部改变了对应形参的值,返回后,实参指向的对象的值也会改变。

this关键字

  • 在函数执行时,this 关键字并不会指向正在运行的函数本身,而是指向调用该函数的对象。
  • 所以,如果你想在函数内部获取函数自身的引用,只能使用函数名或者使用arguments.callee属性 (严格模式下不可用),如果该函数是一个匿名函数,则你只能使用后者。
 /* 定义函数 myFunc */
 function myFunc(theObject)
 {
   //实参 mycar 和形参 theObject 指向同一个对象。
   theObject.brand = "Toyota";
 }

 /*
  * 定义变量 mycar;
  * 创建并初始化一个对象;
  * 将对象的引用赋值给变量 mycar
  */
 var mycar = {
   brand: "Honda",
   model: "Accord",
   year: 1998
 };

 /* 弹出 'Honda' */
 window.alert(mycar.brand);

 /* 将对象引用传给函数 */
 myFunc(mycar);

 /*
  * 弹出 'Toyota',对象的属性已被修改。
  */
 console.log(mycar.brand);

二、函数定义

定义函数有多种方法:

2.1 函数声明 (函数语句)

语法:function name([param[, param[, ... param]]]) { statements }

 test();	//正常执行,弹出1
 function test() {
     alert(1)
 }

2.2 函数表达式 (function expression)

函数表达式和函数声明非常相似,它们甚至有相同的语法。一个函数表达式可能是一个更大的表达式的一部分。可以定义函数“名字”(例如可以在调用堆栈时使用)或者使用“匿名”函数。函数表达式不会提升,所以不能在定义之前调用

语法:
var myFunction = function name([param[, param[, ... param]]]) { statements }

test2()	//不能正常执行,报错
var test2 = function() {
    alert(2)
}

在这里插入图片描述

2.3 匿名函数立即执行

当函数只使用一次时,通常使用IIFE (Immediately Invokable Function Expressions)

(function() {
    statements
})();

IIFE是在函数声明后立即调用的函数表达式。

2.4 函数生成器声明 (function* 语句)

函数声明有一种特殊的语法:
function* name([param[, param[, ...param]]]) { statements }

function* 这种声明方式 (function 关键字后跟一个星号)会定义一个生成器函数 (generator function),它返回一个 Generator 对象。

function* generator(i) {
  yield i;
  yield i + 10;
}

const gen = generator(10);

console.log(gen.next().value);
// Expected output: 10

console.log(gen.next().value);
// Expected output: 20

生成器函数在执行时能暂停,后面又能从暂停处继续执行

  • 调用一个生成器函数并不会马上执行它里面的语句,而是返回一个这个生成器的 迭代器 (iterator)对象。
  • 当这个迭代器的 next() 方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现yield的位置为止, yield 后紧跟迭代器要返回的值。
  • 或者如果用的是 yield*(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)。
  • next()方法返回一个对象,这个对象包含两个属性:valuedonevalue 属性表示本次 yield 表达式的返回值,done 属性为布尔类型,表示生成器后续是否还有 yield 语句,即生成器函数是否已经执行完毕并返回。
  • yield 关键字使生成器函数执行暂停,yield 关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器的版本的 return 关键字。

2.5 函数生成器表达式 (function*表达式)

语法function* [name]([param] [, param] [..., param]) { statements }

  • name:函数名。在声明匿名函数时可以省略。函数名称只是函数体中的一个本地变量。

  • paramN:传入函数的一个参数名。一个函数最多有 255 个参数。

  • statements:函数体。

function*表达式和function* 声明比较相似,并具有几乎相同的语法。function*表达式和function*声明之间主要区别就是函数名,即在创建匿名函数时,function*表达式可以省略函数名。

var x = function*(y) {
   yield y * y;
};

2.6 箭头函数表达式 (=>)

语法([param] [, param]) => { statements } param => expression

  • param:参数名称。零参数需要用 () 表示。只有一个参数时不需要括号。(例如 foo => 1)

  • statements or expression:多个声明 statements 需要用大括号括起来,而单个表达式时则不需要。表达式 expression 也是该函数的隐式返回值。

2.7 Function构造函数

不推荐使用 Function 构造函数创建函数,因为它需要的函数体作为字符串可能会阻止一些 JS 引擎优化,也会引起其他问题。

语法new Function (arg1, arg2, ... argN, functionBody)

  • arg1, arg2, … argN
    函数使用零个或多个名称作为正式的参数名称。每一个必须是一个符合有效的 JavaScript 标识符规则的字符串或用逗号分隔的字符串列表,例如“x”,“theValue”或“a,b”。

  • functionBody
    一个构成的函数定义的,包含 JavaScript 声明语句的字符串。

Function 的构造函数当作函数一样调用 (不使用 new 操作符) 的效果与作为 Function 的构造函数调用一样。

2.8 生成器函数的构造函数

JavaScript 中,生成器函数实际上都是 GeneratorFunction 对象。注意,GeneratorFunction 并不是一个全局对象,但你可以通过下面的代码创建 GeneratorFunction() 构造函数。

const GeneratorFunction = function* () {}.constructor;
  • 不推荐使用构造器函数的构造函数 (GeneratorFunction constructor) 创建函数,因为它需要的函数体作为字符串可能会阻止一些 JS 引擎优化,也会引起其他问题。

语法new GeneratorFunction (arg1, arg2, ... argN, functionBody)

  • arg1, arg2, … argN
    函数使用零个或多个名称作为正式的参数名称。每一个必须是一个符合有效的 JavaScript 标识符规则的字符串或用逗号分隔的字符串列表,例如“x”,“theValue”或“a,b”。

  • functionBody
    一个构成的函数定义的,包含 JavaScript 声明语句的字符串。

三、函数参数

3.1 默认参数

如果没有值或传入了未定义的值,默认函数参数允许形式参数使用默认值初始化。

function multiply(a, b = 1) {
  return a * b;
}

console.log(multiply(5, 2));
// Expected output: 10

console.log(multiply(5));
// Expected output: 5

3.2 剩余参数

剩余参数语法允许将数量不限的参数描述成一个数组。

function sum(...theArgs) {
  let total = 0;
  for (const arg of theArgs) {
    total += arg;
  }
  return total;
}

console.log(sum(1, 2, 3));
// Expected output: 6

console.log(sum(1, 2, 3, 4));
// Expected output: 10

四、arguments对象

arguments 对象是所有(非箭头)函数中都可用的局部变量(一个包含了传递给当前执行函数参数的类似于数组的对象)。你可以使用arguments 对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,第一个参数在索引 0 处。

  • arguments.callee 已弃用: 当前正在执行的函数。
  • arguments.caller已弃用 : 调用当前执行函数的函数。
  • arguments.length: 传给函数的参数的数目。

五、方法函数定义

5.1 Getter 和 setter 函数

你可以在支持添加新属性的任何标准的内置对象或用户定义的对象内定义 getter(访问方法) 和 setter(设置方法)。使用对象字面量语法定义 getterssetters 方法。

  • get
    当查找某个对象属性时,该对象属性将会与被调用函数绑定。
const obj = {
  log: ['a', 'b', 'c'],
  get latest() {
    return this.log[this.log.length - 1];
  }
};

console.log(obj.latest);
// Expected output: "c"
  • set
    当试图设置该属性时,对象属性与被调用函数绑定。
const language = {
  set current(name) {
    this.log.push(name);
  },
  log: []
};

language.current = 'EN';
language.current = 'FA';

console.log(language.log);
// Expected output: Array ["EN", "FA"]

5.2 方法定义语法

ECMAScript 6 开始,你可以用更短的语法定义自己的方法,类似于 getterssetters

 var obj = {
   foo() {},
   bar() {}
 };

六、构造函数 vs 函数声明 vs 函数表达式

6.1 对比案例

对比下面的例子:

  • 一个用Function构造函数定义的函数,被赋值给变量 multiply
var multiply = new Function('x', 'y', 'return x * y');
  • 一个名为multiply的函数声明:
function multiply(x, y) {
   return x * y;
} // 没有分号
  • 一个匿名函数的函数表达式,被赋值给变量multiply
 var multiply = function(x, y) {
   return x * y;
 };
  • 一个命名为func_named的函数的函数表达式,被赋值给变量multiply:
var multiply = function func_name(x, y) {
   return x * y;
};

6.2 差别

虽然有一些细微的差别,但所起的作用都差不多:

6.2.1 函数名和函数的变量

  • 函数名和函数的变量存在着差别。函数名不能被改变,但函数的变量却能够被再分配。
  • 函数名只能在函数体内使用。倘若在函数体外使用函数名将会导致错误。(如果函数之前是通过一个 var 语句声明的则是 undefined)。例如:
var y = function x() {};
alert(x); // throws an error
  • 当函数是通过 Function’s toString() 方法被序列化时,函数名同样也会出现。

  • 另一方面,被函数赋值的变量仅仅受限于它的作用域,该作用域确保包含着该函数被声明时的作用域。正如第四个例子所展示的那样,函数名与被函数赋值的变量是不相同的。彼此之间没有关系。函数声明同时也创建了一个和函数名相同的变量。因此,与函数表达式定义不同,以函数声明定义的函数能够在它们被定义的作用域内通过函数名而被访问到:

6.2.2 new Function 定义的函数

  • 使用用 new Function 定义的函数没有函数名。 然而,在 SpiderMonkey JavaScript 引擎中,其函数的序列化形式表现的好像它拥有一个名叫"anonymous"的名称一样。比如,使用 alert(new Function()) 输出:
function anonymous() {
}
  • 而实际上其函数并没有名称,anonymous 不是一个可以在函数内被访问到的变量。例如,下面的例子将会导致错误:
 var foo = new Function("alert(anonymous);");
foo(); // Uncaught ReferenceError: anoymous is not defined

6.2.3 函数声明的优势

  • 和通过函数表达式定义或者通过 Function 构造函数定义的函数不同,函数声明定义的函数可以在它被声明之前使用。举个例子:
foo(); // alerts FOO!
function foo() {
   alert('FOO!');
}

6.2.4 三者对比

  • 函数表达式定义的函数继承了当前的作用域。换言之,函数构成了闭包。另一方面,Function 构造函数定义的函数不继承任何全局作用域以外的作用域 (那些所有函数都继承的)。

  • 通过函数表达式定义的函数和通过函数声明定义的函数只会被解析一次,而 Function 构造函数定义的函数却不同。也就是说,每次构造函数被调用,传递给 Function 构造函数的函数体字符串都要被解析一次。虽然函数表达式每次都创建了一个闭包,但函数体不会被重复解析,因此函数表达式仍然要快于"new Function(...)"。所以 Function 构造函数应尽可能地避免使用。

  • 有一点应该要注意的,在通过解析 Function 构造函数字符串产生的函数里,内嵌的函数表达式和函数声明不会被重复解析。例如:

var foo = (new Function("var bar = \'FOO!\';\nreturn(function() {\n\talert(bar);\n});"))();
foo(); // 函数体字符串"function() {\n\talert(bar);\n}"的这一部分不会被重复解析。

6.3 举例

// 函数声明
function foo() {}

// 函数表达式
(function bar() {})

// 函数表达式
x = function hello() {}

if (x) {
   // 函数表达式
   function world() {}
}

// 函数声明
function a() {
   // 函数声明
   function b() {}
   if (0) {
      //函数表达式
      function c() {}
   }
}

七、块级函数

ECMAScript 6 开始,在严格模式下,块里的函数作用域为这个块。ECMAScript 6 之前不建议块级函数在严格模式下使用。

'use strict';

function f() {
  return 1;
}

{
  function f() {
    return 2;
  }
}

f() === 1; // true

// f() === 2 in non-strict mode

7.1 非严格模式下的块级函数

一句话:不要用。

在非严格模式下,块中的函数声明表现奇怪。例如:

if (shouldDefineZero) {
   function zero() {     // DANGER: 兼容性风险
      console.log("This is zero.");
   }
}

ECMAScript 6 中,如果shouldDefineZerofalse,则永远不会定义 zero,因为这个块从不执行。然而,这是标准的新的一部分。这是历史遗留问题,无论这个块是否执行,一些浏览器会定义 zero

在严格模式 下,所有支持 ECMAScript 6 的浏览器以相同的方式处理:只有在 shouldDefineZerotrue 的情况下定义 zero,并且作用域只是这个块内。

有条件地定义一个函数的一个更安全的方法是把函数表达式赋给一个变量:

var zero;
if (0) {
   zero = function() {
      console.log("This is zero.");
   };
}

八、示例

8.1 返回格式化数字

下面的函数返回一个字符串,其中包含了一个格式化的、以一个由 0 开头并填充的数字。

// 这个函数返回一个由 0 开头并填充的字符串
function padZeros(num, totalLen) {
   var numStr = num.toString();             // 用字符串返回值进行初始化
   var numZeros = totalLen - numStr.length; // 计算 zeros 顺序
   for (var i = 1; i <= numZeros; i++) {
      numStr = "0" + numStr;
   }
   return numStr;
}

//执行
var result;
result = padZeros(42,4); // returns "0042"
result = padZeros(42,2); // returns "42"
result = padZeros(5,4);  // returns "0005"

8.2 检测函数是否存在

你可以通过 typeof 操作符检测一个函数是否存在。在下面的例子中,用一个测试来演示检测 window 对象是否拥有一个 noFunc 函数的属性。如果存在,那就使用它;否则就采取其他的一些操作。(注意在 if 语句中,使用了 noFunc 的引用 – 在函数名的后面没有括号“()”,所以实际函数并没有被调用。)

if ('function' === typeof window.noFunc) {
   // use noFunc()
 } else {
   // do something else
 }

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

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

相关文章

C#实现将小数值四舍五入为整数

一、需求说明 在项目的开发中&#xff0c;遇到一些除法计算内容会产生小数值&#xff0c;但是又需要根据项目的实际情况将这些小数内容化为整数&#xff0c;方便后续内容的实现。 二、需求分析 将小数内容转为整数【但是转为小数又分为几种情况】&#xff1a; ①将小数取为下…

银河麒麟服务器v10 sp1 部署 mysql 客户端工具 DBeaver

上一篇&#xff1a;银河麒麟服务器v10 sp1 安装mysql_csdn_aspnet的博客-CSDN博客 DBeaver 是数据库管理器的客户端&#xff0c;它允许以舒适的方式管理数据库实例的数据和选项。DBeaver 支持任何具有 JDBC 驱动程序的数据库 – MySQL/MariaDB、PostgreSQL、Oracle、DB2 LUW、…

性能测试工具 Loadrunner 和 Jmeter 测试结果大 PK

目录 前言&#xff1a; 测试一&#xff1a;1 个用户陆续执行登录操作&#xff0c;迭代 100 次&#xff0c;运行完就结束 测试二&#xff1a;50 个用户并发执行登录操作&#xff08;有集合点&#xff09; 前言&#xff1a; 性能测试工具LoadRunner和JMeter都是流行的工具&am…

ASP.net 配置CSP

ASP.net 开启全局CSP配置 <add name"Content-Security-Policy" value"default-src self; script-src self unsafe-inline unsafe-eval http://seal.digicert.com; style-src self unsafe-inline;" />在web.config中添加该行配置即可 配置完成后&am…

Im6ull 系统移植之 命令行

一 系统移植 系统移植 主要分四个不分由系统启动流程决定 U-Boot的移植 Linux内核的移植 根文件系统的构建系统烧写 1.1 交叉编译环境 交叉编译器有很多种&#xff0c;我们使用 Linaro 出品的交叉编译器&#xff0c; Linaro 是一间非营利性质的开 放源代码软件工程公…

LeetCode[11]盛水最多的容器

难度&#xff1a;Medium 题目&#xff1a; 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 …

enreka使用错误

错误 java.net.UnknownHostException: INVENTORYSERVICE 分析&解决&#xff1a; 这里的请求执行错误 但eureka可以找到服务 手动创建RestTemlate到容器中&#xff0c;未加LoadBalanced注解 加上注解后重试&#xff0c;成功

在linux服务器上的基础操作

1.查看该文件最后两行的记录 tail -n 2 book.txt2.查找文本中的内容 grep info book.txt3.查找内容的上下文本内容 $ grep -C 2 info book.txt

leetcode 701. 二叉搜索树中的插入操作

2023.7.12 比较简单&#xff0c;不多说直接看代码&#xff1a; 迭代法&#xff1a; class Solution { public:TreeNode* insertIntoBST(TreeNode* root, int val) {//树为空的情况if(rootnullptr){TreeNode* node new TreeNode(val);return node;}//需要一个pre节点指向父节…

Echart 数据更新了,X轴或者Y轴显示不变化的问题

应用场景&#xff1a;点击不同的图例&#xff0c;显示不同的X轴或者Y轴的图表 问题&#xff1a;数据打印更新了&#xff0c;Y轴仍然不变。 点第一个legend块&#xff0c;Y轴应该[名称1, 名称2, 名称3, 名称4, 名称5, 名称6, 名称7, 名称8], 点第二个legend块&#xff0c;Y轴应…

C#编码规范

一、 命名惯例和规范 注&#xff1a; Pascal&#xff1a;大小写形式&#xff0d;所有单词第一个字母大写&#xff0c;其他字母小写。 驼峰式&#xff1a;大小写形式&#xff0d;除了第一个单词&#xff0c;所有单词第一个字母大写&#xff0c;其他字母小写。 1&#xff1a;类…

Java 动态规划 Leetcode 213. 打家劫舍 II

代码展示&#xff1a; class Solution {public int rob(int[] nums) {int nnums.length;return Math.max(nums[0]childRob(nums,2,n-2),childRob(nums,1,n-1));}public int childRob(int[]nums,int left,int right){if(left>right){return 0;}int nnums.length;//创建数组i…

Unity基础 物理系统 铰链关节 Hinge Joint

铰链关节是一种模拟物理系统中的连接方式&#xff0c;它允许两个物体围绕一个共同的轴点旋转。这种连接方式可以用于模拟门、摆钟、机器人手臂等各种物理运动。 Connected Body&#xff08;连接体&#xff09;&#xff1a;这是铰链关节连接的另一个刚体。通过设置这个属性&…

谷歌自研芯片秘辛首次曝光;黑客要价 10 万美元出售雷蛇源代码等数据;百川智能发布 Baichuan-13B大模型(提供源码)

2023年7月12日科技新闻要点&#xff01;10秒概览&#xff01; 1.科大讯飞&#xff1a;下半年 all in 星火大模型 2.百川智能发布 Baichuan-13B&#xff08;源代码和安装及训练教程作为福利在文章最后告诉获取方式&#xff09; 3.谷歌自研芯片秘辛首次曝光 4.黑客要价 10 万…

第二十五章:InnoDB的数据存储结构

第二十五章&#xff1a;InnoDB的数据存储结构 25.1&#xff1a;数据库的存储结构&#xff1a;页 ​ 索引结构给我们提供了高效的索引方式&#xff0c;不过索引信息以及数据记录都是保存在文件上的&#xff0c;确切说是存储在页结构中。另一方面&#xff0c;索引是在存储引擎中…

N天爆肝数据库——MySQL(3)

本篇文章&#xff0c;主要对DCL、 函数、 约束和多表查询进行知识总结和学习。 期待和大家一起学习进步。DCL DCL-介绍 数据控制语言&#xff0c;用来管理数据库用户、控制数据库的访问权限。DCL-管理用户 查询用户 USE mysql; SELECT * FROM user; 创建用户 CREATE US…

Overleaf论文投稿

1.Overleaf 具体投稿 一步一步教&#xff01;&#xff01;&#xff01;以Springer Nature为例 2.如何在overleaf上寻找会议或期刊的模板 LaTex期刊模板下载与使用

Mocha Pro 2023 v10.0.2(win/mac)无套路安装教程

Mocha Pro 2023 Mocha Pro是一款世界知名的软件和插件&#xff0c;用于平面运动跟踪、旋转描记、对象移除、图像稳定和PowerMesh有机扭曲跟踪。 Mocha的工具对于视觉效果和后期制作至关重要&#xff0c;在最近每一部获得奥斯卡最佳视觉效果提名的电影中都发挥了重要作用。了解…

天翎MyApps低代码平台案例分享—阿米检测设备管理系统

项目背景&#xff1a;阿米检测技术有限公司&#xff08;以下简称为“阿米检测”&#xff09;隶属于中国航天科技集团&#xff0c;是北京航天计量测试技术研究所下属全资公司&#xff0c;2018年由国家财政部正式发文批准成立。司转化航天高端技术&#xff0c;开展测量方法应用、…

【dij变形】牛客练习赛93 C

C-点权_牛客练习赛93 (nowcoder.com) 题意&#xff1a; 思路&#xff1a; 重要的是在松弛的时候要满足什么条件才开始松弛 这里是用两个点来松弛一个点 Code&#xff1a; #include <bits/stdc.h>//#define int long longusing namespace std;const int mxn1e510; co…