- 函數(shù)參數(shù)的默認(rèn)值
ES6 之前帘营,不能直接為函數(shù)的參數(shù)指定默認(rèn)值,只能采用變通的方法
function log(x, y) {
y = y || 'World';
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World
上面代碼檢查函數(shù)log的參數(shù)y有沒有賦值颂鸿,如果沒有择吊,則指定默認(rèn)值為World李根。這種寫法的缺點(diǎn)在于,如果參數(shù)y賦值了几睛,但是對應(yīng)的布爾值為false房轿,則該賦值不起作用。就像上面代碼的最后一行所森,參數(shù)y等于空字符冀续,結(jié)果被改為默認(rèn)值。
為了避免這個問題必峰,通常需要先判斷一下參數(shù)y是否被賦值洪唐,如果沒有,再等于默認(rèn)值吼蚁。
if (typeof y === 'undefined') {
y = 'World';
}
ES6 允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值凭需,即直接寫在參數(shù)定義的后面。
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
可以看到肝匆,ES6 的寫法比 ES5 簡潔許多粒蜈,而且非常自然。下面是另一個例子旗国。
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
const p = new Point();
p // { x: 0, y: 0 }
除了簡潔枯怖,ES6 的寫法還有兩個好處:首先,閱讀代碼的人能曾,可以立刻意識到哪些參數(shù)是可以省略的度硝,不用查看函數(shù)體或文檔;其次寿冕,有利于將來的代碼優(yōu)化蕊程,即使未來的版本在對外接口中,徹底拿掉這個參數(shù)驼唱,也不會導(dǎo)致以前的代碼無法運(yùn)行藻茂。
參數(shù)變量是默認(rèn)聲明的,所以不能用let或const再次聲明玫恳。
function foo(x = 5) {
let x = 1; // error
const x = 2; // error
}
上面代碼中辨赐,參數(shù)變量x是默認(rèn)聲明的,在函數(shù)體中京办,不能用let或const再次聲明掀序,否則會報錯。
使用參數(shù)默認(rèn)值時臂港,函數(shù)不能有同名參數(shù)
// 不報錯
function foo(x, x, y) {
// ...
}
// 報錯
function foo(x, x, y = 1) {
// ...
}
// SyntaxError: Duplicate parameter name not allowed in this context
另外森枪,一個容易忽略的地方是视搏,參數(shù)默認(rèn)值不是傳值的,而是每次都重新計算默認(rèn)值表達(dá)式的值县袱。也就是說浑娜,參數(shù)默認(rèn)值是惰性求值的
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
上面代碼中,參數(shù)p的默認(rèn)值是x + 1式散。這時筋遭,每次調(diào)用函數(shù)foo,都會重新計算x + 1暴拄,而不是默認(rèn)p等于 100
....................................................................................................................................
與解構(gòu)賦值默認(rèn)值結(jié)合使用
參數(shù)默認(rèn)值可以與解構(gòu)賦值的默認(rèn)值漓滔,結(jié)合起來使用
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined
上面代碼只使用了對象的解構(gòu)賦值默認(rèn)值,沒有使用函數(shù)參數(shù)的默認(rèn)值乖篷。只有當(dāng)函數(shù)foo的參數(shù)是一個對象時响驴,變量x和y才會通過解構(gòu)賦值生成。如果函數(shù)foo調(diào)用時沒提供參數(shù)撕蔼,變量x和y就不會生成豁鲤,從而報錯。通過提供函數(shù)參數(shù)的默認(rèn)值鲸沮,就可以避免這種情況
function foo({x, y = 5} = {}) {
console.log(x, y);
}
foo() // undefined 5
上面代碼指定琳骡,如果沒有提供參數(shù),函數(shù)foo的參數(shù)默認(rèn)為一個空對象
下面是另一個解構(gòu)賦值默認(rèn)值的例子
function fetch(url, { body = '', method = 'GET', headers = {} }) {
console.log(method);
}
fetch('http://example.com', {})
// "GET"
fetch('http://example.com')
// 報錯
上面代碼中讼溺,如果函數(shù)fetch的第二個參數(shù)是一個對象楣号,就可以為它的三個屬性設(shè)置默認(rèn)值。這種寫法不能省略第二個參數(shù)怒坯,如果結(jié)合函數(shù)參數(shù)的默認(rèn)值炫狱,就可以省略第二個參數(shù)。這時敬肚,就出現(xiàn)了雙重默認(rèn)值
function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
console.log(method);
}
fetch('http://example.com')
// "GET"
上面代碼中毕荐,函數(shù)fetch沒有第二個參數(shù)時束析,函數(shù)參數(shù)的默認(rèn)值就會生效艳馒,然后才是解構(gòu)賦值的默認(rèn)值生效,變量method才會取到默認(rèn)值GET
請問下面兩種寫法有什么差別员寇?
// 寫法一
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 寫法二
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
上面兩種寫法都對函數(shù)的參數(shù)設(shè)定了默認(rèn)值弄慰,區(qū)別是寫法一函數(shù)參數(shù)的默認(rèn)值是空對象,但是設(shè)置了對象解構(gòu)賦值的默認(rèn)值蝶锋;寫法二函數(shù)參數(shù)的默認(rèn)值是一個有具體屬性的對象陆爽,但是沒有設(shè)置對象解構(gòu)賦值的默認(rèn)值
// 函數(shù)沒有參數(shù)的情況
m1() // [0, 0]
m2() // [0, 0]
// x 和 y 都有值的情況
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]
// x 有值,y 無值的情況
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]
// x 和 y 都無值的情況
m1({}) // [0, 0];
m2({}) // [undefined, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
...................................................................................................................................
參數(shù)默認(rèn)值的位置
通常情況下扳缕,定義了默認(rèn)值的參數(shù)慌闭,應(yīng)該是函數(shù)的尾參數(shù)别威。因?yàn)檫@樣比較容易看出來,到底省略了哪些參數(shù)驴剔。如果非尾部的參數(shù)設(shè)置默認(rèn)值省古,實(shí)際上這個參數(shù)是沒法省略的
// 例一
function f(x = 1, y) {
return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 報錯
f(undefined, 1) // [1, 1]
// 例二
function f(x, y = 5, z) {
return [x, y, z];
}
f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 報錯
f(1, undefined, 2) // [1, 5, 2]
上面代碼中,有默認(rèn)值的參數(shù)都不是尾參數(shù)丧失。這時豺妓,無法只省略該參數(shù),而不省略它后面的參數(shù)布讹,除非顯式輸入undefined
如果傳入undefined琳拭,將觸發(fā)該參數(shù)等于默認(rèn)值,null則沒有這個效果
function foo(x = 5, y = 6) {
console.log(x, y);
}
foo(undefined, null)
// 5 null
上面代碼中描验,x參數(shù)對應(yīng)undefined白嘁,結(jié)果觸發(fā)了默認(rèn)值,y參數(shù)等于null膘流,就沒有觸發(fā)默認(rèn)值
..................................................................................................................................
函數(shù)的 length 屬性
指定了默認(rèn)值以后权薯,函數(shù)的length屬性,將返回沒有指定默認(rèn)值的參數(shù)個數(shù)睡扬。也就是說盟蚣,指定了默認(rèn)值后,length屬性將失真
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
上面代碼中卖怜,length屬性的返回值屎开,等于函數(shù)的參數(shù)個數(shù)減去指定了默認(rèn)值的參數(shù)個數(shù)。比如马靠,上面最后一個函數(shù)奄抽,定義了 3 個參數(shù),其中有一個參數(shù)c指定了默認(rèn)值甩鳄,因此length屬性等于3減去1逞度,最后得到2
這是因?yàn)閘ength屬性的含義是,該函數(shù)預(yù)期傳入的參數(shù)個數(shù)妙啃。某個參數(shù)指定默認(rèn)值以后档泽,預(yù)期傳入的參數(shù)個數(shù)就不包括這個參數(shù)了。同理揖赴,后文的 rest 參數(shù)也不會計入length屬性
(function(...args) {}).length // 0
如果設(shè)置了默認(rèn)值的參數(shù)不是尾參數(shù)馆匿,那么length屬性也不再計入后面的參數(shù)了
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
作用域
一旦設(shè)置了參數(shù)的默認(rèn)值,函數(shù)進(jìn)行聲明初始化時燥滑,參數(shù)會形成一個單獨(dú)的作用域(context)渐北。等到初始化結(jié)束,這個作用域就會消失铭拧。這種語法行為赃蛛,在不設(shè)置參數(shù)默認(rèn)值時恃锉,是不會出現(xiàn)的
var x = 1;
function f(x, y = x) {
console.log(y);
}
f(2) // 2
上面代碼中,參數(shù)y的默認(rèn)值等于變量x呕臂。調(diào)用函數(shù)f時淡喜,參數(shù)形成一個單獨(dú)的作用域。在這個作用域里面诵闭,默認(rèn)值變量x指向第一個參數(shù)x炼团,而不是全局變量x,所以輸出是2
再看下面的例子
let x = 1;
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1
上面代碼中疏尿,函數(shù)f調(diào)用時瘟芝,參數(shù)y = x形成一個單獨(dú)的作用域。這個作用域里面褥琐,變量x本身沒有定義锌俱,所以指向外層的全局變量x。函數(shù)調(diào)用時敌呈,函數(shù)體內(nèi)部的局部變量x影響不到默認(rèn)值變量x
如果此時贸宏,全局變量x不存在,就會報錯
function f(y = x) {
let x = 2;
console.log(y);
}
f() // ReferenceError: x is not defined
下面這樣寫磕洪,也會報錯
var x = 1;
function foo(x = x) {
// ...
}
foo() // ReferenceError: x is not defined
上面代碼中吭练,參數(shù)x = x形成一個單獨(dú)作用域。實(shí)際執(zhí)行的是let x = x析显,由于暫時性死區(qū)的原因鲫咽,這行代碼會報錯”x 未定義“
如果參數(shù)的默認(rèn)值是一個函數(shù),該函數(shù)的作用域也遵守這個規(guī)則谷异。請看下面的例子
let foo = 'outer';
function bar(func = () => foo) {
let foo = 'inner';
console.log(func());
}
bar(); // outer
上面代碼中分尸,函數(shù)bar的參數(shù)func的默認(rèn)值是一個匿名函數(shù),返回值為變量foo歹嘹。函數(shù)參數(shù)形成的單獨(dú)作用域里面箩绍,并沒有定義變量foo,所以foo指向外層的全局變量foo尺上,因此輸出outer
如果寫成下面這樣材蛛,就會報錯
function bar(func = () => foo) {
let foo = 'inner';
console.log(func());
}
bar() // ReferenceError: foo is not defined
上面代碼中,匿名函數(shù)里面的foo指向函數(shù)外層尖昏,但是函數(shù)外層并沒有聲明變量foo仰税,所以就報錯了
- rest參數(shù)
ES6 引入 rest 參數(shù)(形式為 ...變量名),用于獲取函數(shù)的多余參數(shù)抽诉,這樣就不需要使用arguments對象了。rest 參數(shù)搭配的變量是一個數(shù)組吐绵,該變量將多余的參數(shù)放入數(shù)組中
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
上面代碼的add函數(shù)是一個求和函數(shù)迹淌,利用 rest 參數(shù)河绽,可以向該函數(shù)傳入任意數(shù)目的參數(shù)
下面是一個 rest 參數(shù)代替arguments變量的例子
// arguments變量的寫法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest參數(shù)的寫法
const sortNumbers = (...numbers) => numbers.sort();
arguments對象不是數(shù)組,而是一個類似數(shù)組的對象唉窃。所以為了使用數(shù)組的方法耙饰,必須使用Array.prototype.slice.call先將其轉(zhuǎn)為數(shù)組。rest 參數(shù)就不存在這個問題纹份,它就是一個真正的數(shù)組苟跪,數(shù)組特有的方法都可以使用。下面是一個利用 rest 參數(shù)改寫數(shù)組push方法的例子
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
}
var a = [];
push(a, 1, 2, 3)
注意蔓涧,rest 參數(shù)之后不能再有其他參數(shù)(即只能是最后一個參數(shù))件已,否則會報錯
// 報錯
function f(a, ...b, c) {
// ...
}
函數(shù)的length屬性,不包括 rest 參數(shù)
(function(a) {}).length // 1
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1
3.嚴(yán)格模式
從 ES5 開始元暴,函數(shù)內(nèi)部可以設(shè)定為嚴(yán)格模式
function doSomething(a, b) {
'use strict';
// code
}
ES2016 做了一點(diǎn)修改篷扩,規(guī)定只要函數(shù)參數(shù)使用了默認(rèn)值、解構(gòu)賦值茉盏、或者擴(kuò)展運(yùn)算符鉴未,那么函數(shù)內(nèi)部就不能顯式設(shè)定為嚴(yán)格模式,否則會報錯
// 報錯
function doSomething(a, b = a) {
'use strict';
// code
}
// 報錯
const doSomething = function ({a, b}) {
'use strict';
// code
};
// 報錯
const doSomething = (...a) => {
'use strict';
// code
};
const obj = {
// 報錯
doSomething({a, b}) {
'use strict';
// code
}
};
這樣規(guī)定的原因是鸠姨,函數(shù)內(nèi)部的嚴(yán)格模式铜秆,同時適用于函數(shù)體和函數(shù)參數(shù)。但是讶迁,函數(shù)執(zhí)行的時候羽峰,先執(zhí)行函數(shù)參數(shù),然后再執(zhí)行函數(shù)體添瓷。這樣就有一個不合理的地方梅屉,只有從函數(shù)體之中,才能知道參數(shù)是否應(yīng)該以嚴(yán)格模式執(zhí)行鳞贷,但是參數(shù)卻應(yīng)該先于函數(shù)體執(zhí)行
// 報錯
function doSomething(value = 070) {
'use strict';
return value;
}
上面代碼中坯汤,參數(shù)value的默認(rèn)值是八進(jìn)制數(shù)070,但是嚴(yán)格模式下不能用前綴0表示八進(jìn)制搀愧,所以應(yīng)該報錯惰聂。但是實(shí)際上,JavaScript 引擎會先成功執(zhí)行value = 070咱筛,然后進(jìn)入函數(shù)體內(nèi)部搓幌,發(fā)現(xiàn)需要用嚴(yán)格模式執(zhí)行,這時才會報錯迅箩。
雖然可以先解析函數(shù)體代碼溉愁,再執(zhí)行參數(shù)代碼,但是這樣無疑就增加了復(fù)雜性饲趋。因此拐揭,標(biāo)準(zhǔn)索性禁止了這種用法撤蟆,只要參數(shù)使用了默認(rèn)值、解構(gòu)賦值堂污、或者擴(kuò)展運(yùn)算符家肯,就不能顯式指定嚴(yán)格模式。
兩種方法可以規(guī)避這種限制盟猖。第一種是設(shè)定全局性的嚴(yán)格模式讨衣,這是合法的
'use strict';
function doSomething(a, b = a) {
// code
}
第二種是把函數(shù)包在一個無參數(shù)的立即執(zhí)行函數(shù)里面
const doSomething = (function () {
'use strict';
return function(value = 42) {
return value;
};
}());
- name屬性
函數(shù)的name屬性,返回該函數(shù)的函數(shù)名
function foo() {}
foo.name // "foo"
這個屬性早就被瀏覽器廣泛支持式镐,但是直到 ES6反镇,才將其寫入了標(biāo)準(zhǔn)。
需要注意的是碟案,ES6 對這個屬性的行為做出了一些修改愿险。如果將一個匿名函數(shù)賦值給一個變量,ES5 的name屬性价说,會返回空字符串辆亏,而 ES6 的name屬性會返回實(shí)際的函數(shù)名
var f = function () {};
// ES5
f.name // ""
// ES6
f.name // "f"
上面代碼中,變量f等于一個匿名函數(shù)鳖目,ES5 和 ES6 的name屬性返回的值不一樣
如果將一個具名函數(shù)賦值給一個變量扮叨,則 ES5 和 ES6 的name屬性都返回這個具名函數(shù)原本的名字
const bar = function baz() {};
// ES5
bar.name // "baz"
// ES6
bar.name // "baz"
- 箭頭函數(shù)
ES6 允許使用“箭頭”(=>)定義函數(shù)
var f = v => v;
// 等同于
var f = function (v) {
return v;
};
如果箭頭函數(shù)不需要參數(shù)或需要多個參數(shù),就使用一個圓括號代表參數(shù)部分
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
如果箭頭函數(shù)的代碼塊部分多于一條語句领迈,就要使用大括號將它們括起來彻磁,并且使用return語句返回
var sum = (num1, num2) => { return num1 + num2; }
由于大括號被解釋為代碼塊,所以如果箭頭函數(shù)直接返回一個對象狸捅,必須在對象外面加上括號衷蜓,否則會報錯
// 報錯
let getTempItem = id => { id: id, name: "Temp" };
// 不報錯
let getTempItem = id => ({ id: id, name: "Temp" });
如果箭頭函數(shù)只有一行語句,且不需要返回值尘喝,可以采用下面的寫法磁浇,就不用寫大括號了
let fn = () => void doesNotReturn();
箭頭函數(shù)可以與變量解構(gòu)結(jié)合使用
const full = ({ first, last }) => first + ' ' + last;
// 等同于
function full(person) {
return person.first + ' ' + person.last;
}
箭頭函數(shù)使得表達(dá)更加簡潔
const isEven = n => n % 2 == 0;
const square = n => n * n;
上面代碼只用了兩行,就定義了兩個簡單的工具函數(shù)朽褪。如果不用箭頭函數(shù)置吓,可能就要占用多行,而且還不如現(xiàn)在這樣寫醒目缔赠。
箭頭函數(shù)的一個用處是簡化回調(diào)函數(shù)衍锚。
// 正常函數(shù)寫法
[1,2,3].map(function (x) {
return x * x;
});
// 箭頭函數(shù)寫法
[1,2,3].map(x => x * x);
另一個例子是
// 正常函數(shù)寫法
var result = values.sort(function (a, b) {
return a - b;
});
// 箭頭函數(shù)寫法
var result = values.sort((a, b) => a - b);
下面是 rest 參數(shù)與箭頭函數(shù)結(jié)合的例子
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)
// [1,2,3,4,5]
const headAndTail = (head, ...tail) => [head, tail];
headAndTail(1, 2, 3, 4, 5)
// [1,[2,3,4,5]]
使用注意點(diǎn)
箭頭函數(shù)有幾個使用注意點(diǎn)
(1)函數(shù)體內(nèi)的this對象,就是定義時所在的對象嗤堰,而不是使用時所在的對象戴质。
(2)不可以當(dāng)作構(gòu)造函數(shù),也就是說,不可以使用new命令置森,否則會拋出一個錯誤斗埂。
(3)不可以使用arguments對象符糊,該對象在函數(shù)體內(nèi)不存在凫海。如果要用,可以用 rest 參數(shù)代替男娄。
(4)不可以使用yield命令行贪,因此箭頭函數(shù)不能用作 Generator 函數(shù)
上面四點(diǎn)中,第一點(diǎn)尤其值得注意模闲。this對象的指向是可變的建瘫,但是在箭頭函數(shù)中,它是固定的
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42
上面代碼中尸折,setTimeout的參數(shù)是一個箭頭函數(shù)啰脚,這個箭頭函數(shù)的定義生效是在foo函數(shù)生成時,而它的真正執(zhí)行要等到 100 毫秒后实夹。如果是普通函數(shù)橄浓,執(zhí)行時this應(yīng)該指向全局對象window,這時應(yīng)該輸出21亮航。但是荸实,箭頭函數(shù)導(dǎo)致this總是指向函數(shù)定義生效時所在的對象(本例是{id: 42}),所以輸出的是42缴淋。
箭頭函數(shù)可以讓setTimeout里面的this准给,綁定定義時所在的作用域,而不是指向運(yùn)行時所在的作用域重抖。下面是另一個例子
function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭頭函數(shù)
setInterval(() => this.s1++, 1000);
// 普通函數(shù)
setInterval(function () {
this.s2++;
}, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0
上面代碼中露氮,Timer函數(shù)內(nèi)部設(shè)置了兩個定時器,分別使用了箭頭函數(shù)和普通函數(shù)钟沛。前者的this綁定定義時所在的作用域(即Timer函數(shù))畔规,后者的this指向運(yùn)行時所在的作用域(即全局對象)。所以讹剔,3100 毫秒之后油讯,timer.s1被更新了 3 次穆碎,而timer.s2一次都沒更新摄悯。
箭頭函數(shù)可以讓this指向固定化,這種特性很有利于封裝回調(diào)函數(shù)偷厦。下面是一個例子由捎,DOM 事件的回調(diào)函數(shù)封裝在一個對象里面
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方法中兔综,使用了箭頭函數(shù),這導(dǎo)致這個箭頭函數(shù)里面的this,總是指向handler對象软驰。否則涧窒,回調(diào)函數(shù)運(yùn)行時,this.doSomething這一行會報錯锭亏,因?yàn)榇藭rthis指向document對象纠吴。
this指向的固定化,并不是因?yàn)榧^函數(shù)內(nèi)部有綁定this的機(jī)制慧瘤,實(shí)際原因是箭頭函數(shù)根本沒有自己的this戴已,導(dǎo)致內(nèi)部的this就是外層代碼塊的this。正是因?yàn)樗鼪]有this锅减,所以也就不能用作構(gòu)造函數(shù)
所以糖儡,箭頭函數(shù)轉(zhuǎn)成 ES5 的代碼如下
// ES6
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
// ES5
function foo() {
var _this = this;
setTimeout(function () {
console.log('id:', _this.id);
}, 100);
}
上面代碼中,轉(zhuǎn)換后的 ES5 版本清楚地說明了怔匣,箭頭函數(shù)里面根本沒有自己的this握联,而是引用外層的this
請問下面的代碼之中有幾個this
function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}
var f = foo.call({id: 1});
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
上面代碼之中,只有一個this每瞒,就是函數(shù)foo的this金闽,所以t1、t2独泞、t3都輸出同樣的結(jié)果呐矾。因?yàn)樗械膬?nèi)層函數(shù)都是箭頭函數(shù),都沒有自己的this懦砂,它們的this其實(shí)都是最外層foo函數(shù)的this
除了this蜒犯,以下三個變量在箭頭函數(shù)之中也是不存在的,指向外層函數(shù)的對應(yīng)變量:arguments荞膘、super罚随、new.target
function foo() {
setTimeout(() => {
console.log('args:', arguments);
}, 100);
}
foo(2, 4, 6, 8)
// args: [2, 4, 6, 8]
上面代碼中,箭頭函數(shù)內(nèi)部的變量arguments羽资,其實(shí)是函數(shù)foo的arguments變量淘菩。
另外,由于箭頭函數(shù)沒有自己的this屠升,所以當(dāng)然也就不能用call()潮改、apply()、bind()這些方法去改變this的指向
(function() {
return [
(() => this.x).bind({ x: 'inner' })()
];
}).call({ x: 'outer' });
// ['outer']
上面代碼中腹暖,箭頭函數(shù)沒有自己的this汇在,所以bind方法無效,內(nèi)部的this指向外部的this脏答。
長期以來糕殉,JavaScript 語言的this對象一直是一個令人頭痛的問題亩鬼,在對象方法中使用this,必須非常小心阿蝶。箭頭函數(shù)”綁定”this雳锋,很大程度上解決了這個困擾
.................................................................................................................................
嵌套的箭頭函數(shù)
箭頭函數(shù)內(nèi)部,還可以再使用箭頭函數(shù)羡洁。下面是一個 ES5 語法的多重嵌套函數(shù)
function insert(value) {
return {into: function (array) {
return {after: function (afterValue) {
array.splice(array.indexOf(afterValue) + 1, 0, value);
return array;
}};
}};
}
insert(2).into([1, 3]).after(1); //[1, 2, 3]
上面這個函數(shù)玷过,可以使用箭頭函數(shù)改寫
let insert = (value) => ({into: (array) => ({after: (afterValue) => {
array.splice(array.indexOf(afterValue) + 1, 0, value);
return array;
}})});
insert(2).into([1, 3]).after(1); //[1, 2, 3]
下面是一個部署管道機(jī)制(pipeline)的例子,即前一個函數(shù)的輸出是后一個函數(shù)的輸入
const pipeline = (...funcs) =>
val => funcs.reduce((a, b) => b(a), val);
const plus1 = a => a + 1;
const mult2 = a => a * 2;
const addThenMult = pipeline(plus1, mult2);
addThenMult(5)
// 12
如果覺得上面的寫法可讀性比較差焚廊,也可以采用下面的寫法
const plus1 = a => a + 1;
const mult2 = a => a * 2;
mult2(plus1(5))
// 12