V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
wumao
V2EX  ›  JavaScript

问大家一个关于字符串的基础问题

  •  
  •   wumao · 2019-07-14 09:28:50 +08:00 · 3545 次点击
    这是一个创建于 1958 天前的主题,其中的信息可能已经有所发展或是发生改变。

    为什么字符串不可变呢 我给字符串重新赋值 js 底层是如果进行操作的呢 我看网上的理解是 当重新赋值的时候 这时候要把这个字符串当成对象去理解 这个字符串存在堆里面 然后 变量存储内存地址指向这个字符串 然后重新赋值之后 实际上是这个变量的内存地址改变了 然而原本的字符串还存在内存中 可以这么理解吗 但是字符串不是明明就是简单数据类型 存在栈吗

    没学过计算机原理 想了解下 底层是怎么运行的。。

    17 条回复    2019-07-15 11:31:08 +08:00
    muzuiget
        1
    muzuiget  
       2019-07-14 09:41:52 +08:00
    原本的字符串可能还在内存中,也有可能稍后被 GC 回收掉。「字符串」真不算简单的数据类型,因为各个语言怎么定义「字符串」都五花八门。
    wsxyeah
        2
    wsxyeah  
       2019-07-14 09:43:00 +08:00 via iPhone
    引用类型
    Edward4074
        3
    Edward4074  
       2019-07-14 09:47:37 +08:00   ❤️ 3
    这么理解,String s = "abc" 实际上是 String s = String(['a','b','c']),字符串本身是一种经过由语言封装后的基础类型,底层实际上是一个数组。而且由于字符串长度不可预知,不可能预留出足够的内存空间来支持可变长度。从 List 的角度来理解,List 本身也是初始化一个长度,持续往里面去添加对象,空间不够时再重新开辟一个更大的 List,再把旧的数据复制过来。
    wumao
        4
    wumao  
    OP
       2019-07-14 09:48:08 +08:00
    @wsxyeah 复杂数据类型才是引用类型啊 字符串不是简单数据类型吗
    ipwx
        5
    ipwx  
       2019-07-14 09:49:27 +08:00
    @wumao 从原理的角度(即 JS 引擎的实现的角度)来看,字符串不是简单数据类型。不要从语法层面去看问题。
    LeeSeoung
        6
    LeeSeoung  
       2019-07-14 09:50:02 +08:00
    js 不是很清楚 java 是有文本常量池的,当你赋值一个 String 类型的变量时,先看常量池有没有,有就直接对应上,没有就在常量池新建一个,然后再对应。。
    geelaw
        7
    geelaw  
       2019-07-14 09:54:37 +08:00 via iPhone   ❤️ 2
    从理解的角度你不需要知道字符串是怎么实现的。楼主实际上不明白的是“不可变”的含义。

    说字符串不可变和说整数不可变是一样的,例如

    var x=3; x+=1;

    结果并不是 3 这个整数变成了 4 这个整数,而是 x 的内容从一个整数变成另一个整数。

    同样,var x="1"; x+="1"; 结果并不是 1 这个字符串变成了 11 这个字符串,而是 x 的内容从一个字符串变成了另一个字符串。

    此外,"11".replace("1","2") 的效果并不是让 11 这个字符串变成 22 这个字符串,而是从一个字符串算出来另一个字符串。
    geelaw
        8
    geelaw  
       2019-07-14 10:00:34 +08:00 via iPhone   ❤️ 1
    @geelaw #7 Hmmm 似乎 replace 只会替换第一个 occurrence,不过这不影响意思的传达。

    一个生活化的例子是,假设“人”是不可变的,JohnAppleseed 是一个男人,而 JohnAppleseed.MakeGenderFemale() 的结果并不是 JohnAppleseed 变成了女人,而是用克隆技术造出了一个新的人,她其它和 JohnAppleseed 一样,只不过性别改变了。

    如果“人”是可变的,那么结果就可以是 JohnAppleseed 做了变性手术,从此 JohnAppleseed 这个人就变成了女的。
    lhx2008
        9
    lhx2008  
       2019-07-14 10:11:28 +08:00   ❤️ 1
    可变与不可变完全是语言设计上的考量,与底层无关。

    string 一般的实现都是实际内容存在堆,栈上有一个指针指过去,这样子函数传递、对象的时候可以被多个栈复用,减少内存复制的时间。

    那么,在堆上,是不是就必须不可变呢?不是的。主要是,如果 string 可变,这样子就带来了另外一个问题,我公用的时候,我改了一下,你那边也跟着改了,改着改着就乱了,公用的意义就不大了。

    所以,string 被最终设计成了不可变,意思是堆上的内容是不可以改的,如果用 Java 语言里面说,就是这个对象没有一个方法可以修改自己。

    但是我要改一个 string 怎么办呢,就是在堆上面重建一个,再在栈上面弄一个指针指向新的堆上 string,旧的堆上 string 仍然不变,如果旧的没有人再引用它,那么它会被垃圾回收。

    后面人们发现这样子效率也不高,又做了一些优化。比如 Java 又把一些常用的字符串的引用存到一个内置的 hashmap 里面,这样子可以不用老是被销毁又重建。
    lqw3030
        10
    lqw3030  
       2019-07-14 10:21:28 +08:00   ❤️ 1
    因为有常量池
    misaka19000
        11
    misaka19000  
       2019-07-14 10:27:48 +08:00 via Android   ❤️ 1
    js 不清楚,Java 中不可变是为了防止有人手贱去修改字符串的值从而发生一些预期之外的结果
    azh7138m
        12
    azh7138m  
       2019-07-14 10:45:04 +08:00   ❤️ 1
    底层怎么实现是 rt 自己的事情,不同的 rt 实现也不一定相同。
    建议阅读 https://github.com/danbev/learning-v8
    starsriver
        13
    starsriver  
       2019-07-14 10:51:59 +08:00 via Android   ❤️ 1
    字符串是以顺序结构存储在寄存器的字节单元序列中。在 js 里面把这一块存储器按序列读取还是作为数组以字的方式读取在于你接下来的操作。

    比如说 log(arr)或 arr[]会将存储数据类型改变后继续操作。
    比如说 定义 arr='abcdef' //string
    然后 bit = arr[0],这里括号里面的东西相当于是偏移地址,数据类型也会变为 数组。


    接下来是重点。
    主要是为了提高利用率,在重新定义 arr 的时候,会自动将 arr 的地址放在空寄存器的开始,然后就行序列存储。原先的数据不再被进程锁定,将被回收。

    回收的意思是数据没有利用价值,下次定义新变量时可以直接替换 arr 原来空间里的内容,而不是立即清空数据,这样能够节约操作时间。
    lihongjie0209
        14
    lihongjie0209  
       2019-07-14 11:01:13 +08:00   ❤️ 1
    原理就是大家讲的那么多

    如果字符串可以变, 你想想你的代码有多恐怖

    function getFirstLetter;

    API 使用者

    var name = "test"


    getFirstLetter(name)


    assert name==='t'

    还是

    assert name==='test'


    API 提供者

    getFirstLetter 要不要有返回值? 既然入参可变, 那直接修改入参就可以了, 为什么还要使用返回值.


    在异步场景下, 问题会更大


    var name = "test"

    setTimeout(()=>getLastLetter(name))

    getFirstLetter(name)

    现在我问你, name 的值是多少?


    总的来说, 这种"基础类型"可变会给 API 的开发者和使用者带来极大的心智负担.
    BinRelay
        15
    BinRelay  
       2019-07-14 11:10:08 +08:00   ❤️ 1
    假如世界上只有 ascii,那么字符串的实现就简单多了吧……
    dosmlp
        16
    dosmlp  
       2019-07-14 15:11:21 +08:00   ❤️ 1
    那要看 js 引擎是怎么处理的了,可能不同引擎还不一样
    no1xsyzy
        17
    no1xsyzy  
       2019-07-15 11:31:08 +08:00   ❤️ 1
    Pascal string 是一个 256 字节的串,其中第一个字节表示字符串的长度,之后最多 255 个字节表示字符串的内容。只有这个才能算简单类型。
    C String 连长度都不知道,以 '\0' 表示结尾,甚至比定长数组(或者说模拟元组)复杂。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1290 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 17:42 · PVG 01:42 · LAX 09:42 · JFK 12:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.