首頁  >  文章  >  web前端  >  實作函數的函數()

實作函數的函數()

WBOY
WBOY原創
2023-09-04 09:41:201185瀏覽

實作函數的函數()

函數是程式碼語句的容器,可以使用括號 () 運算子來呼叫。呼叫時可以在括號內傳遞參數,以便函數呼叫時函數中的語句可以存取某些值。

#在以下程式碼中,我們使用 new 運算子建立 addNumbers 函數 objectone 的兩個版本,另一個版本使用更常見的文字模式。兩者都需要兩個參數。在每種情況下,我們都會呼叫該函數,並在括號中傳遞參數 () 運算子。

範例:sample76.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

函數可用於傳回值、建構物件或作為簡單運行程式碼的機制。 JavaScript 對函數有多種用途,但就其最基本的形式而言,函數只是可執行語句的唯一範圍。


函數() 參數

Function() 建構子採用無限數量的參數,但 Function() 建構子期望的最後一個參數是一個字串,其中包含構成函數體的語句。在最後一個之前傳遞給建構函數的任何參數都可用於正在建立的函數。也可以以逗號分隔的字串形式傳送多個參數。

在下面的程式碼中,我將 Function() 建構函數的用法與實例化函數物件的更常見模式進行了對比。

範例:sample77.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

不建議或通常不直接利用 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

<!DOCTYPE html><html lang="en"><body><script>

	var sayHi = function () {
		return 'Hi';
	};

	console.log(sayHi()); // Logs "Hi".

</script></body></html>

如果函數沒有指定回傳值,則傳回 undefined。在以下範例中,我們呼叫 yelp 函數,該函數將字串「yelp」記錄到控制台,而不明確傳回值。

範例:sample79.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

這裡要記住的概念是,即使您沒有明確提供要傳回的值,所有函數都會傳回一個值。如果不指定傳回值,則傳回值為 undefined


函數是一等公民(不只是文法,還有值)

在 JavaScript 中,函數就是物件。這意味著函數可以儲存在變數、陣列或物件中。此外,函數可以傳遞給函數或從函數返回。函數具有屬性,因為它是一個物件。所有這些因素使得函數成為 JavaScript 中的一等公民。

範例:sample80.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

認識到函數是一個對象,因此也是一個值,這一點至關重要。它可以像 JavaScript 中的任何其他表達式一樣傳遞或增強。


將參數傳遞給函數

參數是呼叫函數時將值傳遞到函數作用域的工具。在下面的範例中,我們呼叫 addFunction()。由於我們已預先定義它採用兩個參數,因此在其範圍內可以使用兩個附加價值。

範例:sample81.html

<!DOCTYPE html><html lang="en"><body><script>

	var addFunction = function (number1, number2) {
		var sum = number1 + number2;
		return sum;
	}

	console.log(addFunction(3, 3)); // Logs 6.

</script></body></html>

與其他一些程式語言相比,在 JavaScript 中省略參數是完全合法的,即使函數已被定義為接受這些參數。缺少的參數僅被賦予值 undefined。當然,如果省略參數值,則函數可能無法正常運作。

如果向函數傳遞意外參數(建立函數時未定義的參數),則不會發生錯誤。並且可以從 arguments 物件存取這些參數,該物件可用於所有函數。


thisarguments 值可用于所有函数

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

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

示例:sample82.html

<!DOCTYPE html><html lang="en"><body><script>

	var add = function () {
		return arguments[0] + arguments[1];
	};

	console.log(add(4, 4)); // Returns 8.

</script></body></html>

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

示例:sample83.html

<!DOCTYPE html><html lang="en"><body><script>

	var myObject1 = {
		name: 'myObject1',
		myMethod: function () { console.log(this); }
	};

	myObject1.myMethod(); // Logs 'myObject1'.

	var myObject2 = function () { console.log(this); };

	myObject2(); // Logs window.

</script></body></html>

arguments.callee 属性

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

示例:sample84.html

<!DOCTYPE html><html lang="en"><body><script>

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

</script></body></html>

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


函数实例 length 属性和 arguments.length

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

示例:sample85.html

<!DOCTYPE html><html lang="en"><body><script>

	var myFunction = function (z, s, d) {
		return arguments.length;
	};

	console.log(myFunction()); // Logs 0 because no parameters were passed to the function.

</script></body></html>

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

示例:sample86.html

<!DOCTYPE html><html lang="en"><body><script>

	var myFunction = function (z, s, d, e, r, m, q) {
		return myFunction.length;
	};

	console.log(myFunction()); // Logs 7.

</script></body></html>

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


重新定义函数参数

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

示例:sample87.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

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


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

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

示例:sample88.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

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


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

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

示例:sample89.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

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


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

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

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

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

示例:sample90.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

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


匿名函数

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

示例:sample91.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

自调用函数表达式

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

示例:sample92.html

<!DOCTYPE html><html lang="en"><body><script>

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

</script></body></html>

自调用匿名函数语句

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

示例:sample93.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

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


函数可以嵌套

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

示例:sample94.html

<!DOCTYPE html><html lang="en"><body><script>

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

</script></body></html>

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

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


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

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

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

示例:sample95.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

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


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

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

示例:sample96.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

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

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


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

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

示例:sample97.html

<!DOCTYPE html><html lang="en"><body><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></body></html>

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


结论

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

以上是實作函數的函數()的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn