Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

es6必会之arrow functions #109

Open
FrankKai opened this issue Oct 8, 2018 · 2 comments
Open

es6必会之arrow functions #109

FrankKai opened this issue Oct 8, 2018 · 2 comments

Comments

@FrankKai
Copy link
Owner

FrankKai commented Oct 8, 2018

与let,const一样,对箭头函数的掌握也一直是模棱两可,因此需要深入学习一下,mdn的Arrow Functions就是很好的学习资料,除了阅读文档,我也会加入一些自己的思考。

@FrankKai
Copy link
Owner Author

FrankKai commented Oct 9, 2018

Arrow Function

  • Arrow function 的完整叫法是arrow function expression。
  • Arrow function 比函数表达式的语法更短。
  • Arrow function 没有自己的this,arguments,super,new.target
  • Arrow function 非常适用于non-method 函数,并且不能被用作constructor。

method function指的是对象的方法函数,而non-method则反之,也就是说箭头函数最好用在不是对象的method的场景,因此我们的API层将改为ES6的函数shorhand写法。

基本用法

  • 正常用法
  • 返回表达式 函数body不带{}
  • 单个入参 函数头不带()
  • 无参数 函数头写()
(foo, bar, baz) => { statements }
(foo, bar, baz) => { return expression } // (foo, bar, baz) => expression
( foo ) => { statements } // foo => { statements }
() => { statements }

高级用法

  • 返回单个对象 函数body ()包裹
  • Rest parameters和default parameters
  • 函数头里的参数列表支持解构
(foo, bar, baz) => ({a: 'a'})
(foo, bar, ...rest) => { statements }
(foo = 'foo', bar = true, bar = {a: 1, b: 2}) => { statements }
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a +b +c;
f(); // 6

关于Arrow functions,mozilla.org上有一篇深度剖析的文章。ES6 In Depth: Arrow functions

Description

shorter functions

var elements = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];
elements.map(function(element ) { 
  return element.length; 
}); // [8, 6, 7, 9]
elements.map(element => element.length); // [8, 6, 7, 9]
elements.map(({ length }) => length); // [8, 6, 7, 9]

其实最后2步做了这样一个操作:const { length } = element,由于解构element后参数不再是单个,因此需要用()包裹起来。

No this

js中this,其实是与面向对象编程的思想不符的,箭头函数一定程度上消除了这种困惑。
因为this在不同的情况下,指代的东西不同,在constructor中指代新对象,在'use strict'的函数中this为undefined,对象方法函数中的this又是这个对象。

function Person() {
  this.age = 0;
  setInterval(function growUp() {
    this.age++;
    console.log(this); // window
  }, 1000);
}
var p = new Person();
function Person() {
  var that = this;
  that.age = 0;
  setInterval(function growUp() {
    that.age++;
    console.log(this, that); // window, Person实例(创建2个则打印2个)
  }, 1000);
}
var p = new Person();
function Person(){
  this.age = 0;
  setInterval(() => {
    this.age++;
    console.log(this); // Person实例(创建2个则打印2个)
  }, 1000);
}
var p = new Person();
console.log(p);

使用箭头函数后,callback中的this不再由于闭包而指向window,直接指向了当前实例,因此也不需要像es5中的var that = this;显式将实例传入callback中。
使用箭头函数后,this变得不再让人困惑了!

strict mode Arrow Functions

严格模式下,箭头函数中的this也是ok的。

var f = () => { 'use strict'; return this; };
f() === window; // f() -> window

严格模式下,普通函数中的this是undefined。

var f = function() {'use strict'; return this; };
f() === undefined; //f() -> undefined

非严格模式下,this是window。

var f = function() { return this; };
f() === window; //f() -> window

显然箭头函数是strict mode下的更好写法。

通过call or apply调用Arrow Functions

由于call()或者apply()只能通过parameters传值,所以在箭头函数上用call(),apply(),this会被忽略。因为箭头函数没有arguments!

var adder = {
   base: 1,
   add: function(a) {
       var f = v => v + this.base; // {base: 1, add: ƒ, addThruCall: ƒ}
       return f(a);
   },
   addThruCall: function(a) {
       var f = v => v + this.base; // {base: 1, add: ƒ, addThruCall: ƒ}
       var b = {
            base: 2
       }
       return f.call(b, a);
   }
}
console.log(adder.add(1)); // 2
console.log(adder.addThruCall(1)); // 2,因为箭头函数没有arguments,call中的this会被忽略,this依然是当前的对象,因此还是2.
箭头函数中真的没有arguments吗?
const f = function() { console.log(arguments) };
f();

Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]

