- 什么是箭頭函數东跪?看下面的語法畸陡。
- 為什么要用箭頭函數鹰溜?一個字:短。
- 1 箭頭函數最大的優(yōu)點就是簡短丁恭。
- 2 箭頭函數不能用作構造函數曹动。適用于那些本來需要匿名函數的地方。
- 3 引入箭頭函數有兩個方面的作用:更簡短的函數并且不綁定this牲览。
1. 箭頭函數語法
- 標準形式:塊體
(參數1, 參數2, …, 參數N) => { 函數體 }
var arrowFunc1 = (x, y, z) => {
if (x > 0){
return y + z;
}else{
return y -z;
}
}
a = arrowFunc1(1, 2, 3);//5
b = arrowFunc1(-1, 2, 3);//-1
- 如果函數體只有一個表達式墓陈,可以寫成:簡寫體,省略 return
(參數1, 參數2, …, 參數N) => 表達式
// 你看看第献,多么簡潔
var arrowFunc = (x, y) => x + y;
a = arrowFunc(1, 2);//3
- 如果只有一個參數贡必,可以寫成:
單一參數 => {函數體}
單一參數 => 表達式
var arrowFunc = x => x * 2;
a = arrowFunc(2);//4
- 如果沒有參數,應該寫成一對圓括號痊硕。
() => {函數體}
() => 表達式
var arrowFunc = () => console.log('hello world');
arrowFunc(); //hello world
- 支持剩余參數和默認參數
(參數1, 參數2, ...rest) => {函數體}
(參數1 = 默認值1,參數2 = 默認值2, …, 參數N = 默認值N) => {函數體}
- 支持參數列表解構
- ES6 允許按照一定模式赊级,從數組和對象中提取值,對變量進行賦值岔绸,這被稱為解構(Destructuring)理逊。
var arrowFunc = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
a = arrowFunc(); // 6
2. 深入理解箭頭函數
- 更短的函數
var materials = [
'Hydrogen',
'Helium',
'Lithium',
'Beryllium'
];
// 使用普通函數: 求每個元素的長度
a = materials.map(function(elem){
return elem.length;
}); //[8, 6, 7, 9]
// 使用箭頭函數: 求每個元素的長度
a = materials.map(elem => elem.length); //[8, 6, 7, 9]
// 你看看,你看看盒揉,多么簡潔晋被。簡潔明了,
// 節(jié)省打字時間刚盈,節(jié)省閱讀時間羡洛,節(jié)省理解時間。
- 不綁定this
- 在箭頭函數出現之前藕漱,每個新定義的函數都有它自己的 this 值欲侮。
- This 被證明是令人厭煩的面向對象風格的編程。(我倒是很喜歡 this肋联,看見引用類屬性而不加 this 的代碼威蕉,我就想給加上。)
- this 確實會帶來一些問題橄仍,比如下面的內部函數中的 this 和外面的 this 分別指向不同的對象韧涨。但是我們希望它們指向同一個對象。
function Person() {
// Person() 構造函數定義 `this`作為它自己的實例.
// 也就是說侮繁,這個 this 指向 Person() 的實例虑粥。
this.age = 10;
console.log("this.age=", this.age); //輸出 10
setInterval(function growUp() {
// 在非嚴格模式, growUp()函數定義 `this`作為全局對象,
// 與在 Person()構造函數中定義的 `this`并不相同.
// 也就是說,這個 this 指向全局對象而不是 Person() 的實例宪哩,所以計算結果是 NaN
this.age++;
console.log("this.age=", this.age);
}, 1000);
}
a = new Person();
- 在ECMAScript 3/5中娩贷,通過將this值分配給封閉的變量(局部變量),可以解決this問題锁孟。
- 還記得 2008 年第一次遇到 this 引起的 bug 時育勺,查了半天但荤,函數閉包中的 this 居然和外面的不一樣〗е粒看了高手的代碼腹躁,才發(fā)現使用下面這一招就解決了。
- 大家習慣用: var self = this;
function Person() {
var that = this;//在 ActionScript 中南蓬,我經常這么干
that.age = 0;//讓局部變量 that 指向 this
setInterval(function growUp() {
// 回調引用的是`that`變量, 其值是預期的對象.
// 也就是說纺非,內部函數可以訪問父級函數的局部變量,這樣就保證了同一個 that 指向同一個 this
that.age++;
}, 1000);
}
- 箭頭函數不會創(chuàng)建自己的this,它只會從自己的作用域鏈的上一層繼承this赘方。因此烧颖,在下面的代碼中,傳遞給setInterval的函數內的this與封閉函數中的this值相同:
- 箭頭函數很好地解決了 this 問題窄陡,this 就是 this炕淮,只有一個 this,不必再為不同的 this 頭疼了跳夭。
- 不綁定 this涂圆,也就是說作用域不把 this 據為己有,而是繼承作用域鏈上一層的 this币叹,大家共用一個 this 润歉。
function Person(){
this.age = 0;//這個 this 指向 Person 實例
console.log("Person() this.age=", this.age);
// 下面的 this 繼承父級函數的 this 也指向 Person 實例
setInterval(() => {
this.age++;
console.log("箭頭函數 this.age=", this.age);
}, 1000);
}
a = new Person();
console.log("全局對象 this.age=", this.age);//這個 this 指向全局對象
/** 輸出如下
Person() this.age= 0
全局對象 this.age= undefined
箭頭函數 this.age= 1
箭頭函數 this.age= 2
箭頭函數 this.age= 3 每秒輸出一條
*/
- 通過 call 或 apply 調用箭頭函數,thisArg 會被忽略
var adder = {
base: 1,
add: function(a){
// 箭頭函數 this 能訪問到上層的 base
var f = v => v + this.base;
return f(a);//返回的是箭頭函數的計算結果
},
addThruCall: function(a){
var f = v => v + this.base;
var b = {
base: 2
};
// 通過 call() 調用箭頭函數 f 指定參數 thisArg 的值是 b颈抚,
// 普通函數的 this 會指向 b, 但是箭頭函數會忽略 b 仍然用作用域鏈上層的 this
// 箭頭函數中的 this.base 指向 adder 中的 1 而不是 b 中的 2
return f.call(b, a);
},
};
//通過 . 語法調用踩衩,則 this 指向 adder
console.log(adder.add(1)); // 輸出 2
console.log(adder.addThruCall(1)); // 仍然輸出 2
- 箭頭函數不綁定 arguments 對象
- 也就是說,在箭頭函數內調用的 arguments 對象并不是箭頭函數自身的贩汉,而是外層函數的驱富。
- 或者說,箭頭函數沒有自身的 arguments 對象匹舞。
function foo(n) {
//普通函數內部的箭頭函數:arguments[0] 是 n
var f = () => arguments[0] + n;
return f();//不是返回箭頭函數 f 而是返回 f 的計算結果
}
console.log(foo(3));//輸出:6
// 不在普通函數內部褐鸥,箭頭函數調用 arguments 對象報錯
var arr = () => arguments[0];
arr();
- 如果確實需要實現箭頭函數與外層函數參數對象的分離,那么最好使用剩余參數(rest parameter)策菜。
function foo(n) {
// 使用剩余參數實現箭頭函數與外層函數參數對象的分離
// args[0] 是箭頭函數的第一個剩余參數 2,而不是 n
var f = (...args) => args[0] + n;
return f(2);
}
console.log(foo(1));//輸出:3
- 像函數一樣使用箭頭函數
- 箭頭函數本來就是函數酒贬,但它是特殊的函數又憨,所以有些特殊行為:不綁定 this,不綁定 arguments 锭吨。
var obj = {
i: 10,
// 箭頭函數的 this 指向哪里蠢莺? App 實例(調用方所在的類)
b: () => console.log(this.i, this),
// 普通函數的 this 指向哪里? obj(調用方)
c: function(){
console.log(this.i, this);
}
}
//調用方法函數 b() 輸出異常零如,c() 輸出正確躏将。
obj.b(); // undefined App {props: Object, context: Object…}
obj.c(); // 10 Object {i: 10}
- 非方法函數用箭頭函數來定義锄弱,是最佳選擇。方法函數不要用箭頭函數定義祸憋,否則 this 指向錯誤会宪。
var obj = {
a: 10
};
// 默認情況下,使用 Object.defineProperty() 添加的屬性值是不可修改的蚯窥。
Object.defineProperty(obj, 'b', {
// 一個給屬性提供 getter 的方法掸鹅,當訪問該屬性時,該方法會被執(zhí)行拦赠,
// 方法執(zhí)行時沒有參數傳入巍沙,但是會傳入this對象(由于繼承關系,這里的 this 并不一定是定義該屬性的對象)荷鼠。
get:() => { // 使用箭頭函數句携,this 指向哪里?App 實例(調用方所在的類)允乐。
// 我們不希望這樣矮嫉,那么就應該用普通函數。
console.log(this.a, typeof this.a, this); // undefined "undefined" App {props: Object…}
return this.a+10;
}
});
Object.defineProperty(obj, 'c', {
get:function () { // 使用普通函數喳篇,this 指向調用方 obj 這才是我們想要的
console.log(this.a, typeof this.a, this); // 10 "number" Object {a: 10}
return this.a+10;
}
});
a = obj.b; // a= NaN
b = obj.c; // b= 20
- 箭頭函數的 prototype 屬性:箭頭函數原型
var Foo = () => {console.log('箭頭函數 Foo')};
console.log("Foo.prototype=", Foo.prototype);
// Foo.prototype= Foo {} constructor: Foo() __proto__: Object
Foo();// 箭頭函數 Foo
// 下面這樣調用敞临,和直接調用 Foo() 效果相同
Foo.prototype.constructor(); // 箭頭函數 Foo
- 返回對象字面量:要用圓括號把對象字面量包起來
var func = () => ({foo:1});
a = func(); // a= Object {foo: 1}
// 用這種簡單的語法返回對象字面量是行不通的。
var func2 = () => { foo: 1 }; // Failed to compile.
b = func2();
- yield 關鍵字通常不能在箭頭函數中使用(除非是嵌套在允許使用的函數內)麸澜。
- 箭頭函數在參數和箭頭之間不能換行挺尿。
- 解析順序
- 雖然箭頭函數中的箭頭不是運算符,但箭頭函數具有與常規(guī)函數不同的特殊運算符優(yōu)先級解析規(guī)則炊邦。
var callback;
callback = callback || function(){};
// foo = foo || () => {}; // Failed to compile.
callback = callback || (() => {});
// 很多情況下编矾,需要這種寫法,用于處理默認情況
callback();
// 有時會這樣寫
callback && callback();
- 空的箭頭函數返回 undefined
// 什么情況下會用到空的箭頭函數呢馁害?
var empty = () => {};
a = empty(); // a= undefined
- 立即執(zhí)行函數表達式 IIFE
// 前面是箭頭函數定義窄俏,加上括號就是執(zhí)行
a = (() => '箭頭函數')();// a= 箭頭函數
- 詳解 javascript 立即執(zhí)行函數表達式(IIFE) https://www.cnblogs.com/zichi/p/4401755.html
- 使用立即執(zhí)行函數鎖住變量保存狀態(tài)。
- 立即執(zhí)行函數在模塊化中也大有用處碘菜。用立即執(zhí)行函數處理模塊化可以減少全局變量造成的空間污染凹蜈,構造更多的私有變量。
- 使用箭頭函數實現數組過濾忍啸、映射仰坦。。计雌。簡潔明了
var arr = [3, 1, 2, 8, 1, 12, 21];
//元素求和:前驅值 + 當前值 = 結果悄晃,作為下次運算的前驅值
var sum = arr.reduce((a, b) => a + b);// 48
// 過濾,留下奇數凿滤,不會自動去重
var even = arr.filter(v => v % 2 == 1); // [3, 1, 1, 21]
// 映射:所有元素 2 倍
var double = arr.map(v => v * 2); // [6, 2, 4, 16, 2, 24, 42]
- 實現更簡明的 promise 鏈
- 什么是 Promise 妈橄?Promise 其實很簡單庶近,就是一個處理異步的方法。
- 為什么要用 Promise 眷蚓?如果用傳統(tǒng)的回調函數來處理異步鼻种,存在缺陷:
- 隨著業(yè)務邏輯變復雜,回調層級會越來越深溪椎。
- 代碼耦合度比較高普舆,不易修改。
- 每一步操作都需要手動進行異常處理校读,比較麻煩沼侣。不科學。自動化才科學歉秫,才靠譜蛾洛。
- Promise 的鏈式調用與中止 https://segmentfault.com/a/1190000007598894
- 使用 promise 編碼之前,可以先思考兩個問題雁芙。一是如何鏈式調用轧膘,二是如何中止鏈式調用。
// promise 鏈式調用開頭兔甘,生成一個 promise 對象
function start(){
return new Promise((resolve, reject) => {
resolve('家里蹲大學歡迎您谎碍!');
reject('嬰兒不能上大學');
})
}
start().then(data => { //執(zhí)行 start() 函數返回的 promise
console.log("result of start: ", data);
return Promise.resolve('大一過關'); // 返回一個 promise 對象 p1
}).then(data => { //執(zhí)行 p1
console.log("result of p1: ", data);
return Promise.reject('大二失敗'); // 返回一個 promise 對象 p2
}).then(data => { //執(zhí)行 p2
console.log("result of p2: ", data);
return Promise.resolve('大三過關'); // 返回一個 promise 對象 p3
}).catch(ex => { // 捕獲異常
console.log('ex: ', ex);
return Promise.resolve('大二重修'); // 返回一個 promise 對象 p4
}).then(data => { //執(zhí)行 p4
console.log('result of p4: ', data);
})
/** 上面的代碼最終會輸出:
result of start: 家里蹲大學歡迎您!
result of p1: 大一過關
ex: 大二失敗
result of p4: 大二重修
*/
你看看洞焙,你-看-看蟆淀,使用箭頭函數多么簡明!!!
- 箭頭函數也可以使用條件(三元)運算符:
// 定義箭頭函數 simple: 條件表達式作為函數體
var simple = a => a > 15 ? 15 : a;
a = simple(16);
b = simple(10);
console.log("a=", a,"b=", b); // a= 15 b= 10
// 定義箭頭函數 max
let max = (a, b) => a > b ? a : b;
a = max(1,2);
b = max(4,3);
console.log("a=", a,"b=", b); // a= 2 b= 4
- 箭頭函數內定義的變量及其作用域
// 常規(guī)寫法
var greeting = () => {
let now = new Date();
return (((now.getHours() > 22) ? '睡了嗎勋磕?' : '嘎哈呢?') + '老鐵');
};
a = greeting();
console.log("a=", a); // a= 嘎哈呢渺杉?老鐵
// console.log("now=", now); //標準的 let 作用域:'now' is not defined
- 參數括號內定義的變量是局部變量(默認參數)
var greeting = (now=new Date()) => (now.getHours() < 22 ? '揍嘛里?' : '睡嘍邁?') + '老鄉(xiāng)';
a = greeting();
console.log("a=", a); // a= 揍嘛里?老鄉(xiāng)
// console.log(now); // 編譯失數敕选:'now' is not defined
- 函數體內{} 用 var 定義的變量是局部變量
var greeting = () => {
var now = new Date();
return (now.getHours() > 22 ? '睡了嗎?' : '嘎哈呢抢韭?') + '老鐵';
};
a = greeting();
console.log("a=", a); // a= 嘎哈呢薪贫?老鐵
// console.log(now); // 編譯失敗:'now' is not defined
- 箭頭函數也可以使用閉包:
- 標準的閉包函數
function foo(){
var i=0;
// 返回一個閉包函數:函數內的函數
return function bar(){
// 可以訪問函數 foo() 內的局部變量
return (++i);
};
};
a = foo(); //得到一個閉包函數:bar()
b = a(); //得到計算結果:1
c = a(); //得到計算結果:2
- 箭頭函數體的閉包
// 定義箭頭函數篮绰,返回一個箭頭函數:閉包后雷。一行代碼實現季惯,夠簡潔吧吠各?
var subtract = (i=100) => {return () => --i};
a = subtract(); // 得到一個閉包函數:() 能訪問 subtract() 的局部變量 i
b = a(); // 得到計算結果:99
c = a(); // 得到計算結果:98
// 還能更簡潔臀突,但是可讀性略差〖致可以用來裝逼候学。。纵散。
var subtract2 = (i=100) => () => --i;
a = subtract2();
b = a(); // 得到計算結果:99
c = a(); // 得到計算結果:98
- 箭頭函數遞歸
// 定義箭頭函數梳码,接收參數 x ,遞歸調用
var fact = (x) => x === 0 ? 1 : x * fact(x - 1);
a = fact(4); // 24