Покажчики в JavaScript?


81

Чи можемо ми передати посилання на змінну, яка є незмінною як аргумент у функції?

Приклад:

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

Можливо, це поганий стиль, оскільки ви збираєтеся використовувати побічні ефекти функції, щоб викликати мутацію. Це може ускладнити дотримання вашого коду.
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 - Так, цю функцію щойно додали ще у вересні. Дуже зручно!
Mike Christensen,

Що робити, якщо ви не знаєте назви властивості obj, наприклад, якщо це було obj.name, тоді наступного разу, коли ви захочете оновити obj.surname? Як ви можете зробити функцію пристосованою до такої гнучкості?
Сер

1
@Dave це може бути пізньою відповіддю, але ви можете змусити вашу функцію очікувати для 3 параметрів, obj, імені властивості та значення. Пам'ятайте, що ви можете отримати доступ до властивості об'єкта, виконавши obj.propertyname або obj ['propertyname'], що буде так, як ви хочете, коли вам потрібна гнучкість. Передайте 3 параметри та виконайте obj ['propertyname'] = значення, і це буде повторно використане
Carlos Calla

19

Це запитання може допомогти: Як передати змінну за посиланням у javascript? Зчитування даних з функції ActiveX, яка повертає більше одного значення

Підводячи підсумок, примітивні типи Javascript завжди передаються за значенням, тоді як значення всередині об'єктів передаються за посиланням (дякую коментаторам за те, що вони вказали на мій нагляд). Отже, щоб обійти це, ви повинні помістити ціле число всередину об’єкта:

var myobj = {x:0};

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

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

  


2
Об’єктні змінні завжди передаються також за значенням - просто значення є посиланням на об’єкт. Існує різниця між передачею за посиланням та передачею посилання за значенням . А саме, коли ви передаєте xзначення, незалежно від того, xє це об’єкт чи ні, ви не можете замінити його цілим іншим значенням будь-яким способом, який побачить абонент.
cHao

тоді як об'єкти передаються за посиланням просто не відповідає дійсності. Ніщо в JavaScript не передається за посиланням.
Mike Christensen

Якщо ви передаєте тип примата за значенням (на нього посилається x), чому x не змінюється після, якщо ви не
передаєте

14

Я знайшов дещо інший спосіб реалізації покажчиків, який є, можливо, більш загальним і простішим для розуміння з точки зору С (і, отже, більше вписується у формат прикладу користувачів).

В 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 код можна скоротити, використовуючи лямбда-вирази:

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 повинен просто розміщувати покажчики в суміші, оскільки це вирішує багато проблем. Це означає, що код може посилатися на невідоме ім'я змінної або змінні, які були створені динамічно. Це також полегшує модульне кодування та введення.

Це те, що я бачу як найближче до практичних вказівок

в 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
Посилання на об'єкт передається як значення, тому все передається як значення.
Carlos Calla

цікаве спостереження; здається, НЕМОЖЛИВО створити посилання на існуючий примітив у JavaScript; як тільки примітив пов'язаний, будь-які зміни цього примітиву ніколи не можуть бути помічені його наївними референтами, оскільки референти вказують на значення змінної, яке є примітивним; отже, єдиний спосіб "вказати" на примітив - це якщо примітив обернутий змінною, тип якої - "Об'єкт"; оскільки жоден інший тип не може зберігати посилання. TLDR; в JavaScript немає оператора Perl "\"; це або посилання для початку, або ви ніколи не можете вказати на нього.
Дмитро

Неприємна частина цього - коли ви хочете створити пакет спільного стану, наприклад "window.shared" як карту посилань на змінні, які в даний час є нульовими / невизначеними, які ви встановите пізніше; у цьому випадку, коли ви встановлюєте значення стану, яке на даний момент є нульовим / невизначеним, змінна "window.shared" не розпізнає зміни, навіть якщо вони зберігаються в типі Object, це вже занадто пізно; "нульові / невизначені" значення повинні бути всередині типів об'єктів, щоб це працювало, наприклад [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. Цікаво побачити, що при виборі того, що робити з 2 однойменними змінними, javascript поєднується з назвою функції і ігнорує змінну, що не входить в область дії.

Можливо, не безпечно припускати, що javascript завжди буде поводитися так ...

Ура!


Ця відповідь не додає жодної цінності цій публікації. Відповідь @Mike Christensen є більш детальною і вже прийнятою.
Олександр

-1

Ні, ви не можете. Браузер не надає вам доступу до системної пам'яті та її змінних. Це повинно бути можливо з таким середовищем, як вузол, але всередині браузера це не зовсім можливо, якщо ви не хакер.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.