你不知道的JavaScript:对象

在前面我们看到过关于函数调用的位置不同this所绑定的对象也不同,但是对象是何物呢?

  • 语法

对象的定义可以通过声明形式和构造形式。

//对象字面量形式
var obj = {};

//构造形式
var obj = new Object();
obj.name = "bob";

构造形式和文字形式生成的对象是一样的。唯一的区别是,在字面量形式中我们可以任意添加多个属性,但是在构造形式中我们必须逐个添加属性。

  • 类型

在JavaScript中主要有6中数据类型:

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

注意,简单的基本类型并不是对象(string,number,boolean,null,undefined)。null有时会被当做是一种对象类型。对null进行typeof null时会返回字符串"object"。实际上null的本身是基本类型。

  • 内置对象

JavaScript中还有一些对象的子类型,通常被称为内置对象。

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

这些内置对象可以理解像是其他语言中的type或者class。但是在JavaScript中,他们只是一些内置函数,这些内置函数可以当做构造函数来使用。

  • 内容

对象的内容是由一些存储在特定命名位置的,任意类型的值组成的,我们称之为属性。

在引擎内部,对象的值得存储方式是多样的,一般并不会存在对象容器内部。存储在对象容器内部的是这些属性的名称,它们类似于指针(引用)一样,指向这些值真正的存储位置。

访问对象属性的值有两种方式,“.”和“[]”,其访问并没有什么区别。

var obj = {a: 2};
obj.a;  //称为属性访问
obj['a']; //称为键访问
  • 数组

在数组中也支持[]访问,不过数组有一套更加结构化的值存储机制。数组期望的是数值下标,也就是说值存储的位置是非负整数。

数组也是对象,所以每个下标都是整数,你任然可以给数组添加属性。

var arr = [1, 2, 3];
arr.baz = 'baz';
arr.length; //3
arr.baz; //'baz'

可以看到给数组添加了属性,但是并未对数组的长度进行改变。单数如果你给数组添加一个“数字”属性,那么它会将整个“数字”属性转换为数组的下标。

var arr = [1, 2, 3];
aa['3'] = 4;
arr.length = 4;
arr[3]; // 4
  • 属性描述符

在ES5之前,JavaScript没有提供直接检测属性特性的方法,但是在此之后,所有属性都具备了属性描述符。

var myObj = {
  a: 2
}
myObj.getOwnPropertyDescriptor(myObj, 'a');
// {
//   configurable: true,
//   enumerable: true,
//   writable: true,
//   value: 2
// }

如上,该对象对应的属性描述符包含configurable,enumerable,writable,value四个特性。分别代表可配置,可枚举,可写,数据值。

在创建普通属性时属性描述符会使用默认值,我们还可以通过Object.defineProperty(...)来添加一个新属性或者修改一个已有的属性(如果它的configuable为true)。

var obj = {

}

Object.defineProperty(obj, 'a', {
  configurable: true,
  enumerable: true,
  writable: true,
  value: 4
});

obj.a; //4

一. Writable

writable决定是否可以修改属性的值。

var myObject = {

}

Object.defineProperty(myObject, 'a', {
  value: 2,
  writable: false,  //不可写
  enumerable: true,
  configurable: true
});

myObject.a; //2
myObject.a = 4;
myObject.a; //2

如上,我们将writable设置为false,所以后来我们对该属性值的修改失败,如果在严格模式下,这种方法会出错。

二. Configurable

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

var myObj = {
  a: 2
};

myObj.a = 3;
myObj.a; //3

Object.defineProperty(myObj, 'a', {
  value: 4,
  writable: true,
  configurable: false, //不可配置
  enumerable: true
});

myObj.a; //4
myObj.a = 5;
myObj.a; //5

myObj.defineProperty(myObj, 'a', {
  value: 6,
  writable: true,
  configurable: true,
  enumerable: true
}); //TypeError

如上操作,最后使用defineProperty(...)会产生一个报错,不管是否处于严格模式下,尝试修改一个不可配置的描述符属性都会出错。注意,将configurable设置为FALSE是一个单向操作。(configurable为false的情况下,我们还是可以将writable设置为false,但是不可以将writable从false设置为true。)

除了无法设置该属性,configuable为false的情况下,也是禁止删除掉该属性。

三. Enumerable

该属性是用来控制属性时否会出现在对象的属性枚举中,比如for-in循环,如果将enumerable设置为false,那么这个属性就不会出现在枚举当中。

