符号Symbol介绍及应用
前言
随着前端不断的发展,ES5
原本的 6 种数据类型 number
、string
、boolean
、undefined
、null
、object
已经不够使用,所有 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介绍及应用相关推荐
- html符号实体----介绍
html符号实体--介绍在网页上显示一些特殊的符号,我们需要使用html的符号实体,有些人把它称为字符实体,html的代码:<!..我们html内容是放在body体..><b> ...
- 比特(bits),符号(symbol),码片(chips)区别
比特(bits) 进入物理层进行基带信号处理之前的有效信息 比特率:每秒传送的比特(bit)数,单位bps(b/s),比特率越高,单位时间传送的数据量(位数)越大. 计算机中的信息都用二进制的0和1来 ...
- det曲线_Winform中设置ZedGraph的曲线符号Symbol以及对应关系
场景 Winforn中设置ZedGraph曲线图的属性.坐标轴属性.刻度属性: Winform中实现ZedGraph的多条Y轴(附源码下载): 添加多条曲线后,会默认生成不带任何Symbol的曲线,如 ...
- chatgpt赋能python:Python中的Tilde符号的介绍
Python中的Tilde符号的介绍 在Python的编程环境中,有一个比较神秘的符号,就是波浪线符号,即 "~" 或称为 "Tilde" 符号.这个符号在Py ...
- android log 如何获取double类型后小数点的值_【ES6基础】Symbol介绍:独一无二的值...
开篇 ES6之前我们都清楚JS有六种数据类型:Undefined.Null.布尔值(Boolean).字符串(String).数值(Number).对象(Object),今天笔者讲的Symbol类型是 ...
- ES6——符号Symbol
符号 一.普通符号 1.创建一个符号 2.符号的特点 二.共享符号 1.基础 2.实现 三.知名符号 1.Symbol.hasInstance 2.Symbol.isConcatSpreadable ...
- 【ES6基础】Symbol介绍:独一无二的值
开篇 ES6之前我们都清楚JS有六种数据类型:Undefined.Null.布尔值(Boolean).字符串(String).数值(Number).对象(Object),今天笔者讲的Symbol类型是 ...
- 如何修改Series和DataFrame类型中的元素值_「ES6基础」Symbol介绍:独一无二的值
ES6之前我们都清楚JS有六种数据类型:Undefined.Null.布尔值(Boolean).字符串(String).数值(Number).对象(Object),今天笔者讲的Symbol类型是ES6 ...
- Winform中设置ZedGraph的曲线符号Symbol以及对应关系
场景 Winforn中设置ZedGraph曲线图的属性.坐标轴属性.刻度属性: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/10 ...
- WinDBG 技巧:列出模块(DLL/EXE)里面所有的符号(symbol)
想对某个函数下断点,但是记不清楚的函数具体的名字,这个时侯可以使用x命令来列举所有的符号. 命令格式为: x [选项] 模块名字!符号匹配表达式 这里的符号匹配表达式类似dos的文件名匹配表达式,可以 ...
最新文章
- java知识积累——单元测试和JUnit(二)
- 基于STM32的SPI基本介绍
- python支持的几种并发方式进行简单的总结
- SemEval-2022组队参赛啦!八节课带你入门全球最大的语义测评大赛
- Pro Android学习笔记(三三):Menu(4):Alternative菜单
- 动态类型语言和静态类型语言
- 自动驾驶—— Image Caption的学习笔记(legacy)
- QML 插件开发 Plugin封装
- 基于Python操作Excel实战案例
- 视频教程-C#多层架构项目实战房屋中介管理系统-C#
- 关于小米路由器设置无线中继失败的问题
- 应急通信系统|消防应急指挥系统
- Ethernet0没有有效的IP配置
- LINUX gdk/X11正确获取DPI/Resolution的函数
- 时间序列:Shapelets
- c语言实现多目标优化,MOPSO 多目标例子群优化算法
- OpenCV视频篇——颜色跟踪
- AQI(空气质量指数)分析与预测(四)
- 【论文精读】TACRED Revisited: A Thorough Evaluation of the TACRED Relation Extraction Task
- 一篇文章解读提速、降费黑科技:PCDN定义、功能、架构、场景和优势
热门文章
- 谈谈在ISA防火墙中的端口映射方法
- mysql启动报错:由于找不到vcruntime140_1.dll,无法继续执行代码
- 【Java课设】学生信息管理系统简易版(文件IO代替数据库)
- css图片悬停特效,使用CSS3实现图片悬停放大的特效
- matlab中subs赋值范围,[转载]Matlab的accumarray(subs, val) 解释
- [论文笔记] Sigcomm 2018 Elastic Sketch: Adaptive and Fast Network-wide Measurements
- imac2017拆机
- 你真的认为iPhone只是一部手机?苹果惊天秘密之 四
- Linux禁用scp和sftp
- 智能名片小程序开发文档概要