《JavaScript Api》系列之Object构造方法操作集合(数据属性与访问器属性、对象分类)

442 阅读7分钟

前言

关于 JavaScript 对象的知识比较广泛,本文只介绍整理了关于 Object构造器 方法的相关操作,同样基础重要,希望能帮助到你。

Object 构造器方法

创建对象

create

create 方法可以创建一个对象实例,并为其指定原型对象和属性。

/**
*   prototype:必需,被创建对象的原型对象。
*   descriptors:可选,包含一个或多个属性描述符的 JavaScript 对象。
**/
Object.create(prototype, descriptors)
let obj=Object.create(null,{
    webName:{
        value:"魔鬼鱼"
    }
});
console.log(obj.webName);  // 魔鬼鱼
console.log(obj.__proto__);  // undefined

Object.create() 可以用来实现单一继承。

assing

assign 方法用于对象的合并,将源对象的所有可枚举属性,复制到目标对象。

// 源对象: 可以有多个。
Object.assing('目标对象','源对象')
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

需要注意的地方:

  • assign方法实行的是浅拷贝,不会进行深拷贝。
  • 一旦遇到同名属性,assign的处理方法是替换,而不是添加。

其他用途:

  • 为对象添加属性。
  • 为对象添加方法。
  • 克隆对象。

Object.assign() 可以用来实现多重继承。

操作对象属性

defineProperty

添加或修改一个对象的多个属性,并返回此对象。

/**
*  obj 必需,要定义属性的对象。
*  prop 必需,要定义或修改的属性的名称或 Symbol 。
*  descriptor 必需,要定义或修改的属性描述符。
*/
Object.defineProperty(obj,prop,descriptor)
let myObj = {};
Object.defineProperty( myObj, "a", {
    value: 2,
    writable: true,
    configurable: true,
    enumerable: true,
    get:function(){},
    set:function(){}
});
myObj.a; // 2

Vue 中的双向数据绑定实现原理就是基于这个完成的,不过在 vue3.0 中已经改用了 Proxy

defineProperty 有什么缺陷?为什么 Vue3.0 采用了 Proxy ?

  • Object.defineProperty无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
  • Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy可以劫持整个对象,并返回一个新的对象。
  • Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。

defineProperties

Object.defineProperty 方法一次只能为对象添加或者修改一个属性,在实际应用中,可能需要进行批量操作, Object.defineProperties 可以实现,使用方法基本与 Object.defineProperty 方法相同。

let obj = {
  webName:"魔鬼鱼",
  url:"softwhy.com"
};
    
Object.defineProperties(obj,{
  address:{
     value:"沈阳市皇姑区",
     writable:true,
     numerable:true,
     configurable:true
  },
  age:{
    value:2,
    writable:true,
    numerable:true,
    configurable:true
  }
});
console.log(obj.address); // 沈阳市皇姑区
console.log(obj.age); // 2

获取对象属性信息

keys

遍历自身的可枚举属性, 返回数组,不会遍历原型链上的属性以及 Symbol 属性。

Object.keys(对象||数组)
let obj = {
  webName: "魔鬼鱼",
  url: "https://chomper.online/",
  address:"沈阳市皇姑区",
  func:function(){
    //code
  }
}
console.log(Object.keys(obj)); // ["webName", "url", "address", "func"]

console.log(Object.keys(["webName", "url", "address", "func"])) // ["0", "1", "2", "3"]

getOwnPropertyDescriptor

如果prop属性存在对象obj上,则返回其属性描述符,如果不存在就返回undefined。

Object.getOwnPropertyDescriptor(obj,prop)
let antzone={
  webName:"魔鬼鱼",
  age:4
}
let descriptor=Object.getOwnPropertyDescriptor(antzone,"webName");
console.log(descriptor); // {value: "魔鬼鱼", writable: true, enumerable: true, configurable: true}

etOwnPropertyNames

返回一个数组,它包含了指定对象所有的可枚举或不可枚举的属性名。

let antzone={
  webName:"魔鬼鱼",
  age:6
}
Object.defineProperty(antzone, "url", {
    value: "https://chomper.online/",
    enumerable: false
});
console.log(Object.getOwnPropertyNames(antzone)); // ["webName", "age", "url"]

getPrototypeOf

返回对象的原型对象,如果没有的话,则返回null。

function Antzone(){
  this.webName="魔鬼鱼";
  this.url="chomper.onilne";
}
Antzone.prototype={
  age:22,
  address:"沈阳市"
}
let antzone=new Antzone();
console.log(antzone) // {webName: "魔鬼鱼", url: "chomper.onilne"}
console.log(Object.getPrototypeOf(antzone)); // {age: 22, address: "沈阳市"}

对于函数对象,其返回的并不是显式原型(prototype),而是隐式原型(proto)。

getOwnPropertySymbols

返回对象上自身的(非继承的)所有Symbol属性键。

扩展对象

preventExtensions

