Câu hỏi JavaScript có phải là ngôn ngữ truyền qua hoặc tham chiếu không?


Các kiểu nguyên thủy (số, chuỗi, vv) được truyền theo giá trị, nhưng các đối tượng không xác định, vì chúng có thể là cả hai giá trị truyền qua (trong trường hợp chúng ta xem xét một biến đang nắm giữ một đối tượng thực tế là một tham chiếu đến đối tượng ) và thông qua tham chiếu (khi chúng ta xem xét rằng biến đối tượng giữ chính đối tượng đó).

Mặc dù nó không thực sự quan trọng ở cuối, tôi muốn biết cách chính xác để trình bày các lập luận thông qua các quy ước là gì. Có một đoạn trích từ đặc tả JavaScript, trong đó xác định những gì nên là ngữ nghĩa liên quan đến điều này?


1138
2018-02-05 21:23


gốc


Tôi nghĩ bạn vô tình lật các định nghĩa của bạn về giá trị truyền qua và giá trị tham chiếu ... "được truyền theo giá trị (trong trường hợp chúng ta xem xét rằng một biến đang nắm giữ một đối tượng thực tế là một tham chiếu đến đối tượng) và được truyền -by-tham chiếu (khi chúng ta xem xét biến số cho đối tượng chứa chính đối tượng) " - Niko Bellic
Vâng. Bất kể cú pháp, trong bất kỳ cuộc gọi hàm nào trong bất kỳ ngôn ngữ lập trình nào, tham chiếu truyền qua đều có nghĩa là dữ liệu liên quan đến biến được truyền không được sao chép khi được chuyển đến hàm và do đó bất kỳ sửa đổi nào được thực hiện bởi hàm sẽ được giữ lại trong chương trình sau khi cuộc gọi chức năng chấm dứt. Pass-by-value có nghĩa là dữ liệu được kết hợp với biến thực sự được sao chép khi được chuyển đến hàm và bất kỳ sửa đổi nào được thực hiện bởi hàm đó thành biến đó sẽ bị mất khi biến nằm ngoài phạm vi của hàm của hàm khi hàm trả về. - John Sonderson
@MjrKusanagi Tôi không nghĩ Object.create có thể được coi là kỹ thuật sao chép thích hợp, tức là nếu bạn thay đổi bản gốc của đối tượng, nó sẽ ảnh hưởng đến bản sao, chỉ những thay đổi đối với bản sao không ảnh hưởng đến đối tượng gốc. Tất nhiên, nó có nó sử dụng và JavaScript được thiết kế xung quanh khái niệm này (nguyên mẫu thừa kế), nhưng tôi nghĩ rằng nó là sai lầm để gọi Object.create cơ chế duy nhất để nhân bản một đối tượng. - Danail Nachev
Câu hỏi cũ này có phần độc hại bởi vì câu trả lời được bỏ qua rất nhiều của nó là không chính xác. JavaScript đúng giá trị vượt qua. - Pointy
@DanailNachev Các thuật ngữ là đáng tiếc khó hiểu. Vấn đề là, "vượt qua bằng giá trị" và "vượt qua bằng tham chiếu" là các thuật ngữ có trước nhiều tính năng ngôn ngữ lập trình hiện đại hơn. Từ "giá trị" và "tham chiếu" tham chiếu đặc biệt với tham số khi nó xuất hiện trong biểu thức gọi hàm. JavaScript luôn đánh giá từng biểu thức trong danh sách tham số cuộc gọi chức năng trước gọi hàm, vì vậy các tham số luôn là giá trị. Phần khó hiểu là các tham chiếu đến các đối tượng là các giá trị JavaScript phổ biến. Tuy nhiên, điều đó không làm cho nó trở thành ngôn ngữ "truyền bằng tham chiếu". - Pointy


Các câu trả lời:


Thật thú vị trong Javascript. Xem xét ví dụ này:

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);    
console.log(obj2.item);

Điều này tạo ra đầu ra:

