2013-05-27 4 views
1
var a = [3, 4, 5]; 
var b = [6, 7, 8]; 

function why() { 
    b = a; 
    b[0] = 1; 
    alert(a[0] + ' ' + b[0]); 
} 
why(); 

Результат a[0]=1, b[0]=1;. Кажется, что JavaScript проходит по ссылке?Почему JavaScript проходит по ссылке?

Но в этом случае:

var a = [3, 4, 5]; 
var b = [6, 7, 8]; 

function why() { 
    b = a; 
    b = [1, 2, 3]; 
    alert(a + ' ' + b); 
} 
why(); 

В результате a=[3,4,5] и b = [1,2,3]. Почему это происходит по стоимости? Как избежать прохождения по ссылке?

+1

Строго говоря, это всегда «передать по значению», а значение переменных, относящихся к объектам, является ссылкой на объект. «Пропустить по ссылке» не существует в JavaScript: https://en.wikipedia.org/wiki/Evaluation_strategy. –

+0

* «Как избежать передачи по ссылке?» *: Вам нужно скопировать массив: http://stackoverflow.com/q/7486085/218196. –

+0

Вы ничего не передаете по ссылке. Это означает, что вы (в псевдокоде) объявляете свою функцию «function why (const n)» или «function why (var n)».Разница в том, что в первом случае выполняется копия 'n', и если вы оставите функцию, она будет уничтожена, а во втором вы измените переменную, поэтому оставляя эту функцию, вы сохраните свои изменения. В ваших примерах «a» и «b» являются глобальными переменными, поэтому по умолчанию они являются ссылками (функция 'why' не имеет ничего общего). Вы должны были спросить не о ** передаче ** (переменные по ссылке), а ** ** (ссылки на переменные). – Voitcus

ответ

6

Значение не примитивной переменной в JavaScript, как на большинстве языков объектов, является ссылкой на объект.

Во втором случае вы не меняете массив, а ссылку, которая хранится в b.

Если вы хотите скопировать массив, используйте

var c = a.slice(); 

Тогда c и a будут развиваться независимо друг от друга.

+0

