箭头函数

引言

在 ES6 中,箭头函数是其中最有趣也最受欢迎的新增特性。顾名思义,箭头函数是一种使用 (=>) 定义函数的新语法,它与传统的 ES5 函数有些许不同。

这是一个用 ES5 语法编写的函数:

  1. function addTennum){

  2. return num +10;

  3. }

  4. timesTwo5);// 15

有了 ES6 的箭头函数后,我们可以用箭头函数这样表示:

  1. var addTen = num => num +10

  2.  

  3. addTen(5);// 15

箭头函数的写法短的多!由于隐式返回,我们可以省略花括号和 return 语句。

与常规 ES5 函数相比,了解箭头函数的行为方式非常重要。

箭头函数的特点

更短的语法

基础语法如下:

  1. (参数)=>{ statements }

接下来,拆解一下箭头函数的各种书写形式:

当没有参数时,使用一个圆括号代表参数部分

  1. let f =()=>5;

  2.  

  3. f();// 5

当只有一个参数时,可以省略圆括号。

  1. let f = num => num +5;

  2.  

  3. f(10);// 15

当有多个参数时,在圆括号内定义多个参数用逗号分隔。

  1. let f =(a,b)=> a + b;

  2.  

  3. f(1,2);// 3

当箭头函数的代码块部分多余一条语句,就需要使用大括号括起来,并且使用 return 语句。

  1. // 没有大括号,默认返回表达式结果

  2. let f1 =(a,b)=> a + b

  3. f1(1,2)// 3

  4.  

  5. // 有大括号,无return语句,没有返回值

  6. let f2 =(a,b)=>{a + b}

  7. f2(1,2)// undefined

  8.  

  9. // 有大括号,有return语句,返回结果

  10. let f3 =(a,b)=>{return a + b}

  11. f3(1,2)// 3

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

  1. //报错

  2. let f1 = num =>{num:num}

  3.  

  4. //不报错

  5. let f2 = num =>({num:num})

不能通过 new 关键字调用

箭头函数没有[[Construct]]方法,所以不能被用作构造函数。

  1. let F =()=>{};

  2.  

  3. // 报错 TypeError: F is not a constructor

  4. let f =new F();

没有原型

由于不可以通过 new 关键字调用,因而没有构建原型的需求,所以箭头函数不存在 prototype 这个属性。

  1. let F =()=>{};

  2. console.log(F.prototype)// undefined

没有 this 绑定

在 ES5 函数表达式中,this关键字根据调用它的上下文绑定到不同的值。但是,对于箭头函数,它this是词法绑定的。

箭头函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。

  1. window.name ='window_name'

  2. let obj ={

  3. name:'obj_name',

  4. f1:function(){

  5. returnthis.name

  6. },

  7. f2:()=>{

  8. returnthis.name

  9. }

  10. }

  11. obj.f1();// obj_name

  12. obj.f2();// window_name

上面代码中,obj.f1 是一个普通函数,obj.f2 是一个箭头函数。

当调用 obj.f1() 时,obj.f1 中 this 的指向的是 f1 函数的调用者,也就是 obj,所以返回 'obj_name'。

当调用 obj.f2() 时,由于 obj.f2 是箭头函数,所以 obj.f2 中this 指向的是定义 obj.f2 时的 this 指向,也就是 window,所以返回 'window_name'。

对箭头函数使用 call、apply、bind 时,不会改变 this 指向,只会传入参数

  1. window.name ='window_name';

  2.  

  3. let f1 =function(){returnthis.name}

  4. let f2 =()=>this.name

  5.  

  6. let obj ={name:'obj_name'}

  7.  

  8. f1.call(obj)// obj_name

  9. f2.call(obj)// window_name

  10.  

  11. f1.apply(obj)// obj_name

  12. f2.apply(obj)// window_name

  13.  

  14. f1.bind(obj)()// obj_name

  15. f2.bind(obj)()// window_name

上面代码中,声明了普通函数 f1,箭头函数 f2。

普通函数的 this 指向是动态可变的,所以在对 f1 使用 call、apply、bind 时,f1 内部的 this 指向会发生改变。

箭头函数的 this 指向在其定义时就已确定,永远不会发生改变,所以在对 f2 使用 call、apply、bind 时,会忽略传入的上下文参数。

this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this

没有 arguments、super、new.target

