Có vẻ như việc sử dụng chuỗi để phân biệt mọi thứ là điều tự nhiên. Rất có thể trong cơ sở mã của bạn, có những đối tượng có thuộc tính
Đến một thời điểm nào đó, dự án của bạn phát triển (về quy mô, tầm quan trọng, mức độ phổ biến hoặc tất cả cùng một lúc). Dự án cần nhiều chuỗi hơn vì có nhiều thứ cần phân biệt với nhau hơn. Các chuỗi dài ra, cũng như chi phí cho lỗi đánh máy hoặc, chẳng hạn, quy ước đặt tên nhãn của bạn thay đổi. Bây giờ, bạn phải tìm tất cả các trường hợp của những chuỗi đó và thay thế chúng. Do đó, một cam kết cho thay đổi đó trở nên lớn hơn nhiều so với mức cần thiết. Điều đó khiến bạn trông đẹp hơn trong mắt những người không biết gì. Đồng thời, nó khiến cuộc sống của bạn trở nên khốn khổ vì giờ đây khó tìm ra nguyên nhân gây ra sự hồi quy trong lịch sử git của bạn hơn nhiều.
Chuỗi không tốt cho việc nhận dạng. Bạn phải cân nhắc đến tính duy nhất và lỗi đánh máy; trình soạn thảo hoặc IDE của bạn sẽ không kiểm tra xem đó có phải là chuỗi bạn muốn không. Thật tệ. Tôi nghe ai đó nói, "Chỉ cần đưa chúng vào một biến, duh." Đó là một gợi ý hay và nó giải quyết một số mối quan tâm của tôi. Nhưng hãy xem John Smith:
John tình cờ chia sẻ tên với một công ty. Nếu tôi nói với bạn rằng tôi có giải pháp tốt hơn thì sao? Giải pháp loại bỏ mọi mối quan tâm và tăng thêm giá trị — cho phép bạn đạt được nhiều hơn. Bạn sẽ nói gì? Vâng, tôi sẽ không viết lại bài viết chỉ vì câu trả lời của bạn không phù hợp với câu chuyện của tôi. Câu trả lời là các đối tượng. Bạn sử dụng chính các đối tượng để tìm ra xem một đối tượng có phải là đối tượng bạn đang tìm kiếm hay không.
Điều này giúp mục đích rõ ràng hơn. Để tôi đưa cho bạn một ví dụ hay hơn. Giả sử bạn có nhãn trong ứng dụng của mình. Chúng được bản địa hóa, vì vậy chuỗi nhãn được xác định bởi thư viện bản địa hóa mà bạn đang sử dụng và quy trình dịch của nhóm bạn. Bạn lưu các nhãn của mình trong một mô-đun nơi bạn sắp xếp và quản lý tất cả chúng một cách gọn gàng. Khi bạn cần làm điều gì đó đặc biệt cho một số nhãn nhất định, bạn có thể so sánh trực tiếp với nhãn bạn đã có.
Bạn thấy tôi có thể làm được nhiều hơn thế nào với các đối tượng không? Trong mô-đun
Nhưng đó chỉ là một phần của bức tranh. Tôi biết chúng ta sử dụng các đối tượng ở khắp mọi nơi và việc nhóm các thứ lại với nhau không phải là điều gì mới mẻ trong đó. Nhưng tôi cá là bạn không sử dụng chúng chính xác như vậy. Tôi hiếm khi thấy hai đối tượng được so sánh như vậy vì bạn không bao giờ biết có gì trong đó hoặc nó đến từ đâu. Đối tượng được tạo và thay đổi mọi lúc. Chúng có nhiều khả năng được so sánh theo giá trị của thuộc tính hơn là bản thân đối tượng. Và lý do cho điều đó là đối tượng không phù hợp với loại sử dụng đó. Chúng quá có khả năng. Để cho phép trường hợp sử dụng đó và nhiều trường hợp khác, một mặt, chúng ta phải giảm một số khả năng của đối tượng và mặt khác, triển khai thêm một số khả năng khác. Và cuối cùng, chúng ta sẽ có cái mà tôi gọi là Đối tượng nguyên thủy. Th… một giải pháp cho tất cả… một số vấn đề.
Trong phần đầu tiên của loạt bài này, tôi muốn đề cập đến một số khía cạnh của JavaScript giúp đưa đối tượng đến gần hơn với các giá trị nguyên thủy, đổi lại, chúng ta sẽ được hưởng lợi từ các tính năng ngôn ngữ chung thường không liên quan đến đối tượng, như phép so sánh và toán tử số học. Trong phần sau, chúng ta sẽ xem xét kỹ các ví dụ thực tế và công cụ để làm việc với các đối tượng như vậy. Bây giờ chúng ta hãy xem các đối tượng trong JavaScript trông như thế nào.
Đối tượng là một trong những điều cơ bản trong JavaScript. Bạn có thể đã nghe nói rằng trong JavaScript, mọi thứ đều là đối tượng. Điều đó khá đúng. Ngoại trừ một số giá trị dưới cùng, tất cả các nguyên hàm đều là đối tượng. Mặc dù về mặt kỹ thuật, điều này có nhiều sắc thái hơn, nhưng xét theo góc độ mã của chúng ta, thì điều đó là đúng. Trên thực tế, điều đó đủ đúng để tin rằng mọi thứ đều là đối tượng có thể là một mô hình tinh thần hữu ích. Nhưng trước tiên, hãy cố gắng hiểu điều gì đang xảy ra với phép so sánh đối tượng với đối tượng đã từng rất buồn cười đối với tôi khi còn trẻ.
Cú pháp đối tượng theo nghĩa đen được sử dụng để tạo đối tượng mới. Nó cho phép chúng ta khai báo và khởi tạo một đối tượng trong một biểu thức duy nhất.
Sạch hơn nhiều, phải không? Nhưng giờ tôi nghĩ việc thiếu dòng khởi tạo đối tượng là điều khiến tôi bối rối về hai biểu thức bằng nhau đối tượng rỗng đó. Có vẻ như nó cho thấy ngôn ngữ đang đấu tranh để nhận ra sự bằng nhau rõ ràng. Nhưng điều thực sự xảy ra trong biểu thức đó là thế này:
Bây giờ thì rõ ràng là chúng không bằng nhau. Bạn đang so sánh hai đối tượng riêng biệt mà bạn vừa tạo. Mong đợi ngược lại cũng giống như mong đợi
Chúng ta hãy kiểm tra tính hợp lý. Liệu hai biến tham chiếu đến cùng một đối tượng có được coi là bằng nhau không?
Trong trường hợp này, chỉ có dòng đầu tiên có biểu thức tạo ra một đối tượng. Ở dòng thứ hai, chúng ta làm cho biến
Tại sao điều này lại quan trọng? Bởi vì nó cung cấp cho chúng ta một cách để kiểm tra xem một biến có tham chiếu đến một đối tượng mà chúng ta đang tìm kiếm hay không. Và nếu chúng ta nghĩ về nó trong bối cảnh "mọi thứ đều là một đối tượng", thì đó là cách các số và chuỗi hoạt động. Khi bạn so sánh hai biến chứa chuỗi, công cụ không cần phải kiểm tra xem từng ký tự trong các chuỗi đó có giống nhau không. Chỉ cần so sánh xem các biến có tham chiếu đến cùng một đối tượng hay không. Đó là nhờ vào sự khác biệt quan trọng nhất giữa các đối tượng thông thường và các giá trị nguyên thủy — tính bất biến.
Hãy xem xét kỹ cách các số hoạt động. Bạn có thể lấy sáu trong số năm bằng cách tăng nó lên một, nhưng nó không thay đổi bất cứ điều gì về năm.
Một số người có thể nói rằng sử dụng
Năm vẫn là năm. Đó là vì
Chúng ta có thể cho rằng cách duy nhất để thay đổi các giá trị nguyên thủy trong JavaScript là thông qua phép gán, nghĩa là những gì chúng ta thực sự thay đổi chính là những gì một biến tham chiếu đến. Vậy thì đó là các biến đang thay đổi, chứ không phải giá trị. Ít nhất thì không phải các biến nguyên thủy. Nhưng thay vào đó, nó hoạt động như thế nào với các đối tượng?
Sau khi khởi tạo một đối tượng, bạn có thể thay đổi các thuộc tính của nó: xóa chúng, thêm các thuộc tính mới và gán lại các thuộc tính cũ. Chúng ta đều quen thuộc với việc làm như vậy. Nhưng ngoài ra, nó hoạt động giống như các giá trị nguyên thủy. Trên thực tế, nếu bạn quen với một mô hình mà các đối tượng và giá trị nguyên thủy là những thứ giống nhau, bạn sẽ nhìn nhận khác nhau về mọi loại vấn đề trong JavaScript.
Có lẽ bạn đã tình cờ gặp phải một câu hỏi về cách các biến được truyền vào một hàm. Mọi người hỏi liệu các biến được truyền theo giá trị hay theo tham chiếu. Một câu trả lời phổ biến là các giá trị nguyên thủy được truyền theo giá trị trong khi các đối tượng được truyền theo tham chiếu. Nhưng với mô hình tinh thần mà tôi đang áp đặt cho bạn ở đây, bạn có thể đã biết những gì tôi sẽ nói về điều đó. Trước đó, hãy để tôi chỉ cho bạn cách câu hỏi này không có nhiều ý nghĩa trong JavaScript. Tôi cũng sẽ tiết lộ cho bạn một mẹo mà nhiều bài viết và hướng dẫn sử dụng.
Khi bạn truyền biến làm tham số của lệnh gọi hàm, chúng sẽ được gán cho các đối số của hàm. Đối số là các biến cục bộ trong phạm vi của hàm và không có kết nối trở lại với các biến ban đầu, điều này có lý. Nếu bạn truyền một biểu thức cho hàm, bạn phải đặt kết quả của nó ở đâu đó, đúng không?
Hãy xem hai hàm sau. Chúng thực hiện cùng một việc, truyền giá trị qua, nhưng một hàm được định nghĩa bằng một tham số duy nhất, hàm kia không có tham số nào. Hàm thứ hai chứng minh điều gì đang xảy ra với tham số mà chúng ta truyền vào.
Bạn thấy rằng cả hai đều hoạt động giống nhau. Hãy ghi nhớ cách các đối số hàm hoạt động, hãy thử thay đổi một số giá trị. Chúng ta sẽ có một hàm thay đổi đối số duy nhất của nó và trả về đối số đó. Tôi cũng sẽ tạo một số biến mà tôi sẽ truyền cho hàm từng cái một. Hãy thử dự đoán những gì sẽ được in trong bảng điều khiển. (Câu trả lời nằm ở câu thứ hai của đoạn tiếp theo.)
Phỏng đoán của bạn có chứa bất kỳ "OMG" nào không? Không nên có, vì bảng điều khiển sẽ hiển thị "Chuyện gì đang xảy ra
". Bất kể thứ gì được truyền vào hàm trong JavaScript, việc gán lại chỉ thay đổi biến đối số. Vì vậy, cả
Tôi đã tạo một hàm khác để thử thay đổi thuộc tính
Bây giờ có chữ "OMG" nào trong câu đoán của bạn không? Tuyệt, thông báo là “undefined undefined OMG undefined
.” Thời điểm duy nhất hàm có thể thay đổi thuộc tính là với một đối tượng chung. Nó cho chúng ta biết điều gì? Có sự khác biệt nào giữa cách truyền các giá trị nguyên thủy và cách truyền các đối tượng không? Có phải việc truyền đối tượng đóng băng đột nhiên thay đổi nó thành truyền theo giá trị không? Tôi nghĩ sẽ hữu ích hơn nếu coi chúng là bằng nhau.
Bây giờ về trò ảo thuật mà tôi đã đề cập. Hầu như tất cả các tài nguyên đều làm điều đó khi chúng nói rằng các nguyên thủy và đối tượng được truyền khác nhau, sau đó ngay lập tức tiếp nối bằng một ví dụ trong đó chúng xử lý chúng khác nhau. Hãy xem mô tả hàm trong MDN. Vào thời điểm viết bài này, nó được mô tả như thế này (tôi nhấn mạnh):
Tôi không cố chỉ trích, đừng hiểu lầm tôi. Có lẽ điều này được thực hiện vì nó giải thích các điểm kỳ quặc của JavaScript theo cách quen thuộc hơn. Chỉ cần lưu ý rằng đôi khi một lời giải thích cung cấp cho bạn một mô hình suy nghĩ về một vấn đề. Nhưng mô hình đó không bao giờ hoàn toàn đúng với bản chất của một vấn đề.
Xem xét vấn đề này theo quan điểm các kiểu nguyên thủy giống như các đối tượng bị đóng băng giúp bạn nhận ra điều thực sự xảy ra. Các hướng dẫn thay thế trở nên phi logic. Và bây giờ, sau khi khám phá ra khái niệm về một đối tượng nguyên thủy mà không ai có thể thay đổi, chúng ta hãy làm cho chúng thân thiện hơn với phần còn lại của chương trình.
Bạn có thể định nghĩa một cách để chuyển đổi các đối tượng thành các giá trị nguyên thủy như chuỗi hoặc số. Ví dụ, hãy tạo một đối tượng biểu diễn xếp hạng từ không đến năm. Chúng ta cần có khả năng làm việc với biểu diễn số để so sánh và sắp xếp. Chúng ta cũng cần có khả năng xuất nó dưới dạng văn bản.
Có một số phương pháp nhất định mà bạn có thể định nghĩa để mô tả biểu diễn của đối tượng. Bạn còn nhớ
Hãy thay đổi điều đó.
Đó là những gì chúng ta sẽ sử dụng cho các đối tượng xếp hạng của mình. Để thuận tiện, hãy tạo một hàm khởi tạo và đóng băng các đối tượng như vậy. Nó cũng sẽ kiểm tra xem giá trị có nằm trong phạm vi từ không đến năm không và trả về
Bây giờ hãy đánh giá một cái gì đó. Có một cây bút mà tôi thích. Nó khá tuyệt và tôi sẽ cho nó năm sao.
Bọc cả hai đối tượng trong chuỗi ký tự mẫu là những gì tôi dựa vào ở đây để kích hoạt phương thức
Bây giờ có vẻ thừa khi trả về thuộc tính
Giả sử chúng ta lại có đối tượng bút của mình. Và giả sử xếp hạng bây giờ là thuộc tính của nó (chỉ để đơn giản hóa ví dụ). Bây giờ chúng ta có thể lọc ra các mục có ít hơn bốn sao:
Tương tự như vậy, chúng ta có thể sắp xếp các mục theo xếp hạng. Chúng ta có thể thực hiện điều đó bằng phương thức
Bây giờ,
Trong phần đầu tiên của loạt bài Các đối tượng nguyên thủy này, tôi đã cố gắng cung cấp tổng quan về cách làm cho các đối tượng giống với một số thuộc tính nguyên thủy. Bạn đóng băng chúng để biến chúng thành chỉ đọc. Bạn cũng có thể định nghĩa một biểu diễn trong các đối tượng nguyên thủy, có thể là số hoặc chuỗi, để làm cho nó hoạt động với các toán tử số học hoặc xuất chúng dưới dạng văn bản.
Trong các phần tiếp theo vào tuần tới, tôi sẽ đưa ra nhiều ví dụ hơn về cách sử dụng và so sánh với các phương pháp tiếp cận khác mà tôi đã gặp. Bạn sẽ thấy cách tạo các đối tượng nguyên thủy và biến chúng thành các cấu trúc dễ dàng hơn.
Trong loạt bài này, tôi sẽ cố gắng đề cập đến các tính năng JavaScript có thể tin cậy. Ngay cả khi không phải tất cả đều có ý nghĩa, tôi hy vọng rằng bằng cách xem một số ví dụ tôi đưa ra ở đây, bạn sẽ học được điều gì đó hữu ích giúp làm việc với JavaScript dễ dàng hơn mà không cần phải chuyển sang các công cụ bổ sung không cần thiết.
name
, id
hoặc label
được sử dụng để xác định xem một đối tượng có phải là đối tượng bạn đang tìm kiếm hay không.
Mã:
if (element.label === "title") { make_bold(element);}
Chuỗi không tốt cho việc nhận dạng. Bạn phải cân nhắc đến tính duy nhất và lỗi đánh máy; trình soạn thảo hoặc IDE của bạn sẽ không kiểm tra xem đó có phải là chuỗi bạn muốn không. Thật tệ. Tôi nghe ai đó nói, "Chỉ cần đưa chúng vào một biến, duh." Đó là một gợi ý hay và nó giải quyết một số mối quan tâm của tôi. Nhưng hãy xem John Smith:
Mã:
const john_smith_a_person = "John Smith";const john_smith_a_company = "John Smith";// Họ có cùng tên không?john_smith_a_person === john_smith_a_company; // true// Họ có cùng tên không?john_smith_a_person === john_smith_a_company; // true
Mã:
// Chúng có cùng tên không?john_smith_a_person.name === john_smith_a_company.name; // true// Chúng có giống nhau không?john_smith_a_person === john_smith_a_company; // false
Mã:
import React from "react";import labels from "./labels.js";const render_label(label) => ( )function TableOfContents({ items }) { return ( [LIST] {items.map(render_label(item.label)} [/LIST] );}
labels
, tôi đã để riêng một nhãn title
, trong trường hợp này, nhãn này phải được in đậm. Thêm vào đó, là một đối tượng, nhãn của tôi có thể chứa một chuỗi bản địa hóa (gọi theo trí tưởng tượng là text
) và một biểu tượng. Tất cả đều được sắp xếp gọn gàng trước, giúp logic giao diện người dùng của tôi gọn gàng.Nhưng đó chỉ là một phần của bức tranh. Tôi biết chúng ta sử dụng các đối tượng ở khắp mọi nơi và việc nhóm các thứ lại với nhau không phải là điều gì mới mẻ trong đó. Nhưng tôi cá là bạn không sử dụng chúng chính xác như vậy. Tôi hiếm khi thấy hai đối tượng được so sánh như vậy vì bạn không bao giờ biết có gì trong đó hoặc nó đến từ đâu. Đối tượng được tạo và thay đổi mọi lúc. Chúng có nhiều khả năng được so sánh theo giá trị của thuộc tính hơn là bản thân đối tượng. Và lý do cho điều đó là đối tượng không phù hợp với loại sử dụng đó. Chúng quá có khả năng. Để cho phép trường hợp sử dụng đó và nhiều trường hợp khác, một mặt, chúng ta phải giảm một số khả năng của đối tượng và mặt khác, triển khai thêm một số khả năng khác. Và cuối cùng, chúng ta sẽ có cái mà tôi gọi là Đối tượng nguyên thủy. Th… một giải pháp cho tất cả… một số vấn đề.
Trong phần đầu tiên của loạt bài này, tôi muốn đề cập đến một số khía cạnh của JavaScript giúp đưa đối tượng đến gần hơn với các giá trị nguyên thủy, đổi lại, chúng ta sẽ được hưởng lợi từ các tính năng ngôn ngữ chung thường không liên quan đến đối tượng, như phép so sánh và toán tử số học. Trong phần sau, chúng ta sẽ xem xét kỹ các ví dụ thực tế và công cụ để làm việc với các đối tượng như vậy. Bây giờ chúng ta hãy xem các đối tượng trong JavaScript trông như thế nào.
Các thuộc tính của các giá trị nguyên thủy mà chúng ta cần
Trước tiên, chúng ta hãy xác định mục tiêu của mình. Hãy vẽ một bức tranh về nơi chúng ta muốn đến sau đó. Chúng ta muốn các đối tượng của mình có những thuộc tính nào của các giá trị nguyên thủy?- Bất biến
Giá trị nguyên thủy chỉ có thể đọc. Chúng ta muốn đối tượng của mình không thể chỉnh sửa bởi bất kỳ ai sau khi tạo. Hãy nhớ lại ví dụ trước. Chúng ta có ích gì khi một số mã ngoài tầm kiểm soát của chúng ta đã thay đổi văn bản hoặc biểu tượng của đối tượng? Sau khi đối tượng được định nghĩa, nó phải được cố định. - Làm việc với các toán tử.
Biểu thức với một số toán tử nhất định trả về kiểu thích hợp của chúng. Toán tử số học trả về số. So sánh trả về giá trị boolean. - Có cú pháp theo nghĩa đen.
Các giá trị theo nghĩa đen cho các nguyên thủy cung cấp cho bạn giá trị chính xác hoặc đúng hơn là một đối tượng biểu diễn giá trị. Các đối tượng như vậy được tạo một lần cho mỗi giá trị. Mỗi lần bạn có"hello"
trong mã của mình, bạn sẽ nhận được cùng một đối tượng. - Có các kiểu.
Toán tửtypeof
cho bạn biết bạn đang xử lý cái gì (trừnull
). Chúng ta không phải lúc nào cũng biết mình nhận được loại đối tượng nào. Vì vậy, trước khi tìm hiểu các thuộc tính của đối tượng, tốt nhất là nên biết mình đang xử lý cái gì.
Tất cả đều là đối tượng, ngay cả khi không phải là đối tượng
Tôi nhớ mình đã bối rối thế nào khi lần đầu tiên nhìn thấy{} === {}; // false
. Đây là ngôn ngữ gì mà thậm chí không thể phân biệt được hai thứ bằng nhau? Nghe thật nực cười và buồn cười. Phải rất lâu sau đó tôi mới biết rằng có nhiều phần tệ hơn nhiều trong JavaScript, sau đó tôi đã ngừng cười khi xem wat talk.Đối tượng là một trong những điều cơ bản trong JavaScript. Bạn có thể đã nghe nói rằng trong JavaScript, mọi thứ đều là đối tượng. Điều đó khá đúng. Ngoại trừ một số giá trị dưới cùng, tất cả các nguyên hàm đều là đối tượng. Mặc dù về mặt kỹ thuật, điều này có nhiều sắc thái hơn, nhưng xét theo góc độ mã của chúng ta, thì điều đó là đúng. Trên thực tế, điều đó đủ đúng để tin rằng mọi thứ đều là đối tượng có thể là một mô hình tinh thần hữu ích. Nhưng trước tiên, hãy cố gắng hiểu điều gì đang xảy ra với phép so sánh đối tượng với đối tượng đã từng rất buồn cười đối với tôi khi còn trẻ.
Cú pháp đối tượng theo nghĩa đen được sử dụng để tạo đối tượng mới. Nó cho phép chúng ta khai báo và khởi tạo một đối tượng trong một biểu thức duy nhất.
Mã:
// Thay vì thế này.const my_object = new Object();my_object.first_property = "Thuộc tính đầu tiên";my_object.nth_property = "Thuộc tính tiếp theo";// Bạn có thể làm thế này.const my_object = { first_property: "Thuộc tính đầu tiên", nth_property: "Thuộc tính tiếp theo"};
Mã:
new Object() === new Object(); // false
5 === 3
trả về true
. Trong cả hai trường hợp, chúng là những thứ khác nhau.Chúng ta hãy kiểm tra tính hợp lý. Liệu hai biến tham chiếu đến cùng một đối tượng có được coi là bằng nhau không?
Mã:
const my_object = {};const other_thing = my_object;my_object === other_thing; // true
other_thing
tham chiếu đến một đối tượng vừa được tạo. Bây giờ, hai biến đang tham chiếu đến cùng một đối tượng. So sánh chúng cũng giống như so sánh hai số bằng nhau, phải không?Tại sao điều này lại quan trọng? Bởi vì nó cung cấp cho chúng ta một cách để kiểm tra xem một biến có tham chiếu đến một đối tượng mà chúng ta đang tìm kiếm hay không. Và nếu chúng ta nghĩ về nó trong bối cảnh "mọi thứ đều là một đối tượng", thì đó là cách các số và chuỗi hoạt động. Khi bạn so sánh hai biến chứa chuỗi, công cụ không cần phải kiểm tra xem từng ký tự trong các chuỗi đó có giống nhau không. Chỉ cần so sánh xem các biến có tham chiếu đến cùng một đối tượng hay không. Đó là nhờ vào sự khác biệt quan trọng nhất giữa các đối tượng thông thường và các giá trị nguyên thủy — tính bất biến.
Cách đưa các đối tượng thông thường đến gần hơn với các giá trị nguyên thủy
Trong JavaScript, các giá trị nguyên thủy không thể thay đổi. Bạn không thể thay đổi một ký tự đơn lẻ trong một chuỗi cũng như bạn không thể biến một số năm thành sáu. Nếu bạn sử dụngconst
để khởi tạo một biến và đặt một giá trị nguyên thủy vào đó, nó sẽ luôn giữ nguyên. Không ai có thể thay đổi giá trị; nó không thể thay đổi. Không ai có thể gán lại biến; nó được tạo bằng const
.Hãy xem xét kỹ cách các số hoạt động. Bạn có thể lấy sáu trong số năm bằng cách tăng nó lên một, nhưng nó không thay đổi bất cứ điều gì về năm.
Mã:
const five = 5;const six = 5 + 1;five === 5; // true
let
sẽ thay đổi điều đó. Nhưng hãy xem, nó không thể thay đổi năm:
Mã:
const five = 5;let result = 5;result++;result === 6; // truefive === 5; // true
++
chỉ là cách viết tắt của += 1
. Bạn thấy dấu bằng không? Điều đã xảy ra là tôi gán một giá trị mới cho biến result
, giá trị mà tôi nhận được từ biểu thức result + 1
(đó là cách viết tắt của += 1
). Từ khóa const
ngăn không cho gán lại cho một biến. Trong ví dụ trên, đó là cách giúp tôi biết rằng five
luôn tham chiếu đến một đối tượng 5
.Chúng ta có thể cho rằng cách duy nhất để thay đổi các giá trị nguyên thủy trong JavaScript là thông qua phép gán, nghĩa là những gì chúng ta thực sự thay đổi chính là những gì một biến tham chiếu đến. Vậy thì đó là các biến đang thay đổi, chứ không phải giá trị. Ít nhất thì không phải các biến nguyên thủy. Nhưng thay vào đó, nó hoạt động như thế nào với các đối tượng?
Sau khi khởi tạo một đối tượng, bạn có thể thay đổi các thuộc tính của nó: xóa chúng, thêm các thuộc tính mới và gán lại các thuộc tính cũ. Chúng ta đều quen thuộc với việc làm như vậy. Nhưng ngoài ra, nó hoạt động giống như các giá trị nguyên thủy. Trên thực tế, nếu bạn quen với một mô hình mà các đối tượng và giá trị nguyên thủy là những thứ giống nhau, bạn sẽ nhìn nhận khác nhau về mọi loại vấn đề trong JavaScript.
Có lẽ bạn đã tình cờ gặp phải một câu hỏi về cách các biến được truyền vào một hàm. Mọi người hỏi liệu các biến được truyền theo giá trị hay theo tham chiếu. Một câu trả lời phổ biến là các giá trị nguyên thủy được truyền theo giá trị trong khi các đối tượng được truyền theo tham chiếu. Nhưng với mô hình tinh thần mà tôi đang áp đặt cho bạn ở đây, bạn có thể đã biết những gì tôi sẽ nói về điều đó. Trước đó, hãy để tôi chỉ cho bạn cách câu hỏi này không có nhiều ý nghĩa trong JavaScript. Tôi cũng sẽ tiết lộ cho bạn một mẹo mà nhiều bài viết và hướng dẫn sử dụng.
Khi bạn truyền biến làm tham số của lệnh gọi hàm, chúng sẽ được gán cho các đối số của hàm. Đối số là các biến cục bộ trong phạm vi của hàm và không có kết nối trở lại với các biến ban đầu, điều này có lý. Nếu bạn truyền một biểu thức cho hàm, bạn phải đặt kết quả của nó ở đâu đó, đúng không?
Hãy xem hai hàm sau. Chúng thực hiện cùng một việc, truyền giá trị qua, nhưng một hàm được định nghĩa bằng một tham số duy nhất, hàm kia không có tham số nào. Hàm thứ hai chứng minh điều gì đang xảy ra với tham số mà chúng ta truyền vào.
Mã:
function single(arg) { return arg;}function none() { // Tham số đầu tiên được gán cho một biến `arg`. // Lưu ý `let`; nó sẽ có ý nghĩa sau này. let arg = arguments[0]; return arg;}single("hi"); // "hi"none(5); // 5
Mã:
function reassign(arg) { arg = "OMG";}const unreassignable = "What";let reassignable = "is";let non_primitive = { val: "happening" };reassign(unreassignable);reassign(reassignable);reassign(non_primitive);console.log(unreassignable, reassignable, non_primitive.val, "😱");

const
và let
đều không thay đổi bất kỳ thứ gì ở đây vì hàm không lấy chính biến đó. Nhưng điều gì sẽ xảy ra nếu chúng ta thử thay đổi các thuộc tính của đối số?Tôi đã tạo một hàm khác để thử thay đổi thuộc tính
val
của đối số. Hãy xem lần này bạn có thể đoán được thông báo trong bảng điều khiển không.
Mã:
function change_val_prop(arg) { try { arg.val = "OMG"; } catch (ignore) {}}const a_string = "What";const a_number = 15;const non_primitive = { val: "happening" };const non_primitive_read_only = Object.freeze({ my_string: "here" });change_val_prop(a_string);change_val_prop(a_number);change_val_prop(non_primitive);change_val_prop(non_primitive_read_only);console.log( a_string.val, a_number.val, non_primitive.val, non_primitive_read_only.val, "😱");

Bây giờ về trò ảo thuật mà tôi đã đề cập. Hầu như tất cả các tài nguyên đều làm điều đó khi chúng nói rằng các nguyên thủy và đối tượng được truyền khác nhau, sau đó ngay lập tức tiếp nối bằng một ví dụ trong đó chúng xử lý chúng khác nhau. Hãy xem mô tả hàm trong MDN. Vào thời điểm viết bài này, nó được mô tả như thế này (tôi nhấn mạnh):
Tôi vừa chỉ cho bạn thấy việc gán lại cũng sẽ không thay đổi đối tượng. Bạn không thể thay đổi thuộc tính của các kiểu nguyên thủy vì chúng chỉ đọc được, điều này cũng đúng với các đối tượng bị đóng băng. Và hầu hết các ví dụ bạn tìm thấy đều làm như vậy. Đầu tiên, chúng nêu sự khác biệt giữa hai giá trị, sau đó chứng minh sự khác biệt đó bằng các phương pháp khác nhau cho từng giá trị.Các đối số có thể được truyền theo giá trị (trong trường hợp giá trị nguyên thủy) hoặc theo tham chiếu (trong trường hợp đối tượng). Điều này có nghĩa là nếu một hàm gán lại một tham số kiểu nguyên thủy, giá trị sẽ không thay đổi bên ngoài hàm. Trong trường hợp tham số kiểu đối tượng, nếu thuộc tính của nó bị đột biến, sự thay đổi sẽ tác động bên ngoài hàm.
Tôi không cố chỉ trích, đừng hiểu lầm tôi. Có lẽ điều này được thực hiện vì nó giải thích các điểm kỳ quặc của JavaScript theo cách quen thuộc hơn. Chỉ cần lưu ý rằng đôi khi một lời giải thích cung cấp cho bạn một mô hình suy nghĩ về một vấn đề. Nhưng mô hình đó không bao giờ hoàn toàn đúng với bản chất của một vấn đề.
Xem xét vấn đề này theo quan điểm các kiểu nguyên thủy giống như các đối tượng bị đóng băng giúp bạn nhận ra điều thực sự xảy ra. Các hướng dẫn thay thế trở nên phi logic. Và bây giờ, sau khi khám phá ra khái niệm về một đối tượng nguyên thủy mà không ai có thể thay đổi, chúng ta hãy làm cho chúng thân thiện hơn với phần còn lại của chương trình.
Chuyển đổi
Các giá trị nguyên thủy tự đứng vững; bất kỳ chương trình nào cũng biết cách xử lý chúng. Đối tượng có thể là bất kỳ thứ gì. Và ngay cả khi bạn gọi chúng là nguyên thủy, thì điều đó cũng không đủ để chúng đột nhiên trở thành công dân hạng nhất. Để đạt được một số điều đó, chúng ta cần phải làm một số việc.Bạn có thể định nghĩa một cách để chuyển đổi các đối tượng thành các giá trị nguyên thủy như chuỗi hoặc số. Ví dụ, hãy tạo một đối tượng biểu diễn xếp hạng từ không đến năm. Chúng ta cần có khả năng làm việc với biểu diễn số để so sánh và sắp xếp. Chúng ta cũng cần có khả năng xuất nó dưới dạng văn bản.
Có một số phương pháp nhất định mà bạn có thể định nghĩa để mô tả biểu diễn của đối tượng. Bạn còn nhớ
[object Object]
không? Đây là những gì bạn nhận được khi cố gắng biến đối tượng của mình thành chuỗi:
Mã:
String({}); // "[object Object]"
Biểu diễn chuỗi
Đầu ra đó đến từ phương thứctoString
mặc định được xác định trong nguyên mẫu Đối tượng. Nhưng bạn có thể ghi đè lên nó bằng cách xác định nó trên đối tượng của riêng bạn.
Mã:
String({ toString: () => "hello there" }); // "hello there"
undefined
nếu không.
Mã:
function new_rating(value) { const max = 5; // Biểu tượng đó buộc phải biểu diễn văn bản (dù sao thì ai cần biểu tượng cảm xúc chứ 🙄). const text_only = "\ufe0e"; const star = "⭑" + text_only; const no_star = "⭐" + text_only; if ( !Number.isSafeInteger(value) || (value < 0 || value > max) ) { return undefined; } return Object.freeze({ value, toString: () => star.repeat(value) + no_star.repeat(max - value) });}
Mã:
const ratings = new WeakMap();ratings.set(jetstream_pen, new_rating(5));
WeakMap
này dành cho xếp hạng là cách bạn có thể gán thuộc tính cho các đối tượng mà không thực sự thay đổi chúng. Bây giờ, bất cứ khi nào chúng ta muốn có xếp hạng, chúng ta có thể chuyển đổi cả hai đối tượng của mình thành chuỗi.
Mã:
if (ratings.has(jetstream_pen)) { console.log(`${jetstream_pen} ${ratings.get(jetstream_pen)}`); // "Uni-Ball Jetstream 0.5 ⭑︎⭑︎⭑︎⭑︎⭑︎"}
toString
. Nếu không, bạn chỉ cần gọi hàm String
trên chúng, như tôi đã làm ở đầu phần này.Đối với Numberphiles
Đối với số, có phương thứcvalueOf
, được gọi bất cứ khi nào có nỗ lực chuyển đổi thành phép so sánh số hoặc toán tử toán học (trừ +
). Hãy thêm nó vào hàm new_rating
của chúng ta:
Mã:
function new_rating(value) { // ... return Object.freeze({ value, valueOf: () => value, toString: () => star.repeat(value) + no_star.repeat(max - value) });}
value
trực tiếp. Nhưng hãy nhớ rằng không ai ngoài chúng ta biết rằng nó ở đó. Trả về nó từ valueOf
là một cách phổ biến để có được biểu diễn số.Giả sử chúng ta lại có đối tượng bút của mình. Và giả sử xếp hạng bây giờ là thuộc tính của nó (chỉ để đơn giản hóa ví dụ). Bây giờ chúng ta có thể lọc ra các mục có ít hơn bốn sao:
Mã:
articles.filter((item) => item.rating > 3);// [ { name: "Uni-Ball Jetstream 0.5", ... } ]
sort
của Mảng. Có lẽ bạn đã có hàm sắp xếp nhỏ yêu thích mà bạn muốn sử dụng, như hàm này:
Mã:
function sorter(first, second) { return second.rating - first.rating;}const sorted_by_rating = array_of.sort(sorter);
sorted_by_rating
chứa một mảng các mục tốt nhất.Kết luận
Tôi hiếm khi coi các đối tượng là thứ có thể mở rộng những gì có thể được thể hiện trong JavaScript. Với các đối tượng nguyên thủy, đó là những gì tôi đang cố gắng khám phá. Vẫn còn những thứ chúng ta không thể thêm vào, như toán tử mới hoặc cú pháp theo nghĩa đen, nhưng với các đối tượng nguyên thủy, chúng ta vẫn có thể định nghĩa các loại giá trị mới.Trong phần đầu tiên của loạt bài Các đối tượng nguyên thủy này, tôi đã cố gắng cung cấp tổng quan về cách làm cho các đối tượng giống với một số thuộc tính nguyên thủy. Bạn đóng băng chúng để biến chúng thành chỉ đọc. Bạn cũng có thể định nghĩa một biểu diễn trong các đối tượng nguyên thủy, có thể là số hoặc chuỗi, để làm cho nó hoạt động với các toán tử số học hoặc xuất chúng dưới dạng văn bản.
Trong các phần tiếp theo vào tuần tới, tôi sẽ đưa ra nhiều ví dụ hơn về cách sử dụng và so sánh với các phương pháp tiếp cận khác mà tôi đã gặp. Bạn sẽ thấy cách tạo các đối tượng nguyên thủy và biến chúng thành các cấu trúc dễ dàng hơn.
Trong loạt bài này, tôi sẽ cố gắng đề cập đến các tính năng JavaScript có thể tin cậy. Ngay cả khi không phải tất cả đều có ý nghĩa, tôi hy vọng rằng bằng cách xem một số ví dụ tôi đưa ra ở đây, bạn sẽ học được điều gì đó hữu ích giúp làm việc với JavaScript dễ dàng hơn mà không cần phải chuyển sang các công cụ bổ sung không cần thiết.