Возможно, добавьте ссылку на ES Spec [§8.7] (http://es5.github.io/#x8.7) – C5H8NNaO4

+0

@ C5H8NNaO4 Woudln't это делает его более запутанным, поскольку мы, простые смертные, не можем действительно использовать это ссылочный тип? –

+0

@dystroy, если это многомерная матрица 'var a = [1, [1]];' copy' a.slice(); 'не работает. 'JSON.parse (JSON.stringify (a))' или библиотека jQuery расширяют 'jQuery.extend (true, [], a);' работает для этого случая – rab

3

Так как переменные работают.

В первом случае, вы устанавливаете обе переменные в одном экземпляре один массив, затем изменить этот массив через b (b[0] = 1). Вы также можете изменить тот же массив через a, но точка b и a указывают на тот же массив; bне "point" to a.

Во втором, вы устанавливаете и тот же экземпляр одного массива (b = a), но затем установить b к экземпляру совершенно новый, различного массива (b = [1,2,3]). Вы меняете массив, на который указывает b, вы не влияете на a, потому что, как я уже сказал, b не указывал на a, он указывал на такое же значение, что и a.

3

Переменные b и a являются , указывающие на объект. Когда вы делаете b = // something, вы меняете место, на которое указывает переменная. Вы не меняете подстилающий объект.

В первом коде b = a означает, что b и a теперь указывают на тот же объект. Поэтому, когда вы вносите изменения в один, это отражается в другом.

Однако, во втором примере, вы первый указывая b на тот же объект, как a, но затем указывая b к нового массива ([1,2,3]). Тот факт, что он указывал на a на короткий промежуток времени, не имеет значения.

0

В первом случае изменяется одно значение элемента массива. b теперь равно a, поэтому a [0] и b [0] дают такое же значение.

Во втором случае, хотя вы сделали b, указываете на a, вы назначили новый объект b. так что снова b и a - два разных объекта ... так разные значения.

3

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

В первом случае вы мутируете объект указателем указывает на. Но ссылка не изменяется, а только объект, на который указывает эта ссылка. Ссылка все же указывает на тот же объект, объект выглядит иначе, чем раньше.

Этот конкретный случай передачи по значению-where-the-value-is-pointer иногда называется совместным вызовом-вызовом, вызовом-объектом или вызовом-распределением, и способ передачи аргументов работает почти во всех объектно-ориентированных языках: Smalltalk, Python, Ruby, Java, C# (по умолчанию вы можете передавать по ссылке, явно указывая модификатор ref) и т. д.

+0

Большое вам спасибо, что мне делать, если я хочу изменить значение b [0], но не изменить значение [0] в первом случае? – user2424844

+0

@ user2424844 ... но не 'a [0]'? Затем вам нужно клонировать массив. –

+0

Я клонировал массив, но он не работал – user2424844

0

После всех этих отличные ответы, на самом деле нечего сказать, поэтому объясняет объяснение, основанное на спецификации ECMAScript 5.

Также функция deepclone в конце ответа.

Как определено в спецификации ES5,

11.13.1 Simple Assignment (=) Производство ВыражениеПрисваивания: ЛевостороннееВыражение = ВыражениеПрисваивания вычисляется следующим образом:

  1. Пусть LRef быть результатом оценки ЛевостороннегоВыражения.
  2. Позвольте rref быть результатом оценки AssignmentExpression.
  3. Пусть rval будет GetValue(rref).
  4. Бросить исключение SyntaxError, если выполняются следующие условия все верно:
    • Тип (LRef) является Ссылка верно
    • IsStrictReference (LRef) верно
    • Тип (GetBase (LRef)) является среда записи
    • GetReferencedName (LRef) либо "Eval" или "аргументы"
  5. вызовов PutValue(lref, rval).
  6. Обратно rval.

Так Что происходит, когда мы достигаем точку и rref является объектом, является
§8.7.1 (раздел 4В В GetValue(V) //V==rref интересная точка)

4. Если IsPropertyReference (V), а затем

  • (б) Возвращает результат вызова получить внутренний метод с использованием базы, как это его значение, и переходя GetReferencedName (V) для аргумента.

Теперь в этой точке rval держит ссылку на объект, который затем получает положить в на 5.

Где §8.7.2 (опять 4В PutValue(V,W) //V==lref , W==rval интересная часть) приходит в игру.

Примечание: W является атм ссылка на объект, который нужно назначить, и не значение

4. Иначе, если IsPropertyReference (V), а затем

  • (б) Вызвать внутренний метод put, используя base, как его значение, и передать GetReferencedName (V) для имени свойства, W для значения и IsStrictReference (V) для флага Throw.

Как вы можете видеть в вашем случае значение из b что банкомат ссылка на объект [6, 7, 8] заменяется на результат, так сказать GetValue(rref), который является ссылка на [1, 2, 3];

Однако


Видимо вы ищете deep clone

Адрес: 1.

Object.defineProperty(Object.prototype, "clone", { 
     value: function (deep) { 
      var type = Object.prototype.toString.call(this).match(/^\[object (.+?)\]$/)[1]; 
      if (type !== "Object") { 
       return this.valueOf; 
      } 

      var clone = {}; 
      if (!deep) { 
       for (var prp in this) { 
        clone[prp] = this[prp]; 
       } 
      } else { 
       for (var prop in this) { 
        if (typeof this[prop] !== "undefined" && this[prop] !== null) 
         clone[prop] = (typeof this[prop] !== "object" ? this[prop] : this[prop].clone((typeof deep == "boolean" ? deep : (deep - 1)))); 
        else 
         clone[prop] = ""; 
       } 
      } 
      return clone; 
     }, 
     enumerable: false 
    }); 
Object.defineProperty(Array.prototype, "clone", { 
     value: function (deep) { 
      var clone = []; 
      if (!deep) clone = this.concat(); 
      else this.forEach(function (e) { 
         if (typeof e !== "undefined" && e !== null) 
          clone.push((typeof e !== "object" ? e : e.clone((deep - 1)))); 
         else 
          clone.push(""); 
        }); 

      return clone; 
     }, 
     enumerable: false 
    }); 

var a = [1, [2, { a: 3 } ], 4]; 
var b = a.clone(Infinity); 

a[1][1]["a"] = "cloned"; 

console.log(a[1][1]["a"], b[1][1]["a"]); //"cloned" , 3 

И Демо на JSBin

Чтобы использовать это просто вызовите .clone(levelOfDeepness) на объект или массив

Примечание: Я использовал прототип Объекты ради простоте так как я могу напрямую вызвать .clone элементов Object и Array при клонировании m (дает повышение производительности по варианту проверки типов в одной функции)

Смежные вопросы