对象
记录对象中的 api
属性
分为 常规属性
(properties ) 和 排序属性
(elements)
常规属性是键为字符串的属性(除了数字字符串)特点是根据创建时的顺序排序
排序属性是键为数字的属性,特点是根据数字排序
两个属性都有时,排序属性先于常规属性
快属性与慢属性
elements 对象和 properties 对象都是用线性结构存储属性的,但这样存在效率问题,因为在查某个属性的时候,要先去查 elements 对象或 properties 对象,增加了查找步骤。
为了减轻这个问题,引擎会将部分常规属性(默认是 10 个)直接存到对象下面,称为对象内属性。
1、将保存在线性结构里的属性称为快属性,快属性查找快,增删慢;
2、当对象属性很多的时候,就会放大线性结构增删属性的缺点,因此引擎会利用非线性结构(字典)来保存属性,这时候被存储的属性称为慢属性。
解构
解构是一层 浅拷贝
⭐undefined / null
每个解构属性都可以有一个默认值。当属性 不存在或值为 undefined 时,将使用默认值。
如果属性的值为 null,无法使用默认值。
const [ a = 1 ] = []; // a is 1
const { b = 2 } = { b: undefined }; // b is 2
const { c = 2 } = { c: null }; // c is null
赋值给新的变量名
const o = { p: 42, q: true };
const { p: foo, q: bar } = o;
console.log(foo); // 42
console.log(bar); // true
原理是
var o = { p: 42, q: true };
var foo = o.p, bar = o.q;
这么长时间过去了,还是记不住2024/2/1
赋值到新的变量名并提供默认值
一个属性可以同时是两者:
- 从对象提取并分配给具有不同名称的变量。
- 指定一个默认值,以防获取的值为 undefined。
const { a: aa = 10, b: bb = 5 } = { a: 3 };
console.log(aa); // 3
console.log(bb); // 5
从作为函数参数传递的对象中提取属性
const user = {
id: 42,
displayName: 'jdoe',
fullName: {
firstName: 'Jane',
lastName: 'Doe',
},
};
解构 id 属性
function userId({ id }) {
return id;
}
console.log(userId(user)); // 42
重新命名
function userDisplayName({ displayName: dname }) {
return dname;
}
console.log(userDisplayName(user)); // `jdoe`
相当于
function userDisplayName(_a) {
var dname = _a.displayName;
return dname;
}
嵌套对象
function whois({ displayName, fullName: { firstName: name } }) {
return `${displayName} is ${name}`;
}
console.log(whois(user)); // "jdoe is Jane"
可以把解构看成 {k:v}
形式,然后与传入对象一一对应, 所以 v
就是最终形式
设置函数参数的默认值
一定要写 {}
, 因为如果 drawChart
不传值的时候, 无法解构
function drawChart(
{ size = 'big', coords = { x: 0, y: 0 }, radius = 25 } = {}) {
console.log(size, coords, radius);
}
drawChart({
coords: { x: 18, y: 30 },
radius: 30,
});
组合数组和对象解构
const props = [
{ id: 1, name: 'Fizz'},
{ id: 2, name: 'Buzz'},
{ id: 3, name: 'FizzBuzz'}
];
const [,, { name }] = props;
console.log(name); // "FizzBuzz"
解构对象时查找原型链
当解构一个对象时,如果属性本身没有被访问,它将沿着原型链继续查找。
const obj = {
self: '123',
__proto__: {
prot: '456',
},
};
const { self, prot } = obj;
// self "123"
// prot "456" (Access to the prototype chain)
Object.create
Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)
const person = {
isHuman: false,
printIntroduction: function() {
console.log(this)
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};
const me = Object.create(person);
console.log(me.__proto__ == person) // true
Object.is
- 严格相等比较 (也被称作"strict equality", "identity", "triple equals"),使用 === ,
- 抽象相等比较 ("loose equality","double equals") ,使用 ==
- Object.is (ECMAScript 2015/ ES6 新特性)
TIP
⭐ Object.is() 与 === 也不相同。差别是它们对待有符号的零和 NaN 不同,例如,=== 运算符(也包括 == 运算符) 将数字 -0 和 +0 视为相等,而将 NaN 与 NaN 视为不相等。
Object.is(0, -0); // false
Object.is(0, +0); // true
Object.is(-0, -0); // true
Object.is(NaN, 0/0); // true
+0 === -0 // true
NaN === NaN // false
Object.fromEntries / Object.entries
from
从语义理解, 即一个键值对列表创建一个对象。
⭐
entries 是小条目,即 [k : v] 形式
Object.fromEntries(iterable) 列表转换为一个对象。
Object.entries(Object) 将对象转化为列表。
var obj = { foo: "bar", baz: 42 };
var map = new Map(Object.entries(obj));
console.log(map); // Map { 'foo' => 'bar', 'baz' => 42 }
const queryString = "?name=jimmy&age=18&height=1.88";
const queryParams = new URLSearchParams(queryString);
// URLSearchParams { 'name' => 'jimmy', 'age' => '18', 'height' => '1.88' }
queryParams // 和 Map 的数据结构一致
const paramObj = Object.fromEntries(queryParams);
console.log(paramObj); // { name: 'jimmy', age: '18', height: '1.88' }
处理 Array
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
const obj = Object.fromEntries(arr);
console.log(obj); // { 0: "a", 1: "b", 2: "c" }
Object.assign
Object.assign() 静态方法将一个或者多个源对象中所有可枚举的自有属性复制到目标对象,并返回修改后的目标对象。
TIP
Object.assign() 不会在 source 对象值为 null 或 undefined 时抛出错误
原型链上的属性和不可枚举属性不能被复制
const v1 = 'abc';
const v2 = true;
const v3 = 10;
const v4 = Symbol('foo');
const obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 原始类型会被包装,null 和 undefined 会被忽略
// 注意,只有字符串封装对象才拥有可枚举的自有属性。
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
{...undefined} // {}
{...true} // {}
// 类似于 Object.assign({},undefined)
TIP
个人理解: Object.assign
对 iterator
属性的转化为对象形式,所以 abc
可以被转成 { 0:'a', 1:"b", 2:'c' }
展开语法
将数组表达式或者 string 在语法层面展开;还可以在构造字面量对象时,将对象表达式按 key-value 的方式展开 对于不能展开的语法,直接返回 {}
{...undefined} // {}
{...null} // {}
{...1} // {}
{...false} // {}
{...'abcd'} // { 0:'a', 1:'b', 2:'c', 3:'d' }
INFO
展开语法也是对 可迭代对象 有效
null / undefined
var obj = {}
obj.a // undefined
document.getElementById('a') // null
document.parentNode // null
⭐ null 表示无对象,也可以表示为一个对象,但他什么也没有,比如这个 document 本身应该有对象,但没有所以是 null, 也可以理解为 在 栈内存中没有引用堆内存中的指针
Number(null) // 0
// 把他当做 值使用
Number(undefined) // NaN
null == 0 // false
undefined == 0 // false
// 从语义上理解, null 和 undefined 都是表示无值,只不过一个没有对象,一个是没有值
null == undefined // true
🔗可迭代对象
let o = {
a:1,
b:2
}
let [a,b] = o;
因为 o
不是一个可迭代对象,所以会报错,需要把 o
变为可迭代对象,添加 Symbol.iterator
方法并返回 next
对象
let o = {
a: 1,
b: 2,
[Symbol.iterator]() {
let values = Object.values(this);
let index = 0;
return {
next: () => {
if (index <= values.length) {
let value = values[index];
index++;
return { value, done: false };
} else {
return {
done: true
}
}
}
}
}
}
设置/获取 可枚举
Object.defineProperty(obj, "key2", {
enumerable: false,
configurable: false,
writable: false,
value: "static",
});
const obj = {
name: 'John',
age: 25
};
const descriptor = Object.getOwnPropertyDescriptor(obj, 'name');
// {
// value: 'John',
// writable: true,
// enumerable: true,
// configurable: true
// }
console.log(descriptor);