【问题标题】:If a String is immutable in JavaScript why does the following code mutate a string?如果一个字符串在 JavaScript 中是不可变的,为什么下面的代码会改变一个字符串?
【发布时间】:2020-04-08 01:06:31
【问题描述】:

我已经将输入读取为一个字符串,并且在将其拆分后,该字符串变成了一个对象。

process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('data',(c)=>{
    in += c;
});
process.stdin.on('end',()=>{
    spliter(in);
});
function spliter(in){
    console.log(typeof(in));
    in = in.split('\n');
    console.log(typeof(in));
}

【问题讨论】:

  • 字符串在哪里发生了变异?这个全局变量in += c;,你称之为像spliter(in)这样的分割器在你发布的代码中没有改变......参数in,你变成了一个数组,但你没有“改变一个字符串”
  • 数组是 JavaScript 中的一种对象。 typeof [] 将返回 object。如果你想确定它是否真的是一个数组,你可以使用它在 Array 构造函数上找到的 isArray 方法:Array.isArray([])returns true
  • 此外,字符串在 JavaScript 中不是不可变的。不知道你从哪里得到这个误解。只有 const 声明的变量是不可变的。
  • @JacobPenney - 你确定吗?关于 javascript 的每一点文档都不同意你的观点
  • @JacobPenney 字符串(以及所有原语)在 JS 中是不可变的。他们的原型不是,但这是另一个问题......

标签: javascript arrays string split


【解决方案1】:

当你修改一个字符串变量时,javascript实际上并没有修改内存中的字符串,而是创建一个全新的字符串,看起来像是对初始字符串的修改:

    let str = "     hi      ";
    str = str.trim(); // a new string "hi" is created
    console.log(str);

这就是所有 javascript 的意思,说它的字符串是不可变的。如果要防止字符串变量被改变,可以使用const

const str = "Can't change me";
str = "Want to bet?"  // throws error.

【讨论】:

    【解决方案2】:

    您的代码不会改变字符串。


    字符串(连同其他原始类型)在 JS 中是不可变的

    改变某些东西意味着改变它而不创建另一个。

    字符串修饰符方法返回一个新字符串,但不改变原来的字符串,例如:

    const a = 'Hello world!'
    const b = a.slice(0,5)
    
    console.log(a) //Hello world!
    console.log(b) //Hello
    

    但是,您仍然可以使用新字符串重新分配字符串变量(但这不是突变):

    let a = 'Hello world!'
    a = a.slice(0,5)
    
    console.log(a) //Hello
    

    您的代码有点复杂。 String#split() 返回一个字符串数组,但不会改变原来的:

    const a = 'Hello world!'
    const b = a.split('o')
    
    console.log(a) //Hello world!
    console.log(b) //['Hell', ' w', 'rld!']
    

    数组(实际上)是对象,它们可变的,但不是它们包含的字符串(和其他原语)。

    【讨论】:

    • @user1063287 感谢您为改进此答案所做的努力,但现在它包含许多与实际回答问题无关的信息。详细的答案很好,但过于详细的答案可能会使问题的答案更难找到,尤其是当问题不是很笼统的时候。
    • 不用担心,已将答案回滚到上次编辑并添加了对另一个答案的详细回复。
    【解决方案3】:

    很简单,你没有改变字符串

    如果您是,以下代码(基于您的代码)将在两个 console.log 中输出 Object

    let x = 'this is a string';
    function spliter(x){
        x = x.split('\n');
        console.log(typeof(x));
    }
    spliter(x);
    console.log(typeof(x));

    【讨论】:

      【解决方案4】:

      正如其他人所指出的,在提供的示例中,字符串 primitive 没有被变异。

      变量将re-assigned 转换为split() 方法返回的值,该方法在String wrapper object 上调用,在对字符串调用方法时自动使用该方法。 ​

      let myString = 'Hello World!';
      
      console.log("BEFORE reassignment - variable type is: " + typeof(myString));  // string 
      
      myString  = myString.split(' ');  // <----- method called on String object wrapper, returns an array, which is re-assigned to the myString variable   
      
      console.log(myString); //  ['Hello', 'World!']
      
      console.log("AFTER reassignment - variable type is: " + typeof(myString)); // object 
      

      下面是一些进一步的上下文:

      JavaScript 语言中的类型集由primitive valuesobjects 组成。
      来源:MDN Web Docs

      stringprimitive value

      在 JavaScript 中,all primitive types不可变并且没有方法,但是: ​

      重要的是不要将基元本身与分配了基元值的变量混淆。可以为变量重新分配一个新值,但不能以更改对象、数组和函数的方式更改现有值。
      来源:MDN Web Docs

      即使原始类型没有方法,也可以在原始类型上调用方法,因为除nullundefined 之外的所有原始值都具有环绕原始值的对象等效项:

      来源:MDN Web Docs

      当您在 string 原语上调用 String instance method 时,会发生以下情况:

      • 原始值暂时转换为对象
      • 使用了对象的方法属性
        (例如,slice() 将返回一个新的 stringsplit() 将返回一个 array 字符串)
      • 对象被转换回原语

      来源:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-10-06
        • 1970-01-01
        • 2020-02-06
        • 2013-05-08
        • 2018-08-21
        • 2021-05-28
        • 2011-02-19
        相关资源
        最近更新 更多