JavaScript Object数据类型

1、定义

Object 是 JavaScript 的一种 数据类型 ,用花括号 {} 表示。

它用于存储各种键值集合更复杂的实体

Objects 可以通过 构造函数 或者使用 对象字面量 的方式创建。

2、描述

在 JavaScript 中,几乎所有的对象都是 Object 类型的实例,它们都会从 Object.prototype 继承属性和方法,虽然大部分属性都会被覆盖(shadowed)或者说被重写了(overridden)。

除此之外,Object 还可以被故意的创建,但是这个对象并不是一个“真正的对象”(例如:通过 Object.create(null)),或者通过一些手段改变对象,使其不再是一个“真正的对象”(比如说:Object.setPrototypeOf)。

通过原型链,所有的 Object 都能观察到 Object 原型对象(Object prototype object)的改变,除非这些受到改变影响的属性和方法沿着原型链被进一步的重写。

尽管有潜在的危险,但这为覆盖或扩展对象的行为提供了一个非常强大的机制。

Object 构造函数为给定的参数创建一个包装类对象(object wrapper),具体有以下情况:

  • 如果给定值是 null 或 undefined,将会创建并返回一个空对象
  • 如果传进去的是一个基本类型的值,则会构造其包装类型的对象
  • 如果传进去的是引用类型的值,仍然会返回这个值,经他们复制的变量保有和源对象相同的引用地址(传址)

当以非构造函数形式(字面量)被调用时,Object 的行为等同于 new Object()

2.1、对象初始化

2.1.1、定义

对象初始化是一个描述对象初始化过程的表达式。

对象初始化是由一组描述对象的属性组成,属性的值可以是原始类型,也可以是其他对象

2.1.2、创建对象

可以通过下面三种方法初始化对象:

  • new Object()
  • Object.create()(不常用)
  • 字面量
// 创建一个空对象
var obj = new Object();
var obj = Object.create();
var obj = {};

// 创建一个带初始值的对象
var obj = new Object({ a: 1 });
var obj = Object.create({ a: 1 });
var obj = { a: 1 };

// 如果键和值相同,则可以使用简写方式
var a = 1;
var obj = {
	// 基础形式
	// a: a
	// 简写形式
	a
}

2.1.3、属性访问

可以通过下面两种方式访问对象的属性:

  • 小数点
  • 方括号里面带字符串
var obj = {
	a: 1
}
console.log(obj.a); // 1
console.log(obj['a']); // 1

那么这两种方式有什么区别呢?

需要知道,对象的键都是字符串,为了精简代码一般都不会加引号,怎么证明呢?可以通过下述代码证明:

var obj = {
	a: 1
}
// Object.keys():返回对象的所有键的数组
var arr = Object.keys(obj);
console.log(arr); // ["a"]

然而在一些特殊的情况下,对象的键并不是一个简单的字符串,可能会带一些字符,此时,键就必须用引号包裹:

var obj = {
	"a-b": 1
}
console.log(obj.a-b); // 报错
// 此时就需要用到方括号
console.log(obj["a-b"]); // 1

2.1.4、属性定义

直接 对象实例.属性名 就可以完成属性的定义。(如果对象没有该属性则添加,如果对象有该属性则修改)

var obj = {}
obj.a = 1;
console.log(a); // 1

2.1.5、重复属性名

属性使用了同样的名称时,后面的属性会覆盖前面的属性。、

var obj = {
	x: 1,
	x: 2
}
console.log(obj); // { x: 2 }

2.1.6、对象字面量表示法和JSON

区别:

  • JSON 只允许 key: value 的形式,且属性名必须用双引号括起来,属性定义不允许使用简写形式
  • JSON 中,不允许将值设置为函数

2.2、删除属性

Object 自身没有提供方法删除其自身属性,为了删除对象上的属性,必须使用 delete 操作符。

var obj = {
	a: 1
}
delete obj.a;
console.log(obj); // {}

3、构造函数

Object 构造函数将输入转换为一个对象。其行为取决于输入的类型。

  • 如果给定的值是 null 或 undefined,它会创建并返回一个空对象
  • 否则,它将返回一个和给定的值相对应的类型的对象
  • 如果给定值是一个已经存在的对象,则会返回这个值

