共计 4436 个字符,预计需要花费 12 分钟才能阅读完成。
在 JavaScript 中,能够应用许多的办法申明函数。首先通常办法是应用 function
关键字:
// 函数申明
function greet(who) {return `Hello, ${who}!`;
}
// 函数表达式
const greet = function (who) {return `Hello, ${who}`;
};
我将函数申明和函数表达式称之为_惯例函数_。
第二种形式是在 ES2015 中退出的_箭头函数语法_:
const greet = (who) => {return `Hello, ${who}!`;
};
面对两种申明函数的惯例与箭头语法,你是否应用其中的一种代替另一种呢?这是个有意思的问题。
在这篇文章中,我将展现这两者之间的次要不同,让你可能在须要时抉择正确的语法。
#this
的值
# 惯例函数
在 JavaScript 惯例函数中,this
的值(又称为执行上下文)是动静的。
动静的上下文意味着 this
的值依赖于该函数是如何被援用的。在 JavaScript 中,有 4 种形式援用惯例函数。
在_简略援用_中 this
的值等价于全局对象(或者 undefined
,如果该函数运行在严格模式下):
function myFunction() {console.log(this);
}
// 简略援用
myFunction(); // 输入全局对象 (window)
在_办法援用_中 this
的值为领有该办法的对象:
const myObject = {method() {console.log(this);
},
};
// 办法援用
myObject.method(); // 输入 "myObject"
在应用 myFunc.call(context, arg1, ..., argN)
或 myFunc.apply(context, [arg1, ..., argN])
的_间接援用_中 this
的值等价于第一个参数:
function myFunction() {console.log(this);
}
const myContext = {value: 'A'};
myFunction.call(myContext); // 输入 {value: 'A'}
myFunction.apply(myContext); // 输入 {value: 'A'}
在_构造函数援用_中应用 new
关键字的 this
等价于新创建的实例:
function MyFunction() {console.log(this);
}
new MyFunction(); // 输入一个 MyFunction 的实例
# 箭头函数
箭头函数外部 this
的行为与惯例函数的 this
行为有很多的不同。
无论怎样或在任意中央被执行,箭头函数外部 this
的值总是等价于它内部的函数的 this
值。一句话,箭头函数绑定 this
解析。换句话说,箭头函数不能申明它本人的执行上下文。
在上面的例子,myMethod()
是在函数 callback()
内部的箭头函数:
const myObject = {myMethod(items) {console.log(this); // 输入 "myObject"
const callback = () => {console.log(this); // 输入 "myObject"
};
items.forEach(callback);
},
};
myObject.myMethod([1, 2, 3]);
箭头函数 callback()
外部的 this
值等价于内部函数 myMethod()
的 this
值。
this
绑定解析是箭头函数一个巨棒的性能。当在办法外部应用回调函数时能够确认箭头函数不申明它本人的 this
:不再须要应用 const self = this
或 callback.bind(this)
之类的变通技巧。
# 构造函数
# 惯例函数
如同下面章节所见,惯例函数很容易创立构建函数。举例来说,创立 Car()
函数的实例 car:
function Car(color) {this.color = color;}
const redCar = new Car('red');
redCar instanceof Car; // => true
Car
是一个惯例函数,并且应用 new
关键字,它创立属于 Car
类型的新实例。
# 箭头函数
一件重要的事是 this
绑定解析的箭头函数不能被用于构造函数。
如果尝试在箭头函数后面应用 new
关键字,JavaScript 将抛出一个谬误:
const Car = (color) => {this.color = color;};
const redCar = new Car('red');
// TypeError: Car is not a constructor
#arguments 对象
# 惯例函数
在惯例函数的外部,arguments
是一个特地的类数组对象,该对象蕴含函数援用的参数列表。
让咱们援用 2 个参数运行 myFunction
函数:
function myFunction() {console.log(arguments);
}
myFunction('a', 'b'); // 输入 {0: 'a', 1: 'b'}
类数组对象 arguments
蕴含函数运行参数:'a'
和 'b''
。
# 箭头函数
留神,在箭头函数外部没有非凡的 arguments
对象。
再次(与 this
值雷同),按 arguments
词法解析对象:箭头函数中拜访 arguments
从内部函数统一。
让咱们尝试在箭头函数外部拜访 arguments
:
function myRegularFunction() {const myArrowFunction = () => {console.log(arguments);
};
myArrowFunction('c', 'd');
}
myRegularFunction('a', 'b');
// 输入 {0: 'a', 1: 'b'}
箭头函数 myArrowFunction()
援用参数 c
, d
。然而,arguments
对象仍然等价于 myRegularFunction()
的参数:a
, b
。
如果想要间接拜访箭头函数的参数,能够应用扩大参数性能:
function myRegularFunction() {const myArrowFunction = (...args) => {console.log(args);
};
myArrowFunction('c', 'd');
}
myRegularFunction('a', 'b');
// 输入 {0: 'c', 1: 'd'}
...args
扩大参数聚合箭头函数执行时参数:{0: 'c', 1: 'd'}
。
# 外部返回 return
# 惯例函数
仅能通过 return expression
表达式申明函数的返回后果值:
function myFunction() {return 42;}
myFunction(); // => 42
如果缺失 return
申明,或者在 return
申明之后没有表达式,惯例函数将返回 undefined
:
function myEmptyFunction() {42;}
function myEmptyFunction2() {
42;
return;
}
myEmptyFunction(); // => undefined
myEmptyFunction2(); // => undefined
# 箭头函数
你能够在箭头函数中应用与惯例函数雷同的形式返回值,但有一种例外用法。
如果箭头函数蕴含一个表达式,并且删除了函数的括号包裹,那么表达式后果被间接返回。称之为行内箭头函数。
const increment = (num) => num + 1;
increment(41); // => 42
increment()
箭头函数仅蕴含一个表达式 num + 1
。箭头函数不须要应用 return
关键字间接返回该表达式。
# 办法
# 惯例函数
在类中惯例函数应用通常的形式申明办法。
在上面的类 Hero
中,办法 logName()
应用惯例函数申明:
class Hero {constructor(heroName) {this.heroName = heroName;}
logName() {console.log(this.heroName);
}
}
const batman = new Hero('Batman');
通常来说,惯例函数作为办法在应用。有时,须要提供办法作为回调函数,举例来说 setTimeout
或者事件监听器。在这些用例中,可能遭逢不同拜访 this
的形式。
让咱们应用 logName()
办法作为 setTimeout
的回调函数:
setTimeout(batman.logName, 1000);
// after 1 second logs "undefined"
1 秒后,undefined
在控制台中被打印进去。setTimeout
简略援用 logName
(这时 this
是全局对象)。
让咱们手动绑定 this
的值到正确的上下文:
setTimeout(batman.logName.bind(batman), 1000);
// after 1 second logs "Batman"
batman.logName.bind(batman)
绑定 this
到 batman
实例。当初能够确定该办法没有失去上下文。
手动绑定这个须要大量的代码,尤其是当你有很多办法的时候。还有一个更好的办法:将箭头函数作为类字段。
# 箭头函数
感激 Class fields 提案,当初能够应用箭头函数在类外部作为办法。
当初,与惯例函数不同,办法申明应用箭头函数绑定 this
解析到类的实例。咱们应用箭头函数作为字段:
class Hero {constructor(heroName) {this.heroName = heroName;}
logName = () => {console.log(this.heroName);
};
}
const batman = new Hero('Batman');
当初能够应用 batman.logName
作为回调函数而无需手动绑定 this
。logName()
办法外部的 this
值总是类的实例:
setTimeout(batman.logName, 1000);
// after 1 second logs "Batman"
# 总结
理解惯例函数与箭头函数之间的不同有助于在须要时抉择正确的语法。
惯例函数外部的 this
值是动静的并且依赖于援用。然而箭头函数外部的 this
绑定等价于内部函数。
arguments
对象蕴含了惯例函数中的参数列表。而箭头函数则相同,不定义参数(但你能够应用扩大参数 ...args
轻松拜访箭头函数的参数)。
如果箭头函数有一个表达式,那么即便不应用 return
关键字,该表达式也会隐式返回。
最初但同样重要的是,你能够在类外部应用箭头函数语法定义方法。箭头办法将 this
值绑定到类的实例。
无论箭头办法如何被调用,this
值总是等于类实例,这在办法被用作回调时十分有用。
原文链接:https://wenjun.me/2020/05/differences-between-arrow-and-regular-functions.html
本文链接:https://segmentfault.com/a/1190000023892802