四. [[GET]]

当访问某个对象的属性的时候,例如查看obj对象的a属性,看起来只是查找到名字为a的属性。其实是在obj上实现了[[Get]]操作。对象默认的内置[[Get]]操作首先会在对象中查找是否有名称相同的属性。如果找到则返回该值。

然而如果没有找到,按照[[Get]]的算法的定义,遍历可能存在的[[Prototype]]链,就是原型链。如果最终都没有找到则会返回undefined。

五. [[Put]]

[[Put]]和[[Get]]是相对应的,但是[[Put]]被触发时,会取决于多种因素,包括对象中是否已经存在该属性。

如果对象中已经存在该属性那么会进行如下操作:

  • 属性是否是访问描述符,如果是并且存在setter就调用setter。

  • 属性的描述符中writable是否为false。如果是则在非严格模式下失败,在严格模式下会提示异常。

  • 如果都不是则将该值设置为属性的值。

  • Getter和Setter

对象默认的[[Put]]和[[Get]]操作分别可以控制属性值的设置和获取。

在ES5中可以通过getter和setter部分改写默认操作。getter是一个隐藏函数,会在获取属性值时调用,setter也是一个隐藏函数,会在设置属性值时调用。

var myobj = {
  //给a定义一个getter
  get a() {
    return 2;
  }
}

Object.defineProperty(myobj, 'b', {
  get: function() {
    return this.a*2
  },
  enumerable: true
});

myobj.a; //2
myobj.b; //4

不管是对象文字语法中的get a(){...}还是defineProperty(...)中显示的定义,两者都会在对象中创建一个不包含值得属性,对于这个属性的访问会自动调用一个隐藏函数,隐藏函数的返回值会被当做属性访问的返回值。

  • 存在性

我们如何判断某个属性是否在该对象中?

var myobj = {
  a: 2
};

('a' in myobj); //true
('b' in myobj); //false

myobj.hasOwnProperty('a'); //true
myobj.hasOwnProperty('b'); //false

如上,in操作符会检查属性是否在对象中以及该对象的[[Prototype]]原型链中,hasOwnProperty只会检查该属性是否在该对象中。

所有的普通对象都可以通过Object.prototype的委托来访问hasOwnProperty方法。但是例如Object.create(null)来创建的对象并不会委托到。

  • 枚举

关于对象属性是否可枚举,这个关系到了对象属性的数据描述符中的enumerable属性。

var obj = {};

Object.defineProperty(obj, 'a', {
  enumerable: true,
  value: 2
});

Object.defineProperty(obj, 'b', {
  enumerable: false,
  value: 3
});

for( var i in obj){
  console.log(i, obj[i])
} // 'a' 2

如上,属性b并没有出现在for-in循环中,所以可枚举可以理解为可以出现在对象属性的遍历中。

我们还可以通过propertyIsEnumerable(...)检查,该方法判断属性是否属于该对象而不是出现在原型链中,并且enumerable为true。

在ES6中Object.keys()会返回一个数组,包括该对象所有可以枚举的属性的数组。Object.getOwnPropertyNames(...)会返回一个数组,包括所有的属性,无论是否可以枚举。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,999评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,102评论 1 302
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,709评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,439评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,846评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,881评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,062评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,783评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,517评论 1 248
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,762评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,241评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,236评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,145评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,941评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,965评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,802评论 2 275

推荐阅读更多精彩内容

  • 官方中文版原文链接 感谢社区中各位的大力支持,译者再次奉上一点点福利:阿里云产品券,享受所有官网优惠,并抽取幸运大...
    HetfieldJoe阅读 2,557评论 9 22
  • 特别说明,为便于查阅,文章转自https://github.com/getify/You-Dont-Know-JS...
    杀破狼real阅读 638评论 0 1
  • 概述 JavaScript提供了一个内部数据结构,用来描述一个对象的属性的行为,控制它的行为。这被称为“属性描述对...
    zjh111阅读 719评论 0 0
  • 序 从最近的js入门系列的阅读量逐步递减,观众老爷的兴趣也不再能够接受一些细节性的地方深度挖掘,让我有了一些思考。...
    zhaolion阅读 1,553评论 5 19
  • 《大秦帝国之崛起》剧终,颇有疑惑,最甚当为范睢的形象定位。两撇短眉、一抹翘须、尖嘴猴腮,说起话来一副贼头鼠目...
    静水觉阅读 1,817评论 6 3