侧边栏壁纸
博主头像
孔子说JAVA博主等级

成功只是一只沦落在鸡窝里的鹰,成功永远属于自信且有毅力的人!

  • 累计撰写 285 篇文章
  • 累计创建 125 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

ES6新特性2:箭头函数

孔子说JAVA
2022-05-06 / 0 评论 / 0 点赞 / 145 阅读 / 6,264 字 / 正在检测是否收录...

ES6标准新增了一种新的函数:Arrow Function(箭头函数),允许使用箭头 => 定义函数。相对于普通函数,在语法上类似于 C#Java 8 中的相关功能,支持表达式和语句体。与普通函数不同,箭头函数与 this 周围的代码拥有相同的作用域。

1、箭头函数的特征及作用

1.1 箭头函数的特征

  1. 不需要 function 关键字来创建函数。
  2. 省略 return 关键字。
  3. this始终指向函数申明时所在作用域下的this值(即箭头函数里面根本没有自己的this,而是引用外层的this)。

1.2 箭头函数的作用

1)使表达更加简洁

const isEven = n => n % 2 === 0;
const square = n => n * n;

2)简化回调函数

// 普通函数写法
[1,2,3].map(function (x) {
  return x * x;
});

// 箭头函数写法
[1,2,3].map(x => x * x);

2、基本用法

2.1 语法

使用ES6箭头函数语法定义函数,将原函数的“function”关键字和函数名都删掉,并使用 => 连接参数列表和函数体。

  • 箭头函数是匿名函数,若需调用,须赋值给一个变量。匿名函数在递归、解除函数绑定的时候颇有用。
var arrow = v => v;
var arrow2 = () => {do something};
var arrow3 = ({name,age}) => 'name:' + name + ' age:' + age;

等价于以下代码:

var arrow = function (v){
    return v;
};
var arrow2 = function (){
    do something;
}; 
function arrow3(person){
    return 'name:' + person.name + ' age:' + person.age;
};

2.2 箭头函数的参数

箭头函数的参数要用 () 括起来,没有参数或者有多个参数都一样。

var f = (a,b) => a+b;
f(5,4);  //9

2.2.1 无参数箭头函数

// es5 写法
var fun = function() {
}

// es6 箭头函数写法
// 没有参数时,需要用()进行占位,代表参数部分
var fn = () => {
}

// 以上2种写法是等价的。

2.2.2 1个参数箭头函数

当函数参数只有一个,括号可以省略;但是没有参数时,括号不可以省略。

let print = function (obj){
    console.log(obj);
}
// 简写为:
let print2 = obj => console.log(obj);

2.2.3 多个参数箭头函数

let sum = function(a,b){
    return a + b;
}
// 简写为:
let sum2 = (a,b) => a + b;
// 也可以简写为:
let sum3 = (a,b) => { return a + b; }

2.2.4 可变参数箭头函数

var fn = function(a, b, ...args) {
}
// 简写为:
var fn = (a, b, ...args) => {
}

2.3 箭头函数的函数体

函数体如果只包含一个表达式,可以省略{ … }和return, 结果会自动返回。若包含多条语句时需用 {} 包裹起来,表示代码块,这时候就不能省略{ … }和return。

var f = (m,m) => {
 let result = m+n;
 return result;
}
f(3,4);  // 7

let sum3 = (a.b) =>{
    return a+b;
}

let hello3 = () =>{
    console.log("Hello");
    console.log("World");
};
hello3();

2.4 箭头函数的返回值

如果返回多个语句,就用 {} 来将其括起来,并用 return 返回。

var sum = (num1,num2) => {return num1+num2} 
console.log(sum(1,2));  // 3

这里的{}被解析为代码块,所以当箭头函数要返回对象的时候,为了区分于代码块,要用 () 将对象包裹起来,即在在返回的对象外加一层 ()

// 报错
var f = (id,name) => {id: id, name: name};
f(6,2);  // SyntaxError: Unexpected token :
 
// 不报错
var f = (id,name) => ({id: id, name: name});
f(6,2);  // {id: 6, name: 2}

对象参数和返回值

// 注意,用小括号包含大括号则是对象的定义,而非函数主体
x => {key: x} // 报错
x => ({key: x}) // 正确
 
// 如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
// 报错
let getTempItem = id => { id: id, name: "Temp" };
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });
 
// 如果箭头函数只有一行语句,且不需要返回值,可以采用下面的写法,就不用写大括号了。
let fn = () => void doesNotReturn();

3、扩展用法

3.1 显示返回和隐式返回

显示返回如下:

const double3 = numbers.map(number => {
    return number * 2;  
    //return 返回内容;
})

隐式返回:

当你想简单返回一些东西的时候:去掉return和大括号,把返回内容移到一行,较为简洁;

const double3 = numbers.map(number => number * 2);

3.2 函数默认值

ES6 之前,无法给一个函数参数设置默认值,只能采用变通写法。ES6之后可以给参数指定默认值。

// ES6 之前,无法给一个函数参数设置默认值,只能采用变通写法:
function add(a,b){ // 如果没有给参数b传值,则b=1
    b = b || 1;
    return a+b;
}
console.log(add(10));	//输出11
console.log(add(10,20));	//输出30
 
 
// ES6之后
function add(a,b = 1){
    return a+b;
}
console.log(add(10));	//输出11
console.log(add(10,20));	//输出30

3.3 对象的函数属性简写

