重新的标题为:执行功能()

重新的标题为:执行功能()

函数是代码语句的容器,可以使用括号 () 运算符调用。调用时可以在括号内传递参数,以便函数调用时函数中的语句可以访问某些值。

在以下代码中,我们使用 new 运算符创建 addNumbers 函数 objectone 的两个版本,另一个版本使用更常见的文字模式。两者都需要两个参数。在每种情况下,我们都调用该函数,并在括号中传递参数 () 运算符。

示例:sample76.html

 <script>  	var addNumbersA = new Function('num1', 'num2', 'return num1 + num2');  	console.log(addNumbersA(2, 2)); // Logs 4.  	// Could also be written the literal way, which is much more common. 	var addNumbersB = function (num1, num2) { return num1 + num2; };  	console.log(addNumbersB(2, 2)); // Logs 4.  </script>

函数可用于返回值、构造对象或作为简单运行代码的机制。 JavaScript 对函数有多种用途,但就其最基本的形式而言,函数只是可执行语句的唯一范围。


函数() 参数

Function() 构造函数采用无限数量的参数,但 Function() 构造函数期望的最后一个参数是一个字符串,其中包含构成函数体的语句。在最后一个之前传递给构造函数的任何参数都可用于正在创建的函数。还可以以逗号分隔的字符串形式发送多个参数。

在下面的代码中,我将 Function() 构造函数的用法与实例化函数对象的更常见模式进行了对比。

示例:sample77.html

 <script>  	var addFunction = new Function('num1', 'num2', 'return num1 + num2');  	/* Alternately, a single comma-separated string with arguments can be  	the first parameter of the constructor, with the function body following. */ 	var timesFunction = new Function('num1,num2', 'return num1 * num2');  	console.log(addFunction(2, 2), timesFunction(2, 2)); // Logs '4 4'  	// Versus the more common patterns for instantiating a function: 	var addFunction = function (num1, num2) { return num1 + num2; }; // Expression form. 	function addFunction(num1, num2) { return num1 + num2; } // Statement form.  </script>

不建议或通常不直接利用 Function() 构造函数,因为 JavaScript 将使用 eval() 来解析包含函数逻辑的字符串。许多人认为 eval() 是不必要的开销。如果使用它,则代码设计中很可能存在缺陷。

