【问题标题】:Pointers in JavaScript?JavaScript 中的指针?
【发布时间】:2012-04-19 15:49:15
【问题描述】:

我们可以将不可变变量的引用作为函数的参数传递吗?

例子:

var x = 0;
function a(x)
{
    x++;
}
a(x);
alert(x); //Here I want to have 1 instead of 0

【问题讨论】:

  • 可以说这是不好的风格,因为您打算使用函数的副作用来引起突变。这会使您的代码更难遵循。

标签: javascript


【解决方案1】:

由于 JavaScript 不支持通过引用传递参数,因此您需要将变量设为对象:

var x = {Value: 0};

function a(obj)
{
    obj.Value++;
}

a(x);
document.write(x.Value); //Here i want to have 1 instead of 0

在这种情况下,x 是对对象的引用。当x 被传递给函数a 时,该引用被复制到obj。因此,objx 指代内存中的同一事物。更改objValue 属性会影响xValue 属性。

Javascript 将始终按值传递函数参数。这只是语言的规范。您可以在两个函数的本地范围内创建x,并且根本不传递变量。

【讨论】:

  • 有没有办法让另一个人这样做:var x=0;a(x);并且该函数设置了一个值为 x 的对象? (或类似的东西)
  • 嘿,谢谢你把我介绍给code snippets on SO。在我看到您的答案中使用它之前没有听说过它们。
  • @Joeytje50 - 是的,this feature 刚刚在 9 月添加回来。非常方便!
  • 如果您不知道 obj 属性的名称,例如如果它是 obj.name 那么下次您想更新 obj.surname 怎么办?如何让函数适应这种灵活性?
  • @Dave 这可能是一个迟到的答案,但您可以让您的函数期望 3 个参数,即 obj、属性名称和值。请记住,您可以通过执行 obj.propertyname 或 obj['propertyname'] 来访问对象的属性,这将是您想要灵活性时想要的方式。传递 3 个参数并执行 obj['propertyname'] = value ,这将是可重用的。
【解决方案2】:

这个问题可能会有所帮助: How to pass variable by reference in javascript? Read data from ActiveX function which returns more than one value

总而言之,Javascript 原始类型始终按值传递,而对象内部的值通过引用传递(感谢评论者指出我的疏忽)。所以为了解决这个问题,你必须把你的整数放在一个对象中:

var myobj = {x:0};

function a(obj)
{
    obj.x++;
}

a(myobj);
alert(myobj.x); // returns 1

  

【讨论】:

  • 对象变量也总是按值传递——只是值是对对象的引用按引用传递按值传递引用之间是有区别的。也就是说,当您通过值传递x 时,无论x 是否是一个对象,您都不能以调用者看到的任何方式将其替换为其他值。
  • 而对象是通过引用传递的 根本不正确。 JavaScript 中没有任何内容是通过引用传递的。
  • 如果你通过值传递一个灵长类类型(被x引用),为什么x之后不改变,除非你传递对象
【解决方案3】:

我发现了一种稍微不同的实现指针的方式,从 C 的角度来看,它可能更通用且更容易理解(因此更适合用户示例的格式)。

在 JavaScript 中,就像在 C 中一样,数组变量实际上只是指向数组的指针,因此您可以像声明指针一样使用数组。这样,代码中的所有指针都可以以相同的方式使用,尽管您在原始对象中命名了变量。

它还允许使用两种不同的符号来表示指针的地址和指针的地址。

这里是一个例子(我用下划线表示一个指针):

var _x = [ 10 ];

function foo(_a){
    _a[0] += 10;
}

foo(_x);

console.log(_x[0]);

产量

output: 20

