前言

随着前端不断的发展,ES5 原本的 6 种数据类型 numberstringbooleanundefinednullobject 已经不够使用,所有 ES6 新增符号数据类型 symbol 基础类型,相信大家会发现在工作中很少会使用到 symbol 从而导致认为符号没有什么作用,而实际上符号的出现减少了 js 中一些魔法使用,下面我们就对符号 symbol 深入学习

符号是ES6新增的一个数据类型,它通过使用函数 Symbol(符号描述)来创建符
符号设计的初衷,是为了给对象设置私有属性
共享符号可以根据某个符号名称(符号描述)能够得到同一个符号,通过使用函数 Symbol.for("符号名/符号描述")来创建

Symbol 特点

  • 没有字面量(每个Symbol 都是通过Symbol函数创建)

  • 每次调用 Symbol 函数得到的符号永远不相等,无论符号名是否相同

  • 符号可以作为对象的属性名存在,这种属性称之为符号属性(es6 之前对象属性只能是字符串或者数字)

  • 符号无法被隐式转换,不能被用于数学运算、字符串拼接或其他隐式转换,但符号可以通过 String 构造函数显式的转换为字符串

  • 不能被 new执行(不能进行构造函数) 但是有原型

Symbol.iterator

该符号可以判断数据是否拥有迭代功能,它可以影响数据迭代器遍历功能

// 对象默认不具备 Symbol.iterator 属于不可以被遍历的数据结构
// 具有可迭代数据有: argument / NodeList / HTMLcollection / String / Set / Map / generator
// 改写对象使对象也具有 for of 遍历
Object.prototype[Symbol.iterator] = function () {let self = this;let keys = Reflect.ownKeys(this); // 获取所有 key 属性只包含 Symbol 符号属性let index = 0;return {next() {let key = keys[index++];// 遍历结束if (index > keys.length) {return {done: true,value: undefined}}return {done: false,value: {key,value: self[key]}}}}
}
// 测试原本对象不具有 for of 现在已经具备
var ob = {a: 1,b: 2,c: 3,d: 5
}
for (var key of ob) {console.log(key);
}
// 由上面结果可知我们可以使用 Symbol.iterator 进行一些不具备迭代数据进行改造以此满足需求

Symbol.hasInstance

该符号用于定义构造函数的静态成员,它将影响 instanceof 的判定

