深入了解js对象

webxiaoma

该文章以收录: 《JavaScript深入探索之路》


前言

对象是Javascript 的基本类型。对象是一种复合值,它将很多值(原始值或者其他对象)聚合在一起,可通过名字访问这些值。对象也可看做是属性的无序集合。每个属性都是一个名/值对。JavaScript对象还可以从一个称为原型的对象继承属性。
<!-- more -->

需要注意的是ES6中对对象也做了一些扩展,本文章并没有特别说明,如果你想了解,可以看看阮老师的ES6书籍。

我们可以把对象的属性分为三大类

  • 内置对象:是有ECMAScript规范定义的对象或类。例如数组、函数、日期和正则表达式都是内置对象。

  • 宿主对象: 是由JavaScript 解析器所嵌入的宿主环境,客户端JavaScript 中表示网页结构的HTMLElement对像均是宿主对象。宿主对象也可以当成内置对象。

  • 自定义对象:是由运行中的JavaScript 代码创建的对象。

另外还有两类属性

  • 自有属性: 是直接在队形中定义的属性

  • 继承属性: 是在对象的原型对象中定义的属性

创建对象

1.对象字面量(也称为对象直接量)

创建对象最简单的方式就是在JavaScripot代码中使用对象直接量。

let obj = {
    name: 'web',
    age: 23,
    sex: 'man'
}

对象的属性名可以是Symbol值,这时ES6新出的一个数据类型。对象直接量是一个表达式,这个表达式的每次运算都创建并初始化一个新的对象。每次计算对象直接量的时候,也都会计算它的每个属性值。也就是说,如果在一个重复调用的函数中的循环体内使用了对象直接量,它将创建很多新对象,并且每次创建的对象那个的属性值也有可能不同。

2.通过new创建的对象

new运算符创建并初始化一个新对象,关键字new 后跟随一个函数调用,这里的函数称作构造函数,构造函数用以初始化一个新创建的对象。JavaScript语言核心中的原始类型都包含内置构造函数。

let obj = new fun();
let ary = new Array();

另外我们需要知道使用new创建的构造函数时没有原型的,但是他们最终都会继承Object.prototype

let ary = new Array();
ary.prototype // undefind

3.Object.create()方法创建对象

Object.create() 该方法创建一个新对象,它有两个参数,第一个参数是这个对象的原型。第二个参数用以对对象的属性进行进一步描述。Object.create() 是一个静态函数,而不是提供给某个对象调用的方法。

let o = {
    name: 'jhon',
    age: 23
}
let obj = Object.create(o);

obj.name // jhon
obj.__proto__ === o // true obj的原型链指向对象 o
obj.name === o.name // true

它的第二个参数是对属性的一些数值,下面我们会讲到,对对象属性特征的一些设置

对象的属性操作

1.查询

对象属性的查询我们有可以使用.运算符、[]运算符或者使用ES6提供的Reflect.get() 方法

let o = {
        name: 'jhon',
        age: 23
    }

console.log(o.name) // 'jhon'
console.log(o['name'])  // 'jhon'
console.log(Reflect.get(o, 'name'))  // 'jhon'

对于[]来说,方括号内必须是一个计算结果为字符串的表达式(也可以直接是字符串),一般我们用来获取动态的属性。另外我这里不在讲解ES6的东西,想了解ES6,可以看看阮一峰老师关于es6基础的书籍。

2.删除

delete 操作符可以删除属性,它的操作数应当是一个属性访问表达式,delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性。

let o = {
    name: 'jhon',
    age: 23,
    other: {
        address: 'HangHai',
    },
}

var obj = o.name;

delete o.name

console.log(o.name) // underfind
console.log(obj) // 'jhon'

另外delete运算符只能删除自有属性,不能删除继承属性,它也不能删除某些属性配置为false的属性。还有全局函数和var声明的变量也是不能删除的。

3.继承

属性是可以继承的,JavaScript对象具有“自有属性”,也有一些是从原型对象上继承的属性。例如函数的toString()方法我们继承自Object的原型。

4.检测

