目錄
bind
回傳函式
,所以通常用在不立刻呼叫
的情況
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
this.antler = '頭上的角';
const angel = {
name: '芙丸',
antler:'耳朵裡的毛',
answerWhatIsAntler: function() {
return `鹿茸就是鹿${this.antler}。(${this.name})`;
}
};
const foo = angel.answerWhatIsAntler; // 脫離angel後,指向變成window,結果為「鹿茸就是鹿頭上的角。(undefined)」
const bar = angel.answerWhatIsAntler.bind(angel); // 重新指向angel,結果為「鹿茸就是鹿耳朵裡的毛。(芙丸)」
call
立刻呼叫
,回傳值
The call() allows for a function/method belonging to one object to be assigned and called for a different object.
this.antler = '頭上的角';
const falsy = {
antler:'耳朵裡的毛'
};
function answerWhatIsAntler(year = '', author = '') {
return `鹿茸就是鹿${this.antler}。(${year} ${author})`;
}
answerWhatIsAntler.call(falsy, '2014', '芙丸'); // this就會改指向falsy
apply
立刻呼叫
,回傳值
和call
只差在
接受的參數是陣列
或者類陣列(array like)
The apply() method calls the specified function with a given this value, and arguments provided as an array (or an array-like object).
this.antler = '頭上的角';
const falsy = {
antler:'耳朵裡的毛'
};
function answerWhatIsAntler(year = '', author = '') {
return `鹿茸就是鹿${this.antler}。(${year} ${author})`;
}
answerWhatIsAntler.apply(falsy, ['2014', '芙丸']); // this就會改指向falsy
bind / call & currying
因為bind和call
都會接受多個參數,所以都可以跟currying一起用
有些文章可能會說只有bind可以跟currying一起用,但我認為這句話只在,下方這種例1的情況成立,至於例2用call會比bind更好
例1
function add(a, b){
return a + b + this.c;
}
const foo = add.bind({c:3}, 1);
const bar = add.call({c:3}, 1);
const fooBar = add.call({c:3}, 1, 2);
foo(2); // 6
foo(4); // 8
bar(2); // bar is not a function
bar; // NaN
fooBar; // 6,雖然這樣也能得到結果,但意義不大
例2
頁面上有2個地圖,還有1個用來在地圖上設置marker的setMarker()。map1的marker都是黃色,map2則都是紅色
不用bind或call可能會長這樣
import { Map, Marker, Popup } from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
function drawMap(style, zoom, token){
return (containerId, lngLat) => {
const map = new Map({
container: containerId,
style: style,
center: lngLat,
zoom: zoom, // starting zoom
accessToken: token,
});
map.on('style.load', () => {
map.addSource('point', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: lngLat,
},
}],
},
});
})
map.setFog({});
return map;
}
}
function setMarker(map, color, lngLat){
const marker = new Marker({ color, draggable: false })
marker.setLngLat(lngLat).addTo(map);
}
// style、zoom、token這些比較固定,id、lngLat則較可能變動,所以使用partial拆成2次傳參數會比currying一個一個傳適合
const drawStreetMap = drawMap('mapbox://styles/mapbox/streets-v11', 10, 'pk.eyJ1IjoidGVtcHVyYTMyNyIsImEiOiJja3Z6eXVqdnQ1YTdxMm9tdHUwMGx4eXBmIn0.7SOTd4xVrpfdvJiDx5R34g');
// 先設好兩個map
const map1 = drawStreetMap('map1', [121.5, 23.5]);
const map2 = drawStreetMap('map2', [0, 35]);
setMarker(map1, 'gold', [121.7, 22.857]);
setMarker(map1, 'gold', [120.92, 22.912]);
setMarker(map2, '#dc3545', [0.242, 34.5]);
setMarker(map2, '#dc3545', [0.255, 35.212]);
使用bind / call + currying改寫後,可以發現只需傳[lng, lat],簡潔多了
import { Map, Marker, Popup } from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
function drawMap(style, zoom, token){
return (containerId, lngLat) => {
const map = new Map({
container: containerId,
style: style,
center: lngLat,
zoom: zoom, // starting zoom
accessToken: token,
});
map.on('style.load', () => {
map.addSource('point', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: lngLat,
},
}],
},
});
})
map.setFog({});
return map;
}
}
function setMarker(color){
return function(lngLat){
const marker = new Marker({ color, draggable: false })
// 待會要使用bind將指向改變,所以寫this.map
marker.setLngLat(lngLat).addTo(this.map);
}
}
// style、zoom、token這些比較固定,id、lngLat則較可能變動,所以使用partial拆成2次傳參數會比currying一個一個傳適合
const drawStreetMap = drawMap('mapbox://styles/mapbox/streets-v11', 10, 'pk.eyJ1IjoidGVtcHVyYTMyNyIsImEiOiJja3Z6eXVqdnQ1YTdxMm9tdHUwMGx4eXBmIn0.7SOTd4xVrpfdvJiDx5R34g');
// 先設好兩個map
const map1 = drawStreetMap('map1', [121.5, 23.5]);
const map2 = drawStreetMap('map2', [0, 35]);
// 各自先改變好指向,然後先傳color進去。這樣就不用每次都要傳map和color
// 因為bind回傳的會是包裹住setMarker內部回傳的匿名函示function,所以需要呼叫讓它回傳內部的匿名函式
const setMarkerTo1 = setMarker.bind({map:map1}, 'gold')();
const setMarkerTo2 = setMarker.call({map:map2}, '#dc3545');
setMarkerTo1([121.7, 22.857]);
setMarkerTo1([120.92, 22.912]);
setMarkerTo2([0.242, 34.5]);
setMarkerTo2([0.255, 35.212]);
參考資料
MDN - bind
MDN - call
MDN - apply
When to Use Bind(), Call(), and Apply() in JavaScript
[筆記] 了解function borrowing和function currying ─ bind(), call(), apply() 的應用