普通函数基本用法
function fn(name,age=17){
console.log(name+","+age);
}
fn("Amy",18); // Amy,18
fn("Amy",""); // Amy,
fn("Amy"); // Amy,17
只有在未传递参数,或者参数为 undefined
时,才会使用默认参数,null
值被认为是有效的值传递。
function fn(name,age=17){
console.log(name+","+age);
}
fn("Amy",null); // Amy,null
不定参数:不定参数用来表示不确定参数个数,形如,...变量名
,由 ...
加上一个具名参数标识符组成。具名参数只能放在参数组的最后,并且有且只有一个不定参数。
function f(...values){
console.log(values.length);
}
f(1,2); // 2
f(1,2,3,4); // 4
this
和箭头函数
箭头函数提供了一种更加简洁的函数书写方式。基本语法是:
参数 => 函数体
var f = v => v;
// 等价于
var f = function(v){
return v;
}
f(1); // 1
-
当箭头函数没有参数或者有多个参数,要用 () 括起来。
var f = (a,b) => { let result = a+b; return result; } f(6,2); // 8
-
当箭头函数要返回对象的时候,为了区分于代码块,要用 () 将对象包裹起来。
// 报错 var f = (id,name) => {id: id, name: name}; f(6,2); // SyntaxError: Unexpected token : // 不报错 var f = (id,name) => ({id: id, name: name}); f(6,2); // {id: 6, name: 2}
学习如何在 JavaScript 里正确使用 this
就好比一场成年礼。
JavaScript 里,this
的值在普通函数被调用的时候才会指定(箭头函数恰恰相反)。这是个既强大又灵活的特点,但是你需要花点时间弄清楚函数调用的上下文是什么。但众所周知,这不是一件很简单的事,尤其是在返回一个函数或将函数当做参数传递的时候。
-
箭头函数能保存函数创建时的
this
值,而不是调用时的值。function fn(){ setTimeout(()=>{ // 定义时,this 绑定的是 fn 中的 this 对象 console.log(this.a); },0) } var a = 20; // fn 的 this 对象为传入的参数: {a: 19} fn.call({a: 18}); // 18
- 箭头函数根本没有自己的
this
,导致箭头函数体内部的this
就是外层函数体的this
,即从作用域链的上一层继承this
。 - 若普通函数是一个对象的方法,则它的
this
指针指向这个对象 - ES6 之前,JavaScript 的
this
对象一直很令人头大,回调函数,经常看到var self = this
这样的代码,为了将外部this
传递到回调函数中,那么有了箭头函数,就不需要这样做了,直接使用this
就行。
/**普通回调函数 */ function fn1() { // var self = this setTimeout(function() { console.log("id:", this.id) }, 0) } fn1.call({id: 12}) // 如果注释掉 `var self = this`, 则:id: undefined /**箭头回调函数 */ function foo() { setTimeout(() => { console.log('id:', this.id); }, 0); } foo.call({ id: 42 }); // id: 42 /**对象里的箭头函数 */ var age = 20; var Person1 = { 'age': 18, 'sayHello': function () { setTimeout(()=>{ console.log(this.age); }); } }; Person1.sayHello(); // 18
- 箭头函数根本没有自己的
-
下面看一个例子
let deck = { suits: ["hearts", "spades", "clubs", "diamonds"], cards: Array(52), createCardPicker: function() { return function() { let pickedCard = Math.floor(Math.random() * 52); let pickedSuit = Math.floor(pickedCard / 13); return {suit: this.suits[pickedSuit], card: pickedCard % 13}; } } } let cardPicker = deck.createCardPicker(); let pickedCard = cardPicker(); console.log("card: " + pickedCard.card + " of " + pickedCard.suit);
可以看到
createCardPicker
是个函数,并且它又返回了一个函数。 如果我们尝试运行这个程序,会发现它并没有弹出对话框而是报错了。 因为createCardPicker
返回的函数里的this
被设置成了window
而不是deck
对象。 因为我们只是独立的调用了cardPicker()
。 顶级的非方法式调用会将this
视为window
。 (注意:在严格模式下,this
为undefined
而不是window
)。为了解决这个问题,我们可以在函数被返回时就绑好正确的
this
。 这样的话,无论之后怎么使用它,都会引用绑定的deck
对象。 我们需要改变函数表达式来使用 ECMAScript 6 箭头语法。 箭头函数能保存函数创建时的this
值,而不是调用时的值:let deck = { suits: ["hearts", "spades", "clubs", "diamonds"], cards: Array(52), createCardPicker: function() { // NOTE: 箭头函数能保存函数创建时的 `this` 值 return () => { let pickedCard = Math.floor(Math.random() * 52); let pickedSuit = Math.floor(pickedCard / 13); return {suit: this.suits[pickedSuit], card: pickedCard % 13}; } } } let cardPicker = deck.createCardPicker(); let pickedCard = cardPicker(); console.log("card: " + pickedCard.card + " of " + pickedCard.suit);
还有:
class Handler { info: string; onClickGood = (e: Event) => { this.info = e.message } } let h = new Handler(); uiElement.addClickListener(h.onClickGood);
箭头函数不适用的场景
-
在对象里定义的函数,且该函数内部包括
this
的时候,不适用。若对象里面的函数使用箭头函数。
const cat = { lives: 9, jumps: () => { this.lives--; } }; cat.jumps(); console.log(cat.lives); // 9 //若改成这样,在对象的方法中返回箭头函数,并且立即执行,则可以达成效果,这是因为箭头函数的 `this`是 jumps 函数方法的 `this`,而该 this 指向本对象 const catt = { lives: 9, jumps() { return () => {this.lives--} } } let jump = catt.jumps() jump() // 立即执行 console.log("catt:", catt.lives) // 8
若对象里面的函数使用普通函数。
const cat = { lives: 9, jumps() { this.lives--; } }; cat.jumps(); console.log(cat.lives); // 8
第一个代码中,
cat.jumps()
方法是一个箭头函数,这是错误的。调用cat.jumps()
时,如果是普通函数,该this
指向cat
对象;如果写成上面那样的箭头函数,不会得到预期结果。这是因为对象不构成单独的作用域,导致jumps
箭头函数定义时指的是 window。 -
回调函数(callback)里的
this
参数当你将一个函数传递到另一个函数里调用时,你可能见到过回调函数里的
this
会报错。 因为当回调函数被调用时,它会被当成一个普通函数调用,this
将为undefined
或者window
。var clientData = { id: 096545, fullName: "Not Set", // setUsrName 是一个在 clientData 对象中的方法 setUserName: function (firstName, lastName){ this.fullName = firstName + " " + lastName; } } function getUserInput(firstName, lastName, callback){ // 调用回调函数 callback(firstName, lastName); } getUserInput("Barack","Obama",clientData.setUserName); console.log(clientData.fullName); // Not Set console.log(window.fullName); // Barack Obama
使用
Call
和Apply
函数来改变this
指向:每个 Javascript 中的函数都有两个方法:
call
和apply
。这些方法被用来设置函数内部的this
对象以及给此函数传递变量。这里我们演示
apply
函数实现,call
函数类似。(call
接收的第一个参数默认被用来在函数内部当做this
的对象(类似 python 类方法的self
),传递给函数的其他参数被挨个传递。)注:
apply
函数的第一个参数也是在函数内部作为this
的对象,然而后面的参数却是传递给函数的参数组成的数组。//注意到我们增加了新的参数作为回调对象,叫做“callbackObj” function getUserInput(firstName, lastName, callback ,callbackObj){ //code ..... callback.apply(callbackObj, [firstName, lastName]); //callback.call(callbackObj, firstName, lastName); } getUserInput("Barack", "Obama", clientData.setUserName, clientData); console.log(clientData.fullName); //Barack Obama
-
需要动态this时,也不应该使用动态函数。
var btn = document.getElementById('btn'); btn.addEventListener('click', () => { console.log(this); });
因为
btn
的监听函数是一个箭头函数,导致里面的this
就是全局对象,而不符合我们想操作按钮本身的需求。如果改成普通函数,this
就会动态指向被点击的按钮对象。
参考: