标签:doc i++ 格式 glob 声明 ref 可选参数 dict new
对象是 JavaScript 的基本数据类型。是一种复合值:将很多值聚合在一起。对象可以看做是无序集合,每个属性都是一个名/值对。这种基本数据结构还有很多叫法,比如「散列」(hash)、「散列表」(hashtable)、「字典」(dictionary)、「关联数组」(associative array)。JavaScript 还可以从一个称为 原型 的对象继承属性
JavaScript 对象是动态的 —— 可以新增属性也可以删除属性,除了字符串、数字、布尔值、null 和 undefined 之外,JavaScript 中的值都是对象
对象是可变的,通过引用操作对象原对象也会受到影响
属性包括名字和值。名字是可以包含空字符串在内的 任意字符串,值可以是任意 JavaScript 值,或者(在 ECMAScript 5中)可以是一个 getter 或者 setter (或都有),每个属性还有一些与之相关的值称为「属性特性」(property attribute):
ECMAScript 5 之前,通过代码给对象创建的所有属性都是可写、可枚举和可配置的
除了包含属性之外,每个对象还拥有三个相关的对象特性(object attribute):
下面这些术语用来区分三类 JavaScript 对象和两类属性:
1 var empty = {} 2 var point = { x:0, y:0 } 3 var point2 = { x:point.x, y:point.y + 1 } 4 var book = { 5 "main title": "JavaScript", 6 "for": "all audiences", 7 author: { 8 firstname: "David", 9 surname: "Flanagan" 10 } 11 }
在 ECMAScript 5 中,保留字可以用做不带引号的属性名。然后对于 ECMAScript 3 来说,使用保留字作为属性名必须使用引号引起来。ECMAScript 5 中属性最后一个逗号会被忽略,但在 IE 中则报错
new 运算符创建并初始化一个新对象。new 后跟随一个函数调用。这里的函数称做构造函数(constructor),用来初始化一个新创建的对象。JavaScript 语言核心的原始类型都包含内置构造函数(另一方面也证实了 JavaScript 中一切皆对象)
1 var o = new Object(); 2 var a = new Array(); 3 var d = new Date(); 4 var r = new RegExp(‘js‘);
每一个 JavaScript 对象(null 除外)都和另一个对象相关联,这个对象就是「原型」,每一个对象都从原型继承属性
通过 new 创建的对象原型就是构造函数的 prototype 属性值,通过 new Object() 创建的对象也继承自 Obejct.property
没有原型对象的为数不多,Obejct.prototype 就是其中之一。它不继承任何属性,普通对象都具有原型。所有的内置构造函数都具有一个继承自 Object.prototype 的原型。例如,Date.prototype 的属性继承自 Object.prototype,因此由 new Date() 创建的 Date 对象的属性现时继承自 Date.prototype 和 Object.prototype,这一系列链接的原型对象就是所谓的「原型链」(prototype chain)
ECMAScript 5 定义了一个名为 Obejct.create() 的方法,用来创建一个新对象,其中第一个参数是这个对象的原型,第二个可选参数用来对对象的属性进行进一步描述,Object.create() 是一个 静态函数,不是提供给对象调用的方法
1 var o1 = Object.create({ x:1, y:2 }); // o1 继承了属性 x 和 y 2 var o2 = Obejct.create(null); // o2 不继承任何属性和方法
在 ECMAScript 3 中可以用类似代码来模拟原型继承:
1 function inherit(p) { 2 if (p == null) throw TypeError(); 3 if (Object.create) return Object.create(p); 4 5 var t = typeof p; 6 if (t !== "object" && t !== "undefined") throw TypeError(); 7 8 function f() {} 9 f.prototype = p; 10 11 return new f(); 12 } 13 14 var o = { x: "test o" } 15 16 var c = inherit(o); 17 18 c.x = "test c"; 19 20 console.log(c.x); // => "test c" 21 console.log(o.x); // => "test o"
1 var author = book.author; // 取得 book 的 author 属性 2 var title = book["main title"]; // 使用 [] 访问属性时 [] 内必须是一个计算结果为字符串的表达式 3 4 book.edition = 6; // 给 book 创建一个名为 edition 的属性,「.」号运算符后的标识符不能是保留字
当通过 [] 来访问对象属性时,属性名通过字符串来表示。字符串是 JavaScript 的数据类型,在程序运行时可以修改创建它们。因此,可以在 JavaScript 中使用下面这种代码来动态添加/查找属性:
1 var addr = ""; 2 for (i = 0; i < 4; i++) { 3 addr += customer["address" + i] + ‘\n‘; 4 }
假设要查询对象 o 的属性 x,如果 o 中不存在 x,那么将会继续在 o 的原型对象中查询属性 x。如果原型对象中也没有 x,但这个原型对象还有原型,那么继续在这个原型对象的原型上执行查找,直到找到 x 或者找到一个原型是 null 的对象为止。可以看出来,原型的属性构成了一个「链接」,通过这个「链」可以实现属性的继承
1 var o = {} 2 o.x = 1; 3 4 var p = inherit(o); 5 p.y = 2; 6 7 var q = inherit(p); 8 q.z = 3; 9 10 var s = q.toString(); // => "[object Object]" 11 q.x + q.y // => 3
属性访问并不总是返回或设置一个值,下页场景给对象 o 设置 属性 p 会失败:
使用 delete 运算符可以删除对象的属性,delete 运算符只能删除 自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除它,而且这会影响到所有继承自这个原型的对象)
如果删除成功或者删除了一个没有影响的值(不存在的属性),delete 表达式返回 true。当 delete 运算符的操作数不是一个对象的属性的时候也返回 true
1 var o = { x: 1 } 2 delete o.x; // => true 3 delete o.x; // => true x 并不存在 4 delete o.toString; // => true toString 是继承属性 5 delete 1 // => true 不是对象属性 6 this.b = 1; 7 delete b; // => true 删除全局对象上的变量 b 8 9 delete Object.property // => false 10 var x = 1; 11 delete this.x; // => false 不能删除这个属性,因为是通过 var 声明的 12 function f() {} 13 delete f // => false 不能删除全局函数
可以通过 in 运算符、hasOwnProperty() 方法和 propertyIsEnumerable() 方法来检测对象是否存在某属性,propertyIsEnumerable 只有检测到是自有属性且这个属性的可枚举性为 true 时它才返回 true
1 var o = { x: 1 }; 2 "x" in o; // => true 3 "y" in o; // => false 4 "toString" in o // => true 5 6 o.hasOwnProperty("x") // => true 7 o.hasOwnProperty("y") // => false 8 o.hasOwnProperty("toString") // => false 9 10 var o = inherit({ y: 2}); 11 o.x = 1; 12 o.propertyIsEnumerable("x") // => true 13 o.propertyIsEnumerable("y") // => false 14 o.propertyIsEnumerable("toString") // => false
还可以通过判断属性是否是 undefined 来模拟 in 运算符
1 o.x !== undefined; // => true 2 o.y !== undefined; // => false 3 o.toString !== undefined; // => true
然而有一种场景只能使用 in 运算符而不能通过只判断 undefined 的方式。in 可以区分不存在的属性和存在但值为 undefined 的属性
1 var o = { x: undefined } 2 o.x !== undefined // => false 存在 x,只是值为 undefined 3 o.y !== undefined // => false 4 "x" in o // => true 5 "y" in o // => false 6 delete o.x // => true 7 "x" in o // => false delete 后 o 完全不存在了
许多工具库给 Object.prototype 添加了新的方法或者属性(通常不建议这么做),这些方法和属性可以被所有对象继承并使用。然而在 ECMAScript 5 标签之前,这些添加的方法是 不能定义为不可枚举的,因此它们都可以在 for/in 循环枚举出来。为了避免这和践情况,需要过滤 for/in 循环返回的属性,下面两种方法是最常见的:
1 Object.prototype.test = 1; 2 var o = { a: 1, b:2, c: function() {} }; 3 for (p in o) { 4 if (!o.hasOwnProperty(p)) continue; 5 console.log(p); 6 } 7 for (p in o) { 8 if (typeof o[p] === "function") continue; 9 }
除了 for/in 循环之外,ECMAScript 5 定义了两个用以枚举属性名称的函数。第一个是 Object.keys(),它返回一个数组,由对象中的 可枚举的自有属性名称 组成,第二个是 Object.getOwnPropertyNames(),它和上面的方法类似,只是它返回对象的 所有自有属性名称,不仅仅是可枚举的属性
在 ECMAScript 5 中,属性的值可以用一个或两个方法替代,这两个方法就是 getter 和 setter。由它们定义的属性称做「存取器属性」(accessor property),不同于「数据属性」(data property),数据属性只有一个简单的值
当程序查询存取器属性的值时,JavaScript 调用 getter 方法(无参数)。这个方法返回属性的存取表达式值。当程序设置一个存取器属性的值时,调用 setter 方法,将赋值表达式右侧的值当做参数传入 setter。从某种意义上讲,这个方法负责「设置」属性值。可以忽略 setter 方法的返回值
使用存取器属性写入的属性不具有可写性(writable)。如果属性同时具有 getter 和 setter 方法,那么它是一个读/写属性。哪果它只有 getter 方法,那么它是一个只读属性。如果只有 setter 方法,那么它是一个只写属性,读取只写属性总是返回 undefined
1 var p = { 2 x: 1.0, 3 y: 1.0, 4 5 get r() { 6 return Math.sqrt(this.x*this.x + this.y*this.y); 7 }, 8 set r(newValue) { 9 var oldValue = Math.sqrt(this.x*this.x + this.y*this.y); 10 var ratio = newValue/oldValue; 11 12 this.x *= ratio; 13 this.y *= ratio; 14 }, 15 get theta() { 16 return Math.atan2(this.y, this.x) 17 } 18 }; 19 p.r // => 1.4142135623730951
除了包含名字和值之外,属性还包含一些标识它们可写、可枚举和可配置的特性。ECMAScript 3 程序创建的属性都是可写、可枚举、可配置的,且无法对这些特性做出修改。ECMAScript 5 中却提供了查询和设置这些属性鹅的 API,这些 API 对于库的开发者来说非常重要,因为:
数据属性 的 4 个属性分别是它的值(value)、可写性(writable)、可枚举性(enumerable)和可配置性(configurable)
存取器属性 不具有值(value)特性和可写性,它们的可写性是由 setter 方法存在与否决定,因此存取器属性的 4 个特性是读取(get)、写入(set)、可枚举性和可配置性
为了实现属性特性的查询和设置操作,ECMAScript 5 中定义了一个名为「属性描述符」(property descriptor)的对象,这个对象代表那 4 个特性。描述符对象的属性和它们所描述的属性特性是同名的。因此,数据属性的描述符对象的属性有 value, writable, enumerable 和 configurable。存取器属性描述符对象则用 get, set 属性代替 value, writable。其中 writable、enumerable 和 configurable 都是布尔值,get、set 都是函数值
通过调用 Object.getOwnPropertyDescriptor() 可以获得某个对象特定属性的属性描述符
1 // => {value: 1, writable: true, enumerable: true, configurable: true} 2 Object.getOwnPropertyDescriptor({ x: 1}, "x") 3 var random = { 4 get octet() { 5 return Math.floor(Math.random() * 256) 6 }, 7 get uint16() { 8 return Math.floor(Math.random() * 65536) 9 }, 10 get int16() { 11 return Math.floor(Math.random() * 65536 - 32768) 12 } 13 } 14 // => {set: undefined, get: function, enumerable: true, configurable: true} 15 Object.getOwnPropertyDescriptor(random, "octet") 16 // => undefined 17 Object.getOwnPropertyDescriptor({}, "x")
从函数名字就可以看出来 Object.getOwnPropertyDescriptor() 只能得到自有属性的描述符。继承属性的特性需要遍历原型链
要想设置属性的特性,或者让新建属性具有某种特性,则需要调用 Object.defineProperty(),传入要修改的对象、要创建或者修改的属性的名称以前属性描述符对象:
1 var o = {}; 2 Object.defineProperty(o, "x", { 3 value: 1, 4 writable: true, 5 enumerable: false, 6 configurable: true 7 }); 8 // x 属性存在但不可枚举 9 Object.keys() // => [] 10 11 Object.defineProperty(o, "x", { writable: false }) 12 o.x = 2 // 试图更改这个属性的值,会操作失败不报错,严格模式中则抛出类型错误异常 13 o.x // => 1 14 15 // 将 x 从数据属性修改为存取器属性 16 Object.defineProperty(o, "x", { value: 2 }) 17 Object.defineProperty(o, "x", { get: function() { return 0} } 18 o.x // => 0
传入 Object.defineProperty() 的属性描述符对象 不必 包含所有 4 个特性。对于创建属性来说,默认的特性值是 false 或 undefined。对于修改的已有属性来说,默认的特性值没有做任何修改。注意,这个方法要么修改已有属性要么新建自胡属性,但 不能修改继承属性,想要同时修改或者创建多个属性则需要使用 Object.defineProperties(),使用方法可以参考 MDN 相关 api
对于那些不允许创建或者修改的属性来说,如果用 Object.defineProperty() 对其操作就会抛出类型错误异常,比如给一个不可扩展的对象新增属性就会抛出类型错误异常。可写性控制着对特定值特性的修改,可配置性控制着对其它特性的修改,使用的时候以下情况会抛出类型错误异常:
1 // 给 Object.prototype 添加一个不可枚举的 extend() 方法 2 // 这个方法继承自调用它的对象,将作为参数什入的对象属性都复制 3 Object.defineProperty(Object.prototype, "extend", { 4 writable: true, 5 enumerable: false, 6 configurable: true, 7 value: function(o) { 8 var names = Object.getOwnPropertyNames(0); 9 10 for (var i = 0, l = names.length; i < l; i++) { 11 if (names[i] in this) continue; 12 13 var desc = Object.getOwnPropertyDescriptor(o, name[i]); 14 Object.defineProperty(this, names[i], desc) 15 } 16 } 17 });
getter 和 setter 的老式 API
在ECMAScript 5标准被采纳之前,大多数 JavaScript 的实现(IE 除外)已经可以支持对象直接量语法中的 get 和 set 写法。这些实现提供了非标准的老式 API 用来查询和设置 getter 和 setter。这些 API 由 4 个方法组成,所有对象都拥有这些方法。__lookupGetter__()
和 __lookupSetter__()
用以返回一个命名属性的 getter 和 setter 方法,__defineSetter__()
和 __defineGetter__()
用以定义 getter 和 setter
每个对象都胡与之相关的 原型(prototype)、类(class)和 可扩展性(extensible attribute)
原型属性是在实例对象创建之初就设置好的,ECMAScript 5 中,对象作为参数传入 Object.getPrototypeOf()
可以查看它的原型,在 ECMAScript 3 中,则没有与之等价的函数,但经常使用表达式 o.constructor.prototype 来检测一个对象的原型。通过 new 表达式创建的对象,通常继承一个 constructor 属性,这个属性指代创建这个对象的构造函数
要想检测一个对象是否是另一个对象的原型(或者处于原型链中),请使用 isPrototypeOf() 方法,这个方法和 instanceof 运算符非常类似,例如:
1 var p = { x:1 }; 2 var o = Object.create(p); 3 p.isPrototypeOf(o) // => true 4 Object.prototype.isPrototypeOf(o) // => true
对象的类属性是一个字符串,用以表示对象的类型信息。ECMAScript 3?5 都未提供设置这个属性的方法,并只有一种间接的方法可以查询它。默认的 toString() 方法(继承自 Object.prototype),返回了如下这种格式的字符串:
[object class]
所以可以通过 toString() 方法返回的字符串截取处理取到 class 名,不过很多对象继承的 toString() 方法被重写了,为了能调用正确的 toString() 版本,必须间接地调用 Function.call() 方法
1 function classof(o) { 2 if (o === null) return "Null"; 3 if (o === undefined) return "Undefined"; 4 return Object.prototype.toString.call(o).slice(8, -2); 5 } 6 classof(null) // => "Null" 7 classof(1) // => "Number" 8 classof("") // => "String" 9 classof(true) // => "Boolean" 10 classof({}) // => "Object" 11 classof([]) // => "Array" 12 classof(/./) // => "Regexp" 13 classof(new Date) // => "Date" 14 function f() {} 15 classof(new f()) // => "Object"
可扩展性用以表示是否可以给对象是添加新属性。所有内置对象和自定义对象都是显式可扩展的,宿主对象的可扩展属性是由 JavaScript 引擎定义的,ECMAScript 5 中,所有的内置对象和自定义对象都是可扩展的,除非将它们转换为不可扩展的,宿主对象的可扩展性也是由实现 ECMAScript 5 的 JavaScript 引擎定义的
ECMAScript 5 定义了用来查询和设置对象可扩展性的函数:Object.isExtensible(),如果将对象转换为不可扩展的,需要调用 Object.preventExtensions(),不过一量旦将对象转换为不可扩展的,就无法再转换回去了。
Object.seal() 和 Object.preventExtensions() 类似,除了能将对象设置为不可扩展的,还可以将对象的所有自有属性都设置为不可配置的,也就是说不能给对象添加新的属性,已有的属性也不能删除或配置,已封闭(sealed)的对象是不能解封的,可以使用 Object.isSealed() 来检测对象是否封闭
Object.freeze() 将更严格地锁定对象 —— 「冻结」,它还可以将它自有的所有数据属性设置为只读,可以使用 Object.isFrozen() 来检测对象是否冻结
对象序列化(serialization)是指将对象的状态转换为字符串,也可将字符串还原为对象。ECMAScript 5 提供了内置函数 JSON.stringify 和 JSON.parse() 用来序列化和还原 JavaScript 对象。这些方法都使用 JSON 作为数据交换格式,JSON的全称是「JavaScript Object Notation」—— JavaScript 对象表示法,正如其名,它的语法和 JavaScript 对象与数组直接量的语法非常相近
ECMAScript 3 环境中可以引用 json2 类库来支持这两个序列化函数
JSON 语法是 JavaScript 语法的子集,它并不能表示 JavaScript 里的所有值,函数、RegExp、Error 对象和 undefined 值不能序列化和不愿。JSON.stringify() 只能序列化对象可枚举的自有属性,关于 JSON 对象更多 API 可以参考 JSON.stringify
toString() 方法没有参数,在需要将对象转换为字符串的时候,JavaScript 都调用这个方法
1 var s = { x: 1, y: 1 } 2 s.toString(); // => "[object Ojbect]"
返回一个对象的本地化字符串。Object 中默认的 toLocaleString() 方法并不做任何本地化自身操作,它仅调用 toString() 方法并返回值。Date 和 Number 类对 toString() 方法做了定制,可以用它对数字、日期和时间做本地化的转换
Object.prototype 实际上不有定义 toJSON() 方法,但对于需要执行序列化的对象来说,JSON.stringify() 方法会调用 toJSON() 方法,如果存在则调用它,返回值即是序列化的结果,而不是原始对象,参见 Date.toJSON
valueOf() 和 toString() 方法非常类似,但往往当 JavaScript 需要 将对象转换为某种原始值而非字符串 的时候才会用到它,尤其是转换为数字的时候。如果在需要使用原始值的上下文中使用了对象,JavaScript 就会自动调用这个方法,同样有些内置类自定义了 valueOf() 方法,比如 Date.valueOf
标签:doc i++ 格式 glob 声明 ref 可选参数 dict new
原文地址:http://www.cnblogs.com/deveil/p/5997745.html