10
changed
unchanged
  • Nếu nó là giá trị thuần túy, thì thay đổi obj1.item sẽ không ảnh hưởng đến obj1 bên ngoài chức năng.
  • Nếu nó đã được thông qua bằng cách tham chiếu, thì mọi thứ sẽ thay đổi. num sẽ là 100obj2.item sẽ đọc "changed".

Thay vào đó, tình huống là mục được truyền vào được truyền theo giá trị. Nhưng vật phẩm được truyền theo giá trị là chinh no một tài liệu tham khảo. Về mặt kỹ thuật, điều này được gọi là chia sẻ cuộc gọi.

Trong điều kiện thực tế, điều này có nghĩa là nếu bạn thay đổi thông số (như với num và obj2), điều đó sẽ không ảnh hưởng đến mục được đưa vào tham số. Nhưng nếu bạn thay đổi NỘI BỘ của tham số, sẽ truyền lại (như với obj1).


1361
2018-03-15 16:38



Điều này là chính xác giống nhau (hoặc ít nhất là ngữ nghĩa) là C #. Đối tượng có hai loại: Giá trị (loại nguyên thủy) và Tham chiếu. - Peter Lee
Tôi nghĩ rằng điều này cũng được sử dụng trong Java: tham chiếu theo giá trị. - Jpnh
lý do thực sự là trong vòng changeStuff, num, obj1 và obj2 là các tham chiếu. Khi bạn thay đổi item thuộc tính của đối tượng được tham chiếu bởi obj1, bạn đang thay đổi giá trị của thuộc tính item ban đầu được đặt thành "không thay đổi". Khi bạn gán obj2 một giá trị {item: "changed"}, bạn đang thay đổi tham chiếu đến một đối tượng mới (mà ngay lập tức nằm ngoài phạm vi khi hàm thoát). Nó sẽ trở nên rõ ràng hơn những gì đang xảy ra nếu bạn đặt tên cho các chức năng params những thứ như numf, obj1f, và obj2f. Sau đó, bạn thấy rằng các params đang ẩn các tên var bên ngoài. - jinglesthula
@BartoNaz Không thực sự. Những gì bạn muốn là chuyển tham chiếu theo tham chiếu, thay vì chuyển tham chiếu theo giá trị. Nhưng JavaScript luôn chuyển tham chiếu theo giá trị, giống như nó truyền mọi thứ khác theo giá trị. (Để so sánh, C # có hành vi truyền-tham chiếu theo giá trị tương tự như JavaScript và Java, nhưng cho phép bạn chỉ định tham chiếu truyền qua tham chiếu với ref Từ khóa.) Thông thường bạn sẽ chỉ có hàm trả về đối tượng mới và thực hiện nhiệm vụ tại điểm mà bạn gọi hàm. Ví dụ., foo = GetNewFoo();thay vì GetNewFoo(foo); - Tim Goodman
Mặc dù câu trả lời này là câu trả lời phổ biến nhất nhưng nó có thể hơi khó hiểu vì nó nói "Nếu nó là giá trị thuần túy". JavaScript Là tinh khiết pass-by-giá trị. Nhưng giá trị được truyền là một tham chiếu. Điều này không bị hạn chế đối với việc truyền tham số. Bạn có thể chỉ cần sao chép biến theo var obj1 = { item: 'unchanged' }; var obj2 = obj1; obj2.item = 'changed'; và sẽ quan sát hiệu ứng tương tự như trong ví dụ của bạn. Vì vậy, cá nhân tôi tham khảo câu trả lời của Tim Goodman - chiccodoro


Nó luôn luôn đi qua giá trị, nhưng đối với các đối tượng giá trị của biến là một tham chiếu. Bởi vì điều này, khi bạn vượt qua một đối tượng và thay đổi nó các thành viên, những thay đổi đó tồn tại bên ngoài chức năng. Điều này làm cho nó nhìn như vượt qua bằng tham chiếu. Nhưng nếu bạn thực sự thay đổi giá trị của biến đối tượng, bạn sẽ thấy rằng thay đổi không tồn tại, chứng minh rằng nó thực sự vượt qua theo giá trị.

Thí dụ:

function changeObject(x) {
  x = {member:"bar"};
  alert("in changeObject: " + x.member);
}

function changeMember(x) {
  x.member = "bar";
  alert("in changeMember: " + x.member);
}

var x = {member:"foo"};

alert("before changeObject: " + x.member);
changeObject(x);
alert("after changeObject: " + x.member); /* change did not persist */

alert("before changeMember: " + x.member);
changeMember(x);
alert("after changeMember: " + x.member); /* change persists */

Đầu ra:

before changeObject: foo
in changeObject: bar
after changeObject: foo

before changeMember: foo
in changeMember: bar
after changeMember: bar

376
2018-02-05 21:37



@daylight: Trên thực tế, bạn đã sai; nếu nó được thông qua bởi const ref cố gắng làm changeObject sẽ gây ra lỗi, thay vì chỉ thất bại. Hãy thử gán một giá trị mới cho tham chiếu const trong C ++ và trình biên dịch từ chối nó. Trong thuật ngữ người dùng, đó là sự khác biệt giữa truyền theo giá trị và chuyển qua tham chiếu const. - deworde
@daylight: Nó không phải là ref liên tục. Trong changeObject, Tôi đã thay đổi x để chứa tham chiếu đến đối tượng mới. x = {member:"bar"}; tương đương với x = new Object(); x.member = "bar";  Những gì tôi nói cũng đúng với C #, nhân tiện. - Tim Goodman
@daylight: Đối với C #, bạn có thể thấy điều này từ bên ngoài hàm, nếu bạn sử dụng ref từ khóa bạn có thể chuyển tham chiếu theo tham chiếu (thay vì mặc định chuyển tham chiếu theo giá trị), và sau đó thay đổi để trỏ đến new Object()  sẽ kiên trì. - Tim Goodman
@adityamenon Thật khó để trả lời "tại sao", nhưng tôi sẽ lưu ý rằng các nhà thiết kế của Java và C # đã thực hiện một sự lựa chọn tương tự; đây không chỉ là một số sự kỳ lạ của JavaScript. Thực sự, nó rất nhất quán theo giá trị, điều khiến mọi người khó hiểu là giá trị có thể là một tham chiếu. Nó không khác nhiều so với việc đi qua một con trỏ xung quanh (theo giá trị) trong C ++ và sau đó dereferencing nó để thiết lập các thành viên. Không ai ngạc nhiên rằng sự thay đổi đó vẫn tồn tại. Nhưng bởi vì những ngôn ngữ này trừu tượng đi con trỏ và âm thầm làm dereferencing cho bạn, mọi người bị lẫn lộn. - Tim Goodman
Nói cách khác, điều gây nhầm lẫn ở đây không phải là giá trị pass-by-value / pass-by-reference. Mọi thứ đều có giá trị vượt qua, đầy đủ. Điều khó hiểu là bạn không thể truyền một đối tượng, bạn cũng không thể lưu trữ một đối tượng trong một biến. Moi lan ban suy nghĩ bạn đang làm điều đó, bạn thực ra chuyển hoặc lưu trữ một tham chiếu đến đối tượng đó. Nhưng khi bạn truy cập vào các thành viên của nó, có một cuộc hội thảo im lặng xảy ra, mà tồn tại viễn tưởng rằng biến của bạn đã giữ đối tượng thực sự. - Tim Goodman


Biến không "giữ" đối tượng, nó giữ một tham chiếu. Bạn có thể gán tham chiếu đó cho biến khác, bây giờ cả hai tham chiếu cùng một đối tượng. Nó luôn chuyển giá trị (ngay cả khi giá trị đó là tham chiếu ...).

Không có cách nào để thay đổi giá trị được giữ bởi một biến được chuyển như một tham số, điều này có thể xảy ra nếu JS hỗ trợ truyền tham chiếu.


132
2017-08-04 11:06



Điều này làm tôi bối rối một chút. Không chuyển qua tham chiếu pass-by-reference?
Tác giả có nghĩa là bằng cách chuyển một tham chiếu, bạn đang truyền một giá trị tham chiếu (một cách khác để suy nghĩ về nó là truyền giá trị của địa chỉ bộ nhớ). Vì vậy, đó là lý do tại sao nếu bạn redeclare đối tượng, bản gốc không thay đổi, bởi vì bạn đang tạo một đối tượng mới tại một vị trí bộ nhớ khác nhau. Nếu bạn thay đổi một thuộc tính, đối tượng gốc sẽ thay đổi vì bạn đã thay đổi nó ở vị trí bộ nhớ ban đầu (không được gán lại). - Huy-Anh Hoang


My 2 Cent .... Đây là cách tôi hiểu nó. (Vui lòng sửa tôi nếu tôi sai)

Đó là thời gian để ném ra tất cả mọi thứ bạn biết về vượt qua theo giá trị / tham khảo.

Bởi vì trong JavaScript, nó không quan trọng cho dù nó được thông qua bởi giá trị hoặc tham chiếu hoặc bất cứ điều gì. Điều quan trọng là đột biến vs chuyển nhượng các tham số được truyền vào một hàm.

OK, hãy để tôi làm hết sức mình để giải thích ý tôi là gì. Giả sử bạn có một vài đối tượng.

var object1 = {};
var object2 = {};

Những gì chúng ta đã làm là "gán" ... Chúng ta đã gán 2 đối tượng rỗng riêng biệt cho các biến "object1" và "object2".

Bây giờ, hãy nói rằng chúng ta thích object1 tốt hơn ... Vì vậy, chúng ta "gán" một biến mới.

var favoriteObject = object1;

Tiếp theo, vì lý do gì đó, chúng tôi quyết định rằng chúng tôi thích đối tượng 2 tốt hơn. Vì vậy, chúng tôi chỉ đơn giản là làm một chút tái phân công.

favoriteObject = object2;

Không có gì xảy ra với object1 hoặc object2. Chúng tôi đã không thay đổi bất kỳ dữ liệu nào cả. Tất cả những gì chúng tôi đã làm là gán lại những gì đối tượng yêu thích của chúng tôi. Điều quan trọng là phải biết rằng object2 và favoritesObject đều được gán cho cùng một đối tượng. Chúng ta có thể thay đổi đối tượng đó thông qua một trong hai biến đó.

object2.name = 'Fred';
console.log(favoriteObject.name) // logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // logs Joe 

OK, bây giờ chúng ta hãy xem xét các kiểu nguyên thủy như các chuỗi ví dụ

var string1 = 'Hello world';
var string2 = 'Goodbye world';

Một lần nữa, chúng tôi chọn một yêu thích.

var favoriteString = string1;

Cả hai biến requestString và string1 của chúng ta đều được gán cho 'Hello world'. Bây giờ, nếu chúng ta muốn thay đổi favoritesString của chúng ta thì sao ??? Chuyện gì sẽ xảy ra???

favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'

Uh oh .... Chuyện gì đã xảy ra. Chúng tôi không thể thay đổi string1 bằng cách thay đổi favoritesString ... Tại sao ?? bởi vì các chuỗi là bất biến và chúng tôi đã không biến đổi nó. Tất cả những gì chúng tôi đã làm là "RE ASSIGN" favoritesString thành chuỗi mới. Điều này về cơ bản đã ngắt kết nối nó khỏi chuỗi1. Trong ví dụ trước, khi chúng tôi đổi tên đối tượng của mình, chúng tôi đã không gán bất cứ thứ gì. (Thực ra ... chúng tôi đã làm, chúng tôi đã gán thuộc tính name cho một chuỗi mới.) Thay vào đó, chúng tôi chỉ đơn giản là tắt đối tượng giữ các kết nối giữa 2 biến và các đối tượng bên dưới.

Bây giờ, vào các chức năng và các tham số truyền .... Khi bạn gọi một hàm, và chuyển một tham số, những gì bạn đang làm là "gán" cho một biến mới, và nó hoạt động giống hệt như khi bạn chỉ định sử dụng bằng (=) dấu.

Lấy những ví dụ này.

var myString = 'hello';

// Assign to a new variable (just like when you pass to a function)
var param1 = myString; 
param1 = 'world'; // Re assignment

console.log(myString); // logs 'hello'
console.log(param1);   // logs 'world'

Bây giờ, điều tương tự, nhưng với một hàm

function myFunc(param1) {
    param1 = 'world';

    console.log(param1);   // logs 'world'
}

var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString); 