let person = {
    name: "Tom",
    // ES6之前
    eat: function (food){
        console.log(this.name + "在吃" + food);
    }
    // 箭头函数写法
    eat2: food => console.log(person.name + "在吃" + food);
	// 简写版
	eat3(food){
        console.log(this.name + "在吃" + food);
    }
}

3.4 箭头函数结合解构表达式

const person = {
    name: "Tom",
    age: 20,
    language: ["Java","Python","JS"]
}
// 普通写法
function hello(person){
    console.log("Hello," + person.name);
}
 
// 以下是使用箭头函数和解构表达式的写法
var hi = ({name}) => console.log("hello," + name);
hi(person);

// 箭头函数和变量解构结合使用
const full = ({first, last}) => first + last

3.5 箭头函数与rest参数结合

const numbers = (...nums) => nums;
console.log(numbers(1,2,3,4,5));    // [1, 2, 3, 4, 5]

4、使用时的注意事项

ES6中的箭头函数,相对于普通函数来说更加简洁,且对this的指向问题进行了改进,应用起来更加的方便。箭头函数使用时的注意点如下:

  1. 函数体内的 this对象是定义时所在的对象,而不是使用时所在的对象。this对象的指向本身是可变的,但是它在箭头函数中是固定的
  2. 箭头函数不可以当做构造函数,不可以使用new命令,否则会抛出错误
  3. 不可以使用arguments对象,arguments对象在函数体内不存在。可以用rest参数(…num)来代替
  4. 不可以使用yield命令,因此箭头函数不能用作generator函数(构造函数)
// 注意点:没有 this、super、arguments 和 new.target 绑定。
var func = () => {
  // 箭头函数里面没有 this 对象,
  // 此时的 this 是外层的 this 对象,即 Window 
  console.log(this)
}
func(55)  // Window 
 
var func = () => {    
  console.log(arguments)
}
func(55);  // ReferenceError: arguments is not defined

// 箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象。
function fn(){
  setTimeout(()=>{
    // 定义时,this 绑定的是 fn 中的 this 对象
    console.log(this.a);
  },0)
}
var a = 20;
// fn 的 this 对象为 {a: 18}
fn.call({a: 18});  // 18
// 不可以作为构造函数,也就是不能使用 new 命令,否则会报错

5、箭头函数的this

5.1 箭头函数的this

对于普通函数来说,内部的this指向函数运行时所在的对象,但是这一点对箭头函数不成立。

  • 箭头函数没有自己的this对象,内部的this就是定义时上层作用域中的this。也就是说,箭头函数内部的this指向是固定的,相比之下,普通函数的this指向是可变的。
  • 无论箭头函数嵌套多少层,this都指向外部的对象。
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

上例中setTimeout()的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到 100 毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以打印出来的是42。

function Timer() {
    this.s1 = 0;
    this.s2 = 0;
    // 箭头函数
    setInterval(() => this.s1++, 1000);
    // 普通函数
    setInterval(function () {
        this.s2++;
    }, 1000);
}

var timer = new Timer();

setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0

上面代码中,Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(即Timer函数),后者的this指向运行时所在的作用域(即全局对象)。所以,3100 毫秒之后,timer.s1被更新了 3 次,而timer.s2一次都没更新。

箭头函数实际上可以让this指向固定化,绑定this使得它不再可变,这种特性很有利于封装回调函数。下面是一个例子,DOM 事件的回调函数封装在一个对象里面。

var handler = {
  id: '123456',

  init: function() {
    document.addEventListener('click',
      event => this.doSomething(event.type), false);
  },

  doSomething: function(type) {
    console.log('Handling ' + type  + ' for ' + this.id);
  }
};

上面代码的init()方法中,使用了箭头函数,这导致这个箭头函数里面的this,总是指向handler对象。如果回调函数是普通函数,那么运行this.doSomething()这一行会报错,因为此时this指向document对象。

总之,箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。

5.2 this适合使用的场景

ES6 之前,JavaScript 的 this 对象一直很令人头大,回调函数,经常看到 var self = this 这样的代码,为了将外部 this 传递到回调函数中,那么有了箭头函数,就不需要这样做了,直接使用 this 就行。

// 回调函数
var Person = {
    'age': 18,
    'sayHello': function () {
      setTimeout(function () {
        console.log(this.age);
      });
    }
};
var age = 20;
Person.sayHello();  // 20
 
var Person1 = {
    'age': 18,
    'sayHello': function () {
      setTimeout(()=>{
        console.log(this.age);
      });
    }
};
var age = 20;
Person1.sayHello();  // 18

所以,当我们需要维护一个 this 上下文的时候,就可以使用箭头函数。

5.3 this不适合使用的场景

定义函数的方法,且该方法中包含 this

var Person = {
    'age': 18,
    'sayHello': ()=>{
        console.log(this.age);
      }
};
var age = 20;
Person.sayHello();  // 20
// 此时 this 指向的是全局对象
 
var Person1 = {
    'age': 18,
    'sayHello': function () {
        console.log(this.age);
    }
};
var age = 20;
Person1.sayHello();   // 18
// 此时的 this 指向 Person1 对象

需要动态 this 的时候

var button = document.getElementById('userClick');
button.addEventListener('click', () => {
     this.classList.toggle('on');
});

button 的监听函数是箭头函数,所以监听函数里面的 this 指向的是定义的时候外层的 this 对象,即 Window,导致无法操作到被点击的按钮对象。

0

评论区