目录
Symbol 是什么
symbol 作为对象属性
防止属性名称冲突
首页 web前端 js教程 JavaScript中Symbol 类型有什么用?

JavaScript中Symbol 类型有什么用?

Mar 19, 2019 am 11:19 AM
es6 javascript 前端 程序员

本篇文章给大家带来的内容是关于JavaScript中Symbol 类型有什么用?有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

Symbols 是 ES6 引入了一个新的数据类型 ,它为 JS 带来了一些好处,尤其是对象属性时。 但是,它们能为我们做些字符串不能做的事情呢?

在深入探讨 Symbol 之前,让我们先看看一些 JavaScript 特性,许多开发人员可能不知道这些特性。

背景

js中的数据类型总体来说分为两种,他们分别是:值类型 和 引用类型

值类型(基本类型):数值型(Number),字符类型(String),布尔值型(Boolean),null 和 underfined引用类型(类):函数,对象,数组等

值类型理解:变量之间的互相赋值,是指开辟一块新的内存空间,将变量值赋给新变量保存到新开辟的内存里面;之后两个变量的值变动互不影响,例如:

var a=10; //开辟一块内存空间保存变量a的值“10”;
var b=a; //给变量 b 开辟一块新的内存空间,将 a 的值 “10” 赋值一份保存到新的内存里;
//a 和 b 的值以后无论如何变化,都不会影响到对方的值;
登录后复制

一些语言,比如 C,有引用传递和值传递的概念。JavaScript 也有类似的概念,它是根据传递的数据类型推断的。如果将值传递给函数,则重新分配该值不会修改调用位置中的值。但是,如果你修改的是引用类型,那么修改后的值也将在调用它的地方被修改。

引用类型理解:变量之间的互相赋值,只是指针的交换,而并非将对象(普通对象,函数对象,数组对象)复制一份给新的变量,对象依然还是只有一个,只是多了一个指引~~;例如:

var a={x:1,y:2} //需要开辟内存空间保存对象,变量 a 的值是一个地址,这个地址指向保存对象的空间;
var b=a; // 将a 的指引地址赋值给 b,而并非复制一给对象且新开一块内存空间来保存;
// 这个时候通过 a 来修改对象的属性,则通过 b 来查看属性时对象属性已经发生改变;
登录后复制

值类型(神秘的 NaN 值除外)将始终与具有相同值的另一个值类型的完全相等,如下:

const first = "abc" + "def";
const second = "ab" + "cd" + "ef";
console.log(first === second); // true
登录后复制

但是完全相同结构的引用类型是不相等的:

const obj1 = { name: "Intrinsic" };
const obj2 = { name: "Intrinsic" };
console.log(obj1 === obj2); // false
// 但是,它们的 .name 属性是基本类型:
console.log(obj1.name === obj2.name); // true
登录后复制

对象在 JavaScript 语言中扮演重要角色,它们的使用无处不在。对象通常用作键/值对的集合,然而,以这种方式使用它们有一个很大的限制: 在 symbol 出现之前,对象键只能是字符串,如果试图使用非字符串值作为对象的键,那么该值将被强制转换为字符串,如下:

const obj = {};
obj.foo = 'foo';
obj['bar'] = 'bar';
obj[2] = 2;
obj[{}] = 'someobj';
console.log(obj);
// { '2': 2, foo: 'foo', bar: 'bar',
     '[object Object]': 'someobj' }
登录后复制

Symbol 是什么

Symbol() 函数会返回 symbol 类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的 symbol 注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。所以使用 Symbol 生成的值是不相等:

const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); // false
登录后复制

实例化 symbol 时,有一个可选的第一个参数,你可以选择为其提供字符串。 此值旨在用于调试代码,否则它不会真正影响symbol 本身。

const s1 = Symbol('debug');
const str = 'debug';
const s2 = Symbol('xxyy');
console.log(s1 === str); // false
console.log(s1 === s2); // false
console.log(s1); // Symbol(debug)
登录后复制

symbol 作为对象属性

symbol 还有另一个重要的用途,它们可以用作对象中的键,如下:

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 的缺点。

不幸的是,与该对象交互的代码仍然可以访问其键为 symbol 的属性。 在调用代码尚不能访问 symbol 本身的情况下,这甚至是可能的。 例如,Reflect.ownKeys() 方法能够获取对象上所有键的列表,包括字符串和 symbol :