console.log(myString); // logs 'hello'

OK, bây giờ cho phép đưa ra một vài ví dụ sử dụng các đối tượng thay vào đó ... đầu tiên, không có hàm.

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;

// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl

console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'

// Now, let's reassign
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object no longer mutates the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';

Bây giờ, điều tương tự, nhưng với một cuộc gọi hàm

function myFunc(otherObj) {

    // Let's mutate our object
    otherObj.firstName = 'Sue';
    console.log(otherObj.firstName); // Logs 'Sue'

    // Now let's re-assign
    otherObj = {
        firstName: 'Jack',
        lastName: 'Frost'
    };
    console.log(otherObj.firstName); // Logs 'Jack'

    // Again, otherObj and myObject are assigned to 2 very different objects
    // And mutating one object no longer mutates the other
}

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);

console.log(myObject.firstName); // Logs 'Sue', just like before

OK, nếu bạn đọc toàn bộ bài đăng này, có lẽ bây giờ bạn đã hiểu rõ hơn về cách gọi hàm hoạt động trong javascript. Nó không quan trọng cho dù một cái gì đó được thông qua bởi tham chiếu hoặc bằng giá trị ... Điều gì quan trọng là chuyển nhượng vs đột biến.

Mỗi khi bạn chuyển một biến cho một hàm, bạn đang "Chỉ định" cho bất kỳ tên nào của biến tham số, giống như khi bạn sử dụng dấu bằng (=).

