Javascript基础巩固系列——标准库Object对象

全手打原创,转载请标明出处:https://www.cnblogs.com/dreamsqin/p/13714848.html, 多谢,=。=~(如果对你有帮助的话请帮我点个赞啦)

重新学习JavaScript是因为当年转前端有点儿赶鸭子上架的意味,我一直在反思我的知识点总是很零散,不能在脑海中形成一个完整的体系,所以这次想通过再次学习将知识点都串联起来,结合日常开发的项目,达到温故而知新的效果。与此同时,总结一下我认为很重要但又被我遗漏的知识点~

Object()方法

可以当作工具方法使用,将任意值转为对象,如果参数为空(或者为undefinednull),返回一个空对象;如果参数是原始类型的值,会转为对应的包装对象;如果参数是一个对象,则直接返回该对象(特殊用法,用于判断变量是否为对象)。

var obj = Object(1);
obj instanceof Object // true
obj instanceof Number // true

// 判断变量是否为对象
function isObject(value) {
  return value === Object(value);
}

isObject([]) // true
isObject(true) // false

Object的静态方法

指部署在Object对象自身的方法。

  • Object.keys()Object.getOwnPropertyNames()
    参数为一个对象,返回一个数组,数组为该对象自身的(而不是继承的)所有属性名,区别是前者只返回可枚举(enumerable: true)的属性,由于 JavaScript 没有提供计算对象属性个数的方法,所以可以用这两个方法代替。
var a = ['Hello', 'World'];
Object.keys(a) // ["0", "1"]
Object.getOwnPropertyNames(a) // ["0", "1", "length"]

var obj = {
  p1: 123,
  p2: 456
};
Object.keys(obj).length // 2
Object.getOwnPropertyNames(obj).length // 2

// 获取对象的所有属性(不管是自身的还是继承的,也不管是否可枚举)
function inheritedPropertyNames(obj) {
  var props = {};
  while(obj) {
    Object.getOwnPropertyNames(obj).forEach(function(p) {
      props[p] = true;
    });
    // 依次获取obj对象的每一级原型对象“自身”的属性,从而获取obj对象的“所有”属性,不管是否可遍历。
    obj = Object.getPrototypeOf(obj);
  }
  return Object.getOwnPropertyNames(props);
}
  • Object.getOwnPropertyDescriptor()
    获取某个属性的描述对象(属性描述对象说明见下文),第一个参数是目标对象,第二个参数是一个字符串,对应目标对象的某个属性名,只能用于对象自身的属性,不能用于继承的属性。
var obj = { p: 'a' };

Object.getOwnPropertyDescriptor(obj, 'p')
// Object { value: "a",
//   writable: true,
//   enumerable: true,
//   configurable: true
// }
  • Object.defineProperty()
    通过描述对象(属性描述对象说明见下文),定义或修改某个属性,然后返回修改后的对象,参数:属性所在对象、属性名字符串、属性描述对象。
var obj = Object.defineProperty({}, 'p', {
  value: 123,
  writable: false,  // 如果原型对象的某个属性的writable为false,那么子对象将无法自定义这个属性,但可以通过defineProperty修改value来绕过限制。
  enumerable: true,
  configurable: false
});

obj.p // 123

obj.p = 246;  // 正常模式下只是默默失败,严格模式(use strict)下会报错
obj.p // 123
  • Object.defineProperties()
    通过描述对象(属性描述对象说明见下文),定义或修改多个属性,然后返回修改后的对象,参数:属性所在对象、属性名与属性描述对象的键值对对象。
var obj = Object.defineProperties({}, {
  p1: { value: 123, enumerable: true },
  p2: { value: 'abc', enumerable: true },
  p3: { get: function () { return this.p1 + this.p2 },
    enumerable:true,
    configurable:true
  }
});

obj.p1 // 123
obj.p2 // "abc"
obj.p3 // "123abc"
  • Object.create()
    可以指定原型对象(参数不能为空且必须是对象)和属性,返回一个新的对象(可以实现由一个实例对象生成另一个实例对象)。
// 原型对象
var A = {
  print: function () {
    console.log('hello');
  }
};

// 实例对象
var B = Object.create(A);

Object.getPrototypeOf(B) === A // true
B.print() // hello
B.print === A.print // true

// 传入属性描述对象参数
var obj = Object.create({}, {
  p1: {
    value: 123,
    enumerable: true,
    configurable: true,
    writable: true,
  },
  p2: {
    value: 'abc',
    enumerable: true,
    configurable: true,
    writable: true,
  }
});

// 等同于
var obj = Object.create({});
obj.p1 = 123;
obj.p2 = 'abc';

生成一个不继承任何属性(比如没有toStringvalueOf方法)的对象。

var obj = Object.create(null);

obj.valueOf()
// TypeError: Object [object Object] has no method 'valueOf'
  • Object.getPrototypeOf()
    获取对象的Prototype对象,即原型。
var F = function () {};
var f = new F();
Object.getPrototypeOf(f) === F.prototype // true

// 空对象的原型是 Object.prototype
Object.getPrototypeOf({}) === Object.prototype // true

// Object.prototype 的原型是 null
Object.getPrototypeOf(Object.prototype) === null // true

// 函数的原型是 Function.prototype
function f() {}
Object.getPrototypeOf(f) === Function.prototype // true
  • Object.setPrototypeOf()
    为参数对象设置原型,返回该参数对象。它接受两个参数,第一个是现有对象,第二个是原型对象。
var a = {};
var b = {x: 1};
Object.setPrototypeOf(a, b);

Object.getPrototypeOf(a) === b // true
a.x // 1

Object的实例方法

指定义在Object.prototype对象上的方法,所有Object的实例对象都继承了这些方法。

  • Object.prototype.valueOf()
    返回当前对象对应的值,默认情况下返回对象本身,主要用途是JavaScript自动类型转换时会默认调用这个方法。
var obj = new Object();
1 + obj // "1[object Object]"
  • Object.prototype.toString()
    返回当前对象对应的字符串形式,默认情况下返回类型字符串"[object object]"(第二个值表示该对象的构造函数),数值、数组、字符串、函数、Date 对象都分别部署了自定义的toString方法,覆盖了原生的Object.prototype.toString方法。
var o = {a:1};
o.toString() // "[object Object]"

// 数值
(10).toString() // "10"

// 数组
[1, 2, 3].toString() // "1,2,3"

// 字符串
'123'.toString() // "123"

// 函数
(function () {
  return 123;
}).toString()
// "function () {
//   return 123;
// }"

// Date对象
(new Date()).toString()
// "Tue May 10 2016 09:11:31 GMT+0800 (CST)"

特殊应用:用于判断数据类型,由于实例对象可能会自定义toString方法,覆盖掉Object.prototype.toString方法,所以可以利用call直接调用原型方法。

Object.prototype.toString.call(value)

//数值:返回[object Number]
//字符串:返回[object String]
//布尔值:返回[object Boolean]
//undefined:返回[object Undefined]
//null:返回[object Null]
//数组:返回[object Array]
//arguments 对象:返回[object Arguments]
//函数:返回[object Function]
Object.prototype.toString.call(Math) // "[object Math]"
//Error 对象:返回[object Error]
//Date 对象:返回[object Date]
//RegExp 对象:返回[object RegExp]
//其他对象:返回[object Object]

//一个比typeof运算符更准确的类型判断函数
var type = function (o){
  var s = Object.prototype.toString.call(o);
  return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};

type({}); // "object"
type([]); // "array"
type(5); // "number"
type(null); // "null"
type(); // "undefined"
type(/abcd/); // "regex"
type(new Date()); // "date"
  • Object.prototype.toLocaleString()
    返回当前对象对应的本地字符串形式,主要作用是留出一个接口,让各种不同的对象实现自己版本的toLocaleString,用来返回针对某些地域的特定的值,目前ArrayNumberDate自定义了toLocaleString方法。
var person = {
  toString: function () {
    return 'Henry Norman Bethune';
  },
  toLocaleString: function () {
    return '白求恩';
  }
};
person.toString() // Henry Norman Bethune
person.toLocaleString() // 白求恩

var date = new Date();
date.toString() // "Tue Jan 01 2018 12:01:33 GMT+0800 (CST)"
date.toLocaleString() // "1/01/2018, 12:01:33 PM"
  • Object.prototype.hasOwnProperty()
    判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。
var obj = {
  p: 123
};

obj.hasOwnProperty('p') // true
obj.hasOwnProperty('toString') // false
  • Object.prototype.isPrototypeOf()
    判断当前对象是否为另一个对象的原型。
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);

o2.isPrototypeOf(o3) // true
o1.isPrototypeOf(o3) // true

Object.prototype.isPrototypeOf(/xyz/) // true
Object.prototype.isPrototypeOf(Object.create(null)) // false
  • Object.prototype.propertyIsEnumerable()
    判断某个属性是否可枚举,只能用于判断对象自身的属性,对于继承的属性一律返回false。
var obj = {};
obj.p = 123;

obj.propertyIsEnumerable('p') // true
obj.propertyIsEnumerable('toString') // false

属性描述对象

JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可写、可遍历等等。这个内部数据结构称为“属性描述对象”(attributes object)。每个属性都有自己对应的属性描述对象,保存该属性的一些元信息。
PS:value+writable:true属性与get+set不能共存,在Object.defineProperty()Object.defineProperties()参数里面的属性描述对象,writableconfigurableenumerable这三个属性的默认值都为false

 // 属性描述对象的各个属性称为“元属性”,因为它们可以看作是控制属性的属性
{
  value: 123,  // 属性值,默认为undefined
  writable: false,  // 布尔值,表示属性值(value)是否可改变(即是否可写),默认为true
  enumerable: true,  // 布尔值,表示该属性是否可遍历,默认为true(不可遍历时for...in循环、Object.keys()、JSON.stringify会跳过该属性)
  configurable: false,  // 布尔值,表示可配置性,默认为true,控制了属性描述对象的可写性(不可配置时无法删除该属性,也不得改变该属性的属性描述对象(value属性在writable为true时除外,writable的true改false除外))
  get: undefined,  // 表示该属性的取值函数(getter),默认为undefined,取值时会调用
  set: undefined  // 表示该属性的存值函数(setter),默认为undefined,存值时会调用
}
  • 存取器settergetter
    存值函数称为setter,使用属性描述对象的set属性;取值函数称为getter,使用属性描述对象的get属性。
// 写法一(enumerable、configurable默认为false)
var obj = Object.defineProperty({}, 'p', {
  get: function () {
    return 'getter';
  },
  set: function (value) {
    console.log('setter: ' + value);
  }
});

obj.p // "getter"
obj.p = 123 // "setter: 123"

// 写法二(更推荐,因为enumerable、configurable默认为true)
var obj = {
  get p() {
    return 'getter';
  },
  set p(value) {
    console.log('setter: ' + value);
  }
};

对象的拷贝

将一个对象的所有属性,拷贝到另一个对象,为了能把存取器定义的属性也成功拷贝,可以使用以下方法。

var extend = function (to, from) {
  for (var property in from) {
    if (!from.hasOwnProperty(property)) continue;  // 过滤掉继承的属性
    Object.defineProperty(
      to,
      property,
      Object.getOwnPropertyDescriptor(from, property)
    );
  }
  return to;
}

extend({}, { get a(){ return 1 } })
// { get a(){ return 1 } })

控制对象状态

有时需要冻结对象的读写状态,防止对象被改变(但有漏洞:可以通过改变原型对象,来为对象增加属性;如果属性值是对象,就只能冻结属性指向的对象(即无法指向其他值),而不能冻结对象本身的内容)。JavaScript 提供了三种冻结方法,最弱的一种是Object.preventExtensions,其次是Object.seal,最强的是Object.freeze

  • Object.preventExtensions()
    可以使得一个对象无法再添加新的属性。
var obj = new Object();
Object.preventExtensions(obj);

Object.defineProperty(obj, 'p', {
  value: 'hello'
});
// TypeError: Cannot define property:p, object is not extensible.

obj.p = 1;
obj.p // undefined
  • Object.isExtensible()
    用于检查一个对象是否使用了Object.preventExtensions方法,也就是说,检查是否可以为一个对象添加属性。
var obj = new Object();

Object.isExtensible(obj) // true
Object.preventExtensions(obj);
Object.isExtensible(obj) // false
  • Object.seal()
    使得一个对象既无法添加新属性,也无法删除旧属性,实质是把属性描述对象的configurable属性设为false
var obj = { p: 'hello' };
Object.seal(obj);

delete obj.p;
obj.p // "hello"

obj.x = 'world';
obj.x // undefined
  • Object.isSealed()
    用于检查一个对象是否使用了Object.seal方法。
var obj = { p: 'a' };

Object.seal(obj);
Object.isSealed(obj) // true
Object.isExtensible(obj) // false
  • Object.freeze()
    可以使得一个对象无法添加新属性、无法删除旧属性、也无法改变属性的值,使得这个对象实际上变成了常量。
var obj = {
  p: 'hello'
};

Object.freeze(obj);

obj.p = 'world';
obj.p // "hello"

obj.t = 'hello';
obj.t // undefined

delete obj.p // false
obj.p // "hello"
  • Object.isFrozen()
    用于检查一个对象是否使用了Object.freeze方法。
var obj = {
  p: 'hello'
};

Object.freeze(obj);
Object.isFrozen(obj) // true
Object.isExtensible(obj) // false

参考资料

JavaScript 语言入门教程 :https://wangdoc.com/javascript/index.html

posted @ 2020-09-22 20:52  Dreamsqin  阅读(216)  评论(0编辑  收藏  举报