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模块:箭头函数 #31

Open
sfsoul opened this issue Jul 28, 2019 · 1 comment
Open

ES6模块:箭头函数 #31

sfsoul opened this issue Jul 28, 2019 · 1 comment

Comments

@sfsoul
Copy link
Owner

sfsoul commented Jul 28, 2019

关于箭头函数,引用 MDN 的介绍:

An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

基本语法

直接返回一个对象:

let func = (name, age) => ({personName: name, personAge: age});

与变量解构结合

const info = {
    name: 'zhangjing',
    age: 26
};

let func = ({name, age}) => ({personName: name, personAge: age});

func(info);  // {personAge: 26, personName: "zhangjing"}

箭头函数与普通函数的区别

自身没有this

箭头函数没有this,需要通过查找作用域链来确定this的值。意味着如果箭头函数被非箭头函数包含,this 绑定的就是最近一层非箭头函数的 this。由于箭头函数没有this,所以也不能用callapplybind这些方法来改变this的指向:

var value = 1;
var result = (() => {
    console.log(this); // Window对象
    return this.value;
}).bind({value: 2})();

console.log(result); // 1

若用let value = 1来定义,则result的结果返回为undefined

没有arguments

箭头函数没有自己的arguments对象,它可以访问外围函数的arguments对象:

function getName() {
    return () => consolel.log(arguments[0]);
}
getName('zj')(); // 'zj'

可以通过命名参数或者 rest 参数的形式访问箭头函数的参数。

let getInfo = (...info) => console.log(info);
getInfo({name :'zj', age: 27}); // [{name :'zj', age: 27}]

不能通过new关键词调用

JS函数有两个内部方法:[[Call]][[Construct]]

当通过 new 调用函数时,执行[[Construct]]方法,创建一个实例对象,然后再执行函数体,将this绑定到实例上。

当直接调用的时候,执行[[Call]]方法,直接执行函数体。

箭头函数并没有[[Construct]]方法,不能被用作构造函数,若通过new的方式调用,会报错。

const Func = (name) => console.log(name);
const zj = new Func('zhangjing'); // Uncaught TypeError: Func is not a constructor.

没有 new.target

因为不能使用 new 调用,所以也没有 new.target值。

没有原型

由于不能使用 new 调用箭头函数,所以也没有构建原型的需求,于是箭头函数不存在 prototype 这个属性。

const Func = (name) => console.log(name);
console.log(Func.prototype); // undefined

没有 super

连原型都没有,自然不能通过 super 来访问原型的属性,所以箭头函数是没有 super 的,跟 thisargumentsnew.target 一样,这些值都由外围最近一层非箭头函数决定。

自执行函数

自执行函数的形式为:

(function(){
    console.log(33);
}())

(function(){
    console.log(33);
})()

利用箭头简化自执行函数

(() => {
    console.log('arrow function');
})()

注意:使用以下写法会报错。原因

(() => {
    console.log('arrow function');    
}())    // Uncaught SyntaxError: Unexpected token (

参考文章

@sfsoul
Copy link
Owner Author

sfsoul commented Jul 29, 2019

一些Demo

定义事件的回调函数

箭头函数的错误写法:

// 在全局上下文下定义的箭头函数执行时 this 会指向 window,当单击事件发生时,浏览器会尝试用 button 作为上下文来执行事件回调函数,
// 但因为箭头函数的 this 上下文是在函数声明时就已经被确定了,是不能修改的。
// 这样 this.innerText 就等价于 window.innerText,最终导致点击按钮并没有发生变化。
const btnDom = document.getElementById('btn');
btnDom.addEventListener('click', () => {
    console.log(this === window);  //  true
    this.innerText = 'Click Me';
})

正确的写法:

//  当用户点击按钮时,事件回调函数中的 this 实际指向 button,这样按钮的文案就能发生改变。
btnDom.addEventListener('click', function(){
    console.log(this === btnDom); //  true
    this.innerText = 'Click Me';
})

箭头函数的正确写法:

btnDom.addEventListener('click', (event) => {
    const { target } = event.target;
    if (target.tagName.toLowerCase() === 'button') {
         target.innerText = 'Click Me';
    }
})

箭头函数和普通函数this指向问题(重点)

箭头函数的 this 指向是:谁调用箭头函数的外层 function,箭头函数的 this 就指向该对象;若箭头函数没有外层函数,则指向 Window 对象。

var name = 'window'

var person1 = {
  name: 'person1',
  show1: function () {
    console.log(this.name)
  },
  show2: () => console.log(this.name),
  show3: function () {
    return function () {
      console.log(this.name)
    }
  },
  show4: function () {
    return () => console.log(this.name)
  }
}
var person2 = { name: 'person2' }

person1.show1() // person1
person1.show1.call(person2) // person2

person1.show2() // window
person1.show2.call(person2) // window

person1.show3()() // window
person1.show3().call(person2) // person2
person1.show3.call(person2)() // window

person1.show4()() // person1
person1.show4().call(person2) // person1
person1.show4.call(person2)() // person2

var name = 'window'

function Person (name) {
  this.name = name;
  this.show1 = function () {
    console.log(this.name)
  }
  this.show2 = () => console.log(this.name)
  this.show3 = function () {
    return function () {
      console.log(this.name)
    }
  }
  this.show4 = function () {
    return () => console.log(this.name)
  }
}

var personA = new Person('personA')
var personB = new Person('personB')

personA.show1() // personA
personA.show1.call(personB) // personB

// new 对象的时候,把Person构造函数产生的闭包推到了show2的函数作用域中。show2没有自己的this,于是往上层作用域链找,就找到了闭包的那个this。
personA.show2() // personA
personA.show2.call(personB) // personA

personA.show3()() // window
personA.show3().call(personB) // personB
personA.show3.call(personB)() // window

personA.show4()() // personA
personA.show4().call(personB) // personA
personA.show4.call(personB)() // personB

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

No branches or pull requests

1 participant