From f354c5454003ebb6b9cd0df0435486eb94247b79 Mon Sep 17 00:00:00 2001 From: Henry Bui Date: Tue, 12 May 2020 23:16:54 +0700 Subject: [PATCH 1/3] WIP --- 1-js/04-object-basics/01-object/article.md | 128 +++++++++++---------- 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/1-js/04-object-basics/01-object/article.md b/1-js/04-object-basics/01-object/article.md index 120e8dde0..6ea952f65 100644 --- a/1-js/04-object-basics/01-object/article.md +++ b/1-js/04-object-basics/01-object/article.md @@ -1,60 +1,62 @@ # Objects -As we know from the chapter , there are seven data types in JavaScript. Six of them are called "primitive", because their values contain only a single thing (be it a string or a number or whatever). +Như chúng ta đã biết từ chương , có 6 loại dữ liệu trong JavaScript. Chúng được gọi là "nguyên thủy", bởi vì giá trị của chúng chỉ chứa một thứ duy nhất (đó có thể là một chuỗi hoặc một số hoặc cái gì đó khác). -In contrast, objects are used to store keyed collections of various data and more complex entities. In JavaScript, objects penetrate almost every aspect of the language. So we must understand them first before going in-depth anywhere else. +Ngược lại, các đối tượng được sử dụng để lưu trữ các bộ sưu tập có khóa của các dữ liệu khác nhau và các thực thể phức tạp hơn. Trong JavaScript, các đối tượng thâm nhập vào hầu hết mọi khía cạnh của ngôn ngữ. Vì vậy, chúng ta phải hiểu chúng trước khi đi sâu vào bất cứ nơi nào khác. -An object can be created with figure brackets `{…}` with an optional list of *properties*. A property is a "key: value" pair, where `key` is a string (also called a "property name"), and `value` can be anything. +Một đối tượng có thể được tạo bằng dấu ngoặc hình `{…}` với một danh sách *thuộc tính* tùy chọn. Một thuộc tính là một cặp "khóa: giá trị", trong đó `khóa` là một chuỗi (còn được gọi là "tên thuộc tính"), và `giá trị` có thể là bất cứ thứ gì. -We can imagine an object as a cabinet with signed files. Every piece of data is stored in its file by the key. It's easy to find a file by its name or add/remove a file. +Chúng ta có thể tưởng tượng một đối tượng như một cái tủ với các tập tin đã ký. Mỗi phần dữ liệu được lưu trữ trong tệp của nó bằng từ khóa. Thật dễ dàng để tìm một tệp theo tên của nó hoặc thêm/xóa một tệp. ![](object.svg) -An empty object ("empty cabinet") can be created using one of two syntaxes: +Một đối tượng rỗng ("tủ rỗng") có thể được tạo bằng một hoặc hai cú pháp: ```js -let user = new Object(); // "object constructor" syntax -let user = {}; // "object literal" syntax +let user = new Object(); // cú pháp "đối tượng constructor" +let user = {}; // cú pháp "đối tượng theo nghĩa đen" ``` ![](object-user-empty.svg) -Usually, the figure brackets `{...}` are used. That declaration is called an *object literal*. +Thông thường, dấu ngoặc `{...}` được sử dụng. Loại khai báo đó được gọi là *đối tượng theo nghĩa đen*. -## Literals and properties +## Đối tượng theo nghĩa đen và thuộc tính -We can immediately put some properties into `{...}` as "key: value" pairs: +Chúng ta có thể ngay lập tức đặt một số thuộc tính vào `{...}` dưới dạng các cặp "khóa: giá trị": ```js -let user = { // an object - name: "John", // by key "name" store value "John" - age: 30 // by key "age" store value 30 +let user = { // một đối tượng + name: "John", // khóa "name" lưu giá trị "John" + age: 30 // khóa "age" lưu giá trị 30 }; ``` -A property has a key (also known as "name" or "identifier") before the colon `":"` and a value to the right of it. +Một thuộc tính có một khóa (còn được gọi là "tên" hoặc "định danh") trước dấu hai chấm `":"` và một giá trị ở bên phải của nó. -In the `user` object, there are two properties: +Trong đối tượng `user`, có hai thuộc tính: -1. The first property has the name `"name"` and the value `"John"`. -2. The second one has the name `"age"` and the value `30`. +1. Thuộc tính thứ nhất có tên là `"name"` và giá trị là `"John"`. +2. Thuộc tính thứ hai có tên là `"age"` và giá trị là `30`. The resulting `user` object can be imagined as a cabinet with two signed files labeled "name" and "age". +Kết quả là đối tượng `user` có thể được tưởng tượng như một cái tủ với hai tệp được ký có nhãn "name" và "age". + ![user object](object-user.svg) -We can add, remove and read files from it any time. +Chúng ta có thể thêm, xóa và đọc tệp từ chúng mọi lúc. -Property values are accessible using the dot notation: +Các giá trị của thuộc tính có thể truy cập bằng cách sử dụng dấu chấm: ```js -// get property values of the object: +// lấy giá trị của thuộc tính trong đối tượng: alert( user.name ); // John alert( user.age ); // 30 ``` -The value can be of any type. Let's add a boolean one: +Giá trị có thể là bất kỳ kiểu dữ liệu nào. Hãy thêm một giá trị boolean: ```js user.isAdmin = true; @@ -62,7 +64,7 @@ user.isAdmin = true; ![user object 2](object-user-isadmin.svg) -To remove a property, we can use `delete` operator: +Để xóa thuộc tính, ta có thể dùng `delete`: ```js delete user.age; @@ -70,40 +72,40 @@ delete user.age; ![user object 3](object-user-delete.svg) -We can also use multiword property names, but then they must be quoted: +Chúng ta cũng có thể sử dụng tên thuộc tính có nhiều từ, nhưng sau đó chúng phải được bọc lại: ```js let user = { name: "John", age: 30, - "likes birds": true // multiword property name must be quoted + "likes birds": true // tên thuộc tính nhiều từ phải được bọc lại bằng dấu ngoặc kép }; ``` ![](object-user-props.svg) -The last property in the list may end with a comma: +Thuộc tính cuối cùng trong danh sách có thể kết thúc bằng dấu phẩy: ```js let user = { name: "John", age: 30*!*,*/!* } ``` -That is called a "trailing" or "hanging" comma. Makes it easier to add/remove/move around properties, because all lines become alike. +Đó được gọi là dấu phẩy "trailing" hay "hanging". Nó khiến dễ dàng thêm/xóa/di chuyển xung quanh các thuộc tính, bởi vì tất cả các dòng trở nên giống nhau. -## Square brackets +## Dấu ngoặc vuông -For multiword properties, the dot access doesn't work: +Với thuộc tính nhiều từ, truy cập bằng dấu chấm không hoạt động: ```js run -// this would give a syntax error +// sẽ có lỗi cú pháp user.likes birds = true ``` -That's because the dot requires the key to be a valid variable identifier. That is: no spaces and other limitations. +Đó là vì dấu chấm yêu cầu khóa phải là một biến định danh hợp lệ. Đó là: không chứa khoảng trắng và các giới hạn khác. -There's an alternative "square bracket notation" that works with any string: +Có một thay thế là "dấu ngoặc vuông" sẽ hoạt động với bất cứ chuỗi nào: ```js run let user = {}; @@ -118,20 +120,20 @@ alert(user["likes birds"]); // true delete user["likes birds"]; ``` -Now everything is fine. Please note that the string inside the brackets is properly quoted (any type of quotes will do). +Bây giờ mọi thứ đã tốt. Hãy lưu ý rằng chuỗi bên trong ngoặc được bao bọc chính xác (bất kỳ loại bao bọc nào cũng được). -Square brackets also provide a way to obtain the property name as the result of any expression -- as opposed to a literal string -- like from a variable as follows: +Dấu ngoặc vuông cũng cung cấp một cách để có được tên thuộc tính là kết quả của bất kỳ biểu thức nào -- trái ngược với chuỗi ký tự -- giống như từ một biến như sau: ```js let key = "likes birds"; -// same as user["likes birds"] = true; +// giống như user["likes birds"] = true; user[key] = true; ``` -Here, the variable `key` may be calculated at run-time or depend on the user input. And then we use it to access the property. That gives us a great deal of flexibility. +Ở đây, biến `key` có thể được tính vào thời gian chạy hoặc phụ thuộc vào đầu vào của người dùng. Và sau đó chúng ta sử dụng nó để truy cập vào thuộc tính. Điều đó giúp chúng ta linh hoạt hơn. -For instance: +Ví dụ: ```js run let user = { @@ -139,13 +141,13 @@ let user = { age: 30 }; -let key = prompt("What do you want to know about the user?", "name"); +let key = prompt("Bạn muốn biết điều gì về người dùng?", "name"); -// access by variable -alert( user[key] ); // John (if enter "name") +// truy cập vào biến +alert( user[key] ); // John (nếu nhập "name") ``` -The dot notation cannot be used in a similar way: +Dấu chấm không thể dùng theo cách như vậy: ```js run let user = { @@ -157,40 +159,40 @@ let key = "name"; alert( user.key ) // undefined ``` -### Computed properties +### Thuộc tính computed -We can use square brackets in an object literal. That's called *computed properties*. +Chúng ta có thể sử dụng dấu ngoặc vuông trong một đối tượng theo nghĩa đen. Đó gọi là *thuộc tính computed*. -For instance: +Ví dụ: ```js run -let fruit = prompt("Which fruit to buy?", "apple"); +let fruit = prompt("Mua loại trái cây nào?", "apple"); let bag = { *!* - [fruit]: 5, // the name of the property is taken from the variable fruit + [fruit]: 5, // tên của thuộc tính được lấy từ biến fruit */!* }; -alert( bag.apple ); // 5 if fruit="apple" +alert( bag.apple ); // 5 nếu fruit="apple" ``` -The meaning of a computed property is simple: `[fruit]` means that the property name should be taken from `fruit`. +Ý nghĩa của thuộc tính computed rất đơn giản: `[fruit]` có nghĩa là tên thuộc tính nên được lấy từ` fruit`. -So, if a visitor enters `"apple"`, `bag` will become `{apple: 5}`. +Do vậy, nếu người dùng nhập `"apple"`, `bag` sẽ thành `{apple: 5}`. -Essentially, that works the same as: +Về cơ bản, nó hoạt động giống như: ```js run -let fruit = prompt("Which fruit to buy?", "apple"); +let fruit = prompt("Mua loại trái cây nào?", "apple"); let bag = {}; -// take property name from the fruit variable +// lấy tên thuộc tính từ biến fruit bag[fruit] = 5; ``` -...But looks nicer. +...Nhưng nhìn tốt hơn. -We can use more complex expressions inside square brackets: +Chúng ta có thể sử dụng các biểu thức phức tạp hơn trong dấu ngoặc vuông: ```js let fruit = 'apple'; @@ -199,16 +201,16 @@ let bag = { }; ``` -Square brackets are much more powerful than the dot notation. They allow any property names and variables. But they are also more cumbersome to write. +Dấu ngoặc vuông có mạnh hơn dấu chấm. Chúng chấp nhận bất cứ tên của thuộc tính và biến nào. Nhưng ngoài ra chúng cũng cồng kềnh khi viết. -So most of the time, when property names are known and simple, the dot is used. And if we need something more complex, then we switch to square brackets. +Vì vậy hầu hết thời gian, khi tên thuộc tính được biết và đơn giản, dấu chấm được sử dụng. Và nếu chúng ta cần một cái gì đó phức tạp hơn, thì chúng ta chuyển sang dấu ngoặc vuông. ````smart header="Reserved words are allowed as property names" -A variable cannot have a name equal to one of language-reserved words like "for", "let", "return" etc. +Một biến không thể có tên bằng một trong những từ dành riêng cho ngôn ngữ như "for", "let", "return", vâng vâng. -But for an object property, there's no such restriction. Any name is fine: +Nhưng đối với một thuộc tính đối tượng, không có hạn chế đó. Tên nào cũng được: ```js run let obj = { @@ -220,7 +222,7 @@ let obj = { alert( obj.for + obj.let + obj.return ); // 6 ``` -Basically, any name is allowed, but there's a special one: `"__proto__"` that gets special treatment for historical reasons. For instance, we can't set it to a non-object value: +Về cơ bản, bất kỳ tên nào cũng được cho phép, nhưng có một tên đặc biệt: `" __proto __ "` được đối xử đặc biệt vì lý do lịch sử. Chẳng hạn, chúng ta không thể đặt nó vào một giá trị phi đối tượng: ```js run let obj = {}; @@ -228,15 +230,15 @@ obj.__proto__ = 5; alert(obj.__proto__); // [object Object], didn't work as intended ``` -As we see from the code, the assignment to a primitive `5` is ignored. +Như chúng ta thấy từ code, việc gán cho một biến nguyên thủy `5` bị bỏ qua. -That can become a source of bugs and even vulnerabilities if we intend to store arbitrary key-value pairs in an object, and allow a visitor to specify the keys. +Điều đó có thể trở thành nguyên nhân gây ra lỗi và thậm chí là lỗ hổng nếu ta dự định lưu trữ các cặp khóa-giá trị tùy ý trong một đối tượng và cho phép người dùng truy cập các khóa cụ thể. -In that case the visitor may choose `__proto__` as the key, and the assignment logic will be ruined (as shown above). +Trong trường hợp đó, người dùng có thể chọn `__proto__` làm khóa và logic gán sẽ bị hủy (như được hiển thị ở trên). -There is a way to make objects treat `__proto__` as a regular property, which we'll cover later, but first we need to know more about objects. +Có một cách để làm cho các đối tượng coi `__proto__` như một thuộc tính thông thường, chúng ta sẽ đề cập sau, nhưng trước tiên chúng ta cần biết thêm về các đối tượng. -There's also another data structure [Map](info:map-set), that we'll learn in the chapter , which supports arbitrary keys. +Ngoài ra còn có một cấu trúc dữ liệu khác [Map](info:map-set), mà chúng ta sẽ tìm hiểu trong chương , nó hỗ trợ các khóa tùy ý. ```` From ee9e1ec496a1ec00090fbafeaec1cc5bf711cb67 Mon Sep 17 00:00:00 2001 From: Henry Bui Date: Wed, 13 May 2020 22:56:50 +0700 Subject: [PATCH 2/3] WIP --- 1-js/04-object-basics/01-object/article.md | 139 ++++++++++----------- 1 file changed, 69 insertions(+), 70 deletions(-) diff --git a/1-js/04-object-basics/01-object/article.md b/1-js/04-object-basics/01-object/article.md index 6ea952f65..81e2a0b3a 100644 --- a/1-js/04-object-basics/01-object/article.md +++ b/1-js/04-object-basics/01-object/article.md @@ -242,18 +242,18 @@ Ngoài ra còn có một cấu trúc dữ liệu khác [Map](info:map-set), mà ```` -## Property value shorthand +## Tốc ký giá trị của thuộc tính -In real code we often use existing variables as values for property names. +Trong code chúng ta thường sử dụng các biến sẵn có làm giá trị cho tên của các thuộc tính. -For instance: +Ví dụ: ```js run function makeUser(name, age) { return { name: name, age: age - // ...other properties + // ...các thuộc tính khác }; } @@ -261,102 +261,101 @@ let user = makeUser("John", 30); alert(user.name); // John ``` -In the example above, properties have the same names as variables. The use-case of making a property from a variable is so common, that there's a special *property value shorthand* to make it shorter. +Trong ví dụ trên, các thuộc tính có cùng tên với các biến. Trường hợp sử dụng để tạo một thuộc tính từ một biến là rất phổ biến, do đó có một loại *tốc ký giá trị của thuộc tính* làm cho nó ngắn hơn. -Instead of `name:name` we can just write `name`, like this: +Thay vì `name:name` chúng ta có thể viết `name`, như thế này: ```js function makeUser(name, age) { *!* return { - name, // same as name: name - age // same as age: age + name, // giống như name: name + age // giống như age: age // ... }; */!* } ``` -We can use both normal properties and shorthands in the same object: +Chúng ta có thể sử dụng cả thuộc tính bình thường và tốc ký trong cùng một đối tượng: ```js let user = { - name, // same as name:name + name, // giống như name:name age: 30 }; ``` -## Existence check +## Kiểm tra tồn tại -A notable objects feature is that it's possible to access any property. There will be no error if the property doesn't exist! Accessing a non-existing property just returns `undefined`. It provides a very common way to test whether the property exists -- to get it and compare vs undefined: +Một tính năng đáng chú ý của đối tượng là có thể truy cập bất kỳ thuộc tính nào. Sẽ không có lỗi nếu thuộc tính không tồn tại! Truy cập một thuộc tính không tồn tại chỉ trả về `undefined`. Nó cung cấp một cách rất phổ biến để kiểm tra xem thuộc tính có tồn tại hay không - lấy nó và so sánh với undefined: ```js run let user = {}; -alert( user.noSuchProperty === undefined ); // true means "no such property" +alert( user.noSuchProperty === undefined ); // true có nghĩa là "no such property" ``` -There also exists a special operator `"in"` to check for the existence of a property. +Ngoài ra còn tồn tại một toán tử đặc biệt `"in"` để kiểm tra sự tồn tại của một thuộc tính. -The syntax is: +Cú pháp: ```js "key" in object ``` -For instance: +Ví dụ: ```js run let user = { name: "John", age: 30 }; -alert( "age" in user ); // true, user.age exists -alert( "blabla" in user ); // false, user.blabla doesn't exist +alert( "age" in user ); // true, user.age tồn tại +alert( "blabla" in user ); // false, user.blabla không tồn tại ``` -Please note that on the left side of `in` there must be a *property name*. That's usually a quoted string. +Hãy lưu ý rằng ở phía bên trái của `in` phải có *tên thuộc tính*. Đó thường là một chuỗi được bao bọc trong dấu ngoặc kép. -If we omit quotes, that would mean a variable containing the actual name will be tested. For instance: +Nếu chúng ta bỏ qua dấu ngoặc kép, điều đó có nghĩa là một biến chứa tên thực tế sẽ được kiểm tra. Ví dụ: ```js run let user = { age: 30 }; let key = "age"; -alert( *!*key*/!* in user ); // true, takes the name from key and checks for such property +alert( *!*key*/!* in user ); // true, lấy tên từ key và kiểm tra thuộc tính đó ``` ````smart header="Using \"in\" for properties that store `undefined`" -Usually, the strict comparison `"=== undefined"` check the property existance just fine. But there's a special case when it fails, but `"in"` works correctly. +Thông thường, sự so sánh chặt `"=== undefined"` kiểm tra sự tồn tại của thuộc tính. Có một trường hợp đặc biệt nó sẽ sai, nhưng với `"in"` thì chạy chính xác. -It's when an object property exists, but stores `undefined`: +Đó là khi một thuộc tính trong đối tượng tồn tại, nhưng lưu trữ là `undefined`: ```js run let obj = { test: undefined }; -alert( obj.test ); // it's undefined, so - no such property? +alert( obj.test ); // thuộc tính không tồn tại, do đó - nó không phải là thuộc tính? -alert( "test" in obj ); // true, the property does exist! +alert( "test" in obj ); // true, thuộc tính tồn tại! ``` -In the code above, the property `obj.test` technically exists. So the `in` operator works right. +Trong đoạn code trên, thuộc tính `obj.test` về mặt kỹ thuật tồn tại. Vì vậy, toán tử `in` hoạt động đúng. -Situations like this happen very rarely, because `undefined` is usually not assigned. We mostly use `null` for "unknown" or "empty" values. So the `in` operator is an exotic guest in the code. -```` +Các tình huống như thế này rất hiếm khi xảy ra, vì `undefined` thường không được chỉ định. Chúng ta chủ yếu sử dụng `null` cho các giá trị "không xác định" hoặc "rỗng". Vì vậy, toán tử `in` là một vị khách kỳ lạ trong code. -## The "for..in" loop +## Vòng lặp "for..in" -To walk over all keys of an object, there exists a special form of the loop: `for..in`. This is a completely different thing from the `for(;;)` construct that we studied before. +Để đi qua tất cả các khóa của một đối tượng, ta có một dạng vòng lặp đặc biệt: `for..in`. Đây là một điều hoàn toàn khác với cấu trúc `for (;;)` mà chúng ta đã học trước đây. -The syntax: +Cú pháp: ```js for (key in object) { - // executes the body for each key among object properties + // thực thi phần thân cho mỗi key của thuộc tính trong đối tượng } ``` -For instance, let's output all properties of `user`: +Ví dụ, in ra tất cả các thuộc tính của `user`: ```js run let user = { @@ -366,33 +365,33 @@ let user = { }; for (let key in user) { - // keys + // các khóa alert( key ); // name, age, isAdmin - // values for the keys + // giá trị của các khóa alert( user[key] ); // John, 30, true } ``` -Note that all "for" constructs allow us to declare the looping variable inside the loop, like `let key` here. +Lưu ý rằng tất cả các cấu trúc "for" cho phép chúng ta khai báo biến vòng lặp bên trong vòng lặp, như `let key` ở đây. -Also, we could use another variable name here instead of `key`. For instance, `"for (let prop in obj)"` is also widely used. +Ngoài ra, chúng ta có thể sử dụng một tên biến khác ở đây thay vì `key`. Chẳng hạn, `"for (let prop in obj)"` cũng được sử dụng rộng rãi. -### Ordered like an object +### Sắp xếp một đối tượng -Are objects ordered? In other words, if we loop over an object, do we get all properties in the same order they were added? Can we rely on this? +Đối tượng có được sắp xếp không? Nói cách khác, nếu chúng ta lặp qua một đối tượng, chúng ta có nhận được tất cả các thuộc tính theo cùng thứ tự chúng đã được thêm không? Chúng ta có thể tin vào điều này không? -The short answer is: "ordered in a special fashion": integer properties are sorted, others appear in creation order. The details follow. +Câu trả lời là: "sắp xếp theo kiểu đặc biệt": thuộc tính số nguyên được sắp xếp, những cái khác xuất hiện theo thứ tự tạo. Các chi tiết theo sau. -As an example, let's consider an object with the phone codes: +Ví dụ: hãy xem xét một đối tượng có chứa mã điện thoại: ```js run let codes = { - "49": "Germany", - "41": "Switzerland", - "44": "Great Britain", + "49": "Đức", + "41": "Thụy Sĩ", + "44": "Anh", // .., - "1": "USA" + "1": "Mỹ" }; *!* @@ -402,56 +401,56 @@ for (let code in codes) { */!* ``` -The object may be used to suggest a list of options to the user. If we're making a site mainly for German audience then we probably want `49` to be the first. +Đối tượng có thể được sử dụng để đề xuất một danh sách các tùy chọn cho người dùng. Nếu chúng ta tạo một trang chủ yếu cho người Đức thì có lẽ chúng ta muốn `49` đứng đầu tiên. -But if we run the code, we see a totally different picture: +Nhưng nếu chúng ta chạy code, chúng ta sẽ thấy một bức tranh hoàn toàn khác: -- USA (1) goes first -- then Switzerland (41) and so on. +- Mỹ (1) đứng đầu +- sau đó Thụy Sỹ (41) và cứ thế. -The phone codes go in the ascending sorted order, because they are integers. So we see `1, 41, 44, 49`. +Các mã điện thoại đi theo thứ tự tăng dần, bởi vì chúng là số nguyên. Vì vậy, chúng ta thấy `1, 41, 44, 49`. ````smart header="Integer properties? What's that?" -The "integer property" term here means a string that can be converted to-and-from an integer without a change. +Thuật ngữ "thuộc tính số nguyên" ở đây có nghĩa là một chuỗi có thể được chuyển đổi thành và từ một số nguyên mà không thay đổi. -So, "49" is an integer property name, because when it's transformed to an integer number and back, it's still the same. But "+49" and "1.2" are not: +Do đó, "49" là thuộc thuộc tính số nguyên, vì khi nó được chuyển đổi sang số nguyên và ngược lại, nó vẫn giống nhau. Nhưng "+49" và "1.2" thì không: ```js run -// Math.trunc is a built-in function that removes the decimal part -alert( String(Math.trunc(Number("49"))) ); // "49", same, integer property -alert( String(Math.trunc(Number("+49"))) ); // "49", not same "+49" ⇒ not integer property -alert( String(Math.trunc(Number("1.2"))) ); // "1", not same "1.2" ⇒ not integer property +// Math.trunc là một hàm dựng sẵn để xóa một phần của số thập phân +alert( String(Math.trunc(Number("49"))) ); // "49", giống nhau, thuộc tính số nguyên +alert( String(Math.trunc(Number("+49"))) ); // "49", không giống "+49" ⇒ không phải thuộc tính số nguyên +alert( String(Math.trunc(Number("1.2"))) ); // "1", không giống "1.2" ⇒ không phải thuộc tính số nguyên ``` ```` -...On the other hand, if the keys are non-integer, then they are listed in the creation order, for instance: +...Mặt khác, nếu các khóa không phải là số nguyên, thì chúng được liệt kê theo thứ tự tạo, Ví dụ: ```js run let user = { name: "John", surname: "Smith" }; -user.age = 25; // add one more +user.age = 25; // thêm một thuộc tính nữa *!* -// non-integer properties are listed in the creation order +// thuộc tính không nguyên được liệt kê theo thứ tự tạo */!* for (let prop in user) { alert( prop ); // name, surname, age } ``` -So, to fix the issue with the phone codes, we can "cheat" by making the codes non-integer. Adding a plus `"+"` sign before each code is enough. +Vì vậy, để khắc phục sự cố với mã điện thoại, chúng ta có thể "gian lận" bằng cách làm cho mã không nguyên. Thêm dấu cộng `"+"` trước mỗi mã là đủ. -Like this: +Như sau: ```js run let codes = { - "+49": "Germany", - "+41": "Switzerland", - "+44": "Great Britain", + "+49": "Đức", + "+41": "Thụy Sỹ", + "+44": "Anh", // .., - "+1": "USA" + "+1": "Mỹ" }; for (let code in codes) { @@ -459,7 +458,7 @@ for (let code in codes) { } ``` -Now it works as intended. +Bây giờ nó hoạt động như ý muốn. ## Copying by reference @@ -467,7 +466,7 @@ One of the fundamental differences of objects vs primitives is that they are sto Primitive values: strings, numbers, booleans -- are assigned/copied "as a whole value". -For instance: +Ví dụ: ```js let message = "Hello!"; @@ -498,7 +497,7 @@ Here, the object is stored somewhere in memory. And the variable `user` has a "r If we imagine an object as a cabinet, then a variable is a key to it. Copying a variable duplicates the key, but not the cabinet itself. -For instance: +Ví dụ: ```js no-beautify let user = { name: "John" }; @@ -557,7 +556,7 @@ For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj An object declared as `const` *can* be changed. -For instance: +Ví dụ: ```js run const user = { @@ -573,7 +572,7 @@ alert(user.age); // 25 It might seem that the line `(*)` would cause an error, but no, there's totally no problem. That's because `const` fixes only value of `user` itself. And here `user` stores the reference to the same object all the time. The line `(*)` goes *inside* the object, it doesn't reassign `user`. -The `const` would give an error if we try to set `user` to something else, for instance: +The `const` would give an error if we try to set `user` to something else, Ví dụ: ```js run const user = { From f9d5f21fa3c5be771495951c1d0d291dfdd61bc5 Mon Sep 17 00:00:00 2001 From: Henry Bui Date: Tue, 12 May 2020 23:16:54 +0700 Subject: [PATCH 3/3] Translate 01-object to Vietnamese --- 1-js/04-object-basics/01-object/article.md | 439 +++++++++++---------- 1 file changed, 220 insertions(+), 219 deletions(-) diff --git a/1-js/04-object-basics/01-object/article.md b/1-js/04-object-basics/01-object/article.md index 120e8dde0..311653864 100644 --- a/1-js/04-object-basics/01-object/article.md +++ b/1-js/04-object-basics/01-object/article.md @@ -1,60 +1,62 @@ -# Objects +# Đối tượng -As we know from the chapter , there are seven data types in JavaScript. Six of them are called "primitive", because their values contain only a single thing (be it a string or a number or whatever). +Như chúng ta đã biết ở chương , có 6 loại dữ liệu trong JavaScript. Chúng được gọi là "nguyên thủy", bởi vì giá trị của chúng chỉ chứa một thứ duy nhất (đó có thể là một chuỗi hoặc một số hoặc cái gì đó khác). -In contrast, objects are used to store keyed collections of various data and more complex entities. In JavaScript, objects penetrate almost every aspect of the language. So we must understand them first before going in-depth anywhere else. +Ngược lại, các đối tượng được sử dụng để lưu trữ các bộ sưu tập có khóa của các dữ liệu khác nhau và các thực thể phức tạp hơn. Trong JavaScript, các đối tượng thâm nhập vào hầu hết mọi khía cạnh của ngôn ngữ. Vì vậy, chúng ta phải hiểu chúng trước khi đi sâu vào bất cứ nơi nào khác. -An object can be created with figure brackets `{…}` with an optional list of *properties*. A property is a "key: value" pair, where `key` is a string (also called a "property name"), and `value` can be anything. +Một đối tượng có thể được tạo bằng dấu ngoặc hình `{…}` với một danh sách *thuộc tính* tùy chọn. Một thuộc tính là một cặp "khóa: giá trị", trong đó `khóa` là một chuỗi (còn được gọi là "tên thuộc tính"), và `giá trị` có thể là bất cứ thứ gì. -We can imagine an object as a cabinet with signed files. Every piece of data is stored in its file by the key. It's easy to find a file by its name or add/remove a file. +Chúng ta có thể tưởng tượng một đối tượng như một cái tủ với các tập tin đã ký. Mỗi phần dữ liệu được lưu trữ trong tệp của nó bằng từ khóa. Thật dễ dàng để tìm một tệp theo tên của nó hoặc thêm/xóa một tệp. ![](object.svg) -An empty object ("empty cabinet") can be created using one of two syntaxes: +Một đối tượng rỗng ("tủ rỗng") có thể được tạo bằng một hoặc hai cú pháp: ```js -let user = new Object(); // "object constructor" syntax -let user = {}; // "object literal" syntax +let user = new Object(); // cú pháp "đối tượng constructor" +let user = {}; // cú pháp "đối tượng theo nghĩa đen" ``` ![](object-user-empty.svg) -Usually, the figure brackets `{...}` are used. That declaration is called an *object literal*. +Thông thường, dấu ngoặc `{...}` được sử dụng. Loại khai báo đó được gọi là *đối tượng theo nghĩa đen*. -## Literals and properties +## Đối tượng theo nghĩa đen và thuộc tính -We can immediately put some properties into `{...}` as "key: value" pairs: +Chúng ta có thể ngay lập tức đặt một số thuộc tính vào `{...}` dưới dạng các cặp "khóa: giá trị": ```js -let user = { // an object - name: "John", // by key "name" store value "John" - age: 30 // by key "age" store value 30 +let user = { // một đối tượng + name: "John", // khóa "name" lưu giá trị "John" + age: 30 // khóa "age" lưu giá trị 30 }; ``` -A property has a key (also known as "name" or "identifier") before the colon `":"` and a value to the right of it. +Một thuộc tính có một khóa (còn được gọi là "tên" hoặc "định danh") trước dấu hai chấm `":"` và một giá trị ở bên phải của nó. -In the `user` object, there are two properties: +Trong đối tượng `user`, có hai thuộc tính: -1. The first property has the name `"name"` and the value `"John"`. -2. The second one has the name `"age"` and the value `30`. +1. Thuộc tính thứ nhất có tên là `"name"` và giá trị là `"John"`. +2. Thuộc tính thứ hai có tên là `"age"` và giá trị là `30`. The resulting `user` object can be imagined as a cabinet with two signed files labeled "name" and "age". +Kết quả là đối tượng `user` có thể được tưởng tượng như một cái tủ với hai tệp được ký có nhãn "name" và "age". + ![user object](object-user.svg) -We can add, remove and read files from it any time. +Chúng ta có thể thêm, xóa và đọc tệp từ chúng mọi lúc. -Property values are accessible using the dot notation: +Các giá trị của thuộc tính có thể truy cập bằng cách sử dụng dấu chấm: ```js -// get property values of the object: +// lấy giá trị của thuộc tính trong đối tượng: alert( user.name ); // John alert( user.age ); // 30 ``` -The value can be of any type. Let's add a boolean one: +Giá trị có thể là bất kỳ kiểu dữ liệu nào. Hãy thêm một giá trị boolean: ```js user.isAdmin = true; @@ -62,7 +64,7 @@ user.isAdmin = true; ![user object 2](object-user-isadmin.svg) -To remove a property, we can use `delete` operator: +Để xóa thuộc tính, ta có thể dùng `delete`: ```js delete user.age; @@ -70,40 +72,40 @@ delete user.age; ![user object 3](object-user-delete.svg) -We can also use multiword property names, but then they must be quoted: +Chúng ta cũng có thể sử dụng tên thuộc tính có nhiều từ, nhưng sau đó chúng phải được bọc lại: ```js let user = { name: "John", age: 30, - "likes birds": true // multiword property name must be quoted + "likes birds": true // tên thuộc tính nhiều từ phải được bọc lại bằng dấu ngoặc kép }; ``` ![](object-user-props.svg) -The last property in the list may end with a comma: +Thuộc tính cuối cùng trong danh sách có thể kết thúc bằng dấu phẩy: ```js let user = { name: "John", age: 30*!*,*/!* } ``` -That is called a "trailing" or "hanging" comma. Makes it easier to add/remove/move around properties, because all lines become alike. +Đó được gọi là dấu phẩy "trailing" hay "hanging". Nó khiến dễ dàng thêm/xóa/di chuyển xung quanh các thuộc tính, bởi vì tất cả các dòng trở nên giống nhau. -## Square brackets +## Dấu ngoặc vuông -For multiword properties, the dot access doesn't work: +Với thuộc tính nhiều từ, truy cập bằng dấu chấm không hoạt động: ```js run -// this would give a syntax error +// sẽ có lỗi cú pháp user.likes birds = true ``` -That's because the dot requires the key to be a valid variable identifier. That is: no spaces and other limitations. +Đó là vì dấu chấm yêu cầu khóa phải là một biến định danh hợp lệ. Đó là: không chứa khoảng trắng và các giới hạn khác. -There's an alternative "square bracket notation" that works with any string: +Có một thay thế là "dấu ngoặc vuông" sẽ hoạt động với bất cứ chuỗi nào: ```js run let user = {}; @@ -118,20 +120,20 @@ alert(user["likes birds"]); // true delete user["likes birds"]; ``` -Now everything is fine. Please note that the string inside the brackets is properly quoted (any type of quotes will do). +Bây giờ mọi thứ đã tốt. Hãy lưu ý rằng chuỗi bên trong ngoặc được bao bọc chính xác (bất kỳ loại bao bọc nào cũng được). -Square brackets also provide a way to obtain the property name as the result of any expression -- as opposed to a literal string -- like from a variable as follows: +Dấu ngoặc vuông cũng cung cấp một cách để có được tên thuộc tính là kết quả của bất kỳ biểu thức nào -- trái ngược với chuỗi ký tự -- giống như từ một biến như sau: ```js let key = "likes birds"; -// same as user["likes birds"] = true; +// giống như user["likes birds"] = true; user[key] = true; ``` -Here, the variable `key` may be calculated at run-time or depend on the user input. And then we use it to access the property. That gives us a great deal of flexibility. +Ở đây, biến `key` có thể được tính vào thời gian chạy hoặc phụ thuộc vào đầu vào của người dùng. Và sau đó chúng ta sử dụng nó để truy cập vào thuộc tính. Điều đó giúp chúng ta linh hoạt hơn. -For instance: +Ví dụ: ```js run let user = { @@ -139,13 +141,13 @@ let user = { age: 30 }; -let key = prompt("What do you want to know about the user?", "name"); +let key = prompt("Bạn muốn biết điều gì về người dùng?", "name"); -// access by variable -alert( user[key] ); // John (if enter "name") +// truy cập vào biến +alert( user[key] ); // John (nếu nhập "name") ``` -The dot notation cannot be used in a similar way: +Dấu chấm không thể dùng theo cách như vậy: ```js run let user = { @@ -157,40 +159,40 @@ let key = "name"; alert( user.key ) // undefined ``` -### Computed properties +### Thuộc tính computed -We can use square brackets in an object literal. That's called *computed properties*. +Chúng ta có thể sử dụng dấu ngoặc vuông trong một đối tượng theo nghĩa đen. Đó gọi là *thuộc tính computed*. -For instance: +Ví dụ: ```js run -let fruit = prompt("Which fruit to buy?", "apple"); +let fruit = prompt("Mua loại trái cây nào?", "apple"); let bag = { *!* - [fruit]: 5, // the name of the property is taken from the variable fruit + [fruit]: 5, // tên của thuộc tính được lấy từ biến fruit */!* }; -alert( bag.apple ); // 5 if fruit="apple" +alert( bag.apple ); // 5 nếu fruit="apple" ``` -The meaning of a computed property is simple: `[fruit]` means that the property name should be taken from `fruit`. +Ý nghĩa của thuộc tính computed rất đơn giản: `[fruit]` có nghĩa là tên thuộc tính nên được lấy từ` fruit`. -So, if a visitor enters `"apple"`, `bag` will become `{apple: 5}`. +Do vậy, nếu người dùng nhập `"apple"`, `bag` sẽ thành `{apple: 5}`. -Essentially, that works the same as: +Về cơ bản, nó hoạt động giống như: ```js run -let fruit = prompt("Which fruit to buy?", "apple"); +let fruit = prompt("Mua loại trái cây nào?", "apple"); let bag = {}; -// take property name from the fruit variable +// lấy tên thuộc tính từ biến fruit bag[fruit] = 5; ``` -...But looks nicer. +...Nhưng nhìn tốt hơn. -We can use more complex expressions inside square brackets: +Chúng ta có thể sử dụng các biểu thức phức tạp hơn trong dấu ngoặc vuông: ```js let fruit = 'apple'; @@ -199,16 +201,16 @@ let bag = { }; ``` -Square brackets are much more powerful than the dot notation. They allow any property names and variables. But they are also more cumbersome to write. +Dấu ngoặc vuông có mạnh hơn dấu chấm. Chúng chấp nhận bất cứ tên của thuộc tính và biến nào. Nhưng ngoài ra chúng cũng cồng kềnh khi viết. -So most of the time, when property names are known and simple, the dot is used. And if we need something more complex, then we switch to square brackets. +Vì vậy hầu hết thời gian, khi tên thuộc tính được biết và đơn giản, dấu chấm được sử dụng. Và nếu chúng ta cần một cái gì đó phức tạp hơn, thì chúng ta chuyển sang dấu ngoặc vuông. ````smart header="Reserved words are allowed as property names" -A variable cannot have a name equal to one of language-reserved words like "for", "let", "return" etc. +Một biến không thể có tên bằng một trong những từ dành riêng cho ngôn ngữ như "for", "let", "return", vâng vâng. -But for an object property, there's no such restriction. Any name is fine: +Nhưng đối với một thuộc tính đối tượng, không có hạn chế đó. Tên nào cũng được: ```js run let obj = { @@ -220,7 +222,7 @@ let obj = { alert( obj.for + obj.let + obj.return ); // 6 ``` -Basically, any name is allowed, but there's a special one: `"__proto__"` that gets special treatment for historical reasons. For instance, we can't set it to a non-object value: +Về cơ bản, bất kỳ tên nào cũng được cho phép, nhưng có một tên đặc biệt: `" __proto __ "` được đối xử đặc biệt vì lý do lịch sử. Chẳng hạn, chúng ta không thể đặt nó vào một giá trị phi đối tượng: ```js run let obj = {}; @@ -228,30 +230,30 @@ obj.__proto__ = 5; alert(obj.__proto__); // [object Object], didn't work as intended ``` -As we see from the code, the assignment to a primitive `5` is ignored. +Như chúng ta thấy từ code, việc gán cho một biến nguyên thủy `5` bị bỏ qua. -That can become a source of bugs and even vulnerabilities if we intend to store arbitrary key-value pairs in an object, and allow a visitor to specify the keys. +Điều đó có thể trở thành nguyên nhân gây ra lỗi và thậm chí là lỗ hổng nếu ta dự định lưu trữ các cặp khóa-giá trị tùy ý trong một đối tượng và cho phép người dùng truy cập các khóa cụ thể. -In that case the visitor may choose `__proto__` as the key, and the assignment logic will be ruined (as shown above). +Trong trường hợp đó, người dùng có thể chọn `__proto__` làm khóa và logic gán sẽ bị hủy (như được hiển thị ở trên). -There is a way to make objects treat `__proto__` as a regular property, which we'll cover later, but first we need to know more about objects. +Có một cách để làm cho các đối tượng coi `__proto__` như một thuộc tính thông thường, chúng ta sẽ đề cập sau, nhưng trước tiên chúng ta cần biết thêm về các đối tượng. -There's also another data structure [Map](info:map-set), that we'll learn in the chapter , which supports arbitrary keys. +Ngoài ra còn có một cấu trúc dữ liệu khác [Map](info:map-set), mà chúng ta sẽ tìm hiểu trong chương , nó hỗ trợ các khóa tùy ý. ```` -## Property value shorthand +## Tốc ký giá trị của thuộc tính -In real code we often use existing variables as values for property names. +Trong code chúng ta thường sử dụng các biến sẵn có làm giá trị cho tên của các thuộc tính. -For instance: +Ví dụ: ```js run function makeUser(name, age) { return { name: name, age: age - // ...other properties + // ...các thuộc tính khác }; } @@ -259,102 +261,101 @@ let user = makeUser("John", 30); alert(user.name); // John ``` -In the example above, properties have the same names as variables. The use-case of making a property from a variable is so common, that there's a special *property value shorthand* to make it shorter. +Trong ví dụ trên, các thuộc tính có cùng tên với các biến. Trường hợp sử dụng để tạo một thuộc tính từ một biến là rất phổ biến, do đó có một loại *tốc ký giá trị của thuộc tính* làm cho nó ngắn hơn. -Instead of `name:name` we can just write `name`, like this: +Thay vì `name:name` chúng ta có thể viết `name`, như thế này: ```js function makeUser(name, age) { *!* return { - name, // same as name: name - age // same as age: age + name, // giống như name: name + age // giống như age: age // ... }; */!* } ``` -We can use both normal properties and shorthands in the same object: +Chúng ta có thể sử dụng cả thuộc tính bình thường và tốc ký trong cùng một đối tượng: ```js let user = { - name, // same as name:name + name, // giống như name:name age: 30 }; ``` -## Existence check +## Kiểm tra tồn tại -A notable objects feature is that it's possible to access any property. There will be no error if the property doesn't exist! Accessing a non-existing property just returns `undefined`. It provides a very common way to test whether the property exists -- to get it and compare vs undefined: +Một tính năng đáng chú ý của đối tượng là có thể truy cập bất kỳ thuộc tính nào. Sẽ không có lỗi nếu thuộc tính không tồn tại! Truy cập một thuộc tính không tồn tại chỉ trả về `undefined`. Nó cung cấp một cách rất phổ biến để kiểm tra xem thuộc tính có tồn tại hay không - lấy nó và so sánh với undefined: ```js run let user = {}; -alert( user.noSuchProperty === undefined ); // true means "no such property" +alert( user.noSuchProperty === undefined ); // true có nghĩa là "no such property" ``` -There also exists a special operator `"in"` to check for the existence of a property. +Ngoài ra còn tồn tại một toán tử đặc biệt `"in"` để kiểm tra sự tồn tại của một thuộc tính. -The syntax is: +Cú pháp: ```js "key" in object ``` -For instance: +Ví dụ: ```js run let user = { name: "John", age: 30 }; -alert( "age" in user ); // true, user.age exists -alert( "blabla" in user ); // false, user.blabla doesn't exist +alert( "age" in user ); // true, user.age tồn tại +alert( "blabla" in user ); // false, user.blabla không tồn tại ``` -Please note that on the left side of `in` there must be a *property name*. That's usually a quoted string. +Hãy lưu ý rằng ở phía bên trái của `in` phải có *tên thuộc tính*. Đó thường là một chuỗi được bao bọc trong dấu ngoặc kép. -If we omit quotes, that would mean a variable containing the actual name will be tested. For instance: +Nếu chúng ta bỏ qua dấu ngoặc kép, điều đó có nghĩa là một biến chứa tên thực tế sẽ được kiểm tra. Ví dụ: ```js run let user = { age: 30 }; let key = "age"; -alert( *!*key*/!* in user ); // true, takes the name from key and checks for such property +alert( *!*key*/!* in user ); // true, lấy tên từ key và kiểm tra thuộc tính đó ``` ````smart header="Using \"in\" for properties that store `undefined`" -Usually, the strict comparison `"=== undefined"` check the property existance just fine. But there's a special case when it fails, but `"in"` works correctly. +Thông thường, sự so sánh chặt `"=== undefined"` kiểm tra sự tồn tại của thuộc tính. Có một trường hợp đặc biệt nó sẽ sai, nhưng với `"in"` thì chạy chính xác. -It's when an object property exists, but stores `undefined`: +Đó là khi một thuộc tính trong đối tượng tồn tại, nhưng lưu trữ là `undefined`: ```js run let obj = { test: undefined }; -alert( obj.test ); // it's undefined, so - no such property? +alert( obj.test ); // thuộc tính không tồn tại, do đó - nó không phải là thuộc tính? -alert( "test" in obj ); // true, the property does exist! +alert( "test" in obj ); // true, thuộc tính tồn tại! ``` -In the code above, the property `obj.test` technically exists. So the `in` operator works right. +Trong đoạn code trên, thuộc tính `obj.test` về mặt kỹ thuật tồn tại. Vì vậy, toán tử `in` hoạt động đúng. -Situations like this happen very rarely, because `undefined` is usually not assigned. We mostly use `null` for "unknown" or "empty" values. So the `in` operator is an exotic guest in the code. -```` +Các tình huống như thế này rất hiếm khi xảy ra, vì `undefined` thường không được chỉ định. Chúng ta chủ yếu sử dụng `null` cho các giá trị "không xác định" hoặc "rỗng". Vì vậy, toán tử `in` là một vị khách kỳ lạ trong code. -## The "for..in" loop +## Vòng lặp "for..in" -To walk over all keys of an object, there exists a special form of the loop: `for..in`. This is a completely different thing from the `for(;;)` construct that we studied before. +Để đi qua tất cả các khóa của một đối tượng, ta có một dạng vòng lặp đặc biệt: `for..in`. Đây là một điều hoàn toàn khác với cấu trúc `for (;;)` mà chúng ta đã học trước đây. -The syntax: +Cú pháp: ```js for (key in object) { - // executes the body for each key among object properties + // thực thi phần thân cho mỗi key của thuộc tính trong đối tượng } ``` -For instance, let's output all properties of `user`: +Ví dụ, in ra tất cả các thuộc tính của `user`: ```js run let user = { @@ -364,33 +365,33 @@ let user = { }; for (let key in user) { - // keys + // các khóa alert( key ); // name, age, isAdmin - // values for the keys + // giá trị của các khóa alert( user[key] ); // John, 30, true } ``` -Note that all "for" constructs allow us to declare the looping variable inside the loop, like `let key` here. +Lưu ý rằng tất cả các cấu trúc "for" cho phép chúng ta khai báo biến vòng lặp bên trong vòng lặp, như `let key` ở đây. -Also, we could use another variable name here instead of `key`. For instance, `"for (let prop in obj)"` is also widely used. +Ngoài ra, chúng ta có thể sử dụng một tên biến khác ở đây thay vì `key`. Chẳng hạn, `"for (let prop in obj)"` cũng được sử dụng rộng rãi. -### Ordered like an object +### Sắp xếp một đối tượng -Are objects ordered? In other words, if we loop over an object, do we get all properties in the same order they were added? Can we rely on this? +Đối tượng có được sắp xếp không? Nói cách khác, nếu chúng ta lặp qua một đối tượng, chúng ta có nhận được tất cả các thuộc tính theo cùng thứ tự chúng đã được thêm không? Chúng ta có thể tin vào điều này không? -The short answer is: "ordered in a special fashion": integer properties are sorted, others appear in creation order. The details follow. +Câu trả lời là: "sắp xếp theo kiểu đặc biệt": thuộc tính số nguyên được sắp xếp, những cái khác xuất hiện theo thứ tự tạo. Các chi tiết theo sau. -As an example, let's consider an object with the phone codes: +Ví dụ: hãy xem xét một đối tượng có chứa mã điện thoại: ```js run let codes = { - "49": "Germany", - "41": "Switzerland", - "44": "Great Britain", + "49": "Đức", + "41": "Thụy Sĩ", + "44": "Anh", // .., - "1": "USA" + "1": "Mỹ" }; *!* @@ -400,56 +401,56 @@ for (let code in codes) { */!* ``` -The object may be used to suggest a list of options to the user. If we're making a site mainly for German audience then we probably want `49` to be the first. +Đối tượng có thể được sử dụng để đề xuất một danh sách các tùy chọn cho người dùng. Nếu chúng ta tạo một trang chủ yếu cho người Đức thì có lẽ chúng ta muốn `49` đứng đầu tiên. -But if we run the code, we see a totally different picture: +Nhưng nếu chúng ta chạy code, chúng ta sẽ thấy một bức tranh hoàn toàn khác: -- USA (1) goes first -- then Switzerland (41) and so on. +- Mỹ (1) đứng đầu +- sau đó Thụy Sỹ (41) và cứ thế. -The phone codes go in the ascending sorted order, because they are integers. So we see `1, 41, 44, 49`. +Các mã điện thoại đi theo thứ tự tăng dần, bởi vì chúng là số nguyên. Vì vậy, chúng ta thấy `1, 41, 44, 49`. ````smart header="Integer properties? What's that?" -The "integer property" term here means a string that can be converted to-and-from an integer without a change. +Thuật ngữ "thuộc tính số nguyên" ở đây có nghĩa là một chuỗi có thể được chuyển đổi thành và từ một số nguyên mà không thay đổi. -So, "49" is an integer property name, because when it's transformed to an integer number and back, it's still the same. But "+49" and "1.2" are not: +Do đó, "49" là thuộc thuộc tính số nguyên, vì khi nó được chuyển đổi sang số nguyên và ngược lại, nó vẫn giống nhau. Nhưng "+49" và "1.2" thì không: ```js run -// Math.trunc is a built-in function that removes the decimal part -alert( String(Math.trunc(Number("49"))) ); // "49", same, integer property -alert( String(Math.trunc(Number("+49"))) ); // "49", not same "+49" ⇒ not integer property -alert( String(Math.trunc(Number("1.2"))) ); // "1", not same "1.2" ⇒ not integer property +// Math.trunc là một hàm dựng sẵn để xóa một phần của số thập phân +alert( String(Math.trunc(Number("49"))) ); // "49", giống nhau, thuộc tính số nguyên +alert( String(Math.trunc(Number("+49"))) ); // "49", không giống "+49" ⇒ không phải thuộc tính số nguyên +alert( String(Math.trunc(Number("1.2"))) ); // "1", không giống "1.2" ⇒ không phải thuộc tính số nguyên ``` ```` -...On the other hand, if the keys are non-integer, then they are listed in the creation order, for instance: +...Mặt khác, nếu các khóa không phải là số nguyên, thì chúng được liệt kê theo thứ tự tạo, Ví dụ: ```js run let user = { name: "John", surname: "Smith" }; -user.age = 25; // add one more +user.age = 25; // thêm một thuộc tính nữa *!* -// non-integer properties are listed in the creation order +// thuộc tính không nguyên được liệt kê theo thứ tự tạo */!* for (let prop in user) { alert( prop ); // name, surname, age } ``` -So, to fix the issue with the phone codes, we can "cheat" by making the codes non-integer. Adding a plus `"+"` sign before each code is enough. +Vì vậy, để khắc phục sự cố với mã điện thoại, chúng ta có thể "gian lận" bằng cách làm cho mã không nguyên. Thêm dấu cộng `"+"` trước mỗi mã là đủ. -Like this: +Như sau: ```js run let codes = { - "+49": "Germany", - "+41": "Switzerland", - "+44": "Great Britain", + "+49": "Đức", + "+41": "Thụy Sỹ", + "+44": "Anh", // .., - "+1": "USA" + "+1": "Mỹ" }; for (let code in codes) { @@ -457,30 +458,30 @@ for (let code in codes) { } ``` -Now it works as intended. +Bây giờ nó hoạt động như ý muốn. -## Copying by reference +## Sao chép bằng tham chiếu -One of the fundamental differences of objects vs primitives is that they are stored and copied "by reference". +Một trong những khác biệt cơ bản của các đối tượng so với nguyên thủy là chúng được lưu trữ và sao chép "bằng tham chiếu". -Primitive values: strings, numbers, booleans -- are assigned/copied "as a whole value". +Các giá trị nguyên thủy: chuỗi, số, booleans - được gán/sao chép "dưới dạng toàn bộ giá trị". -For instance: +Ví dụ: ```js let message = "Hello!"; let phrase = message; ``` -As a result we have two independent variables, each one is storing the string `"Hello!"`. +Kết quả là chúng ta có hai biến độc lập, mỗi biến được lưu trữ chuỗi `"Hello!"`. ![](variable-copy-value.svg) -Objects are not like that. +Đối tượng không như thế. -**A variable stores not the object itself, but its "address in memory", in other words "a reference" to it.** +**Một biến lưu trữ không phải chính là đối tượng, mà là "địa chỉ trong bộ nhớ", nói cách khác là "một tham chiếu" đến nó.** -Here's the picture for the object: +Đây là hình ảnh cho đối tượng: ```js let user = { @@ -490,25 +491,25 @@ let user = { ![](variable-contains-reference.svg) -Here, the object is stored somewhere in memory. And the variable `user` has a "reference" to it. +Ở đây, đối tượng được lưu trữ ở đâu đó trong bộ nhớ. Và biến `user` có "tham chiếu" đến nó. -**When an object variable is copied -- the reference is copied, the object is not duplicated.** +**Khi một biến đối tượng được sao chép -- tham chiếu được sao chép, đối tượng không được sao chép.** -If we imagine an object as a cabinet, then a variable is a key to it. Copying a variable duplicates the key, but not the cabinet itself. +Nếu chúng ta tưởng tượng một đối tượng như một cái tủ, thì một biến là một chìa khóa cho nó. Sao chép một biến là sao chép khóa, nhưng không phải chính tủ. -For instance: +Ví dụ: ```js no-beautify let user = { name: "John" }; -let admin = user; // copy the reference +let admin = user; // sao chép tham chiếu ``` -Now we have two variables, each one with the reference to the same object: +Bây giờ chúng ta có hai biến, mỗi biến có tham chiếu đến cùng một đối tượng: ![](variable-copy-reference.svg) -We can use any variable to access the cabinet and modify its contents: +Chúng ta có thể sử dụng bất kỳ biến nào để truy cập vào bên trong và sửa đổi nội dung của nó: ```js run let user = { name: 'John' }; @@ -516,46 +517,46 @@ let user = { name: 'John' }; let admin = user; *!* -admin.name = 'Pete'; // changed by the "admin" reference +admin.name = 'Pete'; // thay đổi bởi tham chiếu của "admin" */!* -alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference +alert(*!*user.name*/!*); // 'Pete', những thay đổi được nhìn thấy từ tham chiếu của "user" ``` -The example above demonstrates that there is only one object. As if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use the other key (`user`) we would see changes. +Ví dụ trên chứng tỏ rằng chỉ có một đối tượng. Như thể chúng ta có một cái tủ có hai chìa khóa và sử dụng một trong số chúng (`admin`) để vào trong đó. Sau đó, nếu sau này chúng ta sử dụng khóa khác (`user`), chúng ta sẽ thấy các thay đổi. -### Comparison by reference +### So sánh bằng tham chiếu -The equality `==` and strict equality `===` operators for objects work exactly the same. +Các toán tử `==` và đẳng thức `===` cho các đối tượng hoạt động giống hệt nhau. -**Two objects are equal only if they are the same object.** +**Hai đối tượng chỉ bằng nhau nếu chúng là cùng một đối tượng.** -For instance, if two variables reference the same object, they are equal: +Ví dụ, nếu hai biến tham chiếu cùng một đối tượng, chúng bằng nhau: ```js run let a = {}; -let b = a; // copy the reference +let b = a; // sao chép tham chiếu -alert( a == b ); // true, both variables reference the same object +alert( a == b ); // true, hay biến tham chiếu đến cùng một đối tượng alert( a === b ); // true ``` -And here two independent objects are not equal, even though both are empty: +Và ở đây hai đối tượng độc lập không bằng nhau, mặc dù cả hai đều trống: ```js run let a = {}; -let b = {}; // two independent objects +let b = {}; // hai đối tượng độc lập alert( a == b ); // false ``` -For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to tell the truth, such comparisons are necessary very rarely and usually are a result of a coding mistake. +Để so sánh như `obj1 > obj2` hoặc để so sánh với `obj == 5` nguyên thủy, các đối tượng được chuyển đổi thành nguyên thủy. Chúng ta sẽ nghiên cứu cách chuyển đổi đối tượng hoạt động rất sớm, nhưng để nói sự thật, việc so sánh như vậy là rất cần thiết rất hiếm khi và thường là kết quả của một lỗi mã hóa. -### Const object +### Đối tượng hằng số -An object declared as `const` *can* be changed. +Một đối tượng được khai báo là `const` *có thể* được thay đổi. -For instance: +Ví dụ: ```js run const user = { @@ -569,9 +570,9 @@ user.age = 25; // (*) alert(user.age); // 25 ``` -It might seem that the line `(*)` would cause an error, but no, there's totally no problem. That's because `const` fixes only value of `user` itself. And here `user` stores the reference to the same object all the time. The line `(*)` goes *inside* the object, it doesn't reassign `user`. +Có vẻ như dòng `(*)` sẽ gây ra lỗi, nhưng không, hoàn toàn không có vấn đề gì. Đó là bởi vì `const` chỉ sửa giá trị của chính `user`. Và ở đây `user` lưu trữ tham chiếu mọi lúc đến cùng một đối tượng. Dòng `(*)` vào *bên trong* đối tượng, nó không gán lại cho `user`. -The `const` would give an error if we try to set `user` to something else, for instance: +`const` sẽ báo lỗi nếu chúng ta cố gắng gán `user` thành thứ khác, Ví dụ: ```js run const user = { @@ -579,26 +580,26 @@ const user = { }; *!* -// Error (can't reassign user) +// Lỗi (không thể gán lại biến user) */!* user = { name: "Pete" }; ``` -...But what if we want to make constant object properties? So that `user.age = 25` would give an error. That's possible too. We'll cover it in the chapter . +...Nhưng nếu chúng ta muốn tạo các thuộc tính đối tượng không đổi thì sao? Vì vậy, `user.age = 25` sẽ báo lỗi. Điều đó cũng có thể. Chúng tôi sẽ đề cập đến nó trong chương . -## Cloning and merging, Object.assign +## Sao chép và gộp, Object.assign -So, copying an object variable creates one more reference to the same object. +Vì vậy, sao chép một biến đối tượng sẽ tạo thêm một tham chiếu đến cùng một đối tượng. -But what if we need to duplicate an object? Create an independent copy, a clone? +Nhưng nếu chúng ta cần sao chép một đối tượng thì sao? Tạo một sao chép độc lập, một bản sao? -That's also doable, but a little bit more difficult, because there's no built-in method for that in JavaScript. Actually, that's rarely needed. Copying by reference is good most of the time. +Điều đó cũng có thể thực hiện được, nhưng khó khăn hơn một chút, vì không có phương thức tích hợp sẵn cho JavaScript. Trên thực tế, điều đó hiếm khi cần thiết. Sao chép bằng tham chiếu trong nhiều trường hợp là tốt nhất. -But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its properties and copying them on the primitive level. +Nhưng nếu chúng ta thực sự muốn điều đó, thì chúng ta cần tạo một đối tượng mới và sao chép cấu trúc của đối tượng hiện có bằng cách lặp lại các thuộc tính của nó và sao chép chúng ở cấp độ nguyên thủy. -Like this: +Như thế này: ```js run let user = { @@ -607,32 +608,32 @@ let user = { }; *!* -let clone = {}; // the new empty object +let clone = {}; // đối tượng mới rỗng -// let's copy all user properties into it +// hãy sao chép tất cả các thuộc tính của user vào nó for (let key in user) { clone[key] = user[key]; } */!* -// now clone is a fully independent clone -clone.name = "Pete"; // changed the data in it +// bây giờ bản sao là một bản sao hoàn toàn độc lập +clone.name = "Pete"; // thay đổi dữ liệu bên trong nó -alert( user.name ); // still John in the original object +alert( user.name ); // vẫn còn là John trong đối tượng gốc ``` -Also we can use the method [Object.assign](mdn:js/Object/assign) for that. +Ngoài ra, chúng ta có thể sử dụng phương thức [Object.assign](mdn:js/Object/assign) cho điều đó. -The syntax is: +Cú pháp là: ```js Object.assign(dest, [src1, src2, src3...]) ``` -- Arguments `dest`, and `src1, ..., srcN` (can be as many as needed) are objects. -- It copies the properties of all objects `src1, ..., srcN` into `dest`. In other words, properties of all arguments starting from the 2nd are copied into the 1st. Then it returns `dest`. +- Đối số `dest`, và `src1, ..., srcN` (có thể nhiều nhất có thể) là đối tượng. +- Nó sao chép các thuộc tính của tất cả các đối tượng `src1, ..., srcN` vào `dest`. Nói cách khác, các thuộc tính của tất cả các đối số bắt đầu từ thứ 2 được sao chép vào thứ 1. Sau đó, nó trả về `dest`. -For instance, we can use it to merge several objects into one: +Ví dụ, chúng ta có thể sử dụng nó để hợp nhất một số đối tượng thành một: ```js let user = { name: "John" }; @@ -640,25 +641,25 @@ let permissions1 = { canView: true }; let permissions2 = { canEdit: true }; *!* -// copies all properties from permissions1 and permissions2 into user +// sao chép tất cả các thuộc tính từ permissions1 và permissions2 vào user Object.assign(user, permissions1, permissions2); */!* -// now user = { name: "John", canView: true, canEdit: true } +// bây giờ user = { name: "John", canView: true, canEdit: true } ``` -If the receiving object (`user`) already has the same named property, it will be overwritten: +Nếu đối tượng nhận (`user`) đã có cùng thuộc tính được đặt tên, nó sẽ bị ghi đè: ```js let user = { name: "John" }; -// overwrite name, add isAdmin +// ghi đè name, thêm isAdmin Object.assign(user, { name: "Pete", isAdmin: true }); -// now user = { name: "Pete", isAdmin: true } +// bây giờ user = { name: "Pete", isAdmin: true } ``` -We also can use `Object.assign` to replace the loop for simple cloning: +Chúng ta cũng có thể sử dụng `Object.assign` để thay thế vòng lặp để tạo bản sao đơn giản: ```js let user = { @@ -671,11 +672,11 @@ let clone = Object.assign({}, user); */!* ``` -It copies all properties of `user` into the empty object and returns it. Actually, the same as the loop, but shorter. +Nó sao chép tất cả các thuộc tính của `user` vào đối tượng trống và trả về nó. Trên thực tế, nó giống như vòng lặp, nhưng ngắn hơn. -Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. What to do with them? +Cho đến bây giờ chúng ta giả định rằng tất cả các thuộc tính của `user` là nguyên thủy. Nhưng các thuộc tính có thể được tham chiếu đến các đối tượng khác. Làm gì với chúng đây? -Like this: +Như thế này: ```js run let user = { name: "John", @@ -688,9 +689,9 @@ let user = { alert( user.sizes.height ); // 182 ``` -Now it's not enough to copy `clone.sizes = user.sizes`, because the `user.sizes` is an object, it will be copied by reference. So `clone` and `user` will share the same sizes: +Bây giờ nó không đủ để sao chép `clone.sizes = user.sizes`, vì` user.sizes` là một đối tượng, nó sẽ được sao chép bằng tham chiếu. Vì vậy, `clone` và` user` sẽ có cùng kích thước: -Like this: +Như thế này: ```js run let user = { name: "John", @@ -702,49 +703,49 @@ let user = { let clone = Object.assign({}, user); -alert( user.sizes === clone.sizes ); // true, same object +alert( user.sizes === clone.sizes ); // true, cùng đối tượng -// user and clone share sizes -user.sizes.width++; // change a property from one place -alert(clone.sizes.width); // 51, see the result from the other one +// user và clone chia sẻ chung sizes +user.sizes.width++; // thay đổi thuộc tính từ một nơi +alert(clone.sizes.width); // 51, ta thấy kết quả ở một nơi khác ``` -To fix that, we should use the cloning loop that examines each value of `user[key]` and, if it's an object, then replicate its structure as well. That is called a "deep cloning". +Để khắc phục điều đó, chúng ta nên sử dụng vòng lặp nhân bản để kiểm tra từng giá trị của `user [key]` và, nếu đó là một đối tượng, thì cũng sao chép cấu trúc của nó. Điều đó được gọi là "nhân bản sâu". -There's a standard algorithm for deep cloning that handles the case above and more complex cases, called the [Structured cloning algorithm](http://w3c.github.io/html/infrastructure.html#safe-passing-of-structured-data). In order not to reinvent the wheel, we can use a working implementation of it from the JavaScript library [lodash](https://lodash.com), the method is called [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). +Có một thuật toán tiêu chuẩn để nhân bản sâu xử lý trường hợp trên và các trường hợp phức tạp hơn, được gọi là [Thuật toán nhân bản có cấu trúc](http://w3c.github.io/html/infrastructure.html#safe-passing-of-structured-data). Để không phát minh lại bánh xe, chúng ta có thể sử dụng triển khai thực hiện nó từ thư viện JavaScript [lodash](https://lodash.com), phương thức được gọi là [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). -## Summary +## Tổng kết -Objects are associative arrays with several special features. +Đối tượng là mảng kết hợp với một số tính năng đặc biệt. -They store properties (key-value pairs), where: -- Property keys must be strings or symbols (usually strings). -- Values can be of any type. +Họ lưu trữ các thuộc tính (các cặp khóa-giá trị), trong đó: +- Thuộc tính khóa phải là chuỗi hoặc ký hiệu (thường là chuỗi). +- Giá trị có thể là bất kỳ loại nào. -To access a property, we can use: -- The dot notation: `obj.property`. -- Square brackets notation `obj["property"]`. Square brackets allow to take the key from a variable, like `obj[varWithKey]`. +Để truy cập một thuộc tính, chúng ta có thể sử dụng: +- Ký hiệu dấu chấm: `obj.property`. +- Ký hiệu ngoặc vuông `obj["property"]`. Dấu ngoặc vuông cho phép lấy khóa từ một biến, như `obj[varWithKey]`. -Additional operators: -- To delete a property: `delete obj.prop`. -- To check if a property with the given key exists: `"key" in obj`. -- To iterate over an object: `for (let key in obj)` loop. +Toán tử bổ sung: +- Để xóa một thuộc tính: `delete obj.prop`. +- Để kiểm tra xem một thuộc tính có khóa đã cho có tồn tại không: `"key" in obj`. +- Để lặp qua một đối tượng: `for (let key in obj)` -Objects are assigned and copied by reference. In other words, a variable stores not the "object value", but a "reference" (address in memory) for the value. So copying such a variable or passing it as a function argument copies that reference, not the object. All operations via copied references (like adding/removing properties) are performed on the same single object. +Các đối tượng được gán và sao chép bằng tham chiếu. Nói cách khác, một biến không lưu trữ "giá trị của đối tượng", mà là "tham chiếu" (địa chỉ trong bộ nhớ) cho giá trị. Vì vậy, sao chép một biến như vậy hoặc chuyển nó dưới dạng đối số hàm sẽ sao chép tham chiếu đó, không phải đối tượng. Tất cả các hoạt động thông qua sao chép tham chiếu (như thêm/xóa thuộc tính) được thực hiện trên cùng một đối tượng. -To make a "real copy" (a clone) we can use `Object.assign` or [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). +Để tạo một "sao chép thực" (bản sao) chúng ta có thể sử dụng `Object.assign` hoặc [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). -What we've studied in this chapter is called a "plain object", or just `Object`. +Những gì chúng ta đã nghiên cứu trong chương này được gọi là "đối tượng đơn giản", hoặc `Object`. -There are many other kinds of objects in JavaScript: +Có nhiều loại đối tượng khác trong JavaScript: -- `Array` to store ordered data collections, -- `Date` to store the information about the date and time, -- `Error` to store the information about an error. -- ...And so on. +- `Array` để lưu trữ các bộ sưu tập dữ liệu theo thứ tự, +- `Ngày` để lưu trữ thông tin về ngày và giờ, +- `Error` để lưu trữ thông tin về lỗi. +- ...Và nhiều hơn. -They have their special features that we'll study later. Sometimes people say something like "Array type" or "Date type", but formally they are not types of their own, but belong to a single "object" data type. And they extend it in various ways. +Chúng có những tính năng đặc biệt mà chúng ta sẽ nghiên cứu sau. Đôi khi mọi người nói một cái gì đó như "Kiểu mảng" hoặc "Kiểu ngày", nhưng chính thức chúng không có kiểu của riêng chúng, mà thuộc về một loại dữ liệu "đối tượng" duy nhất. Và chúng mở rộng nó theo nhiều cách khác nhau. -Objects in JavaScript are very powerful. Here we've just scratched the surface of a topic that is really huge. We'll be closely working with objects and learning more about them in further parts of the tutorial. +Các đối tượng trong JavaScript rất mạnh mẽ. Chúng ta chỉ vừa vạch ra bề mặt của một chủ đề thực sự rất lớn. Ta sẽ làm việc chặt chẽ với các đối tượng và tìm hiểu thêm về chúng trong các phần tiếp theo.