function tryToAddPrivate(o) {
  o[Symbol('Pseudo Private')] = 42;
}
const obj = { prop: 'hello' };
tryToAddPrivate(obj);
console.log(Reflect.ownKeys(obj));
// [ 'prop', Symbol(Pseudo Private) ]
console.log(obj[Reflect.ownKeys(obj)[1]]); // 42
登录后复制
注意:目前正在做一些工作来处理在JavaScript中向类添加私有属性的问题。这个特性的名称被称为私有字段,虽然这不会使所有对象受益,但会使类实例的对象受益。私有字段从 Chrome 74开始可用。

防止属性名称冲突

符号可能不会直接受益于JavaScript为对象提供私有属性。然而,他们是有益的另一个原因。当不同的库希望向对象添加属性而不存在名称冲突的风险时,它们非常有用。

Symbol 为 JavaScrit 对象提供私有属性还有点困难,但 Symbol 还有别外一个好处,就是避免当不同的库向对象添加属性存在命名冲突的风险。

考虑这样一种情况:两个不同的库想要向一个对象添加基本数据,可能它们都想在对象上设置某种标识符。通过简单地使用 id 作为键,这样存在一个巨大的风险,就是多个库将使用相同的键。

function lib1tag(obj) {
  obj.id = 42;
}
function lib2tag(obj) {
  obj.id = 369;
}
登录后复制

通过使用 Symbol,每个库可以在实例化时生成所需的 Symbol。然后用生成 Symbol 的值做为对象的属性:

const library1property = Symbol('lib1');
function lib1tag(obj) {
  obj[library1property] = 42;
}
const library2property = Symbol('lib2');
function lib2tag(obj) {
  obj[library2property] = 369;
}
登录后复制

出于这个原因,Symbol 似乎确实有利于JavaScript。

但是,你可能会问,为什么每个库在实例化时不能简单地生成随机字符串或使用命名空间?

const library1property = uuid(); // random approach
function lib1tag(obj) {
  obj[library1property] = 42;
}
const library2property = 'LIB2-NAMESPACE-id'; // namespaced approach
function lib2tag(obj) {
  obj[library2property] = 369;
}
登录后复制

这种方法是没错的,这种方法实际上与 Symbol 的方法非常相似,除非两个库选择使用相同的属性名,否则不会有冲突的风险。

在这一点上,聪明的读者会指出,这两种方法并不完全相同。我们使用唯一名称的属性名仍然有一个缺点:它们的键非常容易找到,特别是当运行代码来迭代键或序列化对象时。考虑下面的例子:

const library2property = 'LIB2-NAMESPACE-id'; // namespaced
function lib2tag(obj) {
  obj[library2property] = 369;
}
const user = {
  name: 'Thomas Hunter II',
  age: 32
};
lib2tag(user);
JSON.stringify(user);
// '{"name":"Thomas Hunter II","age":32,"LIB2-NAMESPACE-id":369}'
登录后复制

如果我们为对象的属性名使用了 Symbol,那么 JSON 输出将不包含它的值。这是为什么呢? 虽然 JavaScript 获得了对 Symbol 的支持,但这并不意味着 JSON 规范已经改变! JSON 只允许字符串作为键,JavaScript 不会尝试在最终 JSON 有效负载中表示 Symbol 属性。

const library2property = 'f468c902-26ed-4b2e-81d6-5775ae7eec5d'; // namespaced approach
function lib2tag(obj) {
  Object.defineProperty(obj, library2property, {
    enumerable: false,
    value: 369
  });
}
const user = {
  name: 'Thomas Hunter II',
  age: 32
};
lib2tag(user);
console.log(user); // {name: "Thomas Hunter II", age: 32, f468c902-26ed-4b2e-81d6-5775ae7eec5d: 369}
console.log(JSON.stringify(user)); // {"name":"Thomas Hunter II","age":32}
console.log(user[library2property]); // 369
登录后复制

通过将 enumerable 属性设置为 false 而“隐藏”的字符串键的行为非常类似于 Symbol 键。它们通过 Object.keys() 遍历也看不到,但可以通过 Reflect.ownKeys() 显示,如下的示例所示:

const obj = {};
obj[Symbol()] = 1;
Object.defineProperty(obj, 'foo', {
  enumberable: false,
  value: 2
});
console.log(Object.keys(obj)); // []
console.log(Reflect.ownKeys(obj)); // [ 'foo', Symbol() ]
console.log(JSON.stringify(obj)); // {}
登录后复制