让一个对象永远不能添加新的属性,在严格模式下,如果强行为对象添加属性,会报错。

"use strict";
var obj = {name:"zhang"};
obj.name = "li"// 可以进行修改
Object.preventExtensions(obj); // obj.age = 14;严格模式下会报错
obj.__proto__.age = 13;
console.log(obj); // 能够在原型对象上添加属性
obj.__proto__ = {} // 不能直接重定义原型,会报错。

isExtensible

判断一个对象是否可扩展。

let antzone={
  webName:"魔鬼鱼",
  url:"chomper.onilne"
}
console.log(Object.isExtensible(antzone)) // true 表示可以继续扩展属性
Object.preventExtensions(antzone);
console.log(Object.isExtensible(antzone)) // false 表示以后不可以扩展属性

密封对象

seal

对一个对象进行密封,并返回被密封的对象,这些对象都是不能够添加属性,不能删除已有属性,以及不能够修改已有属性的可枚举型、可配置型、可写性。

let antzone = {
  webName: "魔鬼鱼",
  age:4,
  address:"沈阳市"
};
Object.seal(antzone);
antzone.age = 5;
console.log(antzone.age) // 5

注意:直接量方式创建的属性即使密封后,还是可读写的,但是添加属性无效,不会报错,但在严格模式下会报错。

isSealed

判断一个对象是否是密封的。

let antzone = {
  webName:"魔鬼鱼",
  age:4,
  address:"沈阳"
};
console.log(Object.isSealed(antzone)); // false

冻结对象

freeze

该方法将obj对象冻结,其任何属性都是不可以被修改的。


let obj = {
  webName: "魔鬼鱼",
  age:4
};
Object.freeze(obj);
obj.address="沈阳";
console.log(obj.address); // undefined

注意:可以看到obj对象冻结之后,为它扩展属性虽然没有报错,但是并未成功,如果在严格模式下执行扩展操作,会报错。

isFrozen

判断一个对象是否已经被冻结。


let antzone = {
  webName: "魔鬼鱼",
  url:"chomper.online"
}
Object.freeze(antzone);
console.log(Object.isFrozen(antzone)); // true 

密封、冻结、扩展之间的区别

三个方法都是对 对象进行读写状态的控制,防止对象被改变,最弱一层是preventExtensions(扩展),只能让对象无法添加新的属性,其次是seal(密封),该方法无法添加属性,也无法删除属性,最后是freeze(冻结)

数据属性与访问器属性

javaScript 中,对象的属性分为两种类型:数据属性访问器属性

在学习 Object构造器 方法前必须了解的知识,上面的属性描述符,指的就是数据属性与访问器属性,在我们操作对象的过程中常会用到。

数据属性

value

设置属性的值,默认undefined。

writable

是否可以修改属性的值,默认true;

enumerable

是否可以通过for in循环返回(枚举),默认true;

configurable

能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或能否把属性修改为访问器属性,默认为true。

访问器属性

get

在读取属性时调用的函数,默认为undefined。

set

在写入属性时调用的函数,默认为undefined。

其他常用操作

Object.create(null)

当你需要一个非常干净且高度可定制的对象当作数据字典的时候,请使用 Object.create(null),其他时候请使用字面量的方式。

create(null)、字面量、new Object 三者的区别是什么?

  • 字面量和 new 关键字创建的对象是 Object 的实例,原型指向 Object.prototype,继承内置对象 Object
  • Object.create(arg, pro)创建的对象的原型取决于arg,arg为null,新对象是空对象,没有原型,不继承任何对象;arg为指定对象,新对象的原型指向指定对象,继承指定对象。

JavaScript 对象分类

俗话说 JavaScript 万物皆对象,对新人来说并不友好,我现在仍然觉得 JavaScript 的对象概念有些混乱,不过 JavaScript 的对象也是有分类的,这里我就简单介绍下。

宿主对象

宿主对象,由 JavaScript 宿主环境提供的对象,或者说就是 javascript 运行的地方提供的对象,比如浏览器环境中的 Window 对象。

内置对象

内置对象,是由 JavaScript 语言提供的对象,内置对象分为:普通对象、原生对象、固有对象。

普通对象

Object 构造器创造出的对象为普通对象,字面量创造出的对象也是普通对象。

原生对象

JavaScript 标准中,提供了 30 多个构造器,盗用一张大佬的图,自己领会。

固有对象

有对象在任何 JavaScript 代码执行前就已经被创建出来了,它们通常扮演者类似基础库的角色,它会随着 JavaScript 运行时创建而自动创建的对象实例。

结尾

我一直认为模仿和借鉴是学习一个新东西最高效的方法,作为一名开发者,你需要时刻保持警惕,有危机意识,有持续学习的能力。

如果这篇文章帮助到了你,欢迎点赞和关注,搜索《海洋里的魔鬼鱼》加入我们的技术群一起学习讨论,共同探索前端的边界。