箭头函数中是没有 arguments、super、new.target 的绑定,这些值由外围最近一层非箭头函数决定。

以 arguments 为例,看如下代码:

  1. let f =()=>console.log(arguments);

  2.  

  3. //报错

  4. f();// arguments is not defined

由于在全局环境下,定义箭头函数 f,对于 f 来说,无法获取到外围非箭头函数的 arguments 值,所以此处报错。

再看一个例子:

  1. function fn(){

  2. let f =()=> console.log(arguments)

  3. f();

  4. }

  5. fn(1,2,3)// [1,2,3]

上面的代码,箭头函数 f 内部的 arguments,其实是函数 fn 的 arguments 变量。

若想在箭头函数中获取不定长度的参数列表,可以使用 ES6 中的 rest 参数解决:

  1. let f =(...args)=>console.log(args)

  2.  

  3. f(1,2,3,4,5)// [1,2,3,4,5]

不能用作 Generator 函数

在箭头函数中,不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。

自执行函数

在 ES6 的箭头函数出现之前,自执行函数一般会写成这样:

  1. (function(){

  2. console.log(1)

  3. })()

或者写成这样:

  1. (function(){

  2. console.log(1)

  3. }())

箭头函数当然也可以被用作自执行函数,可以这样写:

  1. (()=>{

  2. console.log(1)

  3. })()

但是,令大多数人想不到的是,下面这种写法会报错:

  1. (()=>{

  2. console.log(1)

  3. }())

那么,为什么会报错呢?

原因是,箭头函数属于 AssignmentExpression 的一种,当 CallExpression 时,要求左边的表达式是 MemberExpression 或其他 CallExpression。

原理就是这样了,具体可参见ECMAScript® 2015 规范

关于箭头函数的题目

在面试中关于箭头函数的考察,主要集中在 arguments 关键字的指向和箭头函数的this指向上,下面几道题目,供大家参考一下。

先上题目,由浅入深,答案后面给出。

题目1

  1. function foo(n){

  2. var f =()=> arguments[0]+ n;

  3. return f();

  4. }

  5.  

  6. let res = foo(2);

  7.  

  8. console.log(res);// 4

题目2

  1. function A(){

  2. this.foo =1

  3. }

  4.  

  5. A.prototype.bar =()=> console.log(this.foo)

  6.  

  7. let a =new A()

  8. a.bar()

题目3

  1. let res =(function(){

  2. return[

  3. (()=>this.x).bind({ x:'inner'})()

  4. ];

  5. }).call({ x:'outer'});

  6.  

  7. console.log(res)

题目4

  1. window.name ='window_name';

  2.  

  3. let obj1 ={

  4. name:'obj1_name',

  5. print1:function(){

  6. console.log(this.name)

  7. },

  8. print2:()=>console.log(this.name),

  9. print3:function(){

  10. returnfunction(){

  11. console.log(this.name)

  12. }

  13. },

  14. print4:function(){

  15. return()=>console.log(this.name)

  16. }

  17. }

  18.  

  19. let obj2 ={name:'obj2_name'}

  20.  

  21. obj1.print1()

  22. obj1.print1.call(obj2)

  23. obj1.print2()

  24. obj1.print2.call(obj2)

  25. obj1.print3()()

  26. obj1.print3().call(obj2)

  27. obj1.print3.call(obj2)()

  28. obj1.print4()()

  29. obj1.print4().call(obj2)

  30. obj1.print4.call(obj2)()

答案如下:

  1. // 题目1:4

  2. // 题目2:undefined

  3. // 题目3:["outer"]

  4. /**

  5. * 题目4:

  6. * obj1.print1() --obj1_name

  7. * obj1.print1.call(obj2) --obj2_name

  8. * obj1.print2() --window_name

  9. * obj1.print2.call(obj2) --window_name

  10. * obj1.print3()() --window_name

  11. * obj1.print3().call(obj2) --obj2_name

  12. * obj1.print3.call(obj2)() --window_name

  13. * obj1.print4()() --obj1_name

  14. * obj1.print4().call(obj2) --obj1_name

  15. * obj1.print4.call(obj2)() --obj2_name

  16. */

  17.  

箭头函数

 冰山工作室沙翼 冰山工作室 今天

在 ES6 中,箭头函数是其中最有趣也最受欢迎的新增特性。顾名思义,箭头函数是一种使用 (=>) 定义函数的新语法,它与传统的 ES5 函数有些许不同。

