草庐IT

一道题开始认识Symbol

林恒 2023-03-28 原文

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

最近每天学习的时候,发现了一道很有趣的面试题

1.const [a, b] = { a: 100, b: 200 }
2.console.log(a)
3.console.log(b)
如何在不改变1的情况下,让代码不报错

这个时候,我突然想起来,之前看红宝书的时候,有过这么一篇内容

 最后我的解题代码为

Object.prototype[Symbol.iterator] = function(){
    return Object.values(this)[Symbol.iterator]
}
const [a, b] = { a: 100, b: 200 }
console.log(a)
console.log(b)

试了下,果然ok

 接下来给大家分享下我在红宝书的基础上,在网上找到的几个基础资料,方便大家理解代码

1.什么是symbol

首先symbol是es6新增的类型,它是一个基本类型,symbol英文翻译是符号的意思。

在叙述symbol之前,推出一个概念,“以操作目标为程序本身的行为特性的编程,我们称为元编程。”

我们已知symbol是es6新增类型,按照逻辑推演:作为新增的类型,symbol一定是为了弥补了之前的不足,所以才出现的,是这样的吗?

首先我们说说它的特别之处:

symbol属性值对应的值是唯一的,这解决了命名冲突的问题,类似于id的作用。
symbol值不能与其他数据进行计算,包括与字符串拼接。
for/in ,for/of遍历时不会遍布symbol属性
这样看是不是觉得没什么特别的?最多也就弥补了命名冲突的问题。

 下面推出第二个问题:

2.Iterotor是什么?

Iterator是一个遍历器的接口,是部署在数据结构上的,很多数据结构原生具备Iterator接口,这就意味着,我们不需要任何处理就可以使用for/of了,拥有symbol.Iterator属性返回的对象,都会在使用for/of时被当作Iterator接口当这个对象符合Iterator接口的标准时,for/of就可以完成任务,不符合就会报错!

刨除这个问题,我们知道symbol可以作为属性值存在。并且它具有唯一的特性,举个栗子:

直接let s = Symbol();测试s就是Symbol类型了。怎么说他是唯一的呢?
let s = Symbol(); let ss = Symbol(); s == ss ; 结果是false;或者
let s = Symbol('a'); let ss = Symbol('a'); s == ss ; 结果是false

有人会好奇Symbol('a')里面的参数a又是怎么回事呢?字符串a表示一种修饰,对你当前创建的Symbol类型的一种修饰,作为区分使用,否则当你创建多个Symbol数据时,容易混淆。

现在我们回过头说Iterotor是symbol的内置符号,而字符串a是自定义的符号。

3.symbol的内置符号symbol.Iterator,有什么用?

这个符号可以是任意对象上的一个专门属性,语言机制会自动的在这个属性上寻找一个方法,这个方法会构造一个迭代器来迭代这个对象的值,这个方法就是next方法,...展开和for/of循环会自动使用它,我们可以自定义symbol.iterator属性为任意对象值定义自己的迭代器逻辑,他将覆盖默认的迭代器。相当于是定义了一种元编程行为,提供给Javascript其他部分(也就是运算符和循环结构)在处理定义的对象时使用。

在Js中迭代器对象实现了可迭代协议,迭代器对象由Symbol.iterator属性的值返回。
Symbol.iterator属性的值是一个函数,它返回一个迭代器对象。
迭代器指的是拥有next方法的对象。
该next方法必须返回一个带有value和done的对象。

4.为什么会出现for..of?

为了理解条件语句,我们曾想象JavaScript解释器在源代码中会经过不同路径。而循环语句则是把这些路径弯曲又折回起点,以重复执行代码中的某部分。

es6定义了一个新循环语句:for/of。这种新循环虽然使用for关键字,但它们是完全不一同的两种循环,你说和for/in像不像?为什么?

for/of循环是专门用于可迭代对象的,什么是可迭代对象呢?

我们前文提到具有symbol.iterator属性的对象就是可以迭代的。而这个对象就是可迭代对象。

对象本身默认是不可迭代的。运行时尝试对常规对象使用for/of会抛出TypeError:

let o={x:1,y:2,z:3};
for(let element of o){//抛出TypeError,因为o不是迭代的对象
    console.log(element);
}

如果你想迭代对象,可以使用for/in循环,或者基于Object.keys()方法的结果使用for/of:

