「箭頭函式」也是 ES6 的新增語法
關於 ES6 的新增語法
先宣告一個變數 callName,後面接函式的內容。把此函式賦予到變數 callName 上。
const callName = function(someone){ // 會傳入一個參數
return '我是' + someone; // 會 return 一個值
}
console.log(callName('小明')); // 我是小明
const callName = (someone) => {
return '我是' + someone;
}
console.log(callName('小明')); // 我是小明
表達式就是可以回傳一個值。
以上例來說,函式內沒有其他內容,直接回傳一個值,就可做縮寫。
const callName = (someone) => '我是' + someone;
console.log(callName('小明')); // 我是小明
當只有一個參數時,可以把小括號拿掉。
const callName = someone => '我是' + someone;
console.log(callName('小明')); // 我是小明
當沒有參數時,此小括號不可省略。
const callName = () => '小括號不可省略';
console.log(callName()); // 小括號不可省略
當二個參數時,此小括號不可省略。
const callName = (a, b) => '我是' + a + b;
console.log(callName('林', '小花')); // 我是林小花
箭頭函式屬於新增的方法,和傳統函式會有不同。
const nums = function(){
console.log(arguments);
}
nums(10, 50, 100, 50, 5, 1, 1, 1, 500);
// 看到 arguments 物件,裡面帶入的值就是我們所傳入的參數
把上例改成「箭頭函式」:
因為「箭頭函式」沒有 arguments 這個參數,所以跳錯。
const nums = () => {
console.log(arguments); // 跳錯,ReferenceError: arguments is not defined
}
nums(10, 50, 100, 50, 5, 1, 1, 1, 500); // ReferenceError (無法去存取一個不存在的變數)
Q : 但有些時候必須把未列出的參數也取出,那該怎麼做 ?
A : 使用「其餘參數」
前面加上「3個點」,後面接「變數名稱」
const nums = (...arg) => {
console.log(arg);
}
nums(10, 50, 100, 50, 5, 1, 1, 1, 500);
var myName = '全域'
var person = {
myName: '小明',
callName: function(){
console.log('1', this.myName); // 1 小明
setTimeout(function(){ // 簡易呼叫, this 指向 window
console.log('2', this.myName); // 2 全域
console.log('3', this); // 3 (this 指向 window)
}, 10);
}
}
person.callName();
// 在 person 物件下呼叫 callName 函式時,若 callName 函式內有 this,this 就會指向 person
因為「箭頭函式」沒有自己的 this,若在「箭頭函式」裡看到 this 就先當作不存在,this 會使用外層作用域的 this (指向外層的 person 物件)。
目前 setTimeout 在 callName 函式裡,改成「箭頭函式」就變成套用外層 person 物件的 this。
var myName = '全域'
var person = {
myName: '小明',
callName: function(){
console.log('1', this.myName); // 1 小明
setTimeout(() => { // 箭頭函式,this 指向外層作用域的 person 物件
console.log('2', this.myName); // 2 小明
console.log('3', this); // this 指向外層作用域的 person 物件
}, 10);
}
}
person.callName();
接著,再把 callName 函式也變成「箭頭函式」
目前 callName 在 person 物件裡,改成「箭頭函式」就變成套用外層 window 環境的 this。
var myName = '全域'
var person = {
myName: '小明',
callName: () => { // 箭頭函式,沒有自己的 this,this 指向 window
console.log('1', this.myName); // 1 全域
setTimeout(() => { // 箭頭函式,沒有自己的 this,this 指向 window
console.log('2', this.myName); // 2 全域
console.log('3', this); // this 指向 window
}, 10);
}
}
person.callName();
HTML 部分
<p>這裡具有一段話</p>
JS 部分 : 透過 click 方式把 DOM 位置取出
const ele = document.querySelector('p');
ele.addEventListener('click', function(){ // 透過 click 方式取出 DOM 的位置
console.log(this); // <p>這裡具有一段話</p> (這裡 this 指的是 HTML 裡的 DOM 元素)
this.style.background = 'yellow'; // 點擊 DOM 元素即變色
});
const ele = document.querySelector('p');
ele.addEventListener('click', () => { // 透過 click 方式取出 DOM 的位置
console.log(this); // 指向全域 window (「箭頭函式」沒有自己的 this)
// this.style.background = 'yellow';
});
先前章節有提到可以透過 call 的方式把另外一個物件傳入,並作為函式執行的 this 使用。
const family = {
myName: '小明家',
}
const fn = function(para1, para2) {
console.log(this, para1, para2);
}
fn.call(family, '小明', '杰倫'); // {myName: "小明家"} "小明" "杰倫"
若改成「箭頭函式」
const family = {
myName: '小明家',
}
const fn = (para1, para2) => {
console.log(this, para1, para2);
}
fn.call(family, '小明', '杰倫'); // 全域 window "小明" "杰倫"
假設有兩段「建構函式」,一個是用「傳統函式」撰寫、另一個是用「箭頭函式」撰寫。
const Fn = function (a) {
this.name = a; // this 指向傳入的參數 (作為建構函式用)
}
const ArrowFn = (a) => { // 無法作為建構函式使用
this.name = a;
}
來看一下「傳統函式」、「箭頭函式」撰寫的「建構函式」prototype 長怎樣 ~
console.log(Fn.prototype, ArrowFn.prototype); // {constructor: ƒ} undefined
結果 :
const a = new Fn('a'); // 可以另外傳入參數作為物件的屬性用
console.log(a); // Fn {name: "a"}
const b = new ArrowFn('b'); // 跳錯,(ArrowFn is not a constructor)
// ArrowFn 並不是一個建構函式,他沒有 prototype
const arrFn = () => 1;
console.log(arrFn()) // 1
假設預期要回傳一個物件 {data: 1},為什麼卻回傳了 undefined ?
const arrFn = () => {data: 1}; // 預期要回傳此物件 {data: 1}
console.log(arrFn()) // undefined
整理一下程式碼發現 :
{data: 1} 其中的大括號其實是做為「箭頭函式」裡的程式碼片段範圍使用,並不是物件實字裡所使用的大括號。
const arrFn = () => { // 此大括號為「箭頭函式」裡的程式碼片段範圍使用
data: 1 // 無法回傳
};
console.log(arrFn()) // undefined
若在實作中,想要回傳物件內容該如何做調整 ?
在物件外層再加一對小括號,就能正確回傳物件內容。
const arrFn = ()=> ({data: 1}); // 在 {data: 1} 兩邊加上小括號,即可回傳物件內容
console.log(arrFn()); // {data: 1}
「傳統函式」範例 : (第 2 行) 由判斷式得知
let num = 0; // 先宣告一個數字
const numFn = num || function(){ return 1 };
console.log(numFn()); // 1
「箭頭函式」範例 : 結構上會有錯誤,錯誤碼 (SyntaxError)
let num = 0;
const numFn = num || () => 1;
console.log(numFn()); // 跳錯 (SyntaxError)
「箭頭函式」範例 : 修正以上問題,要加上小括號
let num = 0;
const numFn = num || (() => 1);
console.log(numFn()); // 1
定義一個物件,裡面有一個 callName 方法(使用「箭頭函式」)。
使用「箭頭函式」要注意 this 的指向。
const person = {
myName: '小明',
callName: () => { // 「箭頭函式」沒有自己的 this,這個 this 指向全域
console.log(this.myName); // undefined
},
}
person.callName(); // this 指向全域,無法正確取到 person 物件內的 myName
解決方法 : 使用「傳統函式」
const person = {
myName: '小明',
callName: function () {
console.log(this.myName); // 小明
},
}
person.callName();
Vue 的生命週期 created :
created 是當我們的 Vue 執行到這個元件時,在一開始就優先執行這段程式碼。
使用「傳統函式」可以正確在 Vue 的元件裡取得自己的資料內容。
const app = new Vue({ // 將 Vue 實體建立出來
data: {
num: 1
},
created: function(){ // 傳統函式
console.log(this.num); // 1 (可以取到自己的 data)
}
})
若把 created 改成「箭頭函式」就會出錯
const app = new Vue({ // 將 Vue 實體建立出來
data: {
num: 1
},
created: () => {
console.log(this.num); // undefined
}
})
ps : 此段範例需載入 vue 的 CDN 才能實作
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
下面一段程式碼,使用「傳統函式」做為「建構函式」使用
const Fn2 = function (a) {
this.name = a;
}
在 Fn2「建構函式」的原型下,使用「箭頭函式」來新增原型方法。
此段程式碼裡的箭頭函式 this 指向不同,指向全域。無法正確的指到 Fn2「建構函式」物件本身。
Fn2.prototype.protoFn = () => {
return this.name;
}
使用 new 把 Fn2「建構函式」實體化
const newObj = new Fn2('函式'); // 實體化
console.log(newObj);
可以在 newObj 的物件實體下找到新增的原型方法。
但是可能在執行時會出錯,找不到資料。 無法得到 newObj 物件裡的 name。
console.log(newObj.protoFn()); // 得到空值
原因是「箭頭函式」this 指向和「傳統函式」不一樣。
this 其實是指向「全域」,無法正確的指到 Fn2「建構函式」物件本身。