关于Symbol你肯定不知道的几个点

本文介绍了JavaScript中的Symbol类型,包括其唯一性、基本用法,如作为对象属性使用,以及如何通过全局符号注册表创建和重用。还探讨了常用的内置Symbol,如Symbol.hasInstance用于自定义`instanceof`行为,Symbol.isConcatSpreadable控制数组`concat()`方法的合并方式,以及如何重构正则表达式的`match()`方法。总结中强调了Symbol在多库协作和减少代码误操作中的重要性。


一、Symbol是什么?

  1. Symbol 是 ES6 新推出的一种基本类型,它的值是唯一的.
  2. 它可以接受一个字符串作为参数,带有相同参数的两个Symbol值不相等.
  3. 参数只是表示Symbol值的描述,主要用于程序调试时的跟踪.
  4. 当然也可以不传参,也可以通过typeof来判断是否为Symbol类型.

二、基本用法

  • 如果你对Symbol有一定的了解,可直接看第三部分.

1.Symbol值的基本获取

  • 任意两个symbol的值是不相等的
const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); // false
  • 可以写参数,但仅仅用于描述.
const s1 = Symbol('test');
const str = 'test';
const s2 = Symbol('test');
console.log(s1 === str); // false
console.log(s1 === s2); // false
console.log(s1); // Symbol(debug)
  • Symbol也属于基本类型,可以使用typeof检测
const s1 = Symbol('test');
console.log(typeof s1); // symbol

2.一些使用

  • 作为对象的属性使用,这也是最多的用法,可是一般对象属性,用这个属实没啥意思.
	  let sym1 = Symbol('test');
	  let obj={name: 'lin', [sym1]: 'foo'};
	  console.log(obj);
  • 获取Symbol作为属性值时的属性.
	   let s1 = Symbol('foo'),
		s2 = Symbol('bar');
	   let o={
		   [s1]:'foo val',
		   [s2]:'foo bar',
		   baz:'baz val',
		   qux:'quxval'
	   }
	   console.log(Object.getOwnPropertySymbols(o)); 
	   // [Symbol(foo), Symbol(bar)]
	   console.log(Object.getOwnPropertyDescriptors(o));
	   // {baz: {…}, qux: {…}, Symbol(foo): {…}, Symbol(bar): {…}}

3. 使用全局符号注册

  • 如果运行时不同部分需要和重用符号实例,可以用一个字符串作为键,在全局符号注册表中创建并重用符号.
  • 使用Symbol.for()即可.
let id = Symbol.for("test"); //获取desc为test的symbol  无则创建
let id1 = Symbol.for("test"); //获取desc为test的symbol	有则获取
console.log(id == id1); //true
  • 在全局注册表中定义符号和Symbol()中定义的符号并不相等.
let id = Symbol.for("test"); //全局注册
let id1 = Symbol('test');
console.log(id==id1); // false
  • 使用keyFor() 查询全局注册表.
let a1 = Symbol.for("a");
Symbol.keyFor(a1);    // "a"

let a2 = Symbol("a");
Symbol.keyFor(a2);    // undefined

三、常用内置符号

  • ES6中引入一些常用内置符号,用于暴露语言内部的行为.
  • 这些内置符号的重要作用就是重新定义他们,从而改变原生结构方法.
  • 如for-of的循环会在相关对象上用Symbol.iterator属性,我们就可以重新定义它.