// instanceof 实现原理是  obj instanceof fn ==>  fn[Symbol.hasInstance](obj) 先查看原型中 fn[Symbol.hasInstance](obj) 返回值
// 写法一(构造函数)
function A() {}
Object.defineProperty(A, Symbol.hasInstance, {value: function (obj) {return false;   // 所有都返回 false}
})
const obj = new A();
console.log(A[Symbol.hasInstance](obj)); // false
console.log(obj instanceof A);  // 实际等同上面面写法// 写法二(类)
class Fn {constructor() {     this.x = Symbol.for("xxx");  // 设置知名符号} static [Symbol.hasInstance](obj) {return obj.x && obj.x === Symbol.for("xxx");}
}
// 方法二只要不是 Fn 创建的构造函数使用 instanceof 返回都是 false

Symbol.isConcatSpreadable

该知名符号会影响数组的 concat 方法

// 数组中使用 concat 方法合并根据连接的数据是否为数组或者类数组进行分割合并
const arr = [3];
const arr2 = [5, 6, 7, 8];
arr2[Symbol.isConcatSpreadable] = false;
const result = arr.concat(56, arr2)
console.log(result)  //  [3, 56, [5,6,7,8]]
// 类数组
var obj = {0: 8,1: 9,2: 10,length: 3,[Symbol.isConcatSpreadable]: true,  // 使用时会进行类数组拆分为数组每项进行拼接,如果不设置则直接连接类数组对象
}
const result2 = arr.concat(4, obj);
console.log(result2);  // [ 3, 4, 8, 9, 10 ]
// 由上面可值类数组设置 Symbol.isConcatSpreadable 时可以进行拆分每项进行添加而不是添加整体

Symbol.toPrimitive

该知名符号会影响类型转换的结果

// Symbol.toPrimitive 知名符号可以改变引用类型转化为基础类型结果
class Temperature {constructor(degree) {this.degree = degree;}// 注意不是静态方法哦[Symbol.toPrimitive](type) {// type: "default"  "string"  "number"if (type === "default") {return this.degree + "摄氏度";}else if (type === "number") {return this.degree;}else if (type === "string") {return this.degree + "℃";}}
}
const t = new Temperature(30);
console.log(t + "");    // 30摄氏度
console.log(t / 2);     // 15
console.log(String(t)); // 30C// 请问下面函数参数为何时候能打印
function isTrue(o) {// 注意不要全等不然真没有结果if (o == 1 && o == 2 && o == 3) {console.log("ojbk");}
}
// 方法一
var o = {a: 1,[Symbol.toPrimitive](hint) {return this.a ++;}
}
// 方法二
var o = {a: 1,valueOf() {return this.a++;}
}
// 方法三
var o = {a: 1,toString() {return this.a++;}
}
// 此题就是利用引用类型隐式转化为基本类型首先看 [Symbol.toPrimitive]()  然后看 valueOf()  最后看 toString()

Symbol.toStringTag

该知名符号会影响 Object.prototype.toString 的返回值

// Symbol.toStringTag 可以实现使用 toString 判断类型时更加精确的知道自身的构造函数
// 写法一
class Person {constructor() {// 通过 Symbol.toStringTag 改写  Object.prototype.toString.call 更准确this[Symbol.toStringTag] = "Person"}
}
// 写法二
Person .prototype[Symbol.toStringTag] = "FN"  // 等价上面写法
const p = new Person();
const arr = [];
console.log(Object.prototype.toString.apply(p));   //[object Person]
console.log(Object.prototype.toString.apply(arr))  //[object Array]
// 由上面可知我们可以改造 toString() 返回更加精确的值

Symbol 应用

  • 对象设置私有属性,(类中设置外部不能访问的属性方法)

  • 给对象设置唯一属性值,(实现 Function.prototype.call 方法)

  • 框架中redux/vuex公共管理状态管理时候,派发的行为标识可以基于Symbol类型进行宏管理

实现类中私有属性

// 实现类中私有属性使其外面不能访问到只能内部使用
const Hero = (() => {const getRandom = Symbol();return class {constructor(a) {this.a= a;}attack() {const dmg = this.a* this[getRandom](0.8, 1.1);console.log(dmg);}// 私有属性随机函数[getRandom](min, max) {  return Math.random() * (max - min) + min;}}
})();
const h = new Hero(3);
h.attack();  // 不报错
h[Symbol]()  // 报错
// 但是我们也可以使用一些不寻常的手段获取使用
const sybs = Object.getOwnPropertySymbols(Hero.prototype);
console.log(h[sybs[0]](3));  // 不报错
// 但是强烈不建议上面使用类中的私有属性

手写实现call方法

// 手写实现自己 mycall 方法
Function.prototype.myCall = function (content, ...arg) {content = (content === undefined || content === null) ? (typeof window === "undefined" ? {} : window) : Object(content);  // 这是确定 this 指向判断是否node环境,如果传入基础类型进行变为包装类型let key = Symbol(); // 唯一值content[key] = this;  // 将 方法绑定到对象唯一属性上let result = content[key](...arg);  // 函数可能有返回值delete content[key];  // 用完要删除该唯一属性return result
}
// 同理我们可以基于 Symbol 符号封装 apply 方法

实现更准确 instanceof

// 重写 instanceof 方法确保不备被别人重写
class fn {constructor() {this.x = Symbol.for("xxx"); }// 设置这个是让 instanceof 不能通过原型链改变, 因为原有的 instanceof 是判断实列是否为构造函数容易改写原型static [Symbol.hasInstance](obj) {return obj.x && obj.x === Symbol.for("xxx");}
}
let f = new fn();
console.log(f instanceof fn)  // true
// instanceof 实现原理是  obj instanceof fn ==>  fn[Symbol.hasInstance](obj) 先查看原型中 fn[Symbol.hasInstance](obj) 返回值
let arr = [1, 2, 3, 4];
Object.setPrototypeOf(arr, fn.prototype);  // 设置原型
console.log(arr instanceof fn);   // false
// 由上面可知虽然上面设置原型但是现在判断依然为 false

实现更准确 toString 返回值

// 实现下面代理
// console.log(Object.prototype.toString.call(f)); ==> "[object,FN]"
// 写法一
class FN {constructor() {// 通过 Symbol.toStringTag 改写  Object.prototype.toString.call 更准确this[Symbol.toStringTag] = "FN"}
}
// 写法二
FN.prototype[Symbol.toStringTag] = "FN"  // 等价方法一
let AA = new FN();
console.log(Object.prototype.toString.call(AA))  // "[object,FN]"
// console.log(AA.constructor == FN)  // 实例的 constructor 指向构造函数
// console.log(AA.__proto__ == FN.prototype)   // 实例的  __proto__  指向构造函数的原型

实现对象具有遍历功能

// 实现所有对象都具备 for of  遍历功能
Object.prototype[Symbol.iterator] = function () {let self = this;let keys = Reflect.ownKeys(self);  // 获取所有的对象属性包括符号属性let index = 0;return {next() {if (index > keys.length - 1) {return {done: true,value: undefined}}let key = keys[index++];  // 保存到外部因为避免两次 ++ return {done: false,value: { key, value: self[key] }}}}
}
// 以上使用到 Reflet.ownKeys() 为反射功能,能获取所有对象属性

总结

通过上面的知名符号的学习是不是相当于发现了新大陆,原来符号 symbol 还能这么玩,因为有了符号的加入,所以我们就能更好的控制魔法的语法,也从而让我们知道一些语法底层是怎么实现的。

  • Symbol.iterator 可以实现改造迭代器使其具备 for of 功能

  • Symbol.hasInstance 可以改写更加精确的 instanceof 返回值使其设置原型无效

  • Symbol.toStringTag 可以设置重写的 toString 方法值返回更加精确指向自身构造函数

  • Symbol.toPrimitive 控制引用类型转化为基础类型值方法

符号Symbol介绍及应用相关推荐

  1. html符号实体----介绍

    html符号实体--介绍在网页上显示一些特殊的符号,我们需要使用html的符号实体,有些人把它称为字符实体,html的代码:<!..我们html内容是放在body体..><b> ...

  2. 比特(bits),符号(symbol),码片(chips)区别

    比特(bits) 进入物理层进行基带信号处理之前的有效信息 比特率:每秒传送的比特(bit)数,单位bps(b/s),比特率越高,单位时间传送的数据量(位数)越大. 计算机中的信息都用二进制的0和1来 ...

  3. det曲线_Winform中设置ZedGraph的曲线符号Symbol以及对应关系

    场景 Winforn中设置ZedGraph曲线图的属性.坐标轴属性.刻度属性: Winform中实现ZedGraph的多条Y轴(附源码下载): 添加多条曲线后,会默认生成不带任何Symbol的曲线,如 ...

  4. chatgpt赋能python:Python中的Tilde符号的介绍

    Python中的Tilde符号的介绍 在Python的编程环境中,有一个比较神秘的符号,就是波浪线符号,即 "~" 或称为 "Tilde" 符号.这个符号在Py ...

  5. android log 如何获取double类型后小数点的值_【ES6基础】Symbol介绍:独一无二的值...

    开篇 ES6之前我们都清楚JS有六种数据类型:Undefined.Null.布尔值(Boolean).字符串(String).数值(Number).对象(Object),今天笔者讲的Symbol类型是 ...

  6. ES6——符号Symbol

    符号 一.普通符号 1.创建一个符号 2.符号的特点 二.共享符号 1.基础 2.实现 三.知名符号 1.Symbol.hasInstance 2.Symbol.isConcatSpreadable ...

  7. 【ES6基础】Symbol介绍:独一无二的值

    开篇 ES6之前我们都清楚JS有六种数据类型:Undefined.Null.布尔值(Boolean).字符串(String).数值(Number).对象(Object),今天笔者讲的Symbol类型是 ...

  8. 如何修改Series和DataFrame类型中的元素值_「ES6基础」Symbol介绍:独一无二的值

    ES6之前我们都清楚JS有六种数据类型:Undefined.Null.布尔值(Boolean).字符串(String).数值(Number).对象(Object),今天笔者讲的Symbol类型是ES6 ...

  9. Winform中设置ZedGraph的曲线符号Symbol以及对应关系

    场景 Winforn中设置ZedGraph曲线图的属性.坐标轴属性.刻度属性: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/10 ...

  10. WinDBG 技巧:列出模块(DLL/EXE)里面所有的符号(symbol)

    想对某个函数下断点,但是记不清楚的函数具体的名字,这个时侯可以使用x命令来列举所有的符号. 命令格式为: x [选项] 模块名字!符号匹配表达式 这里的符号匹配表达式类似dos的文件名匹配表达式,可以 ...

最新文章

  1. java知识积累——单元测试和JUnit(二)
  2. 基于STM32的SPI基本介绍
  3. python支持的几种并发方式进行简单的总结
  4. SemEval-2022组队参赛啦!八节课带你入门全球最大的语义测评大赛
  5. Pro Android学习笔记(三三):Menu(4):Alternative菜单
  6. 动态类型语言和静态类型语言
  7. 自动驾驶—— Image Caption的学习笔记(legacy)
  8. QML 插件开发 Plugin封装
  9. 基于Python操作Excel实战案例
  10. 视频教程-C#多层架构项目实战房屋中介管理系统-C#
  11. 关于小米路由器设置无线中继失败的问题
  12. 应急通信系统|消防应急指挥系统
  13. Ethernet0没有有效的IP配置
  14. LINUX gdk/X11正确获取DPI/Resolution的函数
  15. 时间序列:Shapelets
  16. c语言实现多目标优化,MOPSO 多目标例子群优化算法
  17. OpenCV视频篇——颜色跟踪
  18. AQI(空气质量指数)分析与预测(四)
  19. 【论文精读】TACRED Revisited: A Thorough Evaluation of the TACRED Relation Extraction Task
  20. 一篇文章解读提速、降费黑科技:PCDN定义、功能、架构、场景和优势

热门文章

  1. 谈谈在ISA防火墙中的端口映射方法
  2. mysql启动报错:由于找不到vcruntime140_1.dll,无法继续执行代码
  3. 【Java课设】学生信息管理系统简易版(文件IO代替数据库)
  4. css图片悬停特效,使用CSS3实现图片悬停放大的特效
  5. matlab中subs赋值范围,[转载]Matlab的accumarray(subs, val) 解释
  6. [论文笔记] Sigcomm 2018 Elastic Sketch: Adaptive and Fast Network-wide Measurements
  7. imac2017拆机
  8. 你真的认为iPhone只是一部手机?苹果惊天秘密之 四
  9. Linux禁用scp和sftp
  10. 智能名片小程序开发文档概要