前言
首发地址,欢送点赞评论,指出本文的不足之处。
在 JavaScript 中,咱们能够有多种形式定义函数,如:函数申明、函数表达式和箭头函数
<!–truncate–>
// 函数申明
function normalFn() {
return 'normalFn';
}
// 函数表达式
const normalFn = function() {
return 'normalFn';
}
// 箭头函数
const arrowFn = () => {
return 'arrowFn';
}
其中,箭头函数是在 ES2015(ES6) 规范中新增的,其语法与 ES6 之前的函数申明及函数表达式两种定义形式不同。本文中,将函数申明和函数表达式两种定义形式归为一般函数。
那么,一般函数和箭头函数有什么区别呢?🤔️
1. this 指向
在 JavaScript 中,this
的指向是个根底且重要的知识点。
1.1 一般函数
在一般函数中,this
的指向(执行上下文)是动静的,其值取决于函数是如何被调用的,通常有以下 4 种调用形式:
- 1)间接调用时,其指向为全局对象(严格模式下为 undefined)
function fnc() {
console.log(this);
}
fnc(); // 全局对象(global 或 window)
- 2)办法调用时,其指向为调用该办法的对象
var obj = {
fnc1: function(){
console.log(this === obj);
}
}
obj.fnc2 = function() {
console.log(this === obj);
}
function fnc3() {
console.log(this === obj);
}
obj.fnc3 = fnc3;
obj.fnc1(); // true
obj.fnc2(); // true
obj.fnc3(); // true
- 3)new 调用时,其指向为新创建的实例对象
function fnc() {
console.log(this);
}
new fnc(); // fnc 的实例 fnc {}
- 4)call、apply、bind 调用时,其指向为三种办法的第一个参数
function fnc() {
console.log(this);
}
const ctx = { value: 'a' };
fnc.call(ctx); // { value: 'a' }
fnc.apply(ctx); // { value: 'a' }
fnc.bind(ctx)(); // { value: 'a' }
在旧版的 JavaScript 中,常常应用 bind 显式的设置 this 的指向,这种模式通常能够在 ES6 呈现之前的某些晚期版本的框架(如 React)中找到。而箭头函数的呈现则提供了一种更便捷的形式解决此问题。
1.2 箭头函数
无论如何执行或在何处执行,箭头函数外部的 this 值始终等于内部函数的值,即箭头函数不会扭转 this 的指向,
const obj = {
fnc(arr) {
console.log(this); // obj
const cb = () => {
console.log(this); // obj
};
arr.forEach(cb);
}
};
obj.fnc([1, 2, 3]);
留神:因为箭头函数没有本人的 this 指针,通过 call() 、 apply() 和 bind() 办法调用时,只能传递参数,而不能绑定 this,他们的第一个参数会被疏忽。如下:(例子来源于 MDN)
var adder = {
base : 1,
add : function(a) {
var f = v => v + this.base;
return f(a);
},
addThruCall: function(a) {
var f = v => v + this.base;
var b = {
base : 2
};
return f.call(b, a);
}
};
console.log(adder.add(1)); // 输入 2
console.log(adder.addThruCall(1)); // 依然输入 2
2. 构造函数
在 JavaScript 中,函数和类的继承是通过 prototype
属性实现的,且 prototype
领有属性 constructor
指向构造函数,如下:
function fnc() {}
console.lof(fnc.prototype) // {constructor: ƒ}
而采纳箭头函数定义函数时,其是没有 prototype
属性的,也就无奈指向构造函数。
const arrowFnc = () => {}
console.log(arrowFnc.prototype) // undefined
针对一般函数与箭头函数在构造函数上的区别,能够引出一个问题 — 箭头函数能够通过 new 进行实例化吗?
const arrowFnc = () => {}
const arrowIns = new arrowFnc() // Uncaught TypeError: arrowFnc is not a constructor
答案是【不能够】,那么为什么呢?🤔️
- 没有本人的 this,也就意味着无奈调用 apply、call 等
- 没有 prototype 属性,而 new 命令在执行时须要将构造函数的 prototype 赋值给新的对象的 \_proto_
function newOperator(Con, ...args) {
let obj = {};
Object.setPrototypeOf(obj, Con.prototype); // 相当于 obj.__proto__ = Con.prototype
let result = Con.apply(obj, args);
return result instanceof Object ? result : obj;
}
更具体的起因是,JavaScript函数两个外部办法: [[Call]] 和 [[Construct]],当函数被间接调用时执行的是 [[Call]] 办法,即间接执行函数体,而 new 调用时是执行的 [[Construct]]办法。箭头函数没有 [[Construct]]办法,因而不能被用作构造函数进行实例化。
3. 作为办法属性
'use strict';
var obj = {
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log(this.i, this)
}
}
obj.b(); // undefined, Window{...}
obj.c(); // 10, Object {...}
能够看到,箭头函数是没有 this 绑定的,其指向始终与上一级保持一致。
上文中提到,当构造函数或类被 new 调用时,其 this 指向为新创建的实例对象,须要留神的是,这里的 this 是 constructor 中的 this,而不是该函数或类的任意中央。如下所示:
class Person {
constructor(name) {
this.name = name;
}
getName() {
console.log(this.name, this);
}
}
const p = new Person('Tom');
p.getName(); // Tom
setTimeout(p.getName, 100); // undefined, Window{...}
为了防止这种谬误,咱们通常须要在 constructor 中绑定 this,如下所示:
class Person {
constructor(name) {
this.name = name;
this.getName = this.getName.bind(this);
}
getName() {
console.log(this.name);
}
}
const p = new Person('Tom');
setTimeout(p.getName, 100); // Tom
这种写法,很容易在 React 中发现,其实也是为了填补 JavaScript 的坑 😂。当然,也能够应用箭头函数来防止这种谬误,并简化写法,如下:
class Person {
constructor(name) {
this.name = name;
}
getName = () => {
console.log(this.name);
}
}
const p = new Person('Tom');
setTimeout(p.getName, 100); // Tom
在应用箭头函数时,this 是具备词法束缚的,也就是说箭头函数会主动将 this 绑定到定义它的上下文。
4. 参数
一般函数与箭头函数在参数上区别次要在于,箭头函数不绑定 arguments 对象。
const fn = () => arguments[0];
fn(1); // Uncaught ReferenceError: arguments is not defined
当咱们须要应用参数时,能够思考应用残余参数,如下:
const fn = (...args) => args[0];
fn(1, 2); // 1
另外,当函数参数个数为 1 时,箭头函数能够省略括号,进行缩写,如下所示:
const fn = x => x * x;
5. 返回值
在处理函数的返回值时,相比于一般函数,箭头函数能够隐式返回。
const sum = (a, b) => {
return a + b
}
const sum = (a, b) => (a + b);
隐式返回通常会创立一个单行操作用于 map、filter 等操作,留神:如果不能将函数主题编写为单行代码的话,则必须应用一般的函数体语法,即不能省略花括号和 return。
[1,2,3].map(i => i * 2); // [2,4,6]
[1,2,3].filter(i => i != 2); // [1,3]
总结
本文次要介绍了一般函数与箭头函数的区别,绝对于一般函数来说,ES6 箭头函数的次要区别如下:
- 箭头函数不绑定
arguments
,能够应用...args
代替; - 箭头函数能够进行隐式返回;
- 箭头函数内的
this
是词法绑定的,与外层函数保持一致; - 箭头函数没有
prototype
属性,不能进行 new 实例化,亦不能通过 call、apply 等绑定 this; - 在定义类的办法时,箭头函数不须要在
constructor
中绑定this
。
如果本文对你有所帮忙,欢送点赞👍,如果本文有谬误的中央,也欢送评论斧正🤝。
参考文章
- 5 Differences Between Arrow and Regular Functions
- Understanding Arrow Functions in JavaScript
- Advanced-Frontend/Daily-Interview-Question/issue101
- MDN Web Docs – 箭头函数
发表回复