ES6引入的新的原始數據類型Symbol,表示獨一無二的值,它是js中的第7種數據類型,是一種類似于字符串的數據類型。
Symbol特點:
1、Symbol的值是唯一的,用來解決命名沖突的問題。
2、Symbol值不能與其他數據進行運算。
3、Symbol定義的對象屬性不能使用for...in循環遍歷,但是可以使用Reflect.ownKeys來獲取對象的所有鍵名。然后遍歷。
如何創建?
可以使用函數創建。
let s = Symbol('demo');
或者
let s1 = Symbol.for('demo');
使用for函數可以得到某個字符串唯一的symbol值,例如下面的比較是相等的。
let s2 = Symbol.for('demo');
s1 === s2 返回的就是true。
Symbol內置的值
用做對象的屬性。通過對這些內置值的設置,來改變對象在某些特定場景下的表現。
最常用的有:
在使用操作符時調用。
class Person{
static [Symbol.hasInstance](param){
console.log(param);
console.log('我被用來檢查類型了')
return false;
}
}
let obj = {}
console.log(obj instanceof Person)
使用concat來合并兩個數組的時候,js會默認將數組內的元素展開然后合并為一個數組返回(原數組不變),這個行為es6以后可以使用Symbol.改變,將其置為false就可以將數組作為一整個對象合并到過去。
let animals = ['cat','dog','panda']
let fruit = ['apple','pear']
fruit[Symbol.isConcatSpreadable] = true
console.log(animals.concat(fruit)) // 結果是['cat','dog','panda','apple','pear']
fruit[Symbol.isConcatSpreadable] = false
console.log(animals.concat(fruit)) // 結果是['cat','dog','panda', Array(2)]
用于給對象定義迭代方法,這樣就可以使用es6中新提供的for...of語法,需要注意的是,自定義的這個迭代器方法,需要按照一定的規范來寫,需要返回一個具有next方法的對象,next方法需要返回具有value和done兩個屬性的對象。done會被用來判斷什么時候結束迭代。
const banji = {
name: "終極一班",
stus: [ 'xiaoming', 'xiaoning', 'xiaotian', 'knight' ],
[Symbol.iterator]() {
// 索引變量
let index = 0;
// 保存this
let _this = this;
return {
next: function() {
if (index < _this.stus.length) {
const result = { value: _this.stus[index], done: false };
// 下標自增
index++;
// 返回結果
return result;
} else {
return { value: undefined, done: true };
}
}
};
}
}
// 遍歷這個對象
for (let v of banji) {
console.log(v);
}
一個在調用 String..match() 方法時調用的方法, 直白點說就是如果你定義一個字符串,當你調用.match(),如果這個對象定義了Symbol.match方法時,就會執行這個方法。
let animal = {
[Symbol.match](str){
console.log(str) //打印出字符串 cat
return str.indexOf('animal');
}
}
let kitty = "cat"
console.log(kitty.match(animal)) //返回值-1,這里打印出-1
一個在調用 String..replace() 方法時調用的方法,和上面的一樣,當一個字符串調用.replace()時,會去執行中的Symbol.replace方法。
let animalName = {
/* strValue 會返回調用者本身,replacement為調用repalce方法時傳入的第二個參數 */
[Symbol.replace](strValue, replacement){
console.log("strValue: " + strValue,"replacement: " + replacement) //strValue: cat, its name is {params} replacement: {params}
return strValue.replace("{params}", "kitty")
}
}
let kitty = "cat, its name is {params}"
console.log(kitty.replace(animalName, "{params}")) //cat, its name is kitty
一個在調用 String..search() 方法時調用的方法,用于在字符串中定位子串,這個也和上面str相關擴展一樣直接看示例。
let animalName = {
/* strValue就是調用者本身 */
[Symbol.search](strValue){
console.log(strValue) //cat, its name is kitty
return strValue.split("name is")[1]
}
}
let kitty = "cat, its name is kitty"
console.log(kitty.search(animalName)) //kitty
一個在調用 String..split() 方法時調用的方法,用于分割字符串。
let animalName = {
/* strValue就是調用者本身 */
[Symbol.split](strValue){
console.log(strValue) //cat, its name is kitty
return strValue.indexOf("name")
}
}
let kitty = "cat, its name is kitty"
console.log(kitty.split(animalName)) //8
這個方法決定了當一個對象被轉換為原始值時的行為。引擎在每個類型值的原型上定義了Symbol.方法。這個方法被調用時,會接受一個字符串參數,表示當前運算的模式,一共有三種模式:
number: 該場景下需要轉換成數值.對應操作符:* / -
string: 該場景下需要轉換成字符串,顯示使用string的構造方法
default:該場景下可以轉換成數值,也可以轉換成字符串,對應操作符:+
let obj = {
[Symbol.toPrimitive](hint){
switch(hint){
case 'number':
return 123;
case 'string':
return 'str';
case 'default':
return 'default';
default:
throw new Error();
}
}
}
console.log(2 * obj) //246
console.log(3 + obj) //3default
console.log(obj == 'default') //true
console.log(String(obj)) //str
對象的Symbol.species屬性,指向一個構造函數,創建衍生對象時,會使用該屬性。這個的主要用途是,有些類庫是在基類的基礎上修改的,那么子類使用繼承的方法時,作者可能希望返回基類的實例,而不是子類的實例。
class MyArray extends Array {
static get [Symbol.species](){
return Array;
}
}
//b,c是a的衍生對象
const a = new MyArray(1,2,3)
const b = a.map(x => x+1)
const c = a.filter(x => x > 0)
//如果沒有定義上面的Symbol.species,那么下面的instanceof均返回true
console.log(b instanceof MyArray) //true
console.log(c instanceof MyArray) //true
//定義了上面的Symbol.species時,返回值是下面這樣
console.log(b instanceof MyArray) //false
console.log(b instanceof Array) //true
一個在調用 String..() 方法時使用的字符串,用于創建對象描述。在對象上面調用Object..方法時,如果這個屬性存在,它的返回值會出現在方法返回的字符串中,表示對象的類型。
//第一種寫法,注意這里的get關鍵字是必須的,call方法需要用到get取值器
let animal = {
get [Symbol.toStringTag](){
return 'animal'
}
}
console.log(Object.prototype.toString.call(animal)) // [object animal]
//第二種寫法
console.log({[Symbol.toStringTag]: 'tag'}.toString()) // [object tag]
一個定義了一些不可被 with 語句引用的對象屬性名稱的對象集合。集合中的屬性名稱,會在with環境綁定中被排除。
const animal = {
type: 'animal',
name: 'kitty'
}
with(animal){
console.log(type, name) //animal kitty
}
//定義Symbol.unscopables以便在with環境中排除某些屬性
animal[Symbol.unscopables] = {
type: true
}
with(animal){
console.log(type) //Uncaught ReferenceError: type is not defined
}
以上這些被稱為眾所周知的symbols,其實其中很多值并不常用,為了驗證這些symbols的準確用法,我搜了很多資料,總算把每一個都親自驗證了一遍。
這里附上一個地址,這里面有特別詳細的示例代碼,我也是自己費了半天勁兒快驗證完的時候才發現這個網址的,白白浪費了好多時間。
大家百度搜索一下關鍵字Symbol MDN ,可以看到有mozilla的一個開發者網站,這上面也有可以參考的示例。
另外,之所以要研究這個,主要是最近在學vue3 + ,身為一個沒太多基礎的前端小白,學習的過程中遇到一個知識點可能都會要查半天資料才能弄懂,不過我也樂在其中。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。