let o={x:1;y:2,z:3};
let keys="";
for(let k of Object .keys(o)){
    keys+=k;
}
console.log(keys) //x y z
/*这是因为Object.keys()返回一个对象属性名的数组,而数组是通过for/of来迭代的。也要注意,这种对象的键的迭代并不是实时的,在循环体内修改o不会影响迭代。如果你不在乎对象的属性,也可以像下面这样迭代每个键对应的值:
*/
let o={x:1;y:2,z:3};
let sum=0;
for(let v of Object .values(0)){
    sum+=v;
}
console.log(sum)
//6
如果你即想要对象的属性还想要属性的值,可以基于Object.entries()和解构赋值来使用for/of:
let pairs="";
for(let [k , v] of Object .entries(o)){
    pairs+=k + v;
}
console.log(pairs)//x 1 y 2 z 3
Object .entries()返回一个数组的数组,其中每个内部数组表示对象的一个属性的键值对。这里使用解构赋值把这些内部数组分开并且分别把它们的元素赋值给两个变量。

不知道你懂了没有,如果没有,那么接下来一篇,会加深你的印象

es6中有三类结构生来就具有Iterator接口:数组、类数组对象、Map和Set结构。

var arr = [1,2,3,4];
let iterator = arr[Symbol.iterator]();
 
console.log(iterator.next());  //{ value: 1, done: false }
console.log(iterator.next());  //{ value: 2, done: false }
console.log(iterator.next());  //{ value: 3, done: false }
console.log(iterator.next());  //{ value: 4, done: false }
console.log(iterator.next());  //{ value: undefined, done: true }

至于对象没有布置iterator接口的原因,不知道最近大家有没有看根据《你一生的故事》拍成的电影“降临",片中出现的外星语言是一门非线性的语言。而我们说的数组,Map等结构中的成员都是有顺序的,即都是线性的结构,而对象,各成员并没有一个确定的顺序,所以遍历时先遍历谁后遍历谁并不确定。所以,给一个对象部署iterator接口,其实就是对该对象做一种线性转换。如果你有这种需要,就需要手动给你的对象部署iterator接口咯~

  如:

let obj = {
    data: [ 'hello', 'world' ],
    [Symbol.iterator]() {
        const self = this;
        let index = 0;
        return {
            next() {
                if (index < self.data.length) {
                    return {
                        value: self.data[index++],
                        done: false
                    };
                } else {
                    return { value: undefined, done: true };
                }
            }
        };
    }
};

可以看到,Symbol.iterator会返回一个对象,这就是一个遍历器对象,而作为遍历器对象,其必须具备的特征就是必须具备next()方法。

我们还可以据此实现指针结构的数据结构。具体略~

  至于可以使用Array.from转换成数组的类数组对象,部署iterator有一种很简单的方法,即直接使用数组的[Symbol.iterator]接口。 

fakeArray.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

当然,不知道你们看到next是否想到了es6的一个新玩意儿,即Generator函数。用Generator函数来实现Symbol.iterator接口,事半功倍。

var yieldIterator = {};
yieldIterator[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};
 
[...yieldIterator] // [1, 2, 3]
  注意,yield* 后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。

注意,yield* 后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
  其次,其它调用到遍历器的操作还有解构赋值、扩展操作符、其它任何接受数组作为参数的场合,如:

for...of
Array.from()
Map(), Set(), WeakMap(), WeakSet()(比如)
Promise.all()
Promise.race()

一旦当你给你的结构部署了iterator接口,那么恭喜你,你可以使用for...of来遍历你的结构了!

  遍历器对象除了必须布置next方法以外,还有2个可选方法。return()和throw()。当一个解构在遍历的时候异常提前退出(比如break,continue或者出错)的时候,就会调用return方法,其次,return方法必须返回一个对象。

至于throw方法,则是用于抛出错误,Generator.prototype.throw这里不展开讲了,感兴趣的可以搜索一下。

 for of循环有很多优点,比如不像for...in一样只遍历键名(甚至包括原型链上的键),而且不像foreach不能跳出循环。并且for...of为各种数据结构提供了一个统一的遍历方法。

foreach无法终止循环,当通过break来终止循环时会报错 Illegal break statement。

好了,相信到这里你应该对整个Symbol有一定的理解了,如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

有关一道题开始认识Symbol的更多相关文章

  1. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  2. ruby - 使用 rbenv 和 ruby​​-build 构建 ruby​​ 失败,出现 undefined symbol : SSLv2_method - 2

    我正在尝试在配备ARMv7处理器的SynologyDS215j上安装ruby​​2.2.4或2.3.0。我用了optware-ng安装gcc、make、openssl、openssl-dev和zlib。我根据README中的说明安装了rbenv(版本1.0.0-19-g29b4da7)和ruby​​-build插件。.这些是随optware-ng安装的软件包及其版本binutils-2.25.1-1gcc-5.3.0-6gconv-modules-2.21-3glibc-opt-2.21-4libc-dev-2.21-1libgmp-6.0.0a-1libmpc-1.0.2-1libm

  3. ruby-on-rails - Ruby/Rails 中的夏令时开始和结束日期 - 2

    我正在开发一个Rails应用程序,我需要在其中找到给定特定偏移量或时区的夏令时开始和结束日期。我基本上在我的数据库中保存了从用户浏览器接收到的时区偏移量(“+3”,“-5”),我想在它出现时修改它由于夏令时的变化。我知道Time实例变量有dst?和isdst方法,如果存储在它们中的日期在夏令时与否。>Time.new.isdst=>true但是使用它来查找夏令时的开始和结束日期会占用太多资源,而且我还必须为我拥有的每个时区偏移量执行此操作。我想知道更好的方法。 最佳答案 好的,基于你所说的和@dhouty'sanswer:您希望能够

  4. ruby-on-rails - phusion passenger 和 ruby​​ 1.9.1 已经开始工作了吗? - 2

    我有一台生产机器和一台开发机器,都运行ubuntu8.10并且都运行最新的phusionpassenger。当我在osx上的本地开发机器上使用ruby​​1.9.1时,我想知道外面的人是否已经在使用带有ruby​​1.9.1甚至1.9.2的phusionpassenger?如果是这样,请告诉我们您的设置!此外,有没有办法在apache上使用phusionpassenger同时运行ruby​​1.8.7(ree)和1.9.1?感谢您的指点,我在任何地方都找不到任何提示... 最佳答案 是的,从某些2.2.x版本开始就正式支持它,我不记

  5. ruby - Rails 3 - 我可以将开始日期设置为 date_select 方法吗? - 2

    date_select方法只能设置:start_year,但我想设置开始日期(例如3个月前的日期)(但没有这样的选项)。那么,我可以将开始日期设置为date_select方法吗?或者,要制作这样的选择框,我应该使用select_tag和options_for_select吗?或者,有什么解决办法吗?谢谢, 最佳答案 有可能……例如:start_year–设置年份选择的开始年份。默认为Time.now.year-5参见thisresource. 关于ruby-Rails3-我可以将开始日期

  6. ruby - 从特定索引开始迭代数组 - 2

    我想从特定索引开始遍历数组。我该怎么做?myj.eachdo|temp|...end 最佳答案 执行以下操作:your_array[your_index..-1].eachdo|temp|###end 关于ruby-从特定索引开始迭代数组,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/44151758/

  7. ruby-on-rails - Textmate 'Go to symbol' 相当于 Vim - 2

    在Railcasts上,我注意到一个非常有趣的功能“转到符号”窗口。它像Command-T一样工作,但显示当前文件中可用的类和方法。如何在vim中获取它? 最佳答案 尝试:helptags有各种程序和脚本可以生成标记文件。此外,标记文件格式非常简单,因此很容易将sed(1)或类似的脚本组合在一起,无论您使用何种语言,它们都可以生成标记文件。轻松获取标记文件(除了下载生成器之外)的关键在于格式化样式而不是实际解析语法。 关于ruby-on-rails-Textmate'Gotosymbol

  8. ruby - Heroku - 如何开始工作人员(延迟工作)? - 2

    我有一些使用delayed_job的小程序。在我的本地主机上一切正常,但是当我将我的应用程序部署到Heroku并单击应该由delayed_job执行的链接时,没有任何反应,“任务”只是保存到表delayed_job中。Inthisarticleonherokublog写入时,执行delayed_job表中的任务,当运行此命令时rakejobs:work。但是我怎样才能运行这个命令呢?命令应该放在哪里?在代码中,还是从终端控制台? 最佳答案 如果您正在运行Cedar堆栈,请从终端控制台运行以下命令:herokurunrakejobs:

  9. ruby - 为什么 `Symbol#match` 的行为与 `String#match` 和 `Regexp#match` 不同? - 2

    String#match和Regexp#match在匹配成功时返回一个MatchData:"".match(//)#=>#//.match("")#=>#//.match(:"")#=>#但是Symbol#match返回匹配位置(如String#=~)::"".match(//)#=>0为什么Symbol#match表现不同?有用例吗? 最佳答案 我将其报告为Ruby核心中的错误:https://bugs.ruby-lang.org/issues/11991.让我们看看他们会怎么说。更新被质疑的行为似乎是一个错误。似乎从Ruby2.

  10. ruby - 获取错误 : dyld: Symbol not found: _clock_gettime - 2

    我想用middlemanserver命令启动一个中间人,当我这样做时我得到一个错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_clock_gettimeReferencedfrom:/usr/local/lib/ruby/gems/2.3.0/gems/eventmachine-1.2.0.1/lib/rubyeventmachine.bundle(whichwasbuiltforMacOSX10.12)Expectedin:/usr/lib/libSystem.B.dylibdyld:Symbolnotfound:_clock_gett

随机推荐