你不知道的js—对象(1)

对象

如果不能100%掌握对象,编程将寸步难行。本文篇幅会比较长,读者需要耐心!!!

在讲解this的文章中一直提到指向某个对象,那么对象到底是是什么呢?

语法

文字语法

const obj = {
    a: 1,
    b: 2,
}
复制代码

构造语法

const obj = new Object();
复制代码

构造形式和文字形式生成的对象是一样的。唯一的区别是,在文字声明中你可以添加多个键/值对,但是在构造形式中你必须逐个添加属性。

类型

在JavaScript中一共有六种主要类型(术语是“语言类型”)

  • string
  • number
  • boolean
  • null
  • undefined
  • object

简单基本类型:string、boolean、number、null和undefined。null有时会被当作一种对象类型,但是这其实只是语言本身的一个bug,即对null执行typeof null时会返回字符串"object"。但null本身是基本类型。

复杂基本类型:又叫引用类型,也就是obkect。同时有许多特殊的对象子类型。子类型会在对象的基础上做一些功能延伸。例如:函数、数组都是对象的子类型。下面就介绍对象子类型。

内置对象

对象子类型,通常被称为内置对象。名字看起来和简单基础类型一样,不过它们的关系更复杂,我们稍后会详细介绍。

  • String
  • Number
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error

在js中内置对象可以当作构造函数来使用,从而可以构造一个对应子类型的新对象。举例来说:

var str = 'wo shi string';
var str = new String('wo shi string');
复制代码

字面量和构造,这两种声明方式是一样的。我们平时使用:

console.log(str.length)
console.log(str.split(...))
复制代码
  • 字符串并不是对象,为何会有属性?都是内置对象String的作用。Number、Boolean也是同理。
  • null和undefined没有对应的构造形式,它们只有文字形式。相反,Date只有构造,没有文字形式。
  • Object、Array、Function和RegExp(正则表达式)来说,无论使用文字形式还是构造形式,它们都是对象,不是字面量。
  • Error对象很少在代码中显式创建,一般是在抛出异常时被自动创建。也可以使用new Error(..)这种构造形式来创建,不过一般来说用不着。

内容

在引擎内部,内容的存储方式是多种多样的,一般并不会存在对象容器内部。存储在对象容器内部的是这些属性的名称,它们就像指针(从技术角度来说就是引用)一样,指向这些值真正的存储位置。

const obj = { a: 1 };
console.log(obj.a)
复制代码

指针还可以用变量表示

扫描二维码关注公众号,回复: 13619711 查看本文章
const obj = { a: 1 };
const key = 'a';
console.log(obj[key])
复制代码

属性名永远都是字符串。如果使用以外的其他值作为属性名,那它首先会被转换为一个字符串。

  • 对象转为[object Object];
  • 数字111转为'111';
  • true转为'true';
  • ...

大家可以多试试

const obj = {};
const obj2 = {};
obj[obj2] = 111;
console.log(obj['[object Object]']) // 111
复制代码

可计算的属性名

ES6增加了可计算属性名,可以在文字形式中使用[]包裹一个表达式来当作属性名:

const key = 'foo';
const obj = {
    [key + 'bar']: 'foobar'
};
console.log(obj['foobar']) // foobar
复制代码

函数作为属性

对象的属性值如果是一个函数,这个函数往往被称为'方法'。会影响到函数内this的指向,详情可以去看this全面分析

数组

数组是对象子类型,数组有一套更加结构化的值存储机制(不过仍然不限制值的类型)。

数组的属性

数组期望的是数值下标,也就是说值存储的位置(通常被称为索引)是非负整数:

const arr = [1, 2, 3, 'foo'];
console.log(arr[1]) // 2
console.log(arr[3]) // foo
复制代码

数组也是对象,所以虽然每个下标都是整数,你仍然可以给数组添加属性:但是非数字的下标,只会有储存功效,不会影响length属性的值,而且会影响性能,所以尽量不要这样使用。

const arr = [1, 2, 3, 'foo'];
console.log(arr.length) // 4
arr.baz = 'baz';
console.log(arr.length) // 4
console.log(arr.baz) // baz
复制代码

如果你试图向数组添加一个属性,但是属性名“看起来”像一个数字,那它会变成一个数值下标。

const arr = [1, 2, 3];
console.log(arr.length) // 3
arr['3'] = 'baz';
console.log(arr.length) // 4
console.log(arr['3']) // baz
复制代码

对象的存储

对象的存储和基本类型不同,普通类型的标识符(名称)和值是1对1存储的。对象的大小是可延伸、不固定的,因此存放的方式也不同。会存在多个标识符对应同一个值的情况。下面我们详细分析:

var str = 'wo shi string';
var obj1 = {a: 1};
var num = 1;
var obj2 = obj1;
var obj3 = {a: 1};
复制代码

上面代码是这样存储的:

WechatIMG39.png obj1和obj2指向的是同一个数据,obj3虽然表面和obj1、obj2值相同,但却不是同一数据。下面证实一下:

console.log(obj1 === obj2) // true
console.log(obj2 === obj3) // false
console.log(obj1 === obj3) // false
复制代码

属性描述符

对象的每个属性都有自己的描述符:writable(可写)、enumerable(可枚举)和configurable(可配置)。

const obj = { a: 1 }
Object.getOwnPropertyDescriptor(obj, 'a')
// {
//     value: 2,
//     writable: true,
//     enumerable: true,
//     configurable: true,
// }
复制代码

writable(可写)

writable决定是否可以修改属性的值。 writable:false时,属性修改不生效。严格模式下,修改会报错。

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

// 严格模式
'use strict'
const obj = { a: 1 }
Object.defineProperty('a', {
    writable:false
})
obj.a = 2; // 报错
复制代码

enumerable(可枚举)

这个描述符控制的是属性是否会出现在对象的属性枚举中,比如说for..in循环。

const obj = { a: 1 }
Object.defineProperty('a', {
    enumerable:false
})

for (let key in obj) {
    console.log(key) // 不能打印出a
}
复制代码

configurable(可配置)

只要属性是可配置的,就可以使用defineProperty(..)方法来修改属性描述符:

const obj = { a: 1 }
Object.defineProperty('a', {
    configurable:false
})
Object.defineProperty('a', {
    enumerable:false // 报错
})
delete obj.a; // 不生效
复制代码

不管是不是处于严格模式,尝试修改一个不可配置的属性描述符都会出错。还会禁止删除这个属性,delete语句(静默)失败了。

不变性

如果要控制对象,不可扩展、不可修改、不可删除,怎样能实现:

对象常量

结合writable:false和configurable:false就可以创建一个真正的常量属性(不可修改、重定义或者删除)。

禁止扩展

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

密封

Object.seal(..)会创建一个“密封”的对象,密封之后不仅不能添加新属性,也不能重新配置或者删除任何现有属性(虽然可以修改属性的值)。

相当于现有对象上调用Object.preventExtensions(..)并把所有现有属性标记为configurable:false。

冻结

Object.freeze(..)会创建一个冻结对象,这个方法实际上会在一个现有对象上调用Object.seal(..)并把所有“数据访问”属性标记为writable:false,这样就无法修改它们的值。不可扩展、不可修改、不可删除

深度操作

preventExtensions、seal、freeze都只针对当前对象。如果对象是多层的,需要递归操作。

下一篇

你不知道的js—对象(2)

猜你喜欢

转载自juejin.im/post/7042193970767593485