1 认识Symbol.hasInstance

  • 可以判断某对象是否为某构造器的实例。
  • 因此可以用它自定义 instanceof 操作符在某个类上的行为.
  class Foo{}
  let f = new Foo();
  console.log(f instanceof Foo);  // true
  • 在 ES6中 instanceof 会调用Symbol.hasInstance来确定关系.
  class Foo{}
  let f = new Foo();
  console.log(Foo[Symbol.hasInstance](f));  // true
  • 该属性定义在Function的原型上,故任何的类和函数都可调用.
  • 因为instanceof会在原型链上寻找这个属性定义,因此我们可以重构它.
  class Bar{}
  // 在Baz 中重构,该静态方法
  class Baz extends Bar{
	  static [Symbol.hasInstance](){
		  return false;
	  }
  }
  
  let b = new Baz();
  console.log(Bar[Symbol.hasInstance](b)); // true
  console.log(b instanceof Bar);  // true
  
  // 此时在判断,统一返回false
  console.log(b instanceof Baz);  // false 
  console.log(Baz [Symbol.hasInstance](b));  // false 

2 重构数组concat()方法

  • Symbol.isConcatSpreadable属性主要控制数组concat()的合并方式.
  • 返回值为true时,则Array.prototype.concat()将打平数组.
 let arr1=['foo'],arr2=['bar'];
  console.log(arr1.concat(arr2)); // ['foo', 'bar']
  • 返回值为false时,则Array.prototype.concat()会将整个对象追加到数组末尾
  let arr1=['foo'],arr2=['bar'];
  // 设置为false,则将数组直接添加到最后,而不会平铺
  arr2[Symbol.isConcatSpreadable]=  false;
  console.log(arr1.concat(arr2));  //  ['foo', Array(1)]

3 重构正则match() 方法

  • match()主要是用正则表达式匹配字符串
  • String.prototype.match()会使用以Symbol.match为键的函数来对正则表达式求值.
console.log(RegExp.prototype[Symbol.match]); 
// ƒ [Symbol.match]() { [native code] }
 console.log('foobar'.match(/bar/));
  // ['bar', index: 3, input: 'foobar', groups: undefined]
  • 给match() 传入非正则时会被转化为哦RegExp对象.
  • 我们可以重新定义函数,让方法直接使用参数.
 class StringMatcher{
 // 接收外部传递的参数
	 constructor(str){
		 this.str = str;
	 }
	 // 重新定义math的匹配规则
	 static [Symbol.match](target){
		 return target.incluedes(this.str)
	 }
 }
  
  console.log('fooBar'.match(new StringMatcher('foo'))); 
  //true ['o', index: 1, input: 'fooBar', groups: undefined]
  console.log('barbaz'.match(new StringMatcher('foo')));
  //false ['b', index: 0, input: 'barbaz', groups: undefined]

正则的replace() 底层也是调用 Symbol.replace().
正则的search() 底层也是调用 Symbol.search().

更多请点击


总结

  1. Symbol在JS中,应被用作一种对象的归类方法,或着对象的标记方法。在共同协作或多库运行时,能够有效的减少代码的误操作几率。
  2. 在ES6中很多api的底层已经在使用Symbol进行定义.
  3. 凡是能用字符串和数字作为属性的地方都可以使用Symbol.
