Con trỏ trong JavaScript?


80

Chúng ta có thể chuyển một tham chiếu của một biến không thay đổi làm đối số trong một hàm không?

Thí dụ:

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

Có thể cho rằng đây là phong cách không tốt vì bạn đang có ý định sử dụng tác dụng phụ của một chức năng để gây đột biến. Điều này có thể làm cho mã của bạn khó theo dõi hơn.
speechx

Câu trả lời:


139

Vì JavaScript không hỗ trợ truyền tham số bằng tham chiếu, thay vào đó, bạn sẽ cần đặt biến thành một đối tượng:

var x = {Value: 0};

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

a(x);
document.write(x.Value); //Here i want to have 1 instead of 0

Trong trường hợp này, xlà một tham chiếu đến một đối tượng. Khi xđược chuyển đến hàm a, tham chiếu đó sẽ được sao chép sang obj. Do đó, objxđề cập đến điều tương tự trong bộ nhớ. Thay đổi Valuetài sản của objảnh hưởng đến Valuetài sản của x.

Javascript sẽ luôn chuyển các tham số của hàm theo giá trị. Đó chỉ đơn giản là một đặc tả của ngôn ngữ. Bạn có thể tạo xtrong một phạm vi cục bộ cho cả hai hàm và không chuyển biến nào cả.


Có cách nào để làm điều đó để người khác sẽ làm không: var x = 0; a (x); và rằng hàm thiết lập một đối tượng với giá trị của x? (hoặc cái gì đó tương tự)
user1342369

6
Này, cảm ơn bạn đã giới thiệu cho tôi các đoạn mã trên SO . Tôi đã không nghe nói về chúng trước khi tôi thấy nó được sử dụng trong câu trả lời của bạn.
Joeytje50

@ Joeytje50 - Đúng vậy, tính năng này vừa được bổ sung vào tháng 9. Rất tiện dụng!
Mike Christensen

Điều gì sẽ xảy ra nếu bạn không biết tên cho thuộc tính obj, chẳng hạn nếu nó là obj.name thì lần sau bạn muốn cập nhật obj.surname? Làm thế nào bạn có thể làm cho một chức năng thích ứng với loại linh hoạt đó?
Ngài

1
@Dave đây có thể là một câu trả lời muộn nhưng bạn có thể làm cho hàm của mình mong đợi 3 tham số, obj, tên thuộc tính và giá trị. Hãy nhớ rằng bạn có thể truy cập thuộc tính của một đối tượng bằng cách thực hiện obj.propertyname hoặc obj ['propertyname'], đây sẽ là cách bạn muốn khi bạn muốn có sự linh hoạt. Chuyển 3 tham số và do obj ['propertyname'] = value và nó sẽ có thể sử dụng lại được.
Carlos Calla

19

Câu hỏi này có thể hữu ích: Làm thế nào để chuyển biến bằng tham chiếu trong javascript? Đọc dữ liệu từ hàm ActiveX trả về nhiều giá trị

Tóm lại, các kiểu nguyên thủy của Javascript luôn được truyền theo giá trị, trong khi các giá trị bên trong các đối tượng được chuyển bằng tham chiếu (cảm ơn những người bình luận đã chỉ ra sự giám sát của tôi). Vì vậy, để làm tròn điều này, bạn phải đặt số nguyên của mình bên trong một đối tượng:

var myobj = {x:0};

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

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

  


2
Các biến đối tượng cũng luôn được truyền bởi giá trị - chỉ là giá trị là một tham chiếu đến một đối tượng. Có sự khác biệt giữa chuyển theo tham chiếuchuyển tham chiếu theo giá trị . Cụ thể, khi bạn chuyển xtheo giá trị, cho dù xlà một đối tượng hay không, bạn không thể thay thế nó bằng một giá trị hoàn toàn khác theo bất kỳ cách nào mà người gọi sẽ nhìn thấy.
cHao

trong khi các đối tượng được truyền bằng tham chiếu đơn giản là không đúng. Không có gì trong JavaScript được chuyển qua tham chiếu.
Mike Christensen,

Nếu bạn vượt qua một loại linh trưởng theo giá trị (được tham chiếu đến bởi x), tại sao x không thay đổi sau, trừ khi bạn vượt qua các đối tượng
SuperUberDuper

14

Tôi đã tìm thấy một cách hơi khác để triển khai con trỏ có lẽ tổng quát hơn và dễ hiểu hơn từ góc độ C (và do đó phù hợp hơn với định dạng của ví dụ người dùng).

Trong JavaScript, giống như trong C, các biến mảng thực sự chỉ là con trỏ đến mảng, vì vậy bạn có thể sử dụng một mảng giống hệt như khai báo một con trỏ. Bằng cách này, tất cả các con trỏ trong mã của bạn có thể được sử dụng theo cùng một cách, mặc dù bạn đã đặt tên biến trong đối tượng gốc là gì.

Nó cũng cho phép người ta sử dụng hai ký hiệu khác nhau đề cập đến địa chỉ của con trỏ và địa chỉ của con trỏ.

Đây là một ví dụ (tôi sử dụng dấu gạch dưới để biểu thị một con trỏ):

var _x = [ 10 ];

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

foo(_x);

console.log(_x[0]);

Hoa lợi

output: 20

8

Bạn tham khảo 'x' từ đối tượng cửa sổ

var x = 0;

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

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

3

Câu trả lời muộn nhưng tôi đã bắt gặp một cách truyền các giá trị nguyên thủy bằng cách tham chiếu bằng cách đóng. Nó khá phức tạp để tạo một con trỏ, nhưng nó hoạt động.

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

Trong ES6, mã có thể được rút ngắn để sử dụng các biểu thức lambda:

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

helloWorld(myNamePtr); // joe, jack
console.log(myName); // jack

1

Trong JavaScript, đó sẽ là một toàn cầu. Tuy nhiên, hàm của bạn sẽ trông giống như sau:

function a(){
   x++;
};

xlà trong ngữ cảnh toàn cục, bạn không cần phải truyền nó vào hàm.


1
Điểm hợp lệ, mặc dù tôi đã chỉ ra rằng biến không cần phải là toàn cục (và nhiều người sẽ khuyên bạn không nên làm điều này bao giờ), nó chỉ cần ở cùng phạm vi với người gọi và người gọi.
Mike Christensen

1

Javascript chỉ nên đưa con trỏ vào hỗn hợp vì nó giải quyết được rất nhiều vấn đề. Nó có nghĩa là mã có thể tham chiếu đến một tên biến không xác định hoặc các biến đã được tạo động. Nó cũng làm cho việc viết và viết mã mô-đun trở nên dễ dàng.

Đây là những gì tôi thấy là gần nhất bạn có thể đến với con trỏ c trong thực tế

trong 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

trong 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

Có thể là không thể vì JavaScript không có toán tử "\" của Perl để lấy một nguyên thủy bằng cách tham chiếu, nhưng có một cách để tạo một đối tượng "con trỏ một cách hiệu quả" cho một nguyên thủy bằng cách sử dụng mẫu này.

Giải pháp này có ý nghĩa nhất khi bạn đã có bản gốc (vì vậy bạn không thể đặt nó vào một đối tượng nữa mà không cần sửa đổi mã khác), nhưng vẫn cần chuyển một con trỏ tới nó để các phần khác của mã của bạn có thể sửa đổi. với trạng thái của nó; vì vậy bạn vẫn có thể điều chỉnh trạng thái của nó bằng cách sử dụng proxy liền mạch hoạt động như một con trỏ.

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

Trong JavaScript, bạn không thể chuyển các biến bằng cách tham chiếu đến một hàm. Tuy nhiên, bạn có thể chuyển một đối tượng bằng cách tham chiếu.


3
Đơn giản là không đúng. Bạn không thể chuyển bất cứ thứ gì bằng tham chiếu trong JavaScript.
Mike Christensen,

1
Tham chiếu đối tượng được chuyển dưới dạng giá trị, vì vậy mọi thứ được chuyển dưới dạng giá trị.
Carlos Calla

quan sát thú vị; dường như KHÔNG THỂ tạo ra một tham chiếu đến một nguyên thủy hiện có trong JavaScript; một khi một nguyên thủy bị ràng buộc, bất kỳ thay đổi nào đối với nguyên thủy này không bao giờ có thể được nhận thấy bởi các tham chiếu ngây thơ của nó bởi vì các tham chiếu chỉ đến giá trị mà biến, vốn dĩ là biến nguyên thủy; vì vậy cách duy nhất để "trỏ" đến một nguyên thủy là nếu nguyên thủy được bao bọc trong một biến có kiểu là "Đối tượng"; vì không có loại nào khác có thể lưu trữ tham chiếu. TLDR; không có toán tử Perl "\" trong JavaScript; nó là một tham chiếu để bắt đầu, hoặc bạn không bao giờ có thể chỉ vào nó.
Dmitry

Phần khó chịu của điều này là khi bạn muốn tạo một túi trạng thái được chia sẻ, ví dụ "window.shared" như một bản đồ tham chiếu đến các biến hiện đang là null / undefined mà bạn sẽ đặt sau; trong trường hợp này, khi bạn đặt các giá trị của trạng thái hiện là null / undefined, biến "window.shared" sẽ không nhận ra các thay đổi, ngay cả khi chúng được lưu trữ trong kiểu Object, đã quá muộn; các giá trị "null / undefined" phải ở bên trong kiểu Object để bắt đầu nó hoạt động, ví dụ [null] [undefined].
Dmitry

Điều đó nói rằng, bạn có thể thông minh và đặt các hàm trong một biến toàn cục như vậy, có một getter và setter cho mỗi biến chưa được đặt này và các hàm này SẼ động tìm trạng thái HIỆN TẠI của biến nguyên thủy và lấy / đặt nó, nhưng nó không thể trả về một con trỏ cho biến đó (nhưng có thể trả về một proxy có thể hoạt động hiệu quả theo cùng một cách với biến đó và thay đổi nó một cách gián tiếp, sử dụng getters / setters).
Dmitry

0

Tôi nghĩ rằng trái ngược với C hoặc bất kỳ ngôn ngữ nào có con trỏ:

/** Javascript **/
var o = {x:10,y:20};
var o2 = {z:50,w:200};
  • rõ ràng trong javascript bạn không thể truy cập vào các đối tượng ođịa chỉ o2 trong bộ nhớ
  • nhưng bạn cũng không thể so sánh địa chỉ của họ: (không có khả năng nhỏ để sắp xếp chúng và sau đó truy cập bằng cách phân đôi )

.

o == o2 // obviously false : not the same address in memory
o <= o2 // true !
o >= o2 // also true !!

đó là một vấn đề lớn:

  • nó có nghĩa là bạn có thể liệt kê / quản lý mọi đối tượng được tạo (cấp phát) bởi ứng dụng của bạn,

  • từ danh sách các đối tượng khổng lồ đó , hãy tính toán một số thông tin về chúng (ví dụ: cách chúng được liên kết với nhau)

  • nhưng khi bạn muốn truy xuất thông tin bạn đã tạo về một đối tượng cụ thể, bạn không thể tìm thấy nó trong danh sách khổng lồ của mình bằng cách phân đôi: không có số nhận dạng duy nhất cho mỗi đối tượng có thể được sử dụng thay thế địa chỉ bộ nhớ thực

điều này cuối cùng có nghĩa rằng đây là một vấn đề lớn, nếu bạn muốn viết bằng javascript:

  • một javascript debugger / IDE
  • hoặc một bộ thu gom rác được tối ưu hóa đặc biệt
  • một cấu trúc dữ liệu ngăn kéo / phân tích

0

JavaScript không hỗ trợ chuyển các kiểu nguyên thủy bằng tham chiếu. Tuy nhiên, có một cách giải quyết.

Đặt tất cả các biến bạn cần truyền vào một đối tượng. Trong trường hợp này, chỉ có một biến x,. Thay vì truyền biến, hãy chuyển tên biến dưới dạng một chuỗi "x".

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


0

Tùy thuộc vào những gì bạn muốn làm, bạn có thể chỉ cần lưu tên biến, và sau đó truy cập nó sau như sau:

function toAccessMyVariable(variableName){
  alert(window[variableName]);
}

var myFavoriteNumber = 6; 

toAccessMyVariable("myFavoriteNumber");

Để áp dụng cho ví dụ cụ thể của bạn, bạn có thể làm như sau:

var x = 0;
var pointerToX = "x";

function a(variableName)
{
    window[variableName]++;
}
a(pointerToX);
alert(x); //Here I want to have 1 instead of 0

Điều này chỉ hoạt động trong môi trường trình duyệt và dành riêng cho windowđối tượng. Ngoài ra, ít nên sử dụng các biến toàn cục, vì có thể đã có một biến có cùng tên.
JayJay

0

Nó chỉ không hỗ trợ con trỏ, kết thúc câu chuyện :-(.

Tôi muốn nói thêm rằng, mặc dù tôi là thương hiệu mới và đến từ nền tảng C, từ những gì tôi đang đọc về vòng lặp và ngăn xếp sự kiện, bạn nên giữ bất kỳ đối tượng nào càng gần đối tượng gọi của nó càng tốt. Vì tôi là thương hiệu mới, tôi có thể mắc sai lầm này.

Rất nhiều câu trả lời ở đây gợi ý để làm cho nó trở nên toàn cầu, nhưng nếu tôi đọc đúng, điều đó có thể làm tăng các bước nhảy ngăn xếp và theo một cách nào đó, có thể phân mảnh vòng lặp sự kiện của bạn một cách không cần thiết (tùy thuộc vào những gì đối tượng được gọi hiện đang làm).

Ngoài ra, bất kỳ ví dụ nào được đưa ra bắt chước con trỏ, chỉ là 1 trong nhiều cách để làm điều tương tự, sẽ không phải là một con trỏ.


-1

Trong ví dụ của bạn, bạn thực sự có 2 biến có cùng tên. Biến (toàn cục) x và biến phạm vi hàm x. Thật thú vị khi thấy rằng javascript, khi được lựa chọn phải làm gì với 2 biến cùng tên, đi với tên trong phạm vi hàm và bỏ qua biến ngoài phạm vi.

Có lẽ không an toàn khi cho rằng javascript sẽ luôn hoạt động theo cách này ...

Chúc mừng!


Câu trả lời này không thêm bất kỳ giá trị nào cho bài đăng này. Câu trả lời của @Mike Christensen chi tiết hơn và đã được chấp nhận.
Alexander

-1

Không, bạn không thể. Trình duyệt không cung cấp cho bạn quyền truy cập vào bộ nhớ hệ thống và các biến thể của nó. Nó có thể thực hiện được với một envirovment như nút, nhưng bên trong trình duyệt thì điều đó không hoàn toàn khả thi trừ khi bạn là một hacker.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.