在这点上,我们几乎重新创建了 Symbol。隐藏的字符串属性和 Symbol 都对序列化器隐藏。这两个属性都可以使用Reflect.ownKeys()方法读取,因此它们实际上不是私有的。假设我们为属性名的字符串版本使用某种名称空间/随机值,那么我们就消除了多个库意外发生名称冲突的风险。

但是,仍然有一个微小的区别。由于字符串是不可变的,而且 Symbol 总是保证惟一的,所以仍然有可能生成字符串组合会产生冲突。从数学上讲,这意味着 Symbol 确实提供了我们无法从字符串中得到的好处。

在 Node.js 中,检查对象时(例如使用 console.log() ),如果遇到名为 inspect 的对象上的方法,将调用该函数,并将打印内容。可以想象,这种行为并不是每个人都期望的,通常命名为 inspect 的方法经常与用户创建的对象发生冲突。

现在 Symbol 可用来实现这个功能,并且可以在 equire('util').inspect.custom 中使用。inspect 方法在Node.js v10 中被废弃,在 v1 1中完全被忽略, 现在没有人会偶然改变检查的行为。

模拟私有属性

这里有一个有趣的方法,我们可以用来模拟对象上的私有属性。这种方法将利用另一个 JavaScript 特性: proxy(代理)。代理本质上封装了一个对象,并允许我们对与该对象的各种操作进行干预。

代理提供了许多方法来拦截在对象上执行的操作。我们可以使用代理来说明我们的对象上可用的属性,在这种情况下,我们将制作一个隐藏我们两个已知隐藏属性的代理,一个是字符串 _favColor,另一个是分配给 favBook 的 S ymbol :

let proxy;

{
  const favBook = Symbol('fav book');

  const obj = {
    name: 'Thomas Hunter II',
    age: 32,
    _favColor: 'blue',
    [favBook]: 'Metro 2033',
    [Symbol('visible')]: 'foo'
  };

  const handler = {
    ownKeys: (target) => {
      const reportedKeys = [];
      const actualKeys = Reflect.ownKeys(target);

      for (const key of actualKeys) {
        if (key === favBook || key === '_favColor') {
          continue;
        }
        reportedKeys.push(key);
      }

      return reportedKeys;
    }
  };

  proxy = new Proxy(obj, handler);
}

console.log(Object.keys(proxy)); // [ 'name', 'age' ]
console.log(Reflect.ownKeys(proxy)); // [ 'name', 'age', Symbol(visible) ]
console.log(Object.getOwnPropertyNames(proxy)); // [ 'name', 'age' ]
console.log(Object.getOwnPropertySymbols(proxy)); // [Symbol(visible)]
console.log(proxy._favColor); // 'blue'
登录后复制

使用 _favColor 字符串很简单:只需阅读库的源代码即可。 另外,通过蛮力找到动态键(例如前面的 uuid 示例)。但是,如果没有对 Symbol 的直接引用,任何人都不能 从proxy  对象访问'Metro 2033'值。

Node.js警告:Node.js中有一个功能会破坏代理的隐私。 JavaScript语 言本身不存在此功能,并且不适用于其他情况,例 如Web 浏览器。 它允许在给定代理时获得对底层对象的访问权。 以下是使用此功能打破上述私有属性示例的示例:

const [originalObject] = process
  .binding('util')
  .getProxyDetails(proxy);
const allKeys = Reflect.ownKeys(originalObject);
console.log(allKeys[3]); // Symbol(fav book)
登录后复制

现在,我们需要修改全局 Reflect 对象,或者修改 util 流程绑定,以防止它们在特定的 Node.js 实例中使用。但这是一个可怕的兔子洞。

本篇文章到这里就已经全部结束了,更多其他精彩内容可以关注PHP中文网的JavaScript教程视频栏目!

以上是JavaScript中Symbol 类型有什么用?的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它们
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

AI程序员哪家强?探索Devin、通义灵码和SWE-agent的潜力 AI程序员哪家强?探索Devin、通义灵码和SWE-agent的潜力 Apr 07, 2024 am 09:10 AM