使用 Function() 构造函数而不使用 new 关键字与仅使用构造函数创建函数对象具有相同的效果(new Function(‘x’,’return x’) 与 函数((‘x’,’返回x’))。

直接调用 Function() 构造函数时不会创建闭包。


Function() 属性和方法

函数对象具有以下属性(不包括继承的属性和方法):

属性(Function.prototype;):

  • 原型

函数对象实例属性和方法

函数对象实例具有以下属性和方法(不包括继承的属性和方法):

实例属性 (var myFunction = function(x, y, z) {}; myFunction.length;):

  • 参数
  • 构造函数
  • 长度

实例方法 (var myFunction = function(x, y, z) {}; myFunction.toString();):

  • apply()
  • call()
  • toString()

函数总是返回一个值

虽然可以创建一个函数来简单地执行代码语句,但函数返回一个值也很常见。在以下示例中,我们从 sayHi 函数返回一个字符串。

示例:sample78.html

 <script>  	var sayHi = function () { 		return 'Hi'; 	};  	console.log(sayHi()); // Logs "Hi".  </script>

如果函数没有指定返回值,则返回 undefined。在以下示例中,我们调用 yelp 函数,该函数将字符串“yelp”记录到控制台,而不显式返回值。

示例:sample79.html

 <script>  	var yelp = function () { 		console.log('I am yelping!'); 		// Functions return undefined even if we don't. 	}  	/* Logs true because a value is always returned, even if we don't specifically return one. */ 	console.log(yelp() === undefined);  </script>

这里要记住的概念是,即使您没有显式提供要返回的值,所有函数都会返回一个值。如果不指定返回值,则返回值为 undefined。


函数是一等公民(不仅仅是语法,还有值)

在 JavaScript 中,函数就是对象。这意味着函数可以存储在变量、数组或对象中。此外,函数可以传递给函数或从函数返回。函数具有属性,因为它是一个对象。所有这些因素使得函数成为 JavaScript 中的一等公民。

示例:sample80.html

 <script>  	// Functions can be stored in variables (funcA), arrays (funcB), and objects (funcC). 	var funcA = function () { }; // Called like so: funcA() 	var funcB = [function () { } ]; // Called like so: funcB[0]() 	var funcC = { method: function () { } }; // too.method() or funcC['method']()  	// Functions can be sent to and sent back from functions. 	var funcD = function (func) { 		return func 	};  	var runFuncPassedToFuncD = funcD(function () { console.log('Hi'); });  	runFuncPassedToFuncD();  	// Functions are objects, which means they can have properties. 	var funcE = function () { }; 	funcE.answer = 'yup'; // Instance property. 	console.log(funcE.answer); // Logs 'yup'.  </script>

认识到函数是一个对象,因此也是一个值,这一点至关重要。它可以像 JavaScript 中的任何其他表达式一样传递或增强。


将参数传递给函数

参数是在调用函数时将值传递到函数作用域的工具。在下面的示例中,我们调用 addFunction()。由于我们已预定义它采用两个参数,因此在其范围内可以使用两个附加值。

示例:sample81.html

 <script>  	var addFunction = function (number1, number2) { 		var sum = number1 + number2; 		return sum; 	}  	console.log(addFunction(3, 3)); // Logs 6.  </script>

与其他一些编程语言相比,在 JavaScript 中省略参数是完全合法的,即使函数已被定义为接受这些参数。缺少的参数仅被赋予值 undefined。当然,如果省略参数值,该函数可能无法正常工作。

如果向函数传递意外参数(创建函数时未定义的参数),则不会发生错误。并且可以从 arguments 对象访问这些参数,该对象可用于所有函数。


this 和 arguments 值可用于所有函数

在所有函数的范围和主体内,this 和 arguments 值可用。

arguments 对象是一个类似数组的对象,包含传递给函数的所有参数。在下面的代码中,即使我们在定义函数时放弃指定参数,我们也可以依赖传递给函数的 arguments 数组来访问在调用时发送的参数。

示例:sample82.html

 <script>  	var add = function () { 		return arguments[0] + arguments[1]; 	};  	console.log(add(4, 4)); // Returns 8.  </script>

this 关键字,传递给所有函数,是对包含该函数的对象的引用。正如您所期望的,对象中包含的作为属性(方法)的函数可以使用 this 来获取对父对象的引用。当函数定义在全局作用域时,this 的值为全局对象。查看以下代码并确保您了解 this 返回的内容。

示例:sample83.html

 <script>  	var myObject1 = { 		name: 'myObject1', 		myMethod: function () { console.log(this); } 	};  	myObject1.myMethod(); // Logs 'myObject1'.  	var myObject2 = function () { console.log(this); };  	myObject2(); // Logs window.  </script>

arguments.callee 属性

arguments 对象有一个名为 callee 的属性,它是对当前正在执行的函数的引用。此属性可用于从函数范围内引用该函数 (arguments.callee)a 自引用。在下面的代码中,我们使用此属性来获取对调用函数的引用。

示例:sample84.html

 <script>  	var foo = function foo() { 		console.log(arguments.callee); // Logs foo() 		// callee could be used to invoke recursively the foo function (arguments.callee()) 	} ();  </script>

当需要递归调用函数时,这非常有用。


函数实例 length 属性和 arguments.length

arguments 对象具有唯一的 length 属性。虽然您可能认为这个 length 属性将为您提供已定义参数的数量,但它实际上提供了在调用期间发送到函数的参数数量。

示例:sample85.html

 <script>  	var myFunction = function (z, s, d) { 		return arguments.length; 	};  	console.log(myFunction()); // Logs 0 because no parameters were passed to the function.  </script>

使用所有 Function() 实例的 length 属性,我们实际上可以获取函数期望的参数总数。

示例:sample86.html

 <script>  	var myFunction = function (z, s, d, e, r, m, q) { 		return myFunction.length; 	};  	console.log(myFunction()); // Logs 7.  </script>

arguments.length 属性在 JavaScript 1.4 中已弃用,但可以从函数对象的 length 属性访问发送到函数的参数数量。接下来,您可以通过利用 callee 属性来首先获取对正在调用的函数的引用 (arguments.callee.length) 来获取长度值。


重新定义函数参数

函数参数可以直接在函数内部重新定义,也可以使用 arguments 数组。看一下这段代码:

示例:sample87.html

 <script>  	var foo = false; 	var bar = false;  	var myFunction = function (foo, bar) { 		arguments[0] = true; 		bar = true; 		console.log(arguments[0], bar); // Logs true true. 	}  	myFunction();  </script>

请注意,我可以使用 arguments 索引或直接为参数重新分配新值来重新定义 bar 参数的值。


在函数完成之前返回函数(取消函数执行)

通过使用带或不带值的 return 关键字,可以在调用期间随时取消函数。在下面的示例中,如果参数未定义或不是数字,我们将取消 add 函数。

示例:sample88.html

 <script>  	var add = function (x, y) { 		// If the parameters are not numbers, return error. 		if (typeof x !== 'number' || typeof y !== 'number') { return 'pass in numbers'; } 		return x + y; 	} 	console.log(add(3, 3)); // Logs 6. 	console.log(add('2', '2')); // Logs 'pass in numbers'.  </script>

这里要讲的概念是,您可以在函数执行过程中的任何时刻使用 return 关键字来取消函数的执行。


定义函数(语句、表达式或构造函数)

函数可以用三种不同的方式定义:函数构造函数、函数语句或函数表达式。在下面的示例中,我演示了每种变体。

示例:sample89.html

 <script>  	/* Function constructor: The last parameter is the function logic,  	everything before it is a parameter. */ 	var addConstructor = new Function('x', 'y', 'return x + y');  	// Function statement. 	function addStatement(x, y) { 		return x + y; 	}  	// Function expression. 	var addExpression = function (x, y) { 		return x + y; 	};  	console.log(addConstructor(2, 2), addStatement(2, 2), addExpression(2, 2)); // Logs '4 4 4'.  </script>

有人说函数还有第四种类型的定义,称为“命名函数表达式”。命名函数表达式只是一个包含名称的函数表达式(例如, var add = function add(x, y) {return x+y})。


调用函数(函数、方法、构造函数或 call() 和 apply())

使用四种不同的场景或模式调用函数。

  • 作为函数
  • 作为一种方法
  • 作为构造函数
  • 使用 apply() 或 call()

在下面的示例中,我们将检查每种调用模式。

示例:sample90.html

 <script>  	// Function pattern. 	var myFunction = function () { return 'foo' }; 	console.log(myFunction()); // Logs 'foo'.  	// Method pattern. 	var myObject = { myFunction: function () { return 'bar'; } } 	console.log(myObject.myFunction()); // Logs 'bar'.  	// Constructor pattern. 	var Cody = function () { 		this.living = true; 		this.age = 33; 		this.gender = 'male'; 		this.getGender = function () { return this.gender; }; 	} 	var cody = new Cody(); // Invoke via the Cody constructor. 	console.log(cody); // Logs the cody object and properties.  	// apply() and call() pattern. 	var greet = { 		runGreet: function () { 			console.log(this.name, arguments[0], arguments[1]); 		} 	}  	var cody = { name: 'cody' }; 	var lisa = { name: 'lisa' };  	// Invoke the runGreet function as if it were inside of the cody object. 	greet.runGreet.call(cody, 'foo', 'bar'); // Logs 'cody foo bar'.  	// Invoke the runGreet function as if it were inside of the lisa object. 	greet.runGreet.apply(lisa, ['foo', 'bar']); // Logs 'lisa foo bar'.  	/* Notice the difference between call() and apply() in how parameters are sent to the function being invoked. */  </script>

确保您了解所有四种调用模式,因为您将遇到的代码可能包含其中任何一种。


匿名函数

匿名函数是没有给出标识符的函数。匿名函数主要用于将函数作为参数传递给另一个函数。

示例:sample91.html

 <script>  	// function(){console.log('hi');}; // Anonymous function, but no way to invoke it.  	// Create a function that can invoke our anonymous function. 	var sayHi = function (f) { 		f(); // Invoke the anonymous function. 	}  	// Pass an anonymous function as a parameter. 	sayHi(function () { console.log('hi'); }); // Logs 'hi'.  </script>

自调用函数表达式

函数表达式(实际上是除从 Function() 构造函数创建的函数之外的任何函数)可以在定义后使用括号运算符立即调用。在以下示例中,我们创建 sayWord() 函数表达式,然后立即调用该函数。这被认为是一个自调用函数。

示例:sample92.html

 <script>  	var sayWord = function () { console.log('Word 2 yo mo!'); } (); // Logs 'Word 2 yo mo!'  </script>

自调用匿名函数语句

可以创建自调用的匿名函数语句。这称为自调用匿名函数。在下面的示例中,我们创建了几个立即调用的匿名函数。

示例:sample93.html

 <script>  	// Most commonly used/seen in the wild. 	(function (msg) { 		console.log(msg); 	})('Hi');  	// Slightly different, but achieving the same thing: 	(function (msg) { 		console.log(msg) 	} ('Hi'));  	// The shortest possible solution. 	!function sayHi(msg) { console.log(msg); } ('Hi');  	// FYI, this does NOT work! 	// function sayHi() {console.log('hi');}();  </script>

根据 ECMAScript 标准,如果要立即调用函数,则需要在函数两边加上括号(或将函数转换为表达式的任何内容)。


函数可以嵌套

函数可以无限期地嵌套在其他函数中。在下面的代码示例中,我们将 goo 函数封装在 bar 函数内部,该函数位于 foo 函数内部。

示例:sample94.html

 <script>  	var foo = function () { 		var bar = function () { 			var goo = function () { 				console.log(this); // Logs reference to head window object. 			} (); 		} (); 	} ();  </script>

这里的简单概念是函数可以嵌套,并且嵌套的深度没有限制。

请记住,嵌套函数的 this 的值将是 JavaScript 1.5、ECMA-262 第 3 版中的头对象(Web 浏览器中的 window 对象)。


将函数传递给函数以及从函数返回函数

如前所述,函数是 JavaScript 中的一等公民。由于函数是一个值,并且函数可以传递任何类型的值,因此函数可以传递给函数。接受和/或返回其他函数的函数有时称为“高阶函数”。

在下面的代码中,我们将一个匿名函数传递给 foo 函数,然后立即从 foo 函数返回。变量 bar 所指向的正是这个匿名函数,因为 foo 接受并返回匿名函数。

示例:sample95.html

 <script>  	// Functions can be sent to, and sent back from, functions. 	var foo = function (f) { 		return f; 	}  	var bar = foo(function () { console.log('Hi'); });  	bar(); // Logs 'Hi'.  </script>

因此,当调用 bar 时,它会调用传递给 foo() 函数的匿名函数,然后从 foo() 函数传回并从 bar 引用多变的。所有这些都是为了展示函数可以像任何其他值一样传递的事实。


在定义函数语句之前调用函数语句(又名函数提升)

函数语句可以在执行期间在其实际定义之前调用。这有点奇怪,但您应该意识到这一点,以便您可以利用它,或者至少知道当您遇到它时会发生什么。在下面的示例中,我在定义 sayYo() 和 sum() 函数语句之前调用它们。

示例:sample96.html

 <script>  	// Example 1 	var speak = function () { 		sayYo(); // sayYo() has not been defined yet, but it can still be invoked, logs 'yo'. 		function sayYo() { console.log('Yo'); } 	} (); // Invoke  	// Example 2 	console.log(sum(2, 2)); // Invoke sum(), which is not defined yet, but can still be invoked. 	function sum(x, y) { return x + y; }  </script>

发生这种情况是因为在代码运行之前,函数语句被解释并添加到执行堆栈/上下文中。确保您在使用函数语句时意识到这一点。

定义为函数表达式的函数不会被提升。仅提升函数语句。


函数可以调用自身(又名递归)

函数调用自身是完全合法的。事实上,这经常被用在众所周知的编码模式中。在下面的代码中,我们启动 countDownFrom 函数,然后该函数通过函数名称 countDownFrom 调用自身。本质上,这会创建一个从 5 倒数到 0 的循环。

示例:sample97.html

 <script>  	var countDownFrom = function countDownFrom(num) { 		console.log(num); 		num--; // Change the parameter value. 		if (num < 0) { return false; } // If num < 0 return function with no recursion. 		// Could have also done arguments.callee(num) if it was an anonymous function. 		countDownFrom(num); 	};  	countDownFrom(5); // Kick off the function, which logs separately 5, 4, 3, 2, 1, 0.  </script>

您应该意识到,函数调用自身(也称为递归)或重复执行此操作是很自然的。


结论

函数是 JavaScript 最常用的方面之一,希望您现在对如何使用它们有了更好的了解。

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享