备注: Object() 可以使用 new 关键字调用,也可以不使用。两者都会创建一个新的对象。

3.1、语法

// new object():创建一个空对象
var obj = new Object();

// new Object({key: value}):创建一个带初始值的对象
var obj = new Object({a: 1});

通过对象构造函数创建一个空对象的三种方式:

var obj = new Object();
var obj = new Object(null);
var obj = new Object(undefined);

4、方法

4.1、Object.assign(target, …sources)

Object.assign(target, ...sources) 方法将所有可枚举的自有属性从一个或多个源对象复制到目标对象。(返回修改后的对象)

  • target:目标对象,接收源对象属性的对象,也是修改后的返回值
  • sources:源对象,包含将被合并的属性
var target = { a: 1, b: 2 };
var source = { b: 4, c: 5 };
var returnedTarget = Object.assign(target, source);
console.log(target); // { a: 1, b: 4, c: 5 }
console.log(returnedTarget); // { a: 1, b: 4, c: 5 }
console.log(returnedTarget === target); // true

如果赋值期间出错,例如如果属性不可写,则会抛出 TypeError;如果在抛出异常之前添加了任何属性,则会修改 target 对象。
备注: Object.assign() 不会在 source 对象值为 null 或 undefined 时抛出错误。

需要注意的是,Object.assign(target, ...sources) 只赋值属性值(浅拷贝)。

var obj1 = {
	a: 1,
	b: {
		c: 2
	}
}
var obj2 = Object.assign({}, obj1);

// 修改基本数据类型
obj1.a = 2;
console.log(obj1.a); // 2
console.log(obj2.a); // 1

// 修改复杂数据类型
obj1.b.c = 3;
console.log(obj1.b.c); // 3
console.log(obj2.b.c); // 3

那么对象如何实现深拷贝呢?点击这里查看

4.2、Object.create(proto)

Object.create(proto) 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型。(返回一个新对象)

  • proto:新创建对象的原型对象
var obj = Object.create({ a: 1 });
console.log(obj); // { a: 1 }

注意:proto 参数需为 null除基本类型包装对象以外的对象,否则抛出一个 TypeError 异常。

4.2.1、创建以 null 为原型的对象

以 null 为原型的对象存在不可预期的行为,因为它未从 Object.prototype 继承任何对象方法。

// 创建一个null对象
var nullObj = Object.create(null);
nullObj.toString(); // nullObj.toString is not a function

但是可以为以 null 为原型的对象添加方法,如下所示:

var nullObj = Object.create(null);
// 将 Object 原型链上的 toString 方法添加到 nullObj 上
nullObj.toString = Object.prototype.toString;
console.log(nullObj.toString()); // [object Object]

创建以 null 为原型的对象也可以防止原型污染攻击。如果恶意脚本向 Object.prototype 添加了一个属性,这个属性将能够被程序中的每一个对象所访问,而以 null 为原型的对象则不受影响。

4.2.2、实现类式继承

// 创建父元素
function Shape() {
  this.x = 0;
  this.y = 0;
}

// 为父元素的原型添加 move 方法
Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  console.info('Shape moved.');
};

// 创建子元素
function Rectangle() {
  Shape.call(this);
}

// 子元素继承父元素的原型
Rectangle.prototype = Object.create(Shape.prototype);

4.3、Object.defineProperty(obj, prop, descriptor)

Object.defineProperty(obj, prop, descriptor) 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性。(返回此对象)

  • obj:要定义属性的对象
  • prop:要定义或修改的属性的名称或 Symbol
  • descriptor:要定义或修改的属性描述符

备注: 应当直接在 Object 构造器对象上调用此方法,而不是在任意一个 Object 类型的实例上调用。

Object.defineProperty(obj, 'prop', {
  value: 42,
  writable: false
});

备注: 在 ES6 中,由于 Symbol 类型的特殊性,用 Symbol 类型的值来做对象的 key 与常规的定义或修改不同,而 Object.defineProperty 是定义 key 为 Symbol 的属性的方法之一。

该方法允许精确地添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的,在枚举对象属性时会被枚举到(for…in 或 Object.keys方法),可以改变这些属性的值,也可以删除这些属性。

默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改(immutable)的。

对象里目前存在的属性描述符有两种主要形式:数据描述符存取描述符

  • 数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的
  • 存取描述符是由 getter 函数和 setter 函数所描述的属性
  • 一个描述符只能是这两者其中之一;不能同时是两者

这两种描述符都是对象

它们共享以下可选键值(默认值是指在使用 Object.defineProperty() 定义属性时的默认值):

  • configurable:设置该属性的描述符是否能够被改变,以及该属性能否被删除(默认为 false,即不能被改变)
  • enumerable:设置该属性是否会出现在对象的枚举属性中(默认为 false,即不会出现在枚举中)

数据描述符还具有以下可选键值:

  • value:该属性的默认值(默认为 undefined)
  • writable:设置该属性能否被赋值运算符改变(默认为 false,即不能)

存取描述符还具有以下可选键值:

  • get:属性的 getter 函数(默认为 undefined)
    • 当访问该属性时,会调用此函数
    • 执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的 this 并不一定是定义该属性的对象)
    • 该函数的返回值会被用作属性的值
  • set:属性的 setter 函数(默认为 undefined)
    • 当属性值被修改时,会调用此函数
    • 该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象
  • 如果一个描述符不具有 value、writable、get 和 set 中的任意一个键,那么它将被认为是一个数据描述符
  • 如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常
// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(obj, "a", {
  value : 37,
  writable : true,
  enumerable : true,
  configurable : true
});

// 对象 o 拥有了属性 a,值为 37

// 在对象中添加一个设置了存取描述符属性的示例
Object.defineProperty(obj, "b", {
  // get : function() { return bValue; },
  // set : function(newValue) { bValue = newValue; },
  // 可以缩写为:
  get() {
  	return console.log('b属性被获取了');
  },
  set(newValue) {
  	console.log('b属性被改变了');
  },
  enumerable : true,
  configurable : true
});

当试图改变不可配置属性(除了 value 和 writable 属性之外)的值时,会抛出TypeError,除非当前值和新值相同,如下所示:

var obj = {};
Object.defineProperty(obj, 'a', {
  value: 1
});
console.log(obj.a); // 1
Object.defineProperties(obj, 'a', {
  get() {
    console.log(obj.a);
  }
}); // 报错

试图写入非可写属性不会改变它,也不会引发错误,如下所示:

var o = {};
Object.defineProperty(o, 'a', {
  value: 1,
  writable: false
});
console.log(o.a); // 1
o.a = 2;
console.log(o.a); // 1

4.4、Object.defineProperties(obj, props)

Object.defineProperties(obj, props) 方法直接在一个对象上定义新的属性或修改现有属性。(返回该对象)

  • obj:在其上定义或修改属性的对象
  • props:要定义其可枚举属性或修改的属性描述符的对象,对象中存在的属性描述符主要有两种:数据描述符和访问器描述符(和上面的 Object.defineProperty 一样)
    • configurable
    • enumerable
    • value
    • writable
    • get
    • set
var obj = {};
Object.defineProperties(obj, {
  'prop1': {
    value: true,
    writable: true
  },
  'prop2': {
    value: 'Hello',
    writable: false
  }
});

4.5、Object.getOwnPropertyDescriptor(obj, prop)

Object.getOwnPropertyDescriptor(obj, prop) 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

  • obj:需要查找的目标对象
  • prop:目标对象内属性名称
const obj = {
  prop: 1
};
// 对象上存在该属性
const descriptor1 = Object.getOwnPropertyDescriptor(obj, 'prop');
console.log(descriptor1);
/*
  Object {
    configurable: true
    enumerable: true
    value: 1
    writable: true
  }
*/

// 对象上不存在该属性
const descriptor2 = Object.getOwnPropertyDescriptor(obj, 'a');
console.log(descriptor2); // undefined
  • 在 ES5 中,如果该方法的第一个参数不是对象(而是原始类型),那么就会产生出现 TypeError。
  • 而在 ES2015,第一个的参数不是对象的话就会被强制转换为对象。
Object.getOwnPropertyDescriptor('obj', 'prop');
// 类型错误:"foo" 不是一个对象  // ES5 code

Object.getOwnPropertyDescriptor('obj', 'prop');
// Object returned by ES2015 code: {
//   configurable: false,
//   enumerable: true,
//   value: "o",
//   writable: false
// }

4.6、Object.getOwnPropertyDescriptors(obj)

Object.getOwnPropertyDescriptors(obj) 方法用来获取一个对象的所有自身属性的描述符。(所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象)

  • obj:任意对象
const obj = {
  prop: 1,
  prop2: 2
};
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
/*
  Object {
    prop: {
      configurable: true
      enumerable: true
      value: 1
      writable: true
    },
    prop2: {
      configurable: true
      enumerable: true
      value: 2
      writable: true
    }
  }
*/

4.7、Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames(obj) 方法返回一个由指定对象的所有自身属性的属性名组成的数组。(包括不可枚举属性但不包括 Symbol 值作为名称的属性)

  • obj:一个对象,其自身的可枚举和不可枚举属性的名称被返回
// 数组
var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr)); // ["0", "1", "2", "length"]

// 对象
var obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj)); // ["0", "1", "2"]

注意:Object.getOwnPropertyNames(obj) 方法不会获取原型链上继承的属性

function Father(){
	this.id = 1;
}
function Son(){
	this.age = 2;
}
Son.prototype = new Father();
console.log(Object.getOwnPropertyNames(new Son())); // ["age"]

4.7.1、只获取不可枚举的属性

function getNoenum(obj) {
	// enum_and_nonenum:包含可枚举属性和不可枚举属性
	var enum_and_nonenum = Object.getOwnPropertyNames(obj);
	// enum_only:包含可枚举属性
	var enum_only = Object.keys(obj);
	// nonenum_only:包含不可枚举属性
	var nonenum_only = enum_and_nonenum.filter(function(key) {
		var indexInEnum = enum_only.indexOf(key);
		if (indexInEnum == -1) {
			// 没有发现在 enum_only 健集中意味着这个健是不可枚举的,
			// 因此返回 true 以便让它保持在过滤结果中
			return true;
		} else {
			return false;
		}
	});
}
  • 在 ES5 中,如果参数不是一个原始对象类型,将抛出一个 TypeError 异常
  • 在 ES2015 中,非对象参数被强制转换为对象

4.8、Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。

  • obj:要返回 Symbol 属性的对象

注意:因为所有的对象在初始化的时候不会包含任何的 Symbol,除非你在对象上赋值了 Symbol 否则 Object.getOwnPropertySymbols() 只会返回一个空的数组。

var obj = {};
console.log(Object.getOwnPropertySymbols(obj)); // Array []
var a = Symbol('a');
obj[a] = 1;
console.log(Object.getOwnPropertySymbols(obj)); // Array [Symbol(a)]

4.9、Object.getPrototypeOf(obj)

Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)。

  • obj:要返回其原型的对象
const obj = {
  prop: 1
};
// 对象上存在该属性
const prot = Object.getPrototypeOf(obj);
console.log(prot); // Object:对象的原型属性

注意:如果对象没有继承属性,则返回 null。

const obj = Object.create(null);
console.log(Object.getPrototypeOf(obj)); // null
  • 在 ES5 中,如果参数不是一个对象类型,将抛出一个TypeError异常
  • 在 ES2015 中,参数会被强制转换为一个 Object

4.10、Object.prototype.hasOwnProperty(prop)

obj.hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。

  • prop:要检测的属性的 String 字符串形式表示的名称,或者 Symbol
var obj = {
	a: 1
};
console.log(obj.hasOwnProperty('a')); // true
console.log(obj.hasOwnProperty('b')); // false

注意:即使属性的值设置为 null 或 undefined 也会返回 true。

var obj = {
	a: null,
	b: undefined
};
console.log(obj.hasOwnProperty('a')); // true
console.log(obj.hasOwnProperty('b')); // true

建议使用 Object.hasOwn() 替代 Object.prototype.hasOwnProperty(),因为前者更适用于用 Object.create(null) 创建的对象。

4.11、Object.hasOwn(instance, prop)

Object.hasOwn(instance, prop) 方法判断对象是否存在自身的属性,返回布尔值。(若存在则返回 true,如果属性是继承的或者不存在则返回 false)

  • instance:要测试的 JavaScript 实例对象
  • prop:要测试属性的 String 类型的名称或者 Symbol
