JavaScript 函数概述
我们可以整天对函数及其用途进行诗意的描述。与其如此,不如让我们探索一下有函数和没有函数的生活。
没有功能的生活
let pets = 35;
let owners = 15;
let petsPerOwner = pets / owners;
//======Pet Info Form
let answer = prompt("how many pets do you have?");
//============
// update based on answer, add new owner
pets += answer / 1; // coerce string into number
owners += 1; // register new owner
petsPerOwner = pets / owners;
//test
`There are now ${petsPerOwner} pets per owner at Pet Nirvana `;
那比这个更容易读吗?
功能生活
let pets = 35;
let owners = 15;
let petsPerOwner = average(pets, owners);
let answer = prompt("how many pets do you have?");
registerPets(answer);
registerOwner();
updateAvg(); // update based on answer, add new owner
console.log(`There are now ${petsPerOwner} pets per owner at Pet Nirvana `);
function average(total, number){
return total / number;
}
function registerPets(newNum){
pets += Number(newNum); // register new pet(s)
}
function registerOwner(){
++owners;
}
function updateAvg(){
petsPerOwner = Math.ceil(average(pets, owners)); // find new average, round up
}
除了易读性之外,你还能发现,有了这些内置函数,我们的工作变得轻松多了。它们可以Math.ceil
汇总代码并log()
帮助我们调试代码。另外,请注意,第一个示例仍然只是出于必要性而使用函数。
没有函数,就没有 JavaScript,至少没有我们所熟知和喜爱的 JavaScript 的所有优秀部分。
函数的剖析
function multiply(x, y){
return x * y;
}
function // keyword for decleration
multiply // function name
(x,y) // parameters
return x * y; // a return statement allows
//the function to produce value
函数有一个或多个参数。我们可以随意命名它们,就像变量一样。不过,我们应该将参数视为引用而不是存储。我们告诉函数,我们期望用户将某个变量或数据类型插入到这个空间中。然后,我们在函数体中对参数名称进行操作。
很多时候,你需要确保返回预期的结果。否则,undefined
调用函数时会产生错误。如果你打算使用函数来设置值,请添加 return 关键字。
返回
该return
语句可以返回任何数据类型。
数字:
return 2;
字符串:
return "hello";
无效的:
return null;
不明确的:
return undefined;
数组:
return [1,2,3];
对象:
return {one: 1, two: 2, three: 3};
功能:
return function(){
return "I'm in a function";
}
调用函数
通过在函数名称中添加参数来调用函数()
。如果函数需要参数,则必须输入参数,否则会出错。
function multiply(x, y){
return x * y;
}
multiply(2,2); // 4
你可以在函数声明之前调用它,它仍然有效。这称为函数提升。
multiply(2,2); // 4
function multiply(x, y){
return x * y;
}
函数符号
当一个地标或事物在任何人类语言中具有重要意义时,通常有多种方式来表达其名称。
有趣的事实:在古典阿拉伯语中,有数百种方式来命名骆驼。
类似地,函数对于 JavaScript 来说非常重要,因此根据其使用环境,它们有许多名称。
函数声明
您有经过验证的函数声明:
function greet(){
return 'hello';
}
// we can the call or invoke this functions
greet(); // 'hello'
函数表达式
你还有一个函数表达式。它之所以被称为函数表达式,是因为你将一个函数赋值给了一个变量:
let greet = function(){
return 'hello';
}
// we can still call or invoke this functions
greet(); // 'hello'
需要注意的一件重要事情是,提升不适用于函数表达式。
greet(); // undefined
let greet = function(){
return 'hello';
}
匿名函数
function()
后面没有名称的function 关键字 ( ) 被称为匿名函数。ES6 引入了一种编写匿名函数的新方法。您可以不使用 function 关键字,而是将其删除,并=>
在括号中添加箭头运算符。
let greet = ()=>{
return 'hello';
}
在很大程度上,引入语法差异是为了满足那些喜欢编写极简代码的纯粹主义者。不过,箭头函数确实引入了自动绑定。我们不会深入探讨技术细节,稍后会向您展示什么是自动绑定。
匿名函数用途广泛。你可以将它们设置为对象字面量中某个键的值:
let person = {
name: "Mark",
greet: function(){
return 'hello' + ' ' + this.name;
}
}; // end of object literal
person.greet();
注意:在我们的匿名函数
this
中指的是person
。我们也可以简单地写成person.name
。
回调函数
匿名函数也可以传入参数。这样做会将匿名函数变成所谓的回调函数。
//here's a function expression
let greet = (callback, times)=>{
for(let cnt=0; cnt < times; cnt ++){
console.log(callback()); //it doesn't return.
//This will cause a side effect
}
}
//here's our anonymous func AKA callback
greet(()=>{return 'hello'}, 3);
//we could have written it like this:
greet(function(){return 'hello'}, 3);
注意:还记得我们讨论过函数的构造吗?定义函数时,实际上是在创建一个模型。回调函数正是利用了这一点,因为我们可以等待函数调用。我们通过附加
()
参数名称来告诉解释器,我们希望在函数调用时调用该函数。
闭包
函数内的函数称为闭包:
// We have two functions. One is named outie and the other is named closure *wink* *wink*
function outie(){
// this is closure's first and only outer scope
function closure(){
// this is closure's local scope
}
}
如果你曾经接触过回调,你可能已经猜到了,回调其实也是一个闭包。在它存在的某个时刻,它会在另一个函数中被调用。
Context: '
既然我们已经开始嵌套函数,就应该处理上下文。函数会创建自己的上下文,这会影响this
关键字,但如果我们在匿名函数中编写闭包,this
闭包就会引用我们的函数。因此,我们会得到 undefined。
以下是一个例子:
let person = {
name: "Mark",
greet: function(){
return function(){
return 'hello' + ' ' + this.name;
}
}
}
// double invoke ()() can invoke a returned closure
person.greet()();// >'hello undefined'
为了解决这个问题,开发人员只需将其设置this
为一个变量来保存上下文。换句话说,我们正在绑定this。现在来看看自动绑定可能意味着什么吧?
//code excerpt
greet: function(){
let self = this;
return function(){
return 'hello' + ' ' + self.name;
}
}
//end of excerpt
另一种解决方案是明确bind(this)
调用函数的右括号。
//code excerpt
greet: function(){
return function(){
return 'hello' + ' ' + this.name;
}.bind(this)
}
//end of excerpt
它看起来很丑,但是却很有效。
专业提示:还记得新的
()=>
语法吗?上面的例子很好地解释了为什么我们需要自动绑定。以前,你必须this
像我们之前那样记住绑定变量。现在,你只需使用新的语法,哇!你就有了一个可以运行的this
关键字。不妨通过重写闭包来尝试一下。
最终的解决方案是使用Es6箭头函数。
//code excerpt
greet: function(){
let self = this;
return ()=>{
return 'hello' + ' ' + this.name;
}
}
//end of excerpt
注意:在外部匿名函数上使用箭头函数会破坏上下文。由于箭头函数会自动绑定,因此您将绑定
this
到对象外部的上下文person
。因此,this.person
这将不再起作用。
当前执行函数
调用自身的函数称为立即调用函数表达式(IIFE)。
(function(){
return 'hello'; //'hello'
}());
您仍然可以使用其他函数执行任何操作。您可以设置参数并使用“调用器”()
输入数据。
(function(name){
return name; // 'hi'
}("hi"));
你可以将 IIFE 赋值给一个变量,但必须声明变量名。不过你不必调用它。
var greet =
(function(name){
return name;
}("hi"));
greet // 'hi'
函数狂热
我们可以使用 IFFE 和闭包,结合匿名函数来创建一个 android。
//function expression
let android = (function(){
//==private
this.name = "Mark VI";
//declaration
function addStrings(){
return "hello" + " " + this.name;
}
function setName(name){
this.name = name;
}
//==public: we're just returning an object.
return { //anonymous functions
setName:(name)=>{
return setName(name);
},
greet: ()=>{
return addStrings();
}
}
}());//IIFE
android.setName("Raj");
android.greet(); //'Hello, I'm Raj'
上面的代码充分利用了函数提供的所有功能,生成了一个可运行的对象。它管理自身的状态,这意味着我们所做的任何更改都将被保存。因此,如果我们设置一个新名称,并告诉机器人“欢迎”我们,它就会用这个新名称来欢迎我们。这真是太强大了!我们将在另一章中学习更多关于面向对象编程的知识。
注意:如果开发人员希望他们的代码无需通过事件触发即可运行,他们通常会使用 IFFE 包装 JavaScript 代码。
概括
跟踪所有这些不同类型的函数可能很困难,所以让我们列出不同的函数类型。
- 声明函数
- 匿名函数
- 回调
- 闭包
- 立即调用函数表达式
文章来源:https://dev.to/howtocodejs/an-overview-of-javascript-functions-47id挑战:编写一个利用所有这些不同功能的程序