笔记你不知道的JS对象

45次阅读

共计 3190 个字符,预计需要花费 8 分钟才能阅读完成。

1 如何定义

// 声明形式,大部分情况下使用声明形式
let obj ={
  a:2,
  b:3
};
// 构造形式
let obj= = new Object();
obj.a=2;
obj.b=3;

2 类型

7 种类型:string、boolean、number、null、undefined、object、symbol

typeof null === object,原理是这样的,不同的对象在底层都表示为二进制,在 JavaScript 中二进制前三位都为 0 的话会被判断为 object 类型,null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回“object”。

内置对象:String, Number, Object, Date, Boolean, Array, Function, RegExp, Error,在 JavaScript 中,它们实际上只是一些内置函数。这些内置函数可以当作构造函数,使用 new 调用,产生新对象。

在必要时语言会自动把字符串字面量转换成一个 String 对象,也就是说你并不需要显式创建一个对象。

var strPrimitive = "I am a string"; 
console.log(strPrimitive.length); // 13 
console.log(strPrimitive.charAt( 3) ); // "m"

我们都可以直接在字符串字面量上访问属性或者方法,之所以可以这样做,是因为引擎自动把字面量转换成 String 对象,所以可以访问属性和方法。同样引擎会自动把数字字面量转换为 Number 对象,如使用 3.1415.toFixed(2)

null 和 undefined 没有对应的构造形式,它们只有文字形式。相反,Date 只有构造,没有文字形式。
对于 Object、Array、Function 和 RegExp(正则表达式)来说,无论使用文字形式还是构造形式,它们都是对象,不是字面量。

Error 对象很少在代码中显式创建,一般是在抛出异常时被自动创建。也可以使用 newError(..) 这种构造形式来创建。

3 属性

. 操作符要求属性名满足标识符的命名规范。

在对象中,属性名永远都是字符串。如果你使用 string(字面量)以外的其他值作为属性名,那它首先会被转换为一个字符串。

var myObject = { };
let arr = [2,3];
myObject[true] = "foo"; 
myObject[3] = "bar"; 
myObject[myObject] = "baz";
myObject[arr] =4;
myObject["true"]; // "foo"
myObject["3"]; // "bar"
myObject["[object Object]"]; //“baz”myObject["2,3"]; // 4

为数组添加命名属性,但数组的 length 值不会变

4 复制

对于 JSON 安全 (也就是说可以被序列化为一个 JSON 字符串并且可以根据这个字符串解析出一个结构和值完全一样的对象) 的对象来说,有一种巧妙的复制方法:

var newObj = JSON.parse(JSON.stringify( someObj) );
Object.assign(..) 方法的第一个参数是目标对象,之后还可以跟一个或多个源对象。它会遍历一个或多个源对象的所有可枚举 (enumerable,参见下面的代码) 的自有键 (owned key,很快会介绍) 并把它们复制 (使用 = 操作符赋值) 到目标对象,最后返回目标对象。

5 属性描述符

var myObject = {a:2};
Object.getOwnPropertyDescriptor(myObject, "a");
// {
// value: 2,
// writable: true,
// enumerable: true,
// configurable: true 
// }
// writable(可写)、enumerable(可枚举)和 configurable(可配置)

不管是不是处于严格模式,尝试修改一个不可配置的属性描述符都会出错,也不可以删除该属性。注意: 如你所见,把 configurable 修改成 false 是单向操作,无法撤销! 要注意有一个小小的例外: 即便属性是 configurable:false,我们还是可以把 writable 的状态由 true 改为 false,但是无法由 false 改为 true。

enumerable 描述符控制的是属性是否会出现在对象的属性枚举中,比如说 for..in 循环。如果把 enumerable 设置成 false,这个属性就不会出现在枚举中,虽然仍然可以正常访问它。

6 不变性

1 结合 writable:false 和 configurable:false 就可以创建一个真正的常量属性

2 如果你想禁止一个对象添加新属性并且保留已有属性,可以使用 Object.preventExtensions(..)

3 Object.seal(..) 会创建一个“密封”的对象,这个方法实际上会在一个现有对象上调用 Object.preventExtensions(..) 并把所有现有属性标记为 configurable:false。所以,密封之后不仅不能添加新属性,也不能重新配置或者删除任何现有属性(虽然可以修改属性的值)。

4 Object.freeze(..) 会创建一个冻结对象,这个方法实际上会在一个现有对象上调用 Object.seal(..) 并把所有“数据访问”属性标记为 writable:false,这样就无法修改它们的值。你可以“深度冻结”一个对象,具体方法为,首先在这个对象上调用 Object.freeze(..),然后遍历它引用的所有对象并在这些对象上调用 Object.freeze(..)。但是一定要小心,因为这样做有可能会在无意中冻结其他 (共享) 对象。

7 [[Get]]和[[Put]]

在语言规范中,myObject.a 在 myObject 上实际上是实现了 [[Get]] 操作(有点像函数调用:[[Get]]())。对象默认的内置 [[Get]] 操作首先在对象中查找是否有名称相同的属性,如果找到就会返回这个属性的值。如果没有找到名称相同的属性,会遍历可能存在的 [[Prototype]] 链,也就是原型链。若在原型链上也没有找到,那就返回 undefined。

[[Put]] 被触发时,如果已经存在这个属性,[[Put]] 算法大致会检查下面这些内容。

  1. 属性是否是访问描述符(参见 3.3.9 节)? 如果是并且存在 setter 就调用 setter。
  2. 属性的数据描述符中 writable 是否是 false? 如果是,在非严格模式下静默失败,在严格模式下抛出 TypeError 异常。
  3. 如果都不是,将该值设置为属性的值。

如果对象中不存在这个属性,[[Put]] 操作会更加复杂。

8 存在性

in 操作符会检查属性是否在对象及其 [[Prototype]] 原型链中。

hasOwnProperty(..) 只会检查属性是否在 myObject 对象中,不会检查 [[Prototype]] 链。

所有的普通对象都可以通过对于 Object.prototype 的委托来访问 hasOwnProperty(..),但 是 有 的 对 象 可 能 没 有 连 接 到 Object.prototype(通 过 Object.create(null) 来创建)。在这种情况下,形如 myObejct.hasOwnProperty(..)就会失败。这时可以使用 Object.prototype.hasOwnProperty.
call(myObject,”a”)。

9 遍历

for..in 循环可以用来遍历对象的可枚举属性列表(包括 [[Prototype]] 链)。

对于数值索引的数组来说,可以使用标准的 for 循环来遍历值,或者使用 for…of 循环,for..of 循环首先会向被访问对象请求一个迭代器对象,然后通过调用迭代器对象的 next() 方法来遍历所有返回值。

正文完
 0