【问题标题】:What's going on with references in TypeScript? [duplicate]TypeScript 中的引用发生了什么? [复制]
【发布时间】:2021-07-18 10:09:39
【问题描述】:

我对 JavaScript 和 TypeScript 还很陌生。

就我而言,TypeScript 被编译成常规的 JavaScript。

来自 C#,其中对象通过引用传递(如果它们不是原始类型或结构)。我写了一些 TypeScript 代码来测试一些东西:

interface IRunner { 
    run(): string;
}

class TestDI {
    private readonly Runner: IRunner;

    constructor(runner: IRunner) {
        this.Runner = runner;
    }

    test(): void {
        console.log('Result of runner: ' + this.Runner.run());
    }
}

class FooRunner implements IRunner {
    run(): string {
        return 'Foo';
    }
}

class BarRunner implements IRunner {
    run(): string {
        return 'Bar';
    }
}

let runner: IRunner;
runner = new FooRunner();

const SUT: TestDI = new TestDI(runner);

// Expected output: Foo
// Actual output: Foo
console.log(runner.run());

// Expected output: Foo
// Actual output: Foo
SUT.test();

runner = new BarRunner();

// Expected output: Bar
// Actual output: Bar
console.log(runner.run());

// Expected output: Bar
// Actual output: Foo
SUT.test();

在上面的代码中,我将IRunner 接口的一个实例传递给了一个类 c-tor,然后,我的类的一个字段将填充我在 c-tor 中传递的任何内容。

稍后在代码中,我更改了传递给 c-tor 的变量,但是,当我调用 test 函数 Runner 类的字段似乎没有反映对我传递的原始变量所做的任何更改时创建我的 TestDI 类的实例。

在 C# 中,我编写的代码会按照我的预期运行。但这里不是。

发生了什么事?

【问题讨论】:

  • 我不明白你为什么期望Result of runner: Bar 是最后一个SUT.test()。为runner 分配一个新值,无论它是否为IRunner不会改变SUT.Runner 是什么。 “就我而言,TypeScript 被编译成常规 JavaScript” - 是的,你会在 vanilla JS 中看到相同的行为。
  • @lilbroomstick 你的意思是如果你按值传递一个可变对象?你会在 JS 中看到相同的行为。
  • The same applies to JS@lilbroomstick.
  • @Ivar,是的!我的错!当等效的 C# 代码出现在答案中时,它变得更加清晰。
  • “来自 C#,对象通过引用传递” – 嗯?默认情况下,C# 是按值传递的,除非明确使用refout 请求按引用传递。这里有清楚的记录:docs.microsoft.com/dotnet/csharp/language-reference/keywords/…

标签: javascript typescript pass-by-reference


【解决方案1】:

在 C# 中,我编写的代码会按照我的预期运行。但这里不是。

不是真的...如果您将上面的代码转换为 C# 代码并运行它,您会发现它的行为与您的 TypeScript 代码完全相同。

原因是你将runner 传递给TestDI 的构造函数。这里发生了什么......内容只是为调用构造函数而复制,在这种情况下,这意味着复制指定对象的地址。如果您现在将另一个对象的新地址分配给runner,为什么这会反映在您的TestDI 实例中已完成的副本中?我猜你脑子里有点混杂了一些东西;-)

来自 C#,其中对象通过引用传递(如果它们不是原始类型或结构)。我写了一些 TypeScript 代码来测试一些东西:

是的,您可以将它们作为参考传递(例如使用ref),但默认情况下,地址会被简单地复制(如上所述)。

为了让您更轻松:只需复制和粘贴并感到惊讶

class Program
{
    static void Main(string[] args)
    {
        IRunner runner;
        runner = new FooRunner();

        TestDI SUT = new TestDI(runner);

        // Expected output: Foo
        // Actual output: Foo
        Console.WriteLine(runner.Run());

        // Expected output: Foo
        // Actual output: Foo
        SUT.Test();

        runner = new BarRunner();

        // Expected output: Bar
        // Actual output: Bar
        Console.WriteLine(runner.Run());

        // Expected output: Bar
        // Actual output: Foo
        SUT.Test();
    }
}


public interface IRunner
{
    string Run();
}

public class TestDI
{
    private readonly IRunner runner;

    public TestDI(IRunner runner)
    {
        this.runner = runner;
    }

    public void Test()
    {
        Console.WriteLine("Result of runner: " + this.runner.Run());
    }
}

public class FooRunner : IRunner
{
    public string Run()
    {
        return "Foo";
    }
}

public class BarRunner : IRunner
{
    public string Run()
    {
        return "Bar";
    }
}

【讨论】:

  • 哇!我对我的问题深表歉意!我没有注意到我实际上将runner 设置为新的东西!尽管如此,对前一个对象FooRunner 的引用仍保留在我的班级中。一切正常!
  • @lilbroomstick 没什么可道歉的 ;-)
【解决方案2】:

尽量保持简单明了:您传递的是对象 new FooRunner() 的引用,而不是变量 runner 的引用,因此为 runner 分配一个新值不会改变您传递给构造函数和 SUT.Runner 的内容保持不变。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-11
    • 1970-01-01
    • 1970-01-01
    • 2016-11-06
    • 1970-01-01
    • 2016-06-28
    • 2011-04-12
    相关资源
    最近更新 更多