js中的箭头函数
箭头函数是ES6标准中新增的一种函数,在详细的讨论箭头函数之前,我们先来看看函数的四种定义方式
函数的四种定义方式
1、函数声明的方式(常用)
function sum (num1,num2) { return num1 + num2; }
注意函数定义最后没有加分号。必须有名字,会函数提升,在预解析阶段就已经创建,声明前后都可以调用。
2、函数表达式的方式
let sum = function(num1,num2) { return num1 + num2; };
一种变量赋值,函表达式可以没有名字(匿名函数),没有函数提升。函数表达式创建方式和函数声明几乎是等价的,这里代码定义了一个变量sum并将其初始化为一个函数,这个函数可以通过sum来引用。注意这里函数末尾是有分号的,与任何变量初始化语句一样。
3、箭头函数的方式
//多个参数需要括号 let sum = (num1,num2) => { return num1 + num2; }; //以下两种写法都有效,只有一个参数可以不写括号 let double = (x) => { return 2 * x }; let double = x => { return 2 * x }; //没有参数需要括号 let getRandom = () => { return Math.random() };
注意:箭头函数在参数和箭头之间不能换行,但是,可以通过在 ‘=>’ 之后换行,或者用 ‘( )’、'{ }'来实现换行
任何可以使用函数表达式的地方,都可以使用箭头函数。箭头函数语法简单,非常适合嵌入函数的场景。
let ints = [1,2,3]; console.log(ints.map(function (i){ return i + 1 }));//[2,3,4] console.log(ints.map(i => {return i + 1 }));//[2,3,4] //map()方法定义在JavaScript的Array中,它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。
4、构造函数的方式(不推荐)
let sum = new Function("num1","num2","return num1 + num2");
不推荐使用这种语法定义,因为这段代码会被解释两次:第一次是把它作为常规ES代码,第二次是解释传给构造函数的字符串。这显然会影响性能。不过,把函数想象为对象,把函数名想象为指针这很重要。而上面的语法很好地诠释了这个概念。
箭头函数中的this
箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值,即箭头函数内部的this
是词法作用域,由上下文确定
在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值:
- 如果该函数是一个构造函数,this指针指向一个新的对象
- 在严格模式下的函数调用下,this指向undefined
- 如果该函数是一个对象的方法,则它的this指针指向这个对象
- 等等
由于 箭头函数没有自己的this指针,通过 call()
或 apply()
方法调用一个函数时,只能传递参数
var adder = { base : 1, add : function(a) { var f = v => v + this.base; return f(a); }, addThruCall: function(a) { var f = v => v + this.base; var b = { base : 2 }; return f.call(b, a); } }; console.log(adder.add(1)); // 输出 2 console.log(adder.addThruCall(1)); // 仍然输出 2
接下来我们来看一个箭头函数应用的例子:我们想每隔一秒输出this.age的值加1
function Person() { // Person() 构造函数定义 `this`作为它自己的实例. this.age = 0; setInterval(function add() { // 在非严格模式, add()函数定义 `this`作为全局对象, // 与在 Person()构造函数中定义的 `this`并不相同. this.age++; console.log(this.age); }, 1000); } var p = new Person();
但是你会发现,每隔一秒都会有一个NaN
打印出来,而不是累加的数字。到底哪里错了呢? 实际上setInterval
里面的this
绑定到全局对象window,而window.age未定义,那么怎么解决这一问题呢?
在ECMAScript 3/5中,通过将this
值分配给封闭的变量,可以解决this
问题。
function Person() { var that = this; that.age = 0; setInterval(function add() { that.age++;// 回调引用的是`that`变量, 其值是预期的对象. console.log(that.age); }, 1000); } var p = new Person();
使用箭头函数解决,箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this
。因此,在下面的代码中,传递给setInterval
的函数内的this
与封闭函数中的this
值相同:
function Person() { this.age = 0; setInterval(() => { this.age++; console.log(this.age); }, 1000); } var p = new Person();
箭头函数不绑定arguments
箭头函数不绑定arguments对象。
function foo() { console.log(arguments[0]); } foo(5);//5 let bar = () => { console.log(arguments[0]); } bar(5)//arguments is not defined
虽然箭头函数没有arguments对象。但可以在包装函数中把它提供给箭头函数:
function foo() { let bar = () => { console.log(arguments[0]);//5 } bar() } foo(5);
箭头函数不能用作构造函数
和new一起使用会抛出异常
var Foo = () => {}; var foo = new Foo(); // TypeError: Foo is not a constructor
箭头函数没有prototype
属性
var Foo = () => {}; console.log(Foo.prototype); // undefined
返回对象字面量
记住用params => {object:literal}
这种简单的语法返回对象字面量是行不通的。
var func = () => { foo: 1 }; // Calling func() returns undefined! var func = () => ({foo: 1}); //所以,记得用圆括号把对象字面量包起来: var func = () => { foo: function() {} }; // SyntaxError: function statement requires a name
箭头函数也不能使用super和new.target