元編程的概念有不少文章,經過操做更加底層的api作更多個性化的功能。一句話歸納,就是用代碼來寫代碼javascript
一些時候,寫各類下劃線、先後綴,爲了實現一個惟一值或者祕密的特殊輔助值,用來輔助業務邏輯或者說做爲一個私有的東西:java
const onlyone = 'im-only@@我就不信會重複';
const obj = {};
obj[onlyone] = 'xxx';
複製代碼
angular1暴露出來的對象裏面,常常看見$
開頭或者$$
開頭的變量。$
開頭是比較底層的變量了,$$
開頭的是更加底層的,並且咱們是基本不會用上的。還有redux源碼,首次初始化的action.type是var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.');
react
種種例子,均可以看見,搞個花哨的名字,做爲內部運行的輔助變量或者惟一變量使用。花時間想key名字,並且名字越寫越長,是否是總感受是一種麻煩?git
更重要的,對象若是暴露出去了,那人家就能夠看見key值,也能夠直接修改了github
const only = Symbol();
const obj = {};
obj[only] = 'xxx';
複製代碼
此時,只有定義了symbol的做用域之下才能用到它了,暴露對象出去,就算在console裏面給人家看見一個symbol,想修改它或者讀取它也無能爲力。甚至還能夠有obj['Symbol()'] = 1
這種操做:編程
在react打印出來的組件對象裏面,也能夠看見一些symbol的屬性。 redux
此外,symbol是不可枚舉的:api
const only = Symbol();
const obj = { a: 1, b: 2 };
obj[only] = 'xxx';
Object.keys(obj);
複製代碼
一樣,JSON.stringify、for in等等也是會把symbol跳過了。數組
可是,想像其餘變量那樣子用怎麼辦,也就是以不一樣姿式使用一樣的方法,指望獲得一樣的值,指望近似與
symbol('a')
和symbol('a')
是徹底相等這種效果bash
這時候,Symbol.for
剛恰好知足需求了:
const a = Symbol.for('im not alone')
const b = Symbol.for('im not alone')
a === b
複製代碼
這下symbol就是有點簡單數據類型的感受了吧
這個屬性,是一個當前對象默認的遍歷器生成函數,因此咱們用obj[Symbol.iterator]
能夠訪問到它。自定義Symbol.iterator
會覆蓋for of遍歷、...操做符。實現一個僞數組,主要就是要把這個函數設置好就能夠了:
// 方法1
var fakeArr = {}
fakeArr[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...fakeArr] // [1,2,3]
// 方法2
var arr = {
0: 1,
1: 2,
2: 3,
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
[...arr] // [1,2,3]
// 方法3
var arr = {
[Symbol.iterator]: (() => {
var data = ['隨', '機', '試', '一', '下'];
return () => {
var cursor = 0;
return {
next: () => {
if (cursor ++ < 10) { return { value: data[~~(5 * Math.random())], done: false } } return { done: true } } } } })() }; // 這下就不用本身搞一個隨機數組生成器了 [...arr]; // 自定義的遍歷對象 var o = { a: 1, b: 2, hidden: 'u cant find me', [Symbol.iterator]: () => { var cursor = 0; return { next() { if (cursor < Object.keys(o).length - 1) { return { value: Object.keys(o)[cursor ++], done: false, } } return { done: true }; } } } } [...o]; // ['a', 'b'] 複製代碼
隨心所欲的遍歷,能夠在中間加上其餘邏輯。在業務上,對於一系列規定的流程但不必定相同但結果這種邏輯,咱們就能夠用上它。什麼上報、條件判斷、校驗均可以作
// 條件遍歷
// 有一個按鈕數組是這樣的[{ renderer, style,label }...],傳入對象展現按鈕
// 正常狀況咱們直接寫在數組裏面沒問題
// 若是複雜一點,按照條件展現按鈕,咱們就要在外面再寫其餘邏輯
// 若是使用iterator,能夠優雅簡化這個過程
var type = 'btn1';
var Btns = {
btn1: {
renderer() {
return 1
},
style: {
height: '10px'
}
},
btn2: {
renderer() {
return 2
},
},
btn3: {
renderer() {
return 3
},
label: {
name: 'hello'
}
},
*[Symbol.iterator]() {
switch (type) {
case 'btn1':
yield this.btn1
break;
default:
yield this.btn2
break;
}
yield this.btn3
}
};
console.log([...Btns]);
type = 'other';
console.log([...Btns]);
複製代碼
能夠試一下,結果是不同的。這樣子,咱們就能夠徹底不用在外面關心條件遍歷,也不用在多個地方遍歷的時候寫邏輯,這個已經在對象的內部實現
另一篇文章已經講過類型轉換關係,複雜類型隱式轉換會先toPrimitive轉回基本類型。而Symbol也有這種操做到更底層的方法:Symbol.toPrimitive
,能夠自定義
// 不玩包裝類的string
var s = new String('');
s[Symbol.toPrimitive] = () => { return 'this is magic' };
`${s}這不是空字符串`; // "this is magic這不是空字符串"
// 不想再看見[object Object]了
var o = {
msg: '你不能夠正常看見個人'
};
var _o = {
msg: '你能夠正常看見個人',
[Symbol.toPrimitive]: () => JSON.stringify(_o)
};
`奇蹟不會發生: ${o} 奇蹟將會發生: ${_o}`;
複製代碼
在隱式轉換中,自定義的Symbol.toPrimitive
優先級最高:
var transform = {
valueOf() {
return 'valueOf'
},
toString() {
return 'tostring'
},
[Symbol.toPrimitive]() {
return 'symbol'
}
};
1+transform; //"1symbol"
// 永遠地告別[object Object]
Object.prototype[Symbol.toPrimitive] = function() {
return JSON.stringify(this)
}
複製代碼
對象的Symbol.replace
方法被String.prototype.replace
調用時,會返回Symbol.replace
的返回值。同理,match、search也差很少
// 一個具備merge功能的searchvalue
var str = new String('別看我好嗎')
str[Symbol.replace] = (oldStr, newStr) => [...new Set([...oldStr, ...newStr])].join('');
'1234'.replace(str, '3456'); // 123456
// 一個神奇的split
var magic = {
[Symbol.split]: (() => {
var mana = ['*', '&', '$', '#', '@'];
var ran = () => mana[~~(Math.random() * 5)];
return (origin) => {
var res = '';
var cursor = 0;
while(cursor < origin.length) {
var r = ran();
res += r + origin[cursor ++] + r
}
return res;
}
})()
};
'你渴望力量嗎'.split(magic);
複製代碼
正則的比較少用,並且也能夠用簡單的封裝替代。