Luôn nhớ rằng dấu bằng (=) nghĩa là gán. Luôn nhớ rằng việc chuyển một tham số đến một hàm cũng có nghĩa là gán. Chúng giống nhau và 2 biến được kết nối chính xác theo cùng một cách.

Thời gian duy nhất mà sửa đổi một biến ảnh hưởng đến một biến khác là khi đối tượng bên dưới bị đột biến.

Không có điểm trong việc phân biệt giữa các đối tượng và nguyên thủy, bởi vì nó hoạt động theo cùng một cách chính xác như thể bạn không có hàm và chỉ sử dụng dấu bằng để gán cho một biến mới.

Dấu hiệu duy nhất là khi tên của biến mà bạn chuyển vào hàm giống như tên của tham số hàm. Khi điều này xảy ra, bạn phải xử lý tham số bên trong hàm như thể nó là một biến hoàn toàn mới riêng cho hàm (vì nó là)

function myFunc(myString) {
    // myString is private and does not affect the outer variable
    myString = 'hello';
}

var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';

myFunc(myString);
console.log(myString); // logs 'test'

84
2017-09-26 04:06



@Rimbuaj - Tôi thực sự có một blog .. Tôi làm bây giờ anyway. Một trong đó có các cuộc thảo luận về mã hóa. Tôi vừa đăng bài đầu tiên của tôi về mã hóa. constalink.com/blog - Ray Perea
Đối với bất kỳ lập trình viên C nào, hãy nghĩ đến char *. foo(char *a){a="hello";}  không làm gì cả, nhưng nếu bạn làm foo(char *a){a[0]='h';a[1]='i';a[2]=0;} nó được thay đổi bên ngoài bởi vì a là một vị trí bộ nhớ được truyền bởi giá trị tham chiếu một chuỗi (mảng char). Các cấu trúc truyền (tương tự như đối tượng js) theo giá trị trong C được cho phép, nhưng không được khuyến nghị. JavaScript đơn giản thực thi những thực hành tốt nhất này và che giấu các tàu không cần thiết và thường không mong muốn ... và nó chắc chắn làm cho việc đọc dễ dàng hơn. - technosaurus
Điều này là đúng - các điều khoản giá trị vượt qua và thông qua tham chiếu có ý nghĩa trong thiết kế ngôn ngữ lập trình, và những ý nghĩa đó không có gì để làm với đột biến đối tượng. Đó là tất cả về cách các tham số chức năng hoạt động. - Pointy
Bây giờ tôi hiểu rằng obj1 = obj2 có nghĩa là cả obj1 và obj2 hiện đang trỏ đến cùng một vị trí tham chiếu, và nếu tôi chỉnh sửa nội bộ của obj2, tham chiếu obj1 sẽ phơi bày cùng nội bộ. Làm thế nào để sao chép một đối tượng như vậy khi tôi làm source = { "id":"1"}; copy = source /*this is wrong*/; copy.id="2" nguồn đó vẫn là {"id": "1"}? - Machtyn
Tôi đã đăng một câu trả lời khác với các định nghĩa truyền thống để hy vọng làm giảm sự nhầm lẫn. Định nghĩa truyền thống của "pass-by-value" và "pass-by-reference" đã được xác định trở lại trong ngày của con trỏ bộ nhớ trước khi dereferencing tự động. Nó hoàn toàn được hiểu rõ rằng giá trị của một biến đối tượng thực sự là vị trí con trỏ bộ nhớ, chứ không phải đối tượng. Mặc dù thảo luận của bạn về chuyển nhượng vs đột biến có lẽ hữu ích, nhưng không nhất thiết phải ném ra các thuật ngữ truyền thống cũng như định nghĩa của chúng. Đột biến, chuyển nhượng, pass-by-value, pass-by-reference, vv không được mâu thuẫn lẫn nhau. - C Perkins


Hãy xem xét những điều sau đây:

  1. Biến là con trỏvào các giá trị trong bộ nhớ.
  2. Chỉ định lại một biến chỉ đơn thuần trỏ con trỏ tới một giá trị mới.
  3. Việc gán lại một biến sẽ không bao giờ ảnh hưởng đến các biến khác đang trỏ vào cùng một đối tượng đó

Vì thế, quên đi "chuyển qua tham chiếu / giá trị" không bị treo lên trên "vượt qua bằng tham chiếu / giá trị" bởi vì:

  1. Các thuật ngữ chỉ được sử dụng để mô tả hành vi của một ngôn ngữ, không nhất thiết phải thực hiện cơ bản thực tế. Kết quả của sự trừu tượng này, các chi tiết quan trọng cần thiết cho một lời giải thích phong nha bị mất, chắc chắn dẫn đến tình trạng hiện tại mà một thuật ngữ không mô tả đầy đủ hành vi thực tế và thông tin bổ sung phải được cung cấp
  2. Những khái niệm này ban đầu không được định nghĩa với mục đích mô tả javascript nói riêng và vì vậy tôi không cảm thấy bắt buộc phải sử dụng chúng khi chúng chỉ thêm vào sự nhầm lẫn.

Để trả lời câu hỏi của bạn: con trỏ được thông qua.


// code
var obj = {
    name: 'Fred',
    num: 1
};

// illustration
               'Fred'
              /
             /
(obj) ---- {}
             \
              \
               1


// code
obj.name = 'George';


// illustration
                 'Fred'


(obj) ---- {} ----- 'George'
             \
              \
               1


// code
obj = {};

// illustration
                 'Fred'


(obj)      {} ----- 'George'
  |          \
  |           \
 { }            1


// code
var obj = {
    text: 'Hello world!'
};

/* function parameters get their own pointer to 
 * the arguments that are passed in, just like any other variable */
someFunc(obj);


// illustration
(caller scope)        (someFunc scope)
           \             /
            \           /
             \         /
              \       /
               \     /
                 { }
                  |
                  |
                  |
            'Hello world'

Một số nhận xét cuối cùng:

  • Thật hấp dẫn khi nghĩ rằng nguyên thủy được thực thi bởi các quy tắc đặc biệt trong khi các đối tượng không phải, nhưng nguyên thủy chỉ đơn giản là kết thúc của chuỗi con trỏ.
  • Như một ví dụ cuối cùng, hãy xem xét lý do tại sao một nỗ lực chung để xóa một mảng không hoạt động như mong đợi.


var a = [1,2];
var b = a;

a = [];
console.log(b); // [1,2]
// doesn't work because `b` is still pointing at the original array

57
2018-06-02 23:04



Câu hỏi tiếp theo để biết thêm tín dụng;) Thu gom rác thải hoạt động như thế nào? Nếu tôi chu kỳ một biến thông qua một triệu {'George', 1} các giá trị, nhưng chỉ sử dụng một trong số chúng tại một thời điểm, vậy thì các giá trị khác được quản lý như thế nào? Và điều gì sẽ xảy ra khi tôi gán một biến cho giá trị của một biến khác? Tôi có đang trỏ đến một con trỏ hay trỏ đến con trỏ của toán hạng bên phải không? Làm var myExistingVar = {"blah", 42}; var obj = myExistingVar; kết quả là obj chỉ đến {"blah", 42}, hoặc để myExistingVar? - Michael Hoffmann
@MichaelHoffmann Những điều này xứng đáng với các câu hỏi SO của riêng họ và có lẽ đã được trả lời tốt hơn tôi có thể quản lý. Điều đó đang được nói, 1) Tôi chạy một hồ sơ bộ nhớ trong các công cụ dev trình duyệt cho một chức năng vòng lặp như một trong những bạn mô tả và thấy gai trong việc sử dụng bộ nhớ trong suốt quá trình lặp. Điều này dường như chỉ ra rằng các đối tượng giống hệt nhau mới thực sự được tạo ra trong mỗi lần lặp của vòng lặp. Khi các đột biến đột ngột rơi xuống, bộ thu gom rác chỉ làm sạch một nhóm các vật không sử dụng này. - geg
@MichaelHoffmann 2) Về một cái gì đó như var a = b, javascript không cung cấp một cơ chế để sử dụng con trỏ và do đó, một biến không bao giờ có thể trỏ đến một con trỏ (như bạn có thể trong C), mặc dù động cơ javascript bên dưới chắc chắn sử dụng chúng. Vì thế...var a = b sẽ chỉ a "tới điểm của toán hạng bên phải" - geg
Tôi đã đặt câu hỏi số 1 ở đây (đặc biệt về Chrome vì việc triển khai có thể khác nhau trong mọi trình duyệt) stackoverflow.com/q/42778439/539997 và tôi vẫn đang cố gắng suy nghĩ về cách đặt câu hỏi # 2. Bất kỳ trợ giúp được đánh giá cao. - Michael Hoffmann
@CPerkins Điểm công bằng. Tôi đã cố làm rõ. - geg


Đối tượng bên ngoài một hàm được truyền vào một hàm bằng cách đưa ra một tham chiếu đến bên ngoài obejct. Khi bạn sử dụng tham chiếu đó để thao tác đối tượng của nó, đối tượng bên ngoài sẽ bị ảnh hưởng. Tuy nhiên, nếu bên trong hàm bạn quyết định trỏ tham chiếu đến một cái gì đó khác, bạn không ảnh hưởng đến đối tượng bên ngoài chút nào, bởi vì tất cả những gì bạn đã làm là chuyển hướng lại tham chiếu đến cái gì khác.


23
2018-02-13 11:00





Hãy nghĩ về nó như thế này: Nó luôn luôn đi qua giá trị. Tuy nhiên, giá trị của một đối tượng không phải là đối tượng, mà là một tham chiếu đến đối tượng đó.

Đây là một ví dụ, truyền một số (một kiểu nguyên thủy)

function changePrimitive(val) {
    // At this point there are two '10's in memory.
    // Changing one won't affect the other
    val = val * 10;
}
var x = 10;
changePrimitive(x);
// x === 10

Lặp lại điều này với một đối tượng mang lại kết quả khác nhau:

function changeObject(obj) {
    // At this point there are two references (x and obj) in memory,
    // but these both point to the same object.
    // changing the object will change the underlying object that
    // x and obj both hold a reference to.
    obj.val = obj.val * 10;
}
var x = { val: 10 };
changeObject(x);
// x === { val: 100 }

Một ví dụ khác:

function changeObject(obj) {
    // Again there are two references (x and obj) in memory,
    // these both point to the same object.
    // now we create a completely new object and assign it.
    // obj's reference now points to the new object.
    // x's reference doesn't change.
    obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
// x === { val: 10}

16
2018-02-05 21:49





Javascript luôn là giá trị vượt qua, mọi thứ đều thuộc loại giá trị. Các đối tượng là các giá trị, các hàm thành viên của các đối tượng là các giá trị của chính chúng (hãy nhớ rằng các hàm là các đối tượng hạng nhất trong Javascript). Ngoài ra, về khái niệm rằng mọi thứ trong Javascript là một vật, cái này sai. Các chuỗi, ký hiệu, số, boolean, null và undefined là nguyên thủy. Thỉnh thoảng họ có thể tận dụng một số chức năng và tính chất của thành viên được thừa kế từ nguyên mẫu cơ sở của họ, nhưng điều này chỉ để thuận tiện, không có nghĩa là chúng là những vật thể. Hãy thử những điều sau đây để tham khảo

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);

Trong cả hai cảnh báo, bạn sẽ thấy giá trị không được xác định.


14
2017-12-30 02:20



-1, nó không phải luôn luôn đi qua giá trị. Từ MDC: "Nếu bạn truyền một đối tượng (tức là một giá trị không nguyên thủy, chẳng hạn như Mảng hoặc đối tượng do người dùng xác định) làm tham số, tham chiếu đến đối tượng được chuyển đến hàm." - Nick
@Nick: Nó luôn luôn đi qua giá trị. Giai đoạn. Một tham chiếu đến đối tượng được truyền theo giá trị cho hàm. Điều đó không đi qua tham chiếu. "Vượt qua tham chiếu" gần như có thể được coi như là truyền biến đó, thay vì giá trị của nó; bất kì thay đổi hàm làm cho đối số (bao gồm thay thế nó bằng một đối tượng khác hoàn toàn!) sẽ được phản ánh trong trình gọi. Đó là bit cuối cùng là không thể trong JS, bởi vì JS không chuyển qua tham chiếu - nó chuyển các tham chiếu theo giá trị. Sự khác biệt là tinh tế, nhưng khá quan trọng để hiểu được những hạn chế của nó. - cHao
Đối với những người xếp chồng trong tương lai ... Về tham chiếu này của bạn: x = "teste"; x.foo = 12; vv Chỉ vì một tài sản không liên tục, nó không có nghĩa là nó không phải là một đối tượng. Như MDN nói: Trong JavaScript, hầu hết mọi thứ đều là một đối tượng. Tất cả các kiểu nguyên thủy ngoại trừ null và undefined đều được coi là đối tượng. Chúng có thể được gán các thuộc tính (các đặc tính được gán của một số kiểu không liên tục) và chúng có tất cả các đặc tính của các đối tượng.  liên kết - slacktracer
MDN là một wiki do người dùng chỉnh sửa và nó sai ở đó. Tham chiếu chuẩn là ECMA-262. Xem S. 8 "Kiểu Đặc tả Tham chiếu", giải thích cách tham chiếu được giải quyết, và cũng là 8.12.5 "[[Đặt]]", được sử dụng để giải thích AssignmentExpression thành Tham chiếu, và đối với đối tượng 9.9 ToObject. Đối với các giá trị nguyên thủy, Michael đã giải thích những gì ToObject thực hiện, như trong đặc tả. Nhưng xem thêm s. 4.3.2 giá trị nguyên thủy. - Garrett
@ WonderLand: Không, anh ấy không. Những người chưa bao giờ có thể vượt qua bằng cách tham chiếu có thể không bao giờ hiểu được sự khác biệt giữa việc truyền tham chiếu và chuyển một tham chiếu theo giá trị. Nhưng họ ở đó, và họ quan trọng. Tôi không quan tâm đến việc hiểu sai mọi người chỉ vì điều đó nghe có vẻ dễ dàng hơn. - cHao