2022年3月3日,距世界首个AI程序员Devin诞生不足一个月,普林斯顿大学的NLP团队开发了一个开源AI程序员SWE-agent。它利用GPT-4模型在GitHub存储库中自动解决问题。SWE-agent在SWE-bench测试集上的表现与Devin相似,平均耗时93秒,解决了12.29%的问题。SWE-agent通过与专用终端交互,可以打开、搜索文件内容,使用自动语法检查、编辑特定行,以及编写和执行测试。(注:以上内容为原内容微调,但保留了原文中的关键信息,未超过指定字数限制。)SWE-A

PHP与Vue:完美搭档的前端开发利器 PHP与Vue:完美搭档的前端开发利器 Mar 16, 2024 pm 12:09 PM

PHP与Vue:完美搭档的前端开发利器在当今互联网高速发展的时代,前端开发变得愈发重要。随着用户对网站和应用的体验要求越来越高,前端开发人员需要使用更加高效和灵活的工具来创建响应式和交互式的界面。PHP和Vue.js作为前端开发领域的两个重要技术,搭配起来可以称得上是完美的利器。本文将探讨PHP和Vue的结合,以及详细的代码示例,帮助读者更好地理解和应用这两

揭秘C语言的吸引力: 发掘程序员的潜质 揭秘C语言的吸引力: 发掘程序员的潜质 Feb 24, 2024 pm 11:21 PM

学习C语言的魅力:解锁程序员的潜力随着科技的不断发展,计算机编程已经成为了一个备受关注的领域。在众多编程语言中,C语言一直以来都备受程序员的喜爱。它的简单、高效以及广泛应用的特点,使得学习C语言成为了许多人进入编程领域的第一步。本文将讨论学习C语言的魅力,以及如何通过学习C语言来解锁程序员的潜力。首先,学习C语言的魅力在于其简洁性。相比其他编程语言而言,C语

前端面试官常问的问题 前端面试官常问的问题 Mar 19, 2024 pm 02:24 PM

在前端开发面试中,常见问题涵盖广泛,包括HTML/CSS基础、JavaScript基础、框架和库、项目经验、算法和数据结构、性能优化、跨域请求、前端工程化、设计模式以及新技术和趋势。面试官的问题旨在评估候选人的技术技能、项目经验以及对行业趋势的理解。因此,应试者应充分准备这些方面,以展现自己的能力和专业知识。

简易JavaScript教程:获取HTTP状态码的方法 简易JavaScript教程:获取HTTP状态码的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教程:如何获取HTTP状态码,需要具体代码示例前言:在Web开发中,经常会涉及到与服务器进行数据交互的场景。在与服务器进行通信时,我们经常需要获取返回的HTTP状态码来判断操作是否成功,根据不同的状态码来进行相应的处理。本篇文章将教你如何使用JavaScript获取HTTP状态码,并提供一些实用的代码示例。使用XMLHttpRequest

Django是前端还是后端?一探究竟! Django是前端还是后端?一探究竟! Jan 19, 2024 am 08:37 AM

Django是一个Python编写的web应用框架,它强调快速开发和干净方法。尽管Django是一个web框架,但是要回答Django是前端还是后端这个问题,需要深入理解前后端的概念。前端是指用户直接和交互的界面,后端是指服务器端的程序,他们通过HTTP协议进行数据的交互。在前端和后端分离的情况下,前后端程序可以独立开发,分别实现业务逻辑和交互效果,数据的交

Go语言前端技术探秘:前端开发新视野 Go语言前端技术探秘:前端开发新视野 Mar 28, 2024 pm 01:06 PM

Go语言作为一种快速、高效的编程语言,在后端开发领域广受欢迎。然而,很少有人将Go语言与前端开发联系起来。事实上,使用Go语言进行前端开发不仅可以提高效率,还能为开发者带来全新的视野。本文将探讨使用Go语言进行前端开发的可能性,并提供具体的代码示例,帮助读者更好地了解这一领域。在传统的前端开发中,通常会使用JavaScript、HTML和CSS来构建用户界面

Django:前端和后端开发都能搞定的神奇框架! Django:前端和后端开发都能搞定的神奇框架! Jan 19, 2024 am 08:52 AM

Django:前端和后端开发都能搞定的神奇框架!Django是一个高效、可扩展的Web应用程序框架。它能够支持多种Web开发模式,包括MVC和MTV,可以轻松地开发出高质量的Web应用程序。Django不仅支持后端开发,还能够快速构建出前端的界面,通过模板语言,实现灵活的视图展示。Django把前端开发和后端开发融合成了一种无缝的整合,让开发人员不必专门学习

See all articles