箭头函数定义:ES6中允许使用(=>)来定义函数。箭头函数相当于箭头函数,并且简化了函数定义。
箭头函数有两种格式:当函数体是单条语句的时候可以省略{}和return;另一种是包含多条语句,不可以省略{}和return。
let foo = (name) => console.log(name);
foo('lalala')
let foo1 = (name) => {
if (name) return `我是${name}`
return 'liyou';
}
console.log(foo1());//liyou
console.log(foo1('猪猪侠'));//猪猪侠
这里需要注意的是如果箭头函数返回的是一个字面量对象,则需要用括号包裹该字面量对象返回
let foo2 = (name) => ({
a:1,
b:2,
name
})
console.log(foo2('zhuzhuxia'));
箭头函数与普通函数的区别:
声明方式不同,匿名函数:声明一个普通函数需要奥使用关键字function来完成,并且function既可以声明一个具名函数也可以声明一个匿名函数;声明一个箭头函数则只需要使用箭头就可以,无需使用关键词function,比普通函数声明更简洁;箭头函数只能声明匿名函数,但可以通过表达式的方式让箭头函数具名;
this指向不同:对与普通函数来说,内部的this指向函数运行时所在的对象,但是这一点在箭头函数上不成立。它没有自己的this对象,内部的this就是定义时上层作用域中的this。也就是说,箭头函数内部的this指向是固定的,相比之下,普通函数的this指向是可变的。
var age = 18;
var person = {
age:20,
sayAge:function() {
console.log('age',this.age);
},
sayAge1:() => {
console.log('age',this.age);
}
}
person.sayAge();//20
person.sayAge1();//18
箭头函数的this永远不会改变,call、apply、bind也无法改变:我们可以通过call、apply、bind来改变普通函数的this指向,但是由于箭头函数的this指向在它定义时就已经确定了,永远指向它定义时的上层作用域中的this,所以使用这些方法永远也改变不了箭头函数的this指向;
var age = 18;
var person = {
age:20,
sayAge:function() {
console.log('age',this.age);
},
sayAge1:() => {
console.log('age',this.age);
}
}
person.sayAge.call({age:30});//'age' 30
person.sayAge1.call({age:30});//'age' 18
箭头函数没有原型prototype:
var age = 18;
var person = {
age:20,
sayAge:function() {
console.log('age',this.age);
},
sayAge1:() => {
console.log('age',this.age);
}
}
console.log(person.sayAge.prototype);//{constructor: ƒ}
console.log(person.sayAge1.prototype);//undefined
箭头函数不能当做一个构造函数:因为箭头函数没有自己的this,它的this其实是继承了外层执行环境中的this,且this指向永远不会改变,并且箭头函数没有原型prototype,没法让他的实例的__proto__属性指向构造函数的prototype,所以箭头函数就无法作为构造函数,用new调用时会报错。
var fn = (name) => {
console.log('name',name);
}
const f = new fn('lalala')
new内部实现的过程:1、新建一个空对象;2、链接到原型;3、绑定this,执行构造函数;4、返回新对象;
function myNew() {
// 1、新建一个空对象
let obj = {}
// 2、获得构造函数
let con = arguments.__proto__.constructor
// 3、链接原型
obj.__proto__ = con.prototype
// 4、绑定this,执行构造函数
let res = con.apply(obj,arguments)
// 5、返回新对象
return typeof res === 'object' ? res : obj
}
没有new.target:new是从构造函数生成实例对象的命令。ES6为new命令引入了一个new.target属性,这个属性一般用在构造函数中,返回new调用的那个构造函数。如果构造函数不是通过new命令或者Reflect。construct()调用的,new.target会返回undefined,所以这个属性可以用来确定搜早函数是怎么调用的。
function fn(name) {
console.log('fn:', new.target)
}
fn('liyou') // undefined
new fn('liyou');/*fn: ƒ fn(name) {
console.log('fn:', new.target)
}*/
注意:new.target属性一般用在构造函数中,返回new调用的那个构造函数;箭头函数的this指向全局对象,在箭头函数中使用new.target会报错;箭头函数的this指向普通函数,他的new.target 就是指向该普通函数的引用;
箭头函数没有自己的arguments:箭头函数处于全局作用域中,没有arguments;
let fn = name => {
console.log(arguments)
}
let fn2 = function(name) {
console.log(arguments)
}
fn2() // Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]
fn() // 报错 Uncaught ReferenceError: arguments is not defined
箭头函数处于普通函数的函数作用域中,arguments则是上层普通函数的arguments
let fn2 = function(name) {
console.log('fn2:',arguments)
let fn = name => {
console.log('fn:',arguments)
}
fn()
}
fn2('liyou')
箭头函数可以用rest参数代替arguments
let fn3 = (a,...arr) => {
console.log(a,arr) //1, [2,3,4,5,6]
}
fn3(1,2,3,4,5,6)
箭头函数不能重复函数参数名称
function fn(name,name) {
console.log('fn2:',name)
}
let fn2 = (name,name) => {
console.log('fn',name)
}
fn('nan','jiu') // 'jiu'
fn2('nan','jiu') // 报错
不可以使用yield命令,因此箭头函数不能用作Generator函数
箭头函数不适用的场景:
对象方法,且方法中使用了this:
var name = '猪猪侠'
var person = {
name: 'zhuzhuxia',
say: function() {
console.log('say:',this.name)
},
say2: () => {
console.log('say2:',this.name)
}
}
person.say() // say: zhuzhuxia
person.say2() //say2: 猪猪侠
当函数需要动态this时
var button = document.querySelector('.btn');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
这里很显然会报错,因为按钮点击的回调是一个箭头函数,而箭头函数内部的this
永远都是指向它的上层作用域中的this,在这里就是window
,所以会报错。这里只需要将箭头函数改成普通函数就能正常调用了!
巩固知识点:
var name = '猪猪侠'
function Person(name) {
this.name = name
this.say1 = function () {
console.log(this.name);
}
this.say2 = () => console.log(this.name);
this.say3 = function () {
return function () {
console.log(this.name);
}
}
this.say4 = function () {
return () => {
console.log(this.name);
}
}
}
var person1 = new Person('zhu')
var person2 = new Person('xia')
person1.say1();//'zhu'
person1.say1.call(person2);//'xia'
person1.say2();//'zhu'
person1.say2.call(person2);//'zhu'
person1.say3()();//'猪猪侠'
person1.say3.call(person2)();//'猪猪侠'
person1.say3().call(person2);//'xia'
person1.say4()();//'zhu'
person1.say4.call(person2)();//'xia'
person1.say4().call(person2);//'zhu'
解析:
全局代码执行,person1 = new Person('zhu'),person2 = new Person('xia')
执行完,person1
中的this.name
为zhu
,person2
中的this.name
为xia
,OK这一点清楚后,继续往下看:
执行person1.foo1()
,foo1
为普通函数,所以this应该指向person1
,打印出zhu
执行person1.foo1.call(person2)
,foo1
为普通函数,并且用call改变了this指向,所以它里面的this应该指向person2
,打印出xia
执行person1.foo2()
,foo2
为箭头函数,它的this指向上层作用域,也就是person1,所以打印出zhu
执行person1.foo2.call(person2)
,箭头函数的this指向无法使用call改变,所以它的this还是指向person1,打印出zhu
执行person1.foo3()()
,这里先执行person1.foo3()
,它返回了一个普通函数,接着再执行这个函数,此时就相当于在全局作用域中执行了一个普通函数,所以它的this指向window,打印出猪猪侠
执行person1.foo3.call(person2)()
这个与上面类似,也是返回了一个普通函数再执行,其实前面的执行都不用关心,它也是相当于在全局作用域中执行了一个普通函数,所以它的this指向window,打印出猪猪侠
执行person1.foo3().call(person2)
这里就是把foo3返回的普通函数的this绑定到person2上,所以打印出xia
执行person1.foo4()()
,先执行person1.foo4()
返回了一个箭头函数,再执行这个箭头函数,由于箭头函数的this始终指向它的上层作用域,所以打印出zhu
执行person1.foo4.call(person2)()
,与上面类似只不过使用call把上层作用域的this改成了person2,所以打印出xia
执行person1.foo4().call(person2)
,这里是先执行了person1.foo4()
,返回了箭头函数,再试图通过call改变改变该箭头函数的this指向,上面我们说到箭头函数的this始终指向它的上层作用域,所以打印出zhu