检测属性的归属我们可以使用in运算符, hasOwnPreperty()protertyIsEnumerable() 方法。

let o = {
    name: 'jhon',
    age: 23,
    other: {
        address: 'HangHai',
    },
}

//使用 in 操作符来判读属性是否是该对象的自有属性
// 或继承的属性

console.log('name' in o) // true
console.log('toString' in o) // true

//hasOwnProperty()判读属性是否是该对象的自有属性

console.log(o.hasOwnProperty('name')) // true
console.log(o.hasOwnProperty('toString')) // false

propertyIsEnumerable()hasOwnProperty() 的增强版,只有检测是自有属性且这个苏醒的可枚举性为true时它才返回true。

// toString 是不可枚举的属性
console.log(o.propertyIsEnumerable('toString')) // false

5.枚举属性

我们可以使用for/in 遍历(枚举) 属性,我们可以使用以下方法来遍历对象的自有属性

 let o = {
    name: 'jhon',
    age: 23,
    other: {
        address: 'HangHai',
    },
}

let obj = {}

for (prop in o){
  if(o.hasOwnProperty[prop]) continue;
  obj[prop] = o[prop];
}

6.属性的set和get

在ECMAScrit5中属性值是可以用一个或两个方法替代,这两个方法就是gettersetter,由getter和setter定义的属性称做“存取器属性”,它不同于“数据属性”。

当程序查询存取器属性的值时,JavaScript调用getter方法(无参数)。这个方法的返回值就是属性存去表达式的值,当程序设置一个存去器属性的值时,JavaScript调用setter方法,将赋值表达式右侧的值当做参数传入setter。

和数据属性不同的是,存取器属性不具有可写性。如果属性同时具有getter和setter方法,那它是一个读/写属性,如果它只有getter方法,那么他是一个只读属性,如果它只有setter方法,那么他是一个只写属性。

定义存取器属性的最简单方法是使用对象直接量的语法:

let o = {
    name: 'jhon',
    age: 23,
    get g(){
        console.log("您已经拿到年龄")
        return this.age + 10;
    },
    set g(value){
        console.log('您设置的年龄为: '+ this.age)
    },

} 

console.log(o)
o.g  // 您已经拿到年龄
o.g = 10  // 您设置的年龄为: 23

属性的几个特征

属性除了名字和值之外,还包含一些表示他们可写、可枚举、可配置的特性。

数据属性特性包括:

  • value

  • 可写性 writable 默认 true

  • 可枚举性 enumerable 默认 true

  • 可配置性 configurable 默认 true

存取器属性特性包括:

  • 读取 get

  • 写入 set

  • 可没举性 enumerable

  • 可配置性 configurable

为了实现属性特性的查询和设置操作,ECMAScript5 定义了一个名为 “属性描述符”的对象

defineProperty() 设置某个属性的特性

let obj = {
    name: 'jhon',
    age: 23,
}
Object.defineProperty(obj,'name',{
    value: 'King', // 改写默认值
    writable: false,  // 不可修改属性值
    enumerable: true, // 可枚举该属性
    configurable: true // 可配置该属性

})    

console.log(obj.name); // 'King'
obj.name = 'Tom'; // 改写不会成功
console.log( obj.name); // 'King'

我们也可以设置器存取属性特性:

let obj = {
    name: 'jhon',
    age: 23,
}
// 我们将name设置成存取属性器
Object.defineProperty(obj,'name',{
    get: function(){
    return '更改为存取属性器'
    },
    enumerable: true, // 可枚举该属性
    configurable: true // 可配置该属性

})    

console.log(obj.name);

definePeoperties() 设置多个属性的特性

我们可以使用 definePeoperties()来对多个属性的特性进行设置

let obj = {
         name: 'jhon',
         age: 23,
}
Object.defineProperties(obj,{
    name:{
    get: function(){
    return '更改为存取属性器'
    },
    enumerable: true, // 可枚举该属性
    configurable: true // 可配置该属性
    },
    age:{
        writable: false, // 禁止改写age
    }

})    

console.log(obj.name); //更改为存取属性器

