[TOC]
箭头函数之前不常用的语法
直接返回一个对象
let func = (value, num) => ({ data: value + num });
func(1, 2); // { data :3 }
与变量解构
let func = ({ value, num }) => ({ data: value + num });
console.log(func({ value: 1, num: 2 })); // { data :3 }
与变量解构的应用(在react中)
一般设置state的方式
state = {
data: {
age: 17
}
};
handleSet = () => {
this.setState({
data: this.state.data.set("key", "value")
});
};
使用箭头函数与变量解构后可以简化为
handleSet2 = () => {
this.setState(({ data }) => ({
data: data.set("key", "value")
}));
};
没有this
箭头函数需要查找作用域链来确定this的值
这就意味着如果箭头函数被非箭头函数包含,this绑定的就是最近一层非箭头函数的this
下面这个例子,箭头函数中的this绑定的就是他的外层函数demo()的this,而demo()是在全局作用域中调用的,所以这个this, 指向的是全局作用域window
var value = 2;
function demo() {
const value = 1;
const b = () => {
console.log(this);
};
b();
}
demo(); // 2
因为箭头函数没有this,所以不能使用call(), apply(), bind()这些方法改变this的指向
var name = "hong";
const person = {
name: "ming"
};
function sayName() {
return this.name;
}
const sayName2 = () => {
return this.name;
};
console.log(sayName()); // "hong"
console.log(sayName.call(person)); // "ming"
console.log(sayName2()); // "hong" => this始终指向window
console.log(sayName2.call(person)); // "hong" => this始终指向window
箭头函数没有this,给我们带来了很多方便,比如之前我们希望函数内部的闭包可以拿到他的包含作用的this, 就需要提前将this保存在一个闭包可以放到的变量里面;
在匿名函数返回之前,我们将sayName()的this(指向obj)保存在变量that中,这样,我们就可以通过that访问到obj的name属性
function Person() {
var that = this;
that.age = 0;
setTimeout(function() {
console.log(that.age); // |this| 正确地指向 p 实例
}, 1000);
}
var p = new Person(); // 0
但是由于箭头根本没有this,所以可以直接访问包含函数this
function Person() {
this.age = 0;
setTimeout(function() {
console.log(this.age); // |this| 正确地指向 p 实例
}, 1000);
}
var p = new Person(); // 0
this和arguments也存在着同样的问题。如果不使用箭头函数,但是想访问作用域中的arguments对象,必须将对该对象的引用保存到另一个闭包能够访问的变量中。
没有arguments
箭头函数没有自己的arguments对象,这不一定是一件坏事,因为这样,箭头函数就可以访问外围函数的arguments对象
如果你非要使用arguments对象怎么办呢
const demo = (...args) => {
return args;
};
demo(1, 2, 3); // [1, 2, 3]
在函数柯里化的时候就很方便了,正常情况下我们需要吧外层函数的除了第一个参数外的参数与内部的参数拼接起来,如下
const add = function(a, b, c, d) {
return a + b + c + d;
};
function curry(fn) {
const args = Array.prototype.slice.call(arguments, 1);
return function() {
- const innerArgs = Array.prototype.slice.call(arguments);
const finalArgs = [...args, ...innerArgs];
return fn.apply(null, finalArgs);
};
}
console.log(curry(add, 1)(2, 3, 4)); // 10
由于箭头函数没有arguments
对象的特性,我们就可以直接访问他的包含函数的arguments对象,函数柯里化我们可以写成这个样子
function curry(fn) {
return (..._args) => {
const finalArgs = [...[].slice.call(arguments, 1), ..._args];
return fn.apply(null, finalArgs);
};
}
console.log(curry(add, 1)(2, 3, 4)); // 10
没有new.target属性(即不能通过new关键字调用)
普通的函数调用中(和作为构造函数来调用相对),new.target
的值是undefined;而在构造函数中调用时,new.target
指向被new
调用的构造函数这使得你可以检测一个函数是否是作为构造函数通过new被调用的
function Demo() {
console.log(new.target === window.Demo);
}
new Demo(); // true
- 这里冴羽的博客说函数有[[call]]和[[constructor]], 当通过new 调用函数时,执行 [[Construct]] 方法,创建一个实例对象,然后再执行函数体,将 this 绑定到实例上; 当直接调用的时候,执行 [[Call]] 方法,直接执行函数体。
- 箭头函数没有[[Construct]]方法,所以不能被用作构造函数,所以通过new的方式调用会报错
- 但是我没有看到书上有介绍过 [[Call]] 方法,等我找到回来改
把箭头当作构造函数,使用new创建的实例报错
const Demo = () => {console.log(1)};
const demo1 = new Demo(); // TypeError: Demo is not a constructor
没有原型
箭头函数不存在prototype这个属性
const DemoA = () => {
console.log(1);
};
const DemoB = function() {
console.log(1);
};
console.log(DemoA.prototype.constructor === DemoA); // TypeError: Cannot read property 'constructor' of undefined
console.log(DemoB.prototype.constructor === DemoB); // true
避免使用箭头函数的三种情况
由于箭头函数没有绑定this, 所以以下三种情况避免使用
- 对象的方法
// bad
const person = {
name: "小红",
sayName: () => {
console.log(this); // Window
return this.name
}
};
person.sayName();
person.sayName(); // undefined
- 定义原型方法
// right
function FooA() {
this.value = 1;
}
FooA.prototype.getValue = function() {
console.log(this); // 指向FooA的实例
console.log(this.value);
};
let foo1 = new FooA()
let foo2 = new FooA()
foo2.value = 2
foo1.getValue() // 1
foo2.getValue() // 2
// bad
function Foo() {
this.value = 1
}
Foo.prototype.getValue = () => {
console.log(this) // Window
console.log(this.value)
}
let foo = new Foo()
foo.getValue(); // undefined
- 作为事件的回调函数
// bad
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});