关于javascript:箭头函数需要注意的点

36次阅读

共计 4089 个字符,预计需要花费 11 分钟才能阅读完成。

一、引言

置信大家对 ES6 的箭头函数都不生疏,咱们也会在日常开发的过程中常常应用到它,然而箭头函数有哪些值得咱们去留神的点呢?

二、箭头函数的几个应用留神点

1. 函数体内的 this 对象,就是定义时所在的对象,而不是应用时所在的对象

咱们都晓得,一般的函数外面的 this 是指向运行时的作用域的 ,然而箭头函数则不然, 箭头函数中的 this 是绑定定义时所在的作用域的

  • 一般函数
function foo() {setTimeout(function() {console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({id: 42});            //id: 21
  • 箭头函数
function foo() {setTimeout(() => {console.log('id:', this.id);
  }, 100);
}

var id = 21;

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

由下面两个例子可见,setTimeout 外面的回调函数运行的时候的作用域是 window,然而 this 定义的时候的作用域是函数 foo。所以,如果是一般函数,执行时 this 应该指向全局对象 window,这是应该输入 21。然而,箭头函数导致 this 总是指向函数定义失效时所在的对象,所以输入 42.

利用箭头函数的这个特点,咱们 能够应用箭头函数使 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。

所以,箭头函数转成 ES5 的代码如下:

// ES6
function foo() {setTimeout(() => {console.log('id:', this.id);        // 其实这里的 this 就是应用了外层作用域的 this
  }, 100);
}

// ES5
function foo() {
  var _this = this;            // 将外层作用域的 this 复制给一个变量

  setTimeout(function () {console.log('id:', _this.id);
  }, 100);
}

2. 不能够当作构造函数,也就是说,不能应用 new 命令,否则会抛出一个谬误

下面第一点说到:箭头函数没有本人的 this,所以天经地义箭头函数也不能够当作构造函数

上面尝试应用箭头函数作为构造函数:

var fun = (id, name) => {
    this.id = id;
    this.name = name;
    this.showName = () => {console.log(this.name);
    }
};

let obj = new fun(1, "hdl");     //TypeError: fun is not a constructor
obj.showName();

果然,报错了,说 fun 不是一个构造函数,这也反过来印证了第一点,箭头函数没有 this。

3. 不能够应用 arguments 对象,该对象在函数体内不存在。如果要用,能够用 rest 参数代替。

function func1(a, b) {console.log(arguments);
}

let func2 = (a, b) => {console.log(arguments);     //arguments is not defined
}

func1(1, 2);
func2(1, 2);

如果非要打印函数参数,能够 在箭头函数中应用 rest 参数代替 arguments 对象.

function func1(a, b) {console.log(arguments);
}

let func2 = (...rest) => {console.log(rest);     //[1, 2]
}

func1(1, 2);
func2(1, 2);

4. 没有 new.target

new.target 是 ES6 新引入的属性,一般函数如果通过 new 调用,new.target 会返回该函数的援用

function Cat() {console.log(new.target); 
}
let cat = new Cat(); // ƒ Cat() {console.log(new.target); }

此属性次要 用于确定构造函数是否为 new 调用的

在箭头函数里应用 new.target 会报错。

// 一般函数
let a = function() {console.log(new.target);
}
a(); // undefined

// 箭头函数
let b = () => {console.log(new.target); // 报错:Uncaught SyntaxError: new.target expression is not allowed here
};
b();

5. 没有原型和 super

因为不能通过 new 关键字调用,不能作为构造函数,所以 箭头函数不存在 prototype 这个属性

let func = () => {};
console.log(func.prototype) // undefined

箭头函数没有原型,故也不能通过 super 来拜访原型的属性,所以箭头函数也是没有 super 的。同 this、arguments、new.target 一样,这些值由外围最近一层非箭头函数决定。

6.call/apply/bind 办法无奈扭转箭头函数中 this 的指向

这个也很容易了解,call()、apply()、bind()办法的独特特点是能够扭转 this 的指向 ,用来动静批改函数执行时 this 的指向。但因为 箭头函数的 this 定义时就曾经确定了且不会扭转。所以这三个办法永远也扭转不了箭头函数 this 的指向。

var name = 'global name';
var obj = {name: 'huangdonglu'}
// 箭头函数定义在全局作用域
let func = () => {console.log(this.name);
};

func();     // global name
// this 的指向不会扭转,永远指向 Window 对象, 放到到 window 下的全局变量
func.call(obj);     // global name
func.apply(obj);    // global name
func.bind(obj)();   // global name

7. 箭头函数的解析程序绝对考前

尽管箭头函数中的箭头不是运算符,但箭头函数具备与惯例函数不同的非凡运算符优先级解析规定。

let a = false || function() {}; // ok
let b = false || () => {}; // SyntaxError: Malformed arrow function parameter list
let c = false || (() => {}); // ok

8. 箭头函数不反对重名参数

function foo(a, a) {console.log(a, arguments); // 2 Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}

var boo = (a, a) => { // 间接报错:Uncaught SyntaxError: Duplicate parameter name not allowed in this context
    console.log(a);
};
foo(1, 2);
boo(1, 2);

9. 不能够应用 yield 命令,因而箭头函数不能用作 Generator 函数。

三、箭头函数不实用场景

咱们都晓得,箭头函数比一般的函数要简洁很多,然而箭头函数也不是任何场景都能够实用的。

1. 第一个场合是定义对象的办法,且该办法外部包含 this。

  • 对象外面的函数应用箭头函数
const cat = {
    lives: 9,
    jumps : () => {this.lives--;}
};

cat.jumps();
console.log(cat.lives);     //9
  • 对象外面的办法应用一般办法
const cat = {
    lives: 9,
    jumps() {this.lives--;}
};

cat.jumps();
console.log(cat.lives);     //8

下面代码中,cat.jumps()办法是一个箭头函数,这是谬误的。调用 cat.jumps()时,如果是一般函数,该办法外部的 this 指向 cat;如果 写成下面那样的箭头函数,使得 this 指向全局对象 ,因而不会失去预期后果。这是因为 对象不形成独自的作用域,导致 jumps 箭头函数定义时的作用域就是全局作用域

2. 须要动静 this 时,也不应该应用动静函数。

var btn = document.getElementById('btn');
btn.addEventListener('click', () => {console.log(this);
});

因为 btn 的监听函数是一个箭头函数,导致外面的 this 就是全局对象 , 而不合乎咱们想操作按钮自身的需要。 如果改成一般函数,this 就会动静指向被点击的按钮对象。

除了下面这两点外,还总结了一下几点:

  • 不应被用在定义对象的办法上
  • 具备动静上下文的回调函数,也不应应用箭头函数
  • 不能利用在构造函数中
  • 防止在 prototype 上应用
  • 防止在须要 arguments 上应用

四、总结

就先总结下面这么多了,感激 @阮一峰 阮老师的文章:箭头函数应用留神点,最近也在浏览 NICHOLAS 的《深刻了解 ES6》,心愿能对 ES6 更加的相熟,也心愿大家可能对本文加以补充和斧正。

正文完
 0