<think> 我们注意到用户在使用JDK1.8时遇到了“Cannot resolve symbol &#39;LinkedHashMap&#39;”的错误。这通常是因为没有正确导入类。在Java中,`LinkedHashMap`位于`java.util`包中,因此需要导入。 ### 解决方案: 在代码文件的开头添加导入语句: ```java import java.util.LinkedHashMap; import java.util.LinkedHashSet; ``` ### 完整代码示例(包括导入): ```java import java.util.*; import java.util.stream.Collectors; // ... 其他代码 // 1. 使用LinkedHashMap和LinkedHashSet保持分组顺序 Map<String, Set<String>> personAndHrbpUserIdMapping = hrbpList.stream() .collect(Collectors.groupingBy( DaiBanTIjiaoVO::getPersonId, LinkedHashMap::new, // 保持personId的插入顺序 Collectors.mapping( DaiBanTIjiaoVO::getUserId, Collectors.toCollection(LinkedHashSet::new) // 保持userId的插入顺序 ) )); List<String> hrbpUserIdList = hrbpList.stream() .map(DaiBanTIjiaoVO::getUserId) .collect(Collectors.toList()); List<HanderUserVo> handerUserVoList = advanceServiceDao.batchGetHrbpInfoBy(hrbpUserIdList); // 2. 使用LinkedHashMap保持userId分组顺序 Map<String, List<HanderUserVo>> userIdMap = handerUserVoList.stream() .collect(Collectors.groupingBy( HanderUserVo::getUserId, LinkedHashMap::new, // 保持userId的插入顺序 Collectors.toList() )); // 3. 最终收集时保持双顺序 return personAndHrbpUserIdMapping.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, entry -> entry.getValue().stream() // LinkedHashSet已有序 .flatMap(userId -> userIdMap.getOrDefault(userId, Collections.emptyList()).stream() ) .collect(Collectors.toList()), (oldVal, newVal) -> oldVal, // 合并函数,当键冲突时使用旧值(因为键会重复,这里可以随意) LinkedHashMap::new // 保持外层personId顺序 )); ``` ### 注意: - 如果已经导入了`java.util.*`,则需要单独导入`LinkedHashMap`和`LinkedHashSet`,因为`java.util`包已经包含它们。但是,如果使用了特定的导入(比如只导入了`java.util.HashMap`),则需要添加上述导入。 - 请确保IDE正确配置了JDK1.8,因为`LinkedHashMap`自JDK1.4就存在,所以1.8肯定支持。 §§相关问题§§ 1. 在Java中,除了LinkedHashMap,还有哪些集合类可以保持插入顺序? 2. 如果我想根据对象的某个属性自然排序,应该使用哪种集合? 3. 在Stream操作中,除了使用有序集合,还有哪些方法可以保证元素的顺序? 4. 当使用Collectors.toMap时,如果出现重复的键,默认会抛出异常,如何避免? </think> ### 为什么报错 "Cannot resolve symbol &#39;LinkedHashMap&#39;" 在 JDK 1.8 环境下出现此错误,通常有以下几个原因及解决方案: #### 1. **缺少导入语句**(最常见原因) `LinkedHashMap` 属于 `java.util` 包,需显式导入: ```java import java.util.LinkedHashMap; import java.util.LinkedHashSet; ``` #### 2. **IDE 缓存问题**(IntelliJ/Eclipse 常见) 尝试以下操作: 1. 击 IDE 菜单:**File > Invalidate Caches / Restart** 2. 选择 **Invalidate and Restart** 3. 等待 IDE 重建索引 #### 3. **JDK 未正确配置** 检查项目配置: 1. 确保项目使用的 JDK 是 1.8 或更高版本 2. 验证 `JAVA_HOME` 环境变量指向 JDK 1.8 3. 在 Maven/Gradle 中确认配置: ```xml <!-- Maven 示例 --> <properties> <java.version>1.8</java.version> </properties> ``` #### 4. **JDK 安装问题** 验证 JDK 完整性: 1. 检查 `$JAVA_HOME/jre/lib/rt.jar` 中是否存在 `LinkedHashMap` 类 2. 命令行验证: ```bash jar tf $JAVA_HOME/jre/lib/rt.jar | grep LinkedHashMap ``` 应输出:`java/util/LinkedHashMap.class` #### 5. **代码修正示例** 使用完全限定名(推荐,但可临时验证): ```java Map<String, Set<String>> personAndHrbpUserIdMapping = hrbpList.stream() .collect(Collectors.groupingBy( DaiBanTIjiaoVO::getPersonId, java.util.LinkedHashMap::new, // 使用完全限定名 Collectors.mapping( DaiBanTIjiaoVO::getUserId, Collectors.toCollection(java.util.LinkedHashSet::new) ) )); ``` > **注意**:`LinkedHashMap` 自 JDK 1.4 就已存在,JDK 1.8 完全支持。99% 的报错都是由于缺失导入或 IDE 索引问题导致。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值