JavaScript中的指针?


79

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

例:

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

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

Answers:


139

由于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内存中的引用相同。改变的Value属性obj影响到的Value属性x

Javascript将始终按值传递函数参数。那只是语言的规范。您可以x在两个函数的局部作用域中创建,而根本不传递变量。


有没有一种方法可以使另一个人也可以这样做:var x = 0; a(x); 并且该函数将一个对象设置为x的值?(或类似的内容)
user1342369

6
嘿,谢谢您向我介绍SO上的代码片段。在我看到它用在您的答案中之前,还没有听说过它们。
Joeytje50 2014年

@ Joeytje50-是的,此功能刚在9月份添加。非常便利!
Mike Christensen 2014年

如果您不知道obj属性的名称(例如,如果它是obj.name),那么下次您要更新obj.surname怎么办?您如何使功能适应这种灵活性?
2015年

1
@Dave这可能是一个较晚的答案,但是您可以让函数期望3个参数,即obj,属性名称和值。请记住,您可以通过执行obj.propertyname或obj ['propertyname']来访问对象的属性,这将是您想要灵活性的方式。传递3个参数,然后执行obj ['propertyname'] = value即可重用。
卡洛斯·卡拉

19

这个问题可能会有所帮助: 如何在javascript中通过引用传递变量?从ActiveX函数读取数据,该函数返回多个值

总而言之,Javascript基本类型总是按值传递,而对象内部的值则按引用传递(感谢注释者指出我的疏忽)。因此,为了解决这个问题,您必须将整数放入对象内:

var myobj = {x:0};

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

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

  


2
对象变量也总是按值传递-只是值是对对象的引用按引用传递按值传递引用之间有区别。即,当您x按值传递值时,无论是否x是对象,您都无法以调用者看到的任何方式将其替换为其他值。
cHao 2012年

而通过引用传递对象完全是不正确的。JavaScript中没有任何内容通过引用传递。
Mike Christensen

如果按值传递灵长类动物类型(由x引用),则x之后为何不更改,除非您传递对象
SuperUberDuper

13

我发现实现指针的方式略有不同,从C角度来看,它也许更通用并且更易于理解(因此更适合于用户示例的格式)。

在JavaScript中,就像在C语言中一样,数组变量实际上只是指向数组的指针,因此您可以使用与声明指针完全相同的数组。这样,即使您在原始对象中为变量命名,也可以以相同的方式使用代码中的所有指针。

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

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

var _x = [ 10 ];

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

foo(_x);

console.log(_x[0]);

产量

output: 20

8

您从窗口对象引用“ x”

var x = 0;

function a(key, ref) {
    ref = ref || window;  // object reference - default window
    ref[key]++;
}

a('x');                   // string
alert(x);

3

答案很晚,但是我遇到了一种通过闭包通过引用传递原始值的方法。创建指针相当复杂,但是可以工作。

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

1

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

function a(){
   x++;
};

由于x处于全局上下文中,因此您无需将其传递给函数。


1
有一点很重要,尽管我指出该变量不需要是全局变量(许多人建议不要这样做),但它只需要与调用方和被调用方处于同一范围即可。
Mike Christensen

1

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

1

由于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);

0

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


3
根本不是真的。您无法在JavaScript中通过引用传递任何内容。
Mike Christensen

1
对象引用作为值传递,因此所有内容都作为值传递。
卡洛斯·卡拉

有趣的观察;在JavaScript中创建对现有原语的引用似乎是不可能的;一旦绑定了一个原语,它的幼稚引用就永远不会注意到对该原语的任何更改,因为这些引用指向该值恰好是原语的变量的值;因此,“指向”基元的唯一方法是将基元包装在typeof为“ Object”的变量中;因为没有其他类型可以存储引用。TLDR;JavaScript中没有Perl“ \”运算符;它既可以作为开始的参考,也永远不能指向它。
德米特里(Dmitry)

烦人的部分是当您要创建一袋共享状态(例如“ window.shared”)作为对变量的引用映射时,这些变量当前为空/未定义,稍后将进行设置;在这种情况下,当您设置当前为null / undefined的状态值时,“ window.shared”变量将无法识别更改,即使它们存储在Object类型中也为时已晚;“ null / undefined”值必须在对象类型内部才能开始,例如,[null] [undefined]。
德米特里(Dmitry)

也就是说,您可以很聪明地将函数放入这样的全局变量中,这些函数对于每个尚未设置的变量都有一个getter和setter,并且这些函数将动态查找原语的CURRENT状态并获取/设置它,但是它不能返回指向该变量的指针(但是可以返回一个代理,该代理可以有效地执行与该变量相同的行为,并使用getter / setters间接对其进行变异)。
德米特里(Dmitry)

0

我在想与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
  • 专门优化的垃圾收集器
  • 一个数据结构抽屉/分析器

0

JavaScript不支持通过引用传递基本类型。不过,有一种解决方法。

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

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


0

根据您要执行的操作,您可以简单地保存变量名,然后像下面这样访问它:

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对象。另外,不建议使用全局变量,因为可能已经有一个具有相同名称的变量。
JayJay

0

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

我想补充一点,虽然我是新手并且来自C背景,但是从我所读到的有关事件循环和堆栈的内容来看,您应该使任何一个对象尽可能地靠近其调用者。因为我是新手,所以我可能会遇到这个错误。

这里有很多答案建议将其设置为全局,但是如果我没有正确阅读的话,那么这又可能会增加堆栈跳数,并在某种程度上可能不必要地分割事件循环(取决于被调用对象当前正在执行的操作)。

同样,任何给出的模仿指针的示例在很多方面都只能做一个相同的事情,而不会成为指针。


0

你不能。浏览器不允许您访问系统内存及其变量。像环境这样的节点应该可以实现,但是除非您是黑客,否则在浏览器内部不太可能实现。


-1

在您的示例中,实际上有2个具有相同名称的变量。(全局)变量x和函数作用域变量x。有趣的是,JavaScript的,给予什么用相同名称的两个变量做一个选择时,去与功能范围的名称而忽略超出范围的变量。

假设javascript始终以这种方式运行可能不安全...

干杯!


这个答案没有为这篇文章增加任何价值。@Mike Christensen的答案更为详细,已被接受。
亚历山大

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.