调试
debugger
- 可以使用
debugger
命令来暂停代码
1 2 3 4 5 6 7 |
function hello(name) { let phrase = `Hello, ${name}!`; debugger; // <-- 调试器会在这停止 say(phrase); } |
代码风格
花括号
1 2 3 4 5 |
if (condition) { // do this // ...and that // ...and that } |
行长度
- 没有人喜欢读一长串代码,最好将代码分割一下。
缩进
- 水平方向上的缩进:2 或 4 个空格。
- 垂直方向上的缩进:用于将代码拆分成逻辑块的空行。
1 2 3 4 5 6 7 8 9 |
function pow(x, n) { let result = 1; // <-- for (let i = 0; i < n; i++) { result *= x; } // <-- return result; } |
对象
- 对象则用来存储键值对和更复杂的实体。
- 可以通过使用带有可选 属性列表 的花括号
{…}
来创建对象。 - 一个属性就是一个键值对(“key: value”),其中键(
key
)是一个字符串(也叫做属性名),值(value
)可以是任何值。
1 2 |
let user = new Object(); // “构造函数” 的语法 let user = {}; // “字面量” 的语法 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
let user = { // 一个对象 name: "John", // 键 "name",值 "John" age: 30 // 键 "age",值 30 }; // 读取文件的属性: alert( user.name ); // John alert( user.age ); // 30 // 添加属性 user.isAdmin = true; // 移除属性 delete user.age; |
1 2 3 4 5 6 7 8 9 10 11 |
let user = { name: "John", age: 30, "likes birds": true // 多词属性名必须加引号 }; // 列表中的最后一个属性应以逗号结尾 let user = { name: "John", age: 30, } |
- 使用 const 声明的对象是可以被修改的(
const
声明仅固定了user
的值,而不是值(该对象)里面的内容。)
1 2 3 4 5 6 7 |
const user = { name: "John" }; user.name = "Pete"; // (*) alert(user.name); // Pete |
方括号
- 对于多词属性,点操作不能用。
- 方括号中的字符串要放在引号中,单引号或双引号都可以。
1 2 3 4 5 6 7 8 9 10 |
let user = {}; // 设置 user["likes birds"] = true; // 读取 alert(user["likes birds"]); // true // 删除 delete user["likes birds"]; |
1 2 3 4 |
let key = "likes birds"; // 跟 user["likes birds"] = true; 一样 user[key] = true; |
- 当创建一个对象时,我们可以在对象字面量中使用方括号。
1 2 3 4 5 6 7 |
let fruit = prompt("Which fruit to buy?", "apple"); let bag = { [fruit]: 5, // 属性名是从 fruit 变量中得到的 }; alert( bag.apple ); // 5 如果 fruit="apple" |
- 属性值简写。
1 2 3 4 5 6 7 8 9 10 |
function makeUser(name, age) { return { name: name, age: age, // ……其他的属性 }; } let user = makeUser("John", 30); alert(user.name); // John |
1 2 3 4 5 6 7 |
function makeUser(name, age) { return { name, // 与 name: name 相同 age, // 与 age: age 相同 // ... }; } |
- 变量名不能是编程语言的某个保留字,但对象的属性名并不受此限制。
1 2 3 4 5 6 7 8 |
// 这些属性都没问题 let obj = { for: 1, let: 2, return: 3 }; alert( obj.for + obj.let + obj.return ); // 6 |
1 2 3 4 5 |
// 一个名为 __proto__ 的属性。我们不能将它设置为一个非对象的值 let obj = {}; obj.__proto__ = 5; // 分配一个数字 alert(obj.__proto__); // [object Object] — 值为对象,与预期结果不同 |
属性存在性测试
- JavaScript 的对象有一个需要注意的特性:能够被访问任何属性。
- 即使属性不存在也不会报错!读取不存在的属性只会得到
undefined
。
1 2 3 4 |
// 可以很容易地判断一个属性是否存在: let user = {}; alert( user.noSuchProperty === undefined ); // true 意思是没有这个属性 |
- 还有一个特别的,检查属性是否存在的操作符
"in"
。
1 2 3 4 |
let user = { name: "John", age: 30 }; alert( "age" in user ); // true,user.age 存在 alert( "blabla" in user ); // false,user.blabla 不存在。 |
- 请注意,
in
的左边必须是 属性名。通常是一个带引号的字符串。如果我们省略引号,就意味着左边是一个变量,它应该包含要判断的实际属性名。
1 2 3 4 |
let user = { age: 30 }; let key = "age"; alert( key in user ); // true,属性 "age" 存在 |
- 为何会有
in
运算符呢?与undefined
进行比较来判断还不够吗?- 大部分情况下与
undefined
进行比较来判断就可以了。 - 但有一个例外情况,这种比对方式会有问题,但
in
运算符的判断结果仍是对的。(属性存在,但存储的值是undefined
的时候)
- 大部分情况下与
1 2 3 4 5 6 7 |
let obj = { test: undefined }; alert( obj.test ); // 显示 undefined,所以属性不存在? alert( "test" in obj ); // true,属性存在! |
for...in
- 语法
1 2 3 |
for (key in object) { // 对此对象属性中的每个键执行的代码 } |
- 示例
1 2 3 4 5 6 7 8 9 10 11 12 |
let user = { name: "John", age: 30, isAdmin: true }; for (let key in user) { // keys alert( key ); // name, age, isAdmin // 属性键的值 alert( user[key] ); // John, 30, true } |
对象的排序
- 如果我们遍历一个对象,我们获取属性的顺序是和属性添加时的顺序相同吗?
- “有特别的顺序”:整数属性会被进行排序,其他属性则按照创建的顺序显示。
1 2 3 4 5 6 7 8 9 10 11 |
let codes = { "49": "Germany", "41": "Switzerland", "44": "Great Britain", // .., "1": "USA" }; for(let code in codes) { alert(code); // 1, 41, 44, 49 } |
- 这里的“整数属性”指的是一个可以在不做任何更改的情况下与一个整数进行相互转换的字符串。
- “49” 是一个整数属性名,因为我们把它转换成整数,再转换回来,它还是一样的。但是 “+49” 和 “1.2” 就不行。
- 如果属性名不是整数,那它们就按照创建时的顺序来排序
1 2 3 4 5 6 7 8 9 10 |
let user = { name: "John", surname: "Smith" }; user.age = 25; // 增加一个 // 非整数属性是按照创建的顺序来排列的 for (let prop in user) { alert( prop ); // name, surname, age } |
对象的引用和复制
- 与原始类型相比,对象的根本区别之一是对象是“通过引用”被存储和复制的,与原始类型值相反:字符串,数字,布尔值等 —— 始终是以“整体值”的形式被复制的。
- 赋值了对象的变量存储的不是对象本身,而是该对象“在内存中的地址”,换句话说就是对该对象的“引用”。
- 当一个对象变量被复制 —— 引用则被复制,而该对象并没有被复制。
1 2 3 |
let user = { name: "John" }; let admin = user; // 复制引用 |
1 2 3 4 5 6 7 |
let user = { name: 'John' }; let admin = user; admin.name = 'Pete'; // 通过 "admin" 引用来修改 alert(user.name); // 'Pete',修改能通过 "user" 引用看到 |
- 比较
1 2 3 4 5 |
let a = {}; let b = a; // 复制引用 alert( a == b ); // true,都引用同一对象 alert( a === b ); // true |
1 2 3 4 |
let a = {}; let b = {}; // 两个独立的对象 alert( a == b ); // false |
- 克隆与合并
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
let user = { name: "John", age: 30 }; let clone = {}; // 新的空对象 // 将 user 中所有的属性拷贝到其中 for (let key in user) { clone[key] = user[key]; } // 现在 clone 是带有相同内容的完全独立的对象 clone.name = "Pete"; // 改变了其中的数据 alert( user.name ); // 原来的对象中的 name 属性依然是 John |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
let user = { name: "John" }; let permissions1 = { canView: true }; let permissions2 = { canEdit: true }; // 将 permissions1 和 permissions2 中的所有属性都拷贝到 user 中 Object.assign(user, permissions1, permissions2); // 现在 user = { name: "John", canView: true, canEdit: true } // 代替for...in let user = { name: "John", age: 30 }; let clone = Object.assign({}, user); |
1 2 3 4 5 6 7 |
// 如果被拷贝的属性的属性名已经存在,那么它会被覆盖: let user = { name: "John" }; Object.assign(user, { name: "Pete" }); alert(user.name); // 现在 user = { name: "Pete" } |
- 深层克隆
JavaScript
中的对象深拷贝是指创建一个新的对象,该对象的所有属性都具有与原始对象相同的值,但在内存中完全独立于原始对象。这意味着对新对象的修改不会影响原始对象,反之亦然- 实现对象的深拷贝通常涉及递归地复制对象的所有属性,包括嵌套对象和数组。方法如下:
- 递归复制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function deepCopy(obj) { if (obj === null || typeof obj !== 'object') { return obj; } let newObj = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = deepCopy(obj[key]); } } return newObj; } |
-
使用
json
序列化和反序列化
利用JSON.stringify()
方法将对象序列化为JSON
字符串,然后使用JSON.parse()
方法将JSON
字符串反序列化为新对象。这种方法简单易行,但不能处理对象中包含函数、正则表达式等非序列化的特殊类型123function deepCopy(obj) {return JSON.parse(JSON.stringify(obj));} -
使用第三方库,如
lodassh
和MARKDOWN_HASHb81453378a0c6e5389111178cf249c11MARKDOWN<em>HASH
JavaScript 库 lodash 中的 [.cloneDeep(obj)](https://lodash.com/docs#cloneDeep)。
1 2 3 4 |
const _ = require('lodash'); let obj = { /* 对象 */ }; let newObj = _.cloneDeep(obj); |
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ HTTP协议相关学习一03/22
- ♥ 【Javascript】数字,字符串,数组07/01
- ♥ 【Javascript】数组,可迭代对象04/04
- ♥ 【Javascript】第一部分05/10
- ♥ 【Javascript】第二部分05/14
- ♥ Electron04/03