百科狗-知识改变命运!
--

Symbol 类型 - JavaScript 数据类型

乐乐1年前 (2023-11-21)阅读数 20#技术干货
文章标签变量

Symbol 类型

在使用别人提供的对象时,在拿到这个对象,然后想往里面添加新的属性名,为了防止和别人的属性名重复,这样是会发生冲突的,对象的属性名必须要求是独一无二的,因此,ES6 中加入了 Symbol 数据类型,确保对象的属性名独一无二Symbol 实际上是引入的一种原始数据类型

// ES6的symbol
let sym = Symbol("addr");
console.log(sym);
console.log(typeof sym);

Symbol函数不是一个构造函数,前面不能用new操作符,所以Symbol类型的值也不是对象,不能添加任何属性,只是类似于字符型的数据类型,强加new操作符会报错!


Symbol 值是唯一的,这是 Symbol 变量最大的特点。通过同样的方式创建的两个变量,即使参数也一样,这两个 Symbol 变量也是不相等的。

let x = Symbol();
let y = Symbol();

console.info(x === x);  // true
console.info(x === y);  // false


symbols 重要的用途,就是用作对象的 key。

const obj = {};
const sym = Symbol();
obj[sym] = 'foo';
obj.bar = 'bar';

console.log(obj);  // { bar: 'bar' }
console.log(sym in obj);   // true
console.log(obj[sym]);   // foo
console.log(Object.keys(obj));   // ['bar']

Symbol 类型 - JavaScript 数据类型

我们注意到使用Object.keys()并没有返回 symbols,这是为了向后兼容性的考虑。老代码不兼容 symbols,因此古老的Object.keys()不应该返回 symbols。


Symbol函数的参数

Symbol 函数还可以接受一个字符参数,来对产生的 Symbol 值进行描述,方便我们区分不同的 Symbol值

let symName = Symbol('test');
let symAddr = Symbol('test');
console.log('symName',symName);
console.log('symAddr',symAddr);
console.log('symName == symAddr?',symName == symAddr);
let sAddr = Symbol('symAddr');
console.log('sAddr == symAddr?',sAddr == symAddr);

如果创建 Symbol 值时传入的是对象,那么会先调用对象的toString方法得到一个字符串,再将结果字符串作为参数生成一个 Symbol 值。所以,Symbol 函数的参数只能是字符串。

const obj = {
  a: "a",
  b: "b", toString() {
    return "xx";
  },
};
console.info(Symbol(obj)); // Symbol(xx)

如果创建 Symbol 值时传入的是对象,没有定义 toString 方法:

const obj = {
  a: "a",
  b: "b",
};

console.info(Symbol(obj)); // Symbol([object Object])


Symbol 值不可以进行运算

Symbol 是一种数据类型,但它与 Number 以及 String 类型不同,不能参与运算,Symbol 值可以转化为字符串或布尔值,但是不能转为数值。

let sym1 = Symbol('sym1');
let sym2 = Symbol('sym2');
//转为字符串
console.log(String(sym1));
//转为布尔值
console.log(Boolean(sym2));
// 直接进行运算
console.log('sym1+sym2=?',sym1+sym2);


Symbol 作为属性

Symbol 就是用于属性去创造的,下面是几种 Symbol 作为属性的写法

//写法一:
let obj = {}; //创建一个对象
 let sym = Symbol(); 
obj[sym] = '湖南省长沙市';
//取属性值
console.log(obj[sym]);
// 写法二
let sym = Symbol(); 
let obj = {
    [sym]:'安徽省芜湖市'
}; 
console.log(obj[sym]);
//写法三
let obj = {}
let sym = Symbol()
Object.defineProperty(obj,sym,{value:'小陈同学'});
console.log(obj[sym]);

Symbol 值作为属性名时,获取相应的属性值不能用作点运算符,如果点运算符来给对象的属性赋 Symbol 类型的值,实际上属性名会变成字符串,而不是 Symbol 值,在对象内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号内,否则就是一个字符串。


getOwnPropertySymbols()

通常遍历对象,会有for...in, for...ofObject.keys()Object.getOwnPropertyNames()这些方法,但是,这些方法都不能获取到对象的 Symbol 值的属性名。那是不是在外部就没有办法获取 Symbol 值的属性名了呢?办法当然还是有的。

Object.getOwnPropertySymbols()就可以返回对象中的所有类型为 Symbol 值的属性名,得到一个元素类型为 symbol 的数组。除此之外,还有一个方法Reflect.ownKeys()可以返回包含 Symbol 属性名的所有属性名。

const obj = {
  a: 1,
  [Symbol()]: 2,
};

console.info("obj", Object.getOwnPropertyNames(obj));   //  [ 'a' ]
console.info("obj", Object.getOwnPropertySymbols(obj));  //  [ Symbol() ]
console.info("obj", Reflect.ownKeys(obj));   //  [ 'a', Symbol() ]
var obj={
  [Symbol('name')]:'like',
  [Symbol('age')]:'18',
  [Symbol()]:'play dou dou',
  sex:'male'
}

obj.sex  // male

//获取不到
obj[Symbol('name')]  //undefined
obj[Symbol('age')]  //undefined
obj[Symbol()]  //undefined

//for 检测不到
for(var i in obj){
  console.log(obj[i])
}
// 输出结果只有:male
let name = Symbol('name');
let age = Symbol('age');
let hobby = Symbol('hobby');

var obj={
  [name]:'like',
  [age]:'18',
  [hobby]:'play dou dou',
  sex:'male'
}

obj[name]  //like
obj[age]  //18

//仍然遍历不到
for(var i in obj){
  console.log(obj[i])
}
// 输出结果只有:male

//Object.getOwnPropertySymbols(obj) 可以遍历到
let symbols = Object.getOwnPropertySymbols(obj)
for(var i in symbols){
  console.log(obj[symbols[i]])
}
/*遍历结果:
like
18
play dou dou
*/


Symbol.for()、Symbol.keyFor()

Symbol 类型的变量除了自身,不会等于其他任何变量,但是依然存在特例。区别于通过Symbol()函数创建 Symbol 变量,我们还可以通过Symbol.for()方法来创建 Symbol 变量,该方法可以传入参数,而且在传入参数相同的时候,创建的 Symbol 也是相等的。

const a = Symbol.for("aa");
const b = Symbol.for("aa");
console.info("a===b", a === b);  // a===b true
console.info("type", typeof a);  // type symbol

Symbol 类型的 a 和 b ,通过===运算可以得到true。这是为什么呢?

因为Symbol.for()其实是带有类似重用机制的,具体的说,就是通过Symbol.for()创建变量时,传入的参数(假设为 x)会作为 Symbol 变量的 key ,然后到全局中搜索,是否已经有相同 key 的 Symbol 变量,如果存在,则直接返回这个 Symbol 变量。如果没有,才会创建一个 key 为传入参数 x 的 Symbol 变量,并将这个变量写到全局,供下次创建时被搜索。

通过Symbol.for()创建的 Symbol 变量,传入的参数是否相等决定得到的 Symbol 变量是否相等。

分析上面的代码,通过Symbol.for("aa")创建变量并赋值给 a 的时候,会在全局检索是否有 key 为'aa'的 Symbol 变量,这里显然是没有的,所有得到一个全新的 Symbol 变量,其带有的 key 是'aa',并且变量被写到全局。然后执行const b = Symbol.for("aa");时,Symbol.for('aa')会找到之前的 key 为'aa'的 Symbol 变量,并赋值给 b ,所以,a 和 b 实际上是同一个 Symbol 变量。此时 Symbol.for("aa")不管执行多少次,得到的都是同一个 Symbol 变量。

既然通过Symbol.for()创建的 Symbol 变量的 key 这么重要,那我们怎么获取到这个 key 呢,那就要Symbol.keyFor()方法了,该函数会返回一个已经写到全局的 Symbol 变量的 key 值。这样获取到 Symbol 变量的 key,就可以创建一个和原 Symbol 变量相等的变量了。


内置的 Symbol 值

  • Symbol.hasInstance:对象的 Symbol.hasInstance 属性指向一个内部方法,当其他对象使用 instance 运算符,判断是否为该对象的实例时,会调用这个方法。
  • Symbol.isConcatSpreadable:布尔值,表示该对象用于 Array.prototype.concat()时,是否可以展开。
  • Symbol.species:对象的 Symbol.species 属性,指向一个构造函数。创建衍生对象时,会使用该属性定义 Symbol.species 属性要采用 get 取值器。
  • Symbol.match:指向一个函数。当执行 str.match(myObject)时,如果该属性存在,会调用他,返回该方法的返回值。
  • Symbol.replace:指向一个方法,当该对象被 String.prototype.replace 方法调用时,会返回该方法的返回值。
  • Symbol.search:指向一个方法,当该对象被 String.prototype.search 方法调用时,会返回该方法的返回值。
  • Symbol.split:指向一个方法,当该对象被 String.prototype.split 方法调用时,会返回该方法的返回值。
  • Symbol.iterator:对象的 Symbol.iterator 属性,指向该对象的默认遍历器方法。
  • Symbol.toPrimitive:指向一个方法,当该对象被转化为原始类型的值时,会返回该方法的返回值。被调用时,会接受一个字符串参数,表示当前运算的模式:Number,String,Default。
  • Symbol.toStringTag:指向一个方法,当该对象被 String.prototype.toString 方法调用时,可以用来定制[object xxx]中 object 后面的那个字符串。
  • Symbol.unscopables:指向一个对象,该对象指定了使用 with 关键字时,那些属性会被 with 环境排除(with 语句的作用是将代码的作用域设置到一个特定的作用域中)。

鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com

免责声明:我们致力于保护作者版权,注重分享,当前被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!邮箱:344225443@qq.com)

图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

内容声明:本文中引用的各种信息及资料(包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主体(包括但不限于公司、媒体、协会等机构)的官方网站或公开发表的信息。部分内容参考包括:(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供参考使用,不准确地方联系删除处理!本站为非盈利性质站点,本着为中国教育事业出一份力,发布内容不收取任何费用也不接任何广告!)