var obj = {
	a: 1
};
console.log(Object.hasOwn(obj, 'a')); // true
console.log(Object.hasOwn(obj, 'b')); // false

4.11.1、和 in 的区别

const obj = {
	a: 1
};

// Object.hasOwn
console.log(Object.hasOwn(obj, 'a')); // true
console.log(Object.hasOwn(obj, 'toString')); // false

// in
console.log('prop' in obj); // true
console.log('toString' in obj); // true

4.12、Object.setPrototypeOf(obj, prototype)

Object.setPrototypeOf() 方法设置一个指定的对象的原型到另一个对象或 null。(返回指定的对象)

建议在对象创建的时候就直接使用 Object.create() 方法来创建一个想要的对象原型,而不是之后通过 Object.setPrototypeOf() 方法来更改对象的原型,原因是因为这个操作在各个浏览器和 JavaScript 引擎上都是一个效率很慢的操作。

  • obj:要设置其原型的对象
  • prototype:该对象的新原型(一个对象或 null)

在下面两种情况下会抛出 TypeError 异常:

  • obj 参数是不可扩展的,或者它是一个不可修改原型的特异对象(exotic object),例如 Object.prototype 或 window
  • prototype 参数不是对象或 null

如果 obj 参数不是一个对象(例如,数字、字符串,等),该方法将什么也不做。

function Human(name, level) {
  this.name = name;
  this.level = level;
}
function SuperHero(name, level) {
  Human.call(this, name, level);
}
Object.setPrototypeOf(SuperHero.prototype, Human.prototype);
console.log(new SuperHero('a', 1).name); // 'a'

出于对性能的考虑,不建议使用 Object.setPrototype() 方法来替代类的继承 extends

4.13、Object.keys(obj)

Object.keys(obj) 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。

  • obj:要返回其枚举自身属性的对象
const obj= { 100: 'a', 2: 'b', 7: 'c' };
// 由于对象没有顺序,键的数组会进行排序
console.log(Object.keys(obj)); // console: ['2', '7', '100']

在 ES5 里,如果此方法的参数不是对象(而是一个原始值),那么它会抛出 TypeError。

在 ES2015 中,非对象的参数将被强制转换为一个对象。

// In ES5
Object.keys('foo');  // TypeError: "foo" is not an object

// In ES2015+
Object.keys('foo');  // ["0", "1", "2"]

4.14、Object.values(obj)

Object.values() 方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用 for…in 循环的顺序相同(区别在于 for-in 循环枚举原型链中的属性)。

  • obj:被返回可枚举属性值的对象
var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
// 返回值的数组与返回键的数组对应
console.log(Object.values(an_obj)); // ['b', 'c', 'a']

4.15、Object.entries(obj)

Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for…in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。

  • obj:可以返回其可枚举属性的键值对的对象
const obj = {
	a: 1,
	b: 2
}
for(const item of Object.entries(obj)) {
	console.log(item);
}
/*
	['a', 1]
	['b', 2]
*/

4.15.1、将 Object 转化为 Map

var obj = {
	a: 1,
	b: 2
}
var map = new Map(Object.entries(obj));
console.log(map); // Map { a: 1, b: 2 }

4.16、Object.fromEntries(iterable)

Object.fromEntries(iterable) 方法把键值对列表转换为一个对象。(返回一个新对象)

  • iterable:类似 Array 、 Map 或者其他实现了可迭代协议的可迭代对象

4.16.1、Map 转 Object

const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);
const obj = Object.fromEntries(entries);
console.log(obj); // { foo: 'bar', baz: 42 }

4.16.2、Array 转 Object

const arr = [['0', 'a'], ['1', 'b'], ['2', 'c']];
const obj = Object.fromEntries(arr);
console.log(obj); // { 0: "a", 1: "b", 2: "c" }

4.17、Object.freeze(obj)

Object.freeze(obj) 方法可以冻结一个对象。(返回传入的参数)

一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。

此外,冻结一个对象后该对象的原型也不能被修改。

  • obj:要冻结的对象
const obj = {
  prop: 1
};
Object.freeze(obj);
obj.prop = 2;
console.log(obj.prop); // 1

被冻结对象自身的所有属性都不可能以任何方式被修改。任何修改尝试都会失败,无论是静默地还是通过抛出TypeError异常(最常见但不仅限于strict mode)。

被冻结的对象并不是完全冻结,而是浅层次的冻结(浅冻结),如下所示:

var obj = {
  a: {}
};
Object.freeze(obj);
obj.a.b = 1;
console.log(obj.a.b); // 1

4.17.1、冻结函数与普通函数的区别

  • 自身的所有属性都不可能以任何方式被修改
  • 在严格模式下,任何修改都会抛出 TypeError

4.17.2、实现深冻结

// 深冻结函数。
function deepFreeze(obj) {
  // 取回定义在 obj 上的属性名
  var propNames = Object.getOwnPropertyNames(obj);
  // 在冻结自身之前冻结属性
  propNames.forEach(function(name) {
    var prop = obj[name];
    // 如果 prop 是个对象,冻结它
    if (typeof prop == 'object' && prop !== null)
      deepFreeze(prop);
  });
  // 冻结自身 (no-op if already frozen)
  return Object.freeze(obj);
}
obj = {
  a: {}
};
deepFreeze(obj);
obj.a.b = 1;
console.log(obj.a.b); // undefined

注意:在 ES5 中,如果这个方法的参数不是一个对象(一个原始值),那么它会导致 TypeError。

4.17.3、和 Object.seal() 的区别

Object.seal() 密封的对象可以改变它们现有的属性。使用 Object.freeze() 冻结的对象中现有属性是不可变的。

4.18、Object.isFrozen(obj)

Object.isFrozen(obj) 方法判断一个对象是否被冻结。(返回布尔值)

  • obj:被检测的对象

一个对象是冻结的是指它不可扩展,所有属性都是不可配置的,且所有数据属性(即没有 getter 或 setter 组件的访问器的属性)都是不可写的。

// 一个对象默认是可扩展的,所以它也是非冻结的。
Object.isFrozen({}); // === false

// 一个不可扩展的空对象同时也是一个冻结对象。
var vacuouslyFrozen = Object.preventExtensions({});
Object.isFrozen(vacuouslyFrozen) //=== true;

// 一个非空对象默认也是非冻结的。
var oneProp = { p: 42 };
Object.isFrozen(oneProp) //=== false

// 让这个对象变的不可扩展,并不意味着这个对象变成了冻结对象,
// 因为 p 属性仍然是可以配置的 (而且可写的).
Object.preventExtensions(oneProp);
Object.isFrozen(oneProp) //=== false

// 此时,如果删除了这个属性,则它会成为一个冻结对象。
delete oneProp.p;
Object.isFrozen(oneProp) //=== true

// 一个不可扩展的对象,拥有一个不可写但可配置的属性,则它仍然是非冻结的。
var nonWritable = { e: "plep" };
Object.preventExtensions(nonWritable);
Object.defineProperty(nonWritable, "e", { writable: false }); // 变得不可写
Object.isFrozen(nonWritable) //=== false

// 把这个属性改为不可配置,会让这个对象成为冻结对象。
Object.defineProperty(nonWritable, "e", { configurable: false }); // 变得不可配置
Object.isFrozen(nonWritable) //=== true

// 一个不可扩展的对象,拥有一个不可配置但可写的属性,则它仍然是非冻结的。
var nonConfigurable = { release: "the kraken!" };
Object.preventExtensions(nonConfigurable);
Object.defineProperty(nonConfigurable, "release", { configurable: false });
Object.isFrozen(nonConfigurable) //=== false

// 把这个属性改为不可写,会让这个对象成为冻结对象。
Object.defineProperty(nonConfigurable, "release", { writable: false });
Object.isFrozen(nonConfigurable) //=== true

// 一个不可扩展的对象,值拥有一个访问器属性,则它仍然是非冻结的。
var accessor = { get food() { return "yum"; } };
Object.preventExtensions(accessor);
Object.isFrozen(accessor) //=== false

// ...但把这个属性改为不可配置,会让这个对象成为冻结对象。
Object.defineProperty(accessor, "food", { configurable: false });
Object.isFrozen(accessor) //=== true

// 使用 Object.freeze 是冻结一个对象最方便的方法。
var frozen = { 1: 81 };
Object.isFrozen(frozen) //=== false
Object.freeze(frozen);
Object.isFrozen(frozen) //=== true

// 一个冻结对象也是一个密封对象。
Object.isSealed(frozen) //=== true

// 当然,更是一个不可扩展的对象。
Object.isExtensible(frozen) //=== false
  • 在 ES5 中,如果参数不是一个对象类型,将抛出一个TypeError异常
  • 在 ES2015 中,非对象参数将被视为一个冻结的普通对象,因此会返回true

4.19、Object.seal(obj)

Object.seal() 方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。(返回传入的参数)

  • obj:将要被密封的对象
const obj = {
  a: 1
};

Object.seal(obj );
obj.a= 2;
console.log(obj.a); // 1
delete obj.a;
console.log(obj.a); // 1

注意:在 ES5 中,如果这个方法的参数不是一个(原始)对象,那么它将导致 TypeError。

4.19.1、封闭函数与普通函数的区别

  • 不能添加新属性
  • 所有已有属性会变得不可配置(属性不可以删除,只有值可以被修改)
  • 在严格模式下,删除属性会抛出 TypeError

4.20、Object.isSealed(obj)

Object.isSealed() 方法判断一个对象是否被密封。(返回布尔值)

  • obj:要被检查的对象

密封对象是指那些不可扩展的,且所有自身属性都不可配置且因此不可删除(但不一定是不可写)的对象。

// 新建的对象默认不是密封的。
var empty = {};
Object.isSealed(empty); // === false

// 如果你把一个空对象变的不可扩展,则它同时也会变成个密封对象。
Object.preventExtensions(empty);
Object.isSealed(empty); // === true

// 但如果这个对象不是空对象,则它不会变成密封对象,因为密封对象的所有自身属性必须是不可配置的。
var hasProp = { fee: "fie foe fum" };
Object.preventExtensions(hasProp);
Object.isSealed(hasProp); // === false

// 如果把这个属性变的不可配置,则这个属性也就成了密封对象。
Object.defineProperty(hasProp, 'fee', {
  configurable: false
});
Object.isSealed(hasProp); // === true

// 最简单的方法来生成一个密封对象,当然是使用 Object.seal.
var sealed = {};
Object.seal(sealed);
Object.isSealed(sealed); // === true

// 一个密封对象同时也是不可扩展的。
Object.isExtensible(sealed); // === false

// 一个密封对象也可以是一个冻结对象,但不是必须的。
Object.isFrozen(sealed); // === true,所有的属性都是不可写的
var s2 = Object.seal({ p: 3 });
Object.isFrozen(s2); // === false,属性"p"可写

var s3 = Object.seal({ get p() { return 0; } });
Object.isFrozen(s3); // === true,访问器属性不考虑可写不可写,只考虑是否可配置
  • 在 ES5 中,如果这个方法的参数不是一个对象(一个原始类型),那么它会导致TypeError
  • 在 ES2015 中,非对象参数将被视为是一个密封的普通对象,只返回true

4.21、Object.preventExtensions(obj)

Object.preventExtensions(obj) 方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。(返回已经不可扩展的对象)

  • obj:将要变得不可扩展的对象

不可扩展的对象:不能向对象中添加新属性。不可扩展对象的属性可能仍然可被删除。尝试将新属性添加到不可扩展对象将静默失败或抛出 TypeError(最常见的情况是在严格模式 (en-US)中,但不排除其他情况)。

注意:一旦将对象变为不可扩展的对象,就再也不能使其可扩展。

// Object.preventExtensions 将原对象变的不可扩展,并且返回原对象。
var obj = {};
var obj2 = Object.preventExtensions(obj);
obj === obj2;  // true

// 字面量方式定义的对象默认是可扩展的。
var empty = {};
Object.isExtensible(empty) //=== true

// ...但可以改变。
Object.preventExtensions(empty);
Object.isExtensible(empty) //=== false

// 使用 Object.defineProperty 方法为一个不可扩展的对象添加新属性会抛出异常。
var nonExtensible = { removable: true };
Object.preventExtensions(nonExtensible);
Object.defineProperty(nonExtensible, "new", { value: 8675309 }); // 抛出 TypeError 异常