引言

在 ES6 中,箭头函数是其中最有趣也最受欢迎的新增特性。顾名思义,箭头函数是一种使用 (=>) 定义函数的新语法,它与传统的 ES5 函数有些许不同。

这是一个用 ES5 语法编写的函数:

  1. function addTennum){

  2. return num +10;

  3. }

  4. timesTwo5);// 15

有了 ES6 的箭头函数后,我们可以用箭头函数这样表示:

  1. var addTen = num => num +10

  2.  

  3. addTen(5);// 15

箭头函数的写法短的多!由于隐式返回,我们可以省略花括号和 return 语句。

与常规 ES5 函数相比,了解箭头函数的行为方式非常重要。

箭头函数的特点

更短的语法

基础语法如下:

  1. (参数)=>{ statements }

接下来,拆解一下箭头函数的各种书写形式:

当没有参数时,使用一个圆括号代表参数部分

  1. let f =()=>5;

  2.  

  3. f();// 5

当只有一个参数时,可以省略圆括号。

  1. let f = num => num +5;

  2.  

  3. f(10);// 15

当有多个参数时,在圆括号内定义多个参数用逗号分隔。

  1. let f =(a,b)=> a + b;

  2.  

  3. f(1,2);// 3

当箭头函数的代码块部分多余一条语句,就需要使用大括号括起来,并且使用 return 语句。

  1. // 没有大括号,默认返回表达式结果

  2. let f1 =(a,b)=> a + b

  3. f1(1,2)// 3

  4.  

  5. // 有大括号,无return语句,没有返回值

  6. let f2 =(a,b)=>{a + b}

  7. f2(1,2)// undefined

  8.  

  9. // 有大括号,有return语句,返回结果

  10. let f3 =(a,b)=>{return a + b}

  11. f3(1,2)// 3

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

  1. //报错

  2. let f1 = num =>{num:num}

  3.  

  4. //不报错

  5. let f2 = num =>({num:num})

不能通过 new 关键字调用

箭头函数没有[[Construct]]方法,所以不能被用作构造函数。

  1. let F =()=>{};

  2.  

  3. // 报错 TypeError: F is not a constructor

  4. let f =new F();

没有原型

由于不可以通过 new 关键字调用,因而没有构建原型的需求,所以箭头函数不存在 prototype 这个属性。

  1. let F =()=>{};

  2. console.log(F.prototype)// undefined

没有 this 绑定

在 ES5 函数表达式中,this关键字根据调用它的上下文绑定到不同的值。但是,对于箭头函数,它this是词法绑定的。

箭头函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。

  1. window.name ='window_name'

  2. let obj ={

  3. name:'obj_name',

  4. f1:function(){

  5. returnthis.name

  6. },

  7. f2:()=>{

  8. returnthis.name

  9. }

  10. }

  11. obj.f1();// obj_name

  12. obj.f2();// window_name

上面代码中,obj.f1 是一个普通函数,obj.f2 是一个箭头函数。

当调用 obj.f1() 时,obj.f1 中 this 的指向的是 f1 函数的调用者,也就是 obj,所以返回 'obj_name'。

当调用 obj.f2() 时,由于 obj.f2 是箭头函数,所以 obj.f2 中this 指向的是定义 obj.f2 时的 this 指向,也就是 window,所以返回 'window_name'。

对箭头函数使用 call、apply、bind 时,不会改变 this 指向,只会传入参数

  1. window.name ='window_name';

  2.  

  3. let f1 =function(){returnthis.name}

  4. let f2 =()=>this.name

  5.  

  6. let obj ={name:'obj_name'}

  7.  

  8. f1.call(obj)// obj_name

  9. f2.call(obj)// window_name

  10.  

  11. f1.apply(obj)// obj_name

  12. f2.apply(obj)// window_name

  13.  

  14. f1.bind(obj)()// obj_name

  15. f2.bind(obj)()// window_name

上面代码中,声明了普通函数 f1,箭头函数 f2。

普通函数的 this 指向是动态可变的,所以在对 f1 使用 call、apply、bind 时,f1 内部的 this 指向会发生改变。

箭头函数的 this 指向在其定义时就已确定,永远不会发生改变,所以在对 f2 使用 call、apply、bind 时,会忽略传入的上下文参数。