const f = () => { console.log(arguments); }
f();

Uncaught ReferenceError: arguments is not defined

arguments是enclosing scope的reference

var arguments = [1, 2, 3];
var arr = () => arguments[0];
arr(); // 1
function foo(n) {
  var f = () => arguments[0] + n; // foo's implicit arguments binding. arguments[0] is n
  return f();
}
foo(3); // 6

通过rest parameters可以获取到箭头函数的parameters!

var customArguments = [1, 2, 3];
var arr = () => arguments[0];
arr(); // 1
function foo(n) {
  var f = (...args) => args[0] + n;
  return f(customArguments[0]);
}
foo(3); // 4

Arrow functions作为methods使用(慎用)

在有this的情况下慎用,最好用function的shorthand。

'use strict';
var obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c: function() {
    console.log(this.i, this);
  }
}
obj.b(); // prints 0, Window {...} 
obj.c(); // prints 10, Object {...}

Object.defineProperty()中也可以说明箭头函数作为method不好。

'use strict';
var obj = {
  a: 10
};
Object.defineProperty(obj, 'b', {
  get: () => {
    console.log(this.a, typeof this.a, this); // undefined 'undefined' Window {...} (or the global object)
    return this.a + 10; // represents global object 'Window', therefore 'this.a' returns 'undefined'
  }
});

改成get()是OK的。

使用new操作符

箭头函数不能被当做constructor使用,否则会抛出一个错误。

var Foo = () => {};
var foo = new Foo(); // Uncaught TypeError: Foo is not a constructor

今天终于明白了Uncaught 仅仅代表error是否被捕获。

使用原型链上的方法

var Foo = () => {};
console.log(Foo.prototype);// undefined

function类型的可以。

var Foo= function() {};
console.log(Foo.prototype); // {constructor: ƒ}

没有constructor,没有prototype,这也是不能使用new的原因。

Function body

  • concise body expression 默认隐式返回值
  • block body statements 必须显式return

为什么{object: literal}必须用()返回?

因为直接写一个{ foo: 1},foo: 1会被当作一个statement。

var func = () => { foo: 1 };               
var func = () => { foo: function() {} }; 

foo会被当作label,1和function(){}被当作statement,没有任何return。

var func = () => ({foo: 1});

Arrow Function可以包含换行符吗?

不可以。

var func = ()
           => 1; 
// SyntaxError: expected expression, got '=>'

箭头函数的解析顺序是怎样的?

let callback;
callback = callback || function() {}; // ok
callback = callback || () => {};      
// SyntaxError: invalid arrow-function arguments
callback = callback || (() => {});    // ok

一些有趣的箭头函数用法

  • 空箭头函数
  • IIFE箭头函数
  • 三元操作符
  • Array filter,map
  • Promise链式调用
  • setTimeout
let empty = () => {};
(() => 'foobar')();
var simple = a => a > 15 ? 15: a;
let max = (a, b) => a > b ? a : b;
var arr = [5, 6, 13, 0, 1, 18, 23];
var sum = arr.reduce((a, b) => a + b);  // 66
var even = arr.filter(v => v % 2 == 0); // [6, 0, 18]
var double = arr.map(v => v * 2);  // [10, 12, 26, 0, 2, 36, 46]
promise.then(foo => {
  // ...
}).then(bar => {
  // ...
});
setTimeout( () => {
  console.log('I happen sooner');
  setTimeout( () => {
    console.log('I happen later');
  }, 1);
}, 1);

@FrankKai FrankKai changed the title es6必回之arrow functions es6必会之arrow functions Oct 9, 2018
@FrankKai
Copy link
Owner Author

FrankKai commented Aug 3, 2019

Vue中的箭头函数

原型链方法

// main.js
Vue.prototype.$appName = ()=> {
    console.log(this); // 绑定的this指向的是main.js
}
Vue.prototype.$appName = function(){
    console.log(this); // 这样才能绑定到Vue类,从而切换this到实例
}

单文件组件中的箭头函数

vue实例除了watcher以外,几乎所有的地方都是箭头函数,这样才可以获取到单文件组件这个实例。从这里可以看出,其实箭头函数的作用是:隐式绑定this到父作用域。这里的父作用域指的是单文件组件实例。

为什么watcher不能是箭头函数?

因为箭头函数中的this指向的是全局对象,不能指向当前vue实例。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant