+2

Tham chiếu và tham trị trong JavaScript.

Mayfest2023

Xin chào tất cả mọi người, hôm nay mình sẽ chia sẻ với mọi người một chủ đề rất cơ bản trong lập trình là tham trị và tham chiếu trong javascript. Tham trị, tham chiếu liên quan đến cách mà một biến trong js được lưu như thế nào?

Tham trị (value type)

Tham trị rất đơn giản nó sẽ lưu dạng giá trị kiểu gì đó, đối với những kiểu dữ liệu kiểu như là : number, string, boolean, null, undefined thì nó sẽ được lưu trực tiếp giá trị xuống vùng nhớ luôn. Khi gán giá trị tham trị cho một biến, một bản sao của giá trị được tạo ra và được lưu trữ trong biến đích. Khi giá trị của biến gốc thay đổi sau đó, giá trị của biến đích không bị ảnh hưởng. Điều này cho phép ta làm việc với các biến độc lập và an toàn hơn trong quá trình xử lý dữ liệu.

Ví dụ

    const = 10;
    // | 10                      |
    // | 'text'                  |
    // |  true, flase            |   => lưu luôn giá trị, ví dụ 10, string 'text'
    // |                         |
    // |-------------------------|
    //tham trị - stored as value type
    let a = 5;
    let b = a;
    a = 10;
    console.log(b);
    // Out put
    // 5
  • Giải thích : Biến a = 5, b = a thì lúc này bản chất b lưu giá trị trực tiếp b = 5, việc thay đổi thằng a không liên quan gì đến biến b, vì b chỉ giữ giá trị là 5.
   //truyền tham số dạng tham trị - pass by value
   function showNumber(number) {
       number = 10;
   }
   
   const a = 5;
   showNumber(a);
   console.log(a);
   // Out put
   // 5
  • Giải thích : javascript sẽ tạo ra một biến a1 = a, khi truyền vào hàm showNumber là truyền a1 và biến a và a1 không liên quan gì với nhau. Nên mặc dù đã đổi giá trị của a trong hàm nhưng kết quả in ra biến a vẫn là 5.

Tham trị (Refenrence type)

Trong JavaScript, tham trị (reference type) là các kiểu dữ liệu mà khi ta gán chúng cho một biến, thì không phải là sao chép giá trị mà chỉ tham chiếu đến vùng nhớ chứa giá trị đó. Điều này có nghĩa là khi ta thay đổi giá trị của một biến tham chiếu, thì tất cả các biến khác tham chiếu đến cùng vùng nhớ đó sẽ bị ảnh hưởng.

Ví dụ

    // | object,array               |           | KHO CHỨA 1E2F              |
    // | const a = {name: 'viblo'}  |           |  {name : 'viblo'}          |
    // | thực chât a = 1E2F         |   =>      |                            |
    // |                            |           |                            |
    // |----------------------------|           |----------------------------|
  • Trong ví dụ trên thực chất a lưu là 1E2F là kho chứa địa chỉ của object đó, như vậy đối với những thằng là object, array nó sẽ tạo một vùng nó trên RAM có địa chỉ nhất định và lưu trữ giá trị đó cho mình, có thể là object, array hay một kiểu dữ liệu phức tạp nào khác và biến a chỉ có có địa chỉ của vùng nhớ đó thôi 😃
  • Một điểm lưu ý với tham chiếu các phép gán với dạng object, array thì tương đương với coppy địa chỉ.
    const b = a;
// b cũng trỏ về địa chỉ 1E2F
  • Vì vậy nếu lỡ như a thay đổi giá trị thì b cũng thay đổi do cùng một địa chỉ vùng nhớ.
    //tham chiếu - stored as reference type
    const a = {name: 'viblo'};
    const b = a;
    a.name = 'change';
    console.log(b.name);
    // Out put
    // change
    
    - Giải thích: Lúc này biến a là một object -> là dạng tham chiếu, b = a tức là biến b chỉ coppy địa chỉ (ví dụ : 1xxx). Khi biến a thay đổi thì biến b thay đổi theo. Lúc này b.name = 'change'
     
    //truyền tham số dạng tham chiếu - pass by reference (truyền tham số dạng object hoặc array)
    function showObject(obj) {
        obj.name = 'change';
    }
    
    const a = {name: 'viblo'};
    showObject(a);
    console.log(a.name);
    // Out put
    // change
  • Giải thích : Javacripts sẽ tạo ra một biến a1 = a cùng trỏ về cùng một địa chỉ, sau đó sẽ pass a1 vào trong hàm . Vì vậy khi a1 thay đổi sẽ cũng ảnh hưởng đến thằng a, mặc dù biến a nằm ở ngoài hàm. Do đó chúng ta phải rất cẩn thận khi truyền tham chiếu vào một hàm.

Làm sao để không bị dính tham chiếu

  • Cách clone array
//Sử dụng spread operator:
const originalArray = [1, 2, 3];
const clonedArray = [...originalArray];
//Sử dụng the slice() method:
const originalArray = [1, 2, 3];
const clonedArray = [...originalArray];
//Sử dụng the Array.from() method:
const originalArray = [1, 2, 3];
const clonedArray = Array.from(originalArray);
//Sử dụng concat() method:
const originalArray = [1, 2, 3];
const clonedArray = originalArray.concat([]);
//Sử dụng Array.prototype.map() method:
const originalArray = [1, 2, 3];
const clonedArray = originalArray.map(item => item);
  • Cách clone object
//Sử dụng spread operator:
const originalObject = { name: 'John', age: 25 };
const clonedObject = { ...originalObject };
//Sử dụng Object.assign() method:
const originalObject = { name: 'John', age: 25 };
const clonedObject = Object.assign({}, originalObject);
//Sử dụng JSON.parse() và JSON.stringify() methods:
const originalObject = { name: 'John', age: 25 };
const clonedObject = JSON.parse(JSON.stringify(originalObject));
//Sử dụng một custom function:
function cloneObject(obj) {
const clonedObj = {};
for (let key in obj) {
 if (obj.hasOwnProperty(key)) {
   clonedObj[key] = obj[key];
 }
}
return clonedObj;
}

const originalObject = { name: 'John', age: 25 };
const clonedObject = cloneObject(originalObject);

Có liên quan gì đến tới ReactJs, Redux không ?

  • Vấn đề : Trong React, một lỗi phổ biến xảy ra khi ta thay đổi giá trị tham chiếu trực tiếp thay vì tạo một bản sao mới. Điều này có thể dẫn đến hành vi không mong đợi và lỗi trong ứng dụng của bạn. Vấn đề xảy ra khi các thay đổi trong giá trị tham chiếu không được React phát hiện, dẫn đến việc không kích hoạt việc render lại của component như mong đợi. Để sửa lỗi này, quan trọng để tránh việc thay đổi giá trị tham chiếu trực tiếp và thay vào đó tạo một bản sao mới. Bằng cách tạo một bản sao mới, React có thể phát hiện chính xác các thay đổi và kích hoạt việc render lại cần thiết. Dưới đây là một đoạn mã ví dụ mô tả vấn đề này:
  //Ví dụ
import React, { useState } from 'react';

function Counter() {
const [count, setCount] = useState(0);
const referenceArray = [1, 2, 3];

const handleClick = () => {
  // Cách không đúng: Thay đổi giá trị tham chiếu trực tiếp
  referenceArray.push(count + 1);

  setCount(count + 1);
};

return (
  <div>
    <p>Count: {count}</p>
    <button onClick={handleClick}>Increment</button>
  </div>
);
}

export default Counter;

-Giải thích: Trong ví dụ trên, hàm handleClick được gọi khi nút được nhấn. Nó cố gắng tăng giá trị của count bằng cách đẩy một giá trị mới (count + 1) vào referenceArray trực tiếp. Tuy nhiên, điều này không đúng vì React không phát hiện thay đổi trong mảng tham chiếu và không kích hoạt việc render lại lại của component.

  • Để sửa lỗi này, bạn nên tránh việc thay đổi giá trị tham chiếu trực tiếp và tạo một bản sao mới thay thế. Dưới đây là đoạn mã đã được sửa lỗi:
  //Ví dụ
import React, { useState } from 'react';

function Counter() {
const [count, setCount] = useState(0);
const [referenceArray, setReferenceArray] = useState([1, 2, 3]);

const handleClick = () => {
  // Cách đúng: Tạo một bản sao mới của mảng
  const newArray = [...referenceArray, count + 1];

  setReferenceArray(newArray);
  setCount(count + 1);
};

return (
  <div>
    <p>Count: {count}</p>
    <button onClick={handleClick}>Increment</button>
  </div>
);
}

export default Counter;

Tóm lại

  • Tham trị chứa giá trị.
  • Tham chiếu chứa địa chỉ (object, array).
  • Nhớ clone object mới khi thay đổi props/state trong ReactJS/ Redux.👍

** CẢM ƠN CÁC BẠN ĐÃ ĐỌC BÀI VIẾT .**😘


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí