FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
362 stars 39 forks source link

es6必会之arrow functions #109

Open FrankKai opened 5 years ago

FrankKai commented 5 years ago

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

FrankKai commented 5 years ago

Arrow Function

基本用法

高级用法

关于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

为什么{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

一些有趣的箭头函数用法

FrankKai commented 4 years ago

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实例。