obj.age = 10;
console.log(obj.age) // 23

getOwnpropertyDescriptor() 获取某个对象自有属性的属性描述

let obj = {
    name:'jhon'
}

let descriptor = Object.getOwnPropertyDescriptor(obj,'name');

// Object {value: "jhon", writable: true, enumerable: true, configurable: true}
console.log(descriptor)

对象的三个属性

1.原型属性

对象的原型属性是用来继承属性的,原型属性实例创建之初就设置好了,通过对象直接量创建的对象使用对象 Object.prototype 作为他们的原型,通过new创建的对象,使用构造函数prototype属性作为它们的原型,通过Object.create() 创建的对象使用第一个参数(也可以是null)作为他们的原型

在ECMAScript5中,将对象作为参数传入Object.getPrototypeOf() 可以查看他的原型。

let obja = {
 name:'jhon'
}

let proto = Object.getPrototypeOf(obja);
console.log(proto) // 返回Objcet的原型

如果想要检测一个对象是否是另一个对象的原型(或处于原型链中),可以使用isPrototypeOf() 方法。

     
function fun(name){
    this.name = name
}

fun.prototype = function(){
    console.log("原型")
}

let newFun = new fun('jhon');

// true
console.log(fun.prototype.isPrototypeOf(newFun))

// 另外我们还可以通过 instanceof 来检测一个对象是否是另一个对象的实例

console.log( newFun instanceof  fun) // true

2.类属性

对象的雷属性是一个字符串,用以表示对象的类型信息,我们可以通过一种间接的方法查询它。
默认的 toString() 方法(继承自Object.prototype) 返回这种格式的字符串:[object class]

我们可以通过 Object.prototype.toString.call() 来检测对象的class

function fun (){

}

let className = Object.prototype.toString.call(fun);

console.log(className) // [object Function]

3.可扩展性

对象的可扩展性用以表示是否可以给对象添加新属相,所有内置对象和自定义对象都是显式可扩展的。
这里我们可以通过 Object.esExtensible(),来判断该对象是否可扩展的。

let obj = {
    name: 'jhon',
    age: 23,
}

// true  对象obj默认是可扩展的
console.log(Object.isExtensible(obj))

如果想将对象转换为不可扩展的,可以使用 Object.preventExtensions()

let obj = {
        name: 'jhon',
        age: 23,
    }

Object.preventExtensions(obj);

obj.address = 'BeiJing'

console.log(obj) // obj并没有多出一个address属性
console.log(Object.isExtensible(obj)) // false

注意一旦将对象转换为不可扩展的,就无法在将其转换回可扩展的了。

如果想将对象转换为不可扩展的,并且对象的所有自有属性都设置为不可配置(也就是封闭对象),可以使用 Object.seal()
判读封闭对象可以使用Object.isSealed()

如果想将对象转换为不可扩展的,对象的所有自有属性都设置为不可配置并且是只读(也就是冻结对象),可以使用 Object.frozen(), 如果对象的存取器属性具有setter方法,存取器属性将不受影响。 使用Object.isFrozen() 来检测对象是否冻结。

Object.preventExtensions()Object.seal()Object.frozen()都返回传入的对象。

对象的方法

所用的JavaScript对象都从Objcet.prototype继承属性(除了那些不通过原型显示创建的对象),以下这些方法我不在详细介绍,因为他们还是比较常用的。

1.toString() 方法

2.toLocaleString() 方法

它仅仅调用了 toString() 方法并返回对应值,DateNumber 类对该方法做了定制。

3.toJson() 方法

Objcet.prototype实际上没有定义toJson() 方法,但对于需要执行序列化的对象来说,JSON.stringify()方法会调用该方法。

4.valueOf 方法

当JavaScript需要将对象转换为某种原始值而非字符串的时候才会调用它。

结束

参考书籍:《JavaScript权威指南》

阅读 1.4k

webxiaoma
我们是代码搬运工

代码搬运工

747 声望
27 粉丝
0 条评论

代码搬运工

747 声望
27 粉丝
文章目录
宣传栏