Указатели в JavaScript?

81

Можем ли мы передать ссылку на неизменяемую переменную в качестве аргумента функции?

Пример:

var x = 0;
function a(x)
{
    x++;
}
a(x);
alert(x); //Here I want to have 1 instead of 0
пользователь1342369
источник
Можно утверждать, что это плохой стиль, поскольку вы собираетесь использовать побочные эффекты функции, чтобы вызвать мутацию. Это может затруднить отслеживание вашего кода.
SPEX

Ответы:

140

Поскольку 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. Таким образом, objи xобращайтесь к тому же в памяти. Изменение Valueсвойства objвлияет на Valueсвойство x.

Javascript всегда передает параметры функции по значению. Это просто спецификация языка. Вы можете создать xв области видимости, локальную для обеих функций, и вообще не передавать переменную.

Майк Кристенсен
источник
Есть ли способ сделать так, чтобы другой человек просто сделал: var x = 0; a (x); и что функция устанавливает объект со значением x? (или что-то в этом роде)
user1342369
6
Привет, спасибо, что познакомили меня с фрагментами кода на SO . Я не слышал о них, пока не увидел, что они используются в вашем ответе.
Joeytje50
@ Joeytje50 - Да, эта функция была добавлена ​​еще в сентябре. Очень удобно!
Майк Кристенсен
Что делать, если вы не знаете имя свойства obj, например, если это было obj.name, тогда в следующий раз вы захотите обновить obj.surname? Как можно заставить функцию адаптироваться к такой гибкости?
Сэр
1
@Dave, это может быть поздний ответ, но вы можете сделать так, чтобы ваша функция ожидала 3 параметра: объект, имя свойства и значение. Помните, что вы можете получить доступ к свойству объекта, выполнив 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

  

n00dle
источник
2
Переменные объекта также всегда передаются по значению - просто значение является ссылкой на объект. Есть разница между передачей по ссылке и передачей ссылки по значению . А именно, когда вы передаете xзначение, независимо от того, xявляется ли он объектом или нет, вы не можете заменить его другим значением любым способом, который увидит вызывающий.
cHao
тогда как объекты передаются по ссылке, это просто неверно. В JavaScript ничего не передается по ссылке.
Майк Кристенсен
Если вы передаете тип приматов по значению (на него ссылается x), почему x не меняется после, если вы не передаете объект
SuperUberDuper
14

Я нашел несколько иной способ реализации указателей, который, возможно, является более общим и легким для понимания с точки зрения 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);
AlexisK
источник
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 код можно сократить, чтобы использовать лямбда-выражения:

var myName = 'joe';
var myNamePtr = ptr(=> myName, v => myName = v);

helloWorld(myNamePtr); // joe, jack
console.log(myName); // jack
Химаншу
источник
1

В JavaScript это будет глобальный. Однако ваша функция будет выглядеть примерно так:

function a(){
   x++;
};

Поскольку он xнаходится в глобальном контексте, вам не нужно передавать его в функцию.

Maess
источник
1
Верный момент, хотя я бы отметил, что переменная не обязательно должна быть глобальной (и многие советуют не делать этого когда-либо), она просто должна находиться в той же области видимости, что и вызывающий и вызываемый.
Майк Кристенсен
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 вы не можете передавать переменные по ссылке в функцию. Однако вы можете передать объект по ссылке.

d4rkpr1nc3
источник
3
Просто неправда. Вы не можете передавать что-либо по ссылке в JavaScript.
Майк Кристенсен
1
Ссылка на объект передается как значение, поэтому все передается как значение.
Карлос Калла
интересное наблюдение; кажется НЕВОЗМОЖНО создать ссылку на существующий примитив в JavaScript; как только примитив связан, любые изменения в этом примитиве никогда не могут быть замечены его наивными референтами, потому что референты указывают на значение этой переменной, которое оказывается примитивом; поэтому единственный способ «указать» на примитив - это заключить примитив в переменную, typeof которой равен «Object»; поскольку никакие другие типы не могут хранить ссылку. TL; DR; в JavaScript нет оператора Perl "\"; это либо ссылка для начала, либо вы никогда не сможете указать на нее.
Дмитрий
Раздражает часть этого, когда вы хотите создать пакет общего состояния, например "window.shared" как карту ссылок на переменные, которые в настоящее время имеют значение null / undefined, которые вы установите позже; в этом случае, когда вы устанавливаете значения состояния, которое в настоящее время равно null / undefined, переменная "window.shared" не распознает изменения, даже если они хранятся в типе объекта, уже слишком поздно; для того, чтобы это работало, значения "null / undefined" должны быть внутри типов объектов, например [null] [undefined].
Дмитрий
Тем не менее, вы можете быть умным и вставлять функции в такую ​​глобальную переменную, у которой есть геттер и сеттер для каждой из этих еще не установленных переменных, и эти функции БУДУТ динамически находить ТЕКУЩЕЕ состояние примитива и получать / устанавливать его, но он не может вернуть указатель на эту переменную (но может вернуть прокси, который может эффективно вести себя так же, как эта переменная, и косвенно изменять ее, используя геттеры / сеттеры).
Дмитрий
0

Я думаю, что в отличие от C или любого языка с указателями:

/** Javascript **/
var o = {x:10,y:20};
var o2 = {z:50,w:200};
  • очевидно, что в javascript вы не можете получить доступ к адресам объектов o и o2 в памяти
  • но вы также не можете сравнить их адрес: (нет тривиальной возможности отсортировать их, а затем получить доступ по дихотомии )

.

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, из того, что я читаю о цикле событий и стеке, вы должны держать любой объект как можно ближе к вызывающему объекту. Поскольку я новенький, я мог ошибаться.

Многие ответы здесь предлагают сделать его глобальным, но опять же, если я правильно читаю, это могло бы увеличить количество прыжков в стеке и, в некотором смысле, возможно, без необходимости фрагментировать ваш цикл событий (в зависимости от того, что вызываемый объект делает в настоящее время).

Кроме того, любой пример, имитирующий указатели, - это всего лишь 1 во многих отношениях, чтобы делать то же самое, но не указатель.


источник
-1

В вашем примере у вас фактически есть 2 переменные с одинаковым именем. (Глобальная) переменная x и переменная x в области видимости функции . Интересно видеть, что javascript, когда ему предоставляется выбор, что делать с двумя переменными с одинаковым именем, идет с именем в области видимости функции и игнорирует переменную вне области видимости.

Вероятно, небезопасно предполагать, что javascript всегда будет вести себя таким образом ...

Ура!

тпайтн
источник
Этот ответ не добавляет ценности этому сообщению. Ответ @Mike Christensen более подробный и уже принят.
Александр
-1

Нет, ты не можешь. Браузер не дает вам доступа к системной памяти и ее разнообразным возможностям. Это должно быть возможно с такой средой, как node, но внутри браузера это невозможно, если вы не хакер.

Ионут Евгений
источник