// 在严格模式中,为一个不可扩展对象的新属性赋值会抛出 TypeError 异常。
function fail()
{
  "use strict";
  nonExtensible.newProperty = "FAIL"; // throws a TypeError
}
fail();
  • 在 ES5 中,如果参数不是一个对象类型(而是原始类型),将抛出一个TypeError异常
  • 在 ES2015 中,非对象参数将被视为一个不可扩展的普通对象,因此会被直接返回

4.22、Object.isExtensible(obj)

Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。(返回布尔值)

  • obj:需要检测的对象

默认情况下,对象是可扩展的:即可以为他们添加新的属性。以及它们的 Object.prototype.__proto__ 已弃用 属性可以被更改。

Object.preventExtensionsObject.sealObject.freeze 方法都可以标记一个对象为不可扩展(non-extensible)。

// 新对象默认是可扩展的。
var empty = {};
Object.isExtensible(empty); // === true

// ...可以变的不可扩展。
Object.preventExtensions(empty);
Object.isExtensible(empty); // === false

// 密封对象是不可扩展的。
var sealed = Object.seal({});
Object.isExtensible(sealed); // === false

// 冻结对象也是不可扩展。
var frozen = Object.freeze({});
Object.isExtensible(frozen); // === false
  • 在 ES5 中,如果参数不是一个对象类型,将抛出一个 TypeError 异常
  • 在 ES6 中,non-object 参数将被视为一个不可扩展的普通对象,因此会返回 false

4.23、Object.is(val1, val2)

Object.is() 方法判断两个值是否为同一个值。(返回布尔值)

  • val1:被比较的第一个值
  • val2:被比较的第二个值

注意:

  1. +0 和 -0 被视为不同的值
  2. NaN 被视为相同的值

4.24、Object.prototype.isPrototypeOf(obj)

isPrototypeOf(obj) 方法用于测试一个对象是否存在于另一个对象的原型链上。(返回布尔值)

  • obj:在该对象的原型链上搜寻
function Father() { };
function Son() { };
Son.prototype = Father;
var son = new Son();
console.log(Father.isPrototypeOf(son)); // true

4.24.1、与 instanceof 的区别

isPrototypeOf()instanceof 运算符不同。

在表达式 object instanceof AFunction 中,object 的原型链是针对 AFunction.prototype 进行检查的,而不是针对 AFunction 本身。

4.25、Object.prototype.propertyIsEnumerable()

obj.propertyIsEnumerable(prop) 方法判断指定的属性是否可枚举。(返回布尔值)

  • prop:需要测试的属性名
// 数组
var arr = [1];
console.log(arr.propertyIsEnumerable(0)); // true

// 对象
var obj = {a: 1};
console.log(obj.propertyIdsEnumerable('a')); // true

4.26、Object.prototype.toLocaleString()

obj.toLocaleString() 方法返回一个该对象的字符串表示。此方法被用于派生对象为了特定语言环境的目的(locale-specific purposes)而重载使用。

4.27、Object.prototype.toString()

obj.toString() 方法返回一个表示该对象的字符串。该方法旨在重写(自定义)派生类对象的类型转换的逻辑。

默认情况下,toString() 不接受任何参数。然而,继承自 Object 的对象可能用它们自己的实现重写它,这些实现可以接受参数。例如,Number.prototype.toString()BigInt.prototype.toString() 方法接受一个可选的 radix 参数(设置转化后的进制,默认十进制)。

返回的字符串类型为 [object Type],Type 为原始数据的类型,下面进行详细的分类:

  • Object:[object Object]
  • Array:[object Array]
  • Function:[object function]
  • Error:[object Error]
  • Boolean:[object Boolean]
  • Number:[object Number]
  • String:[object String]
  • Date:[object Date]
  • RegExp:[object RegExp]
  • Arguments:[object Arguments]
  • null:[object Null]
  • undefined:[object Undefined]

4.28、Object.prototype.valueOf()

obj.valueOf() 方法返回指定对象的 this 值。

var obj = {
	a: 1
};
console.log(obj.valueOf()); // Object { a: 1 }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jackson Mseven

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值