【讨论】:

    【解决方案4】:

    你从窗口对象中引用了“x”

    var x = 0;
    
    function a(key, ref) {
        ref = ref || window;  // object reference - default window
        ref[key]++;
    }
    
    a('x');                   // string
    alert(x);
    

    【讨论】:

      【解决方案5】:

      迟到的答案,但我遇到了一种通过闭包引用传递原始值的方法。创建指针比较复杂,但是可以。

      function ptr(get, set) {
          return { get: get, set: set };
      }
      
      function helloWorld(namePtr) {
          console.log(namePtr.get());
          namePtr.set('jack');
          console.log(namePtr.get())
      }
      
      var myName = 'joe';
      var myNamePtr = ptr(
          function () { return myName; },
          function (value) { myName = value; }
      );
      
      helloWorld(myNamePtr); // joe, jack
      console.log(myName); // jack
      

      在 ES6 中,可以缩短代码以使用 lambda 表达式:

      var myName = 'joe';
      var myNamePtr = ptr(=> myName, v => myName = v);
      
      helloWorld(myNamePtr); // joe, jack
      console.log(myName); // jack
      

      【讨论】:

        【解决方案6】:

        Javascript 应该只是将指针放入组合中,因为它解决了很多问题。这意味着代码可以引用未知变量名称或动态创建的变量。它还使模块化编码和注入变得容易。

        这是我认为在实践中最接近 c 指针的方法

        在js中:

        var a = 78;       // creates a var with integer value of 78 
        var pointer = 'a' // note it is a string representation of the var name
        eval (pointer + ' = 12'); // equivalent to: eval ('a = 12'); but changes value of a to 12
        

        在c中:

        int a = 78;       // creates a var with integer value of 78 
        int pointer = &a; // makes pointer  to refer to the same address mem as a
        *pointer = 12;   // changes the value of a to 12
        

        【讨论】:

          【解决方案7】:

          在 JavaScript 中,这将是一个全局的。但是,您的函数看起来更像这样:

          function a(){
             x++;
          };
          

          由于x在全局上下文中,你不需要将它传递给函数。

          【讨论】:

          • 有效点,虽然我要指出变量不需要是全局的(很多人会建议不要这样做),它只需要与调用者在同一范围内,并且被调用者。
          【解决方案8】:

          这可能是不可能的,因为 JavaScript 没有 Perl 的“\”运算符来通过引用获取原语,但是有一种方法可以使用这种模式为原语创建一个“有效的指针”对象。

          当您已经拥有原语时,此解决方案最有意义(因此您不能再将其放入对象中而无需修改其他代码),但仍需要为您的其他部分传递指向它的指针修改其状态的代码;所以你仍然可以使用像指针一样的无缝代理来修改它的状态。

          var proxyContainer = {};
          
          // | attaches a pointer-lookalike getter/setter pair
          // | to the container object.
          var connect = function(container) {
              // | primitive, can't create a reference to it anymore
              var cant_touch_this = 1337;
          
              // | we know where to bind our accessor/mutator
              // | so we can bind the pair to effectively behave
              // | like a pointer in most common use cases.
              Object.defineProperty(container, 'cant_touch_this', {
                  'get': function() {
                      return cant_touch_this;
                  },                
                  'set': function(val) {
                      cant_touch_this = val;
                  }
              });
          };
          
          // | not quite direct, but still "touchable"
          var f = function(x) {
              x.cant_touch_this += 1;
          };
          
          connect(proxyContainer);
          
          // | looks like we're just getting cant_touch_this
          // | but we're actually calling the accessor.
          console.log(proxyContainer.cant_touch_this);
          
          // | looks like we're touching cant_touch_this
          // | but we're actually calling the mutator.
          proxyContainer.cant_touch_this = 90;
          
          // | looks like we touched cant_touch_this
          // | but we actually called a mutator which touched it for us.
          console.log(proxyContainer.cant_touch_this);
          
          f(proxyContainer);
          
          // | we kinda did it! :)
          console.log(proxyContainer.cant_touch_this);
          

          【讨论】:

            【解决方案9】:

            在 JavaScript 中,您不能通过引用函数来传递变量。但是,您可以通过引用传递对象。

            【讨论】:

            • 完全不正确。你不能在 JavaScript 中通过引用传递任何东西。
            • 对象引用作为值传递,所以一切都作为值传递。
            • 有趣的观察;在 JavaScript 中创建对现有原语的引用似乎是不可能的;一旦一个原语被绑定,这个原语的任何变化都不会被它的幼稚引用者注意到,因为引用者指向变量的值,而该变量恰好是一个原语;因此,“指向”原语的唯一方法是将原语包装在 typeof 为“Object”的变量中;因为没有其他类型可以存储引用。 TLDR; JavaScript 中没有 Perl "\" 运算符;它要么是一个参考,要么你永远不能指向它。
            • 这中令人讨厌的部分是当您想要创建一个共享状态包时,例如“window.shared”作为对当前为空/未定义的变量的引用的映射,您将在以后设置这些变量;在这种情况下,当您设置当前为空/未定义的状态值时,“window.shared”变量将无法识别更改,即使它们存储在 Object 类型中,也为时已晚; “null/undefined”值必须在 Object 类型中才能开始工作,例如 [null] [undefined]。
            • 也就是说,你可以很聪明,在这样一个全局变量中放入函数,其中每个尚未设置的变量都有一个 getter 和 setter,这些函数将动态查找当前状态原语并获取/设置它,但它不能返回指向该变量的指针(但可以返回一个代理,该代理可以有效地以与该变量相同的方式运行并使用 getter/setter 间接改变它)。
            【解决方案10】:

            我认为这与 C 或任何带有指针的语言相反:

            /** Javascript **/
            var o = {x:10,y:20};
            var o2 = {z:50,w:200};
            
            • 显然在 javascript 中您无法访问对象 oo2 内存中的地址
            • 但您也不能比较他们的地址:(不可能排序他们,然后通过二分法访问)李>

            .

            o == o2 // obviously false : not the same address in memory
            o <= o2 // true !
            o >= o2 // also true !!
            

            这是个大问题:

            • 这意味着您可以列出/管理您的应用程序创建(分配)的每个对象,

            • 庞大的对象列表中,计算出有关这些对象的一些信息(例如,它们是如何链接在一起的)

            • 但是当您想要检索您创建的关于特定对象的信息时,您无法通过二分法在 巨大列表 中找到它:每个对象没有唯一标识符 可以用来代替真实内存地址

            这最终意味着这是一个巨大的问题,如果你想用 javascript 编写:

            • 一个javascript调试器/IDE
            • 特别优化的垃圾收集器
            • 一个数据结构抽屉/分析器

            【讨论】:

              【解决方案11】:

              JavaScript 不支持通过引用传递原始类型。不过,有一个解决方法。

              将所有需要传递的变量放入一个对象中。在这种情况下,只有一个变量x。不要传递变量,而是将变量名作为字符串传递,即"x"

              var variables = {x:0};
              function a(x)
              {
                  variables[x]++;
              }
              a("x");
              alert(variables.x);

              【讨论】:

                【解决方案12】:

                根据您的需要,您可以简单地保存变量名称,然后像这样访问它:

                function toAccessMyVariable(variableName){
                  alert(window[variableName]);
                }
                
                var myFavoriteNumber = 6; 
                
                toAccessMyVariable("myFavoriteNumber");
                

                要应用于您的具体示例,您可以执行以下操作:

                var x = 0;
                var pointerToX = "x";
                
                function a(variableName)
                {
                    window[variableName]++;
                }
                a(pointerToX);
                alert(x); //Here I want to have 1 instead of 0
                

                【讨论】:

                • 这仅适用于浏览器环境并且特定于window 对象。此外,不建议使用全局变量,因为可能已经有同名变量。
                【解决方案13】:

                它只是不支持指针,故事结束:-(。

                我想补充一点,虽然我是全新的并且来自 C 背景,但根据我正在阅读的有关事件循环和堆栈的内容,您应该尽可能地让任何对象靠近它的调用者。由于我是全新的,我可能有这个错误。

                这里的很多答案都建议将其设为全局,但如果我没看错的话,这可能会增加堆栈跳数,并且在某种程度上,可能会不必要地分割您的事件循环(取决于当前被调用的对象是什么)做)。

                此外,任何模仿指针的例子,在许多方面都只是做同样事情的 1,它不会是指针。

                【讨论】:

                • 在 JS 中,将变量设为全局变量通常被认为是不好的做法。除非以这种方式大量使用该变量,否则您实际上不需要这样做。例如,在 Web 应用程序中,表示当前用户的对象可能会全局存储,但建议不要这样做,因为这会将其暴露给开发人员工具并弄乱全局命名空间。相反,最好使用带有全局实例的状态管理器,其中包含用户对象。在获取对象中的值时,这些是引用,即 IE 指针。这些行为类似于传统的 C 指针,但是不能手动分配。
                【解决方案14】:

                我会同意最引用的答案,但请注意'var'参数,使用'let',或者在这种情况下使用'const'更好,因为指针或指向对象不应该改变随时。

                const x = {value: 0};
                
                function foo(obj){
                  ++obj.value;
                }
                
                foo(x);
                alert(x.value);
                

                【讨论】:

                  【解决方案15】:

                  在您的示例中,您实际上有 2 个同名变量。 (全局)变量 x 和 作用域 变量 x。有趣的是,当 javascript 选择如何处理 2 个同名变量时,会使用函数范围名称并忽略范围外变量。

                  假设 javascript 总是以这种方式运行可能是不安全的......

                  干杯!

                  【讨论】:

                  • 这个答案不会给这篇文章增加任何价值。 @Mike Christensen 的回答更详细并且已经被接受。
                  猜你喜欢
                  • 2011-12-01
                  • 1970-01-01
                  • 2013-06-27
                  • 1970-01-01
                  • 2014-02-22
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-08-14
                  • 2012-03-23
                  相关资源
                  最近更新 更多