this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this

没有 arguments、super、new.target

箭头函数中是没有 arguments、super、new.target 的绑定,这些值由外围最近一层非箭头函数决定。

以 arguments 为例,看如下代码:

  1. let f =()=>console.log(arguments);

  2.  

  3. //报错

  4. f();// arguments is not defined

由于在全局环境下,定义箭头函数 f,对于 f 来说,无法获取到外围非箭头函数的 arguments 值,所以此处报错。

再看一个例子:

  1. function fn(){

  2. let f =()=> console.log(arguments)

  3. f();

  4. }

  5. fn(1,2,3)// [1,2,3]

上面的代码,箭头函数 f 内部的 arguments,其实是函数 fn 的 arguments 变量。

若想在箭头函数中获取不定长度的参数列表,可以使用 ES6 中的 rest 参数解决:

  1. let f =(...args)=>console.log(args)

  2.  

  3. f(1,2,3,4,5)// [1,2,3,4,5]

不能用作 Generator 函数

在箭头函数中,不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。

自执行函数

在 ES6 的箭头函数出现之前,自执行函数一般会写成这样:

  1. (function(){

  2. console.log(1)

  3. })()

或者写成这样:

  1. (function(){

  2. console.log(1)

  3. }())

箭头函数当然也可以被用作自执行函数,可以这样写:

  1. (()=>{

  2. console.log(1)

  3. })()

但是,令大多数人想不到的是,下面这种写法会报错:

  1. (()=>{

  2. console.log(1)

  3. }())

那么,为什么会报错呢?

原因是,箭头函数属于 AssignmentExpression 的一种,当 CallExpression 时,要求左边的表达式是 MemberExpression 或其他 CallExpression。

原理就是这样了,具体可参见ECMAScript® 2015 规范

关于箭头函数的题目

在面试中关于箭头函数的考察,主要集中在 arguments 关键字的指向和箭头函数的this指向上,下面几道题目,供大家参考一下。

先上题目,由浅入深,答案后面给出。

题目1

  1. function foo(n){

  2. var f =()=> arguments[0]+ n;

  3. return f();

  4. }

  5.  

  6. let res = foo(2);

  7.  

  8. console.log(res);// 4

题目2

  1. function A(){

  2. this.foo =1

  3. }

  4.  

  5. A.prototype.bar =()=> console.log(this.foo)

  6.  

  7. let a =new A()

  8. a.bar()

题目3

  1. let res =(function(){

  2. return[

  3. (()=>this.x).bind({ x:'inner'})()

  4. ];

  5. }).call({ x:'outer'});

  6.  

  7. console.log(res)

题目4

  1. window.name ='window_name';

  2.  

  3. let obj1 ={

  4. name:'obj1_name',

  5. print1:function(){

  6. console.log(this.name)

  7. },

  8. print2:()=>console.log(this.name),

  9. print3:function(){

  10. returnfunction(){

  11. console.log(this.name)

  12. }

  13. },

  14. print4:function(){

  15. return()=>console.log(this.name)

  16. }

  17. }

  18.  

  19. let obj2 ={name:'obj2_name'}

  20.  

  21. obj1.print1()

  22. obj1.print1.call(obj2)

  23. obj1.print2()

  24. obj1.print2.call(obj2)

  25. obj1.print3()()

  26. obj1.print3().call(obj2)

  27. obj1.print3.call(obj2)()

  28. obj1.print4()()

  29. obj1.print4().call(obj2)

  30. obj1.print4.call(obj2)()

答案如下:

  1. // 题目1:4

  2. // 题目2:undefined

  3. // 题目3:["outer"]

  4. /**

  5. * 题目4:

  6. * obj1.print1() --obj1_name

  7. * obj1.print1.call(obj2) --obj2_name

  8. * obj1.print2() --window_name

  9. * obj1.print2.call(obj2) --window_name

  10. * obj1.print3()() --window_name

  11. * obj1.print3().call(obj2) --obj2_name

  12. * obj1.print3.call(obj2)() --window_name

  13. * obj1.print4()() --obj1_name

  14. * obj1.print4().call(obj2) --obj1_name

  15. * obj1.print4.call(obj2)() --obj2_name

  16. */

 

阅读原文
阅读 62
 

写留言

posted on 2019-06-25 19:28  悬弟  阅读(257)  评论(0编辑  收藏  举报