JavaScript中的对象
一、对象
对象是JavaScript的基本数据类型,对象可以看作属性的无序集合,每个属性都是一个名\值对,属性是一个字符串,因此可以把对象看做对象到值得映射。JavaScript中的对象可以从一个称之为原型的对象上继承属性,这种原型式继承JavaScript的核心特性。
JavaScript中对象可以分为三类,内置对象、宿主对象、自定义对象;
内置对象:是有ECMAScript规范定义的对象或类,例如数组,函数,日期对象。
宿主对象:宿主对象是有JavaScript解释器所定义的宿主环境(比如Web浏览器)所定义的 。
自定义对象:是由运行的JavaScript代码创建的对象。
属性可以分为自有属性和继承属性。
自有属性:直接在对象中定义的属性。
继承属性:在对象的原型中定义的属性。
二、对象的原型
每个JavaScript对象都和另外一个对象关联,这个被关联的对象就是对象的原型。对象可以从原型上继承属性。
对象字面量创建的对象的原型是Object.prototype。
通过new关键字创建的对象的原型是构造函数.prototype。例如new Array()创建的对象时原型是Array.prototype.
通过Object.create()方法创建的对象的原型是Object.create()的第一个参数。
三、创建对象
1、对象直接量
创建对象最简单的方式就是使用对象直接量,对象直接量是由若干名/值对组成的映射表。名/值对中间用冒号分割,名/值对之间用逗号分割,整个映射表用花括号括起来。属性名可以是JavaScript标识符也可以是字符串直接量(包括空字符串),属性值可以是任意类型的JavaScript表达式。对象直接量是一个JavaScript表达式,表达式的每次运算都会创建一个新的对象。
1 var obj={
2 x:1,
3 'y':2
4 }
5 var o={};//创建一个空对象
6 var ob={
7 x:1,
8 //构造器属性
9 set y(value){
10 this.x=2;
11 },
12 get y(){
13 return 10;;
14 }
15 }
2、new关键字
new关键字用来创建一个对象,后面跟上一个构造函数。
1 var o=new Object();//创建一个空对象
2 var ob=new Array();创建一个空对象
3 var obj=new RegExp('js');//创建一个正则表达式对象
3、Object.create()
Object.create()可以接受两个参数,第一个参数是新创建对象的原型,第二个参数是可选的。这些属性对应Object.defineProperties()
的第二个参数。
1 var o;
2 o=Object.create(null);//创建一个原型为null的空对象。
3 o=Object.create(Object.prototype);//和var={}一样的空对象
4 o=Object.create(Object.prototype,{
5 foo:{
6 value:10,
7 writable:true,
8 enumerable:true,
9 configurable:false
10 },
11 bar:{
12 configurable:true,
13 enumerable:true,
14 set:function(value){
15 console.log(value);
16 },
17 get:function(){
18 return 10;
19 }
20
21 }
22 });
23 console.log(o.foo);//10
24 console.log(o.bar);//10
四、访问和设置对象属性
访问和设置对象的属性有两种方法,可以通过点(.)运算符和([])运算符来获取和设置对象的属性。运算符的左边应该是一个表达式,返回一个对象,点运算符的右边是一个以属性命名的标识符。([])内必须是计算结果为字符串的表达式。
1 var o;
2 o=Object.create(Object.prototype);//和var={}一样的空对象
3 //设置属性
4 o.bar=10;
5 o['foo']=100;
6 //访问属性
7 console.log(o.foo);//100
8 console.log(o['bar']);//10
通过([])运算符和字符串的方式访问属性看起来更像一个数组,只不过这种数组通过的是字符串索引而不是数字。这种数组成为关联数组。通过([])方式访问对象的属性可以对字符串进行修改,而(.)的方式不能修改标识符。o['arr'+i]
1 function add(obj,name,value){
2 obj[name]=value;
3 }
运行函数之前,我们无法知道要给对象添加什么属性。所以只能用([])方式。
可以通过for/in循环遍历对象。
1 var o={x:1,y:0}
2 for(var p in o){
3 console.log(o[p]);// 1 0
4 }
自有属性和继承属性
对象的属性有自有属性和继承属性,当我们访问一个对象的某个属性x时,先要在查找对象上是否存在x属性,当对象上不存在x属性时,会到对象的原型上查找,如果对象的原型上不存在x属性,则继续在这个对象的原型上找,直到找到在对象的原型上找到x或者找到一个原型是null的对象。
1 var o={x:1,y:10}
2 var obj=Object.create(o);
3 obj.z=11;
4 console.log(obj.z);//11 对象的自有属性
5 console.log(obj.x);//1 对象的原型属性
6 console.log(obj.toString()); //对象的原型的原型Object.prototype的属性
如果给对象属性x赋值,对象如果不存在这个属性,那么对象会添加一个属性x,如果对象存在属性x,那么会修改这个属性的值,如果对象没有这个属性但是对象的原型有这个属性x,那么会在对象上添加这个属性,访问对象的x属性将不会访问到对象的原型上的x属性。
1 var o={x:1,y:10}
2 var obj=Object.create(o);
3 //对象上没有的属性赋值,新建一个属性z
4 obj.z=11;
5 //给对象的已有属性赋值,修改属性值。
6 obj.z=1;
7 //给对象上不存在但原型上存在的属性赋值,对象会添加这个属性
8 obj.x=2;
9 console.log(obj.x)//2
10 console.log(o.x)//1
对象的属性赋值操作会首先检查原型链,以此判断是否允许赋值。如果对象继承了一个只读属性,那么对这个属性赋值是不允许的。
1 var o={};
2 Object.defineProperty(o, "key", {
3 enumerable: false,
4 configurable: false,
5 writable: false,
6 value: "static"
7 });
8 var obj=Object.create(o);
9 console.log(obj.key);//"static"
10 obj.key=3;
11 console.log(obj.key);//"static"
对象的赋值总是在原始对象上操作的,不会影响原型对象。对象的继承只有在查询是才能体会到。对象的赋值会创建一个属性或者修改一个属性,但是有一个例外,如果对象继承的是一个构造器属性x,那么给对象对象赋值不会创建x双属性,只是调用setter方法。如果调用setter方法时定义了属性,那么这个属性只是针对对象o,与原型无关。
属性的访问出错
访问对象不存在的属性不会出错,会返回undefined。
如果访问不存在对象的属性时会出错,null和undefined没有属性,访问它们的属性会出错。
下面提供了两种防止出错的方法。
var len;
if(book){
if(book.subtitle) len=book.subtitle.length;
}
也可以写成
var len=book&&book.subtitle&&book.subtitle.length;
设置属性出错
给null和undefined属性赋值会出错。给其他对象设置属性有可能失败,但是不会出错。
下面是对象设置值失败的情况;
1、对象的属性p是只读的。
2、对象的属性p是继承属性,且是可读的。
3、对象没有属性p,且没有继承构造器属性setter,则一定可以设置p属性,但是如果对象是不可以扩展的,则不能给对象设置属性。
五、删除属性
可以使用delete运算符删除对象的属性,delete运算符的操作数应当是一个属性访问表达式。
delete运算只是断开属性和宿主对象的联系。
1 var obj={
2 x:{y:1}
3 }
4 var o=obj.x;
5 delete obj.x;
6 console.log(o.y);//1
delete运算符只能删除对象的自有属性,而不能删除对象的继承属性。
detete删除成功或者没有副作用(删除对象不存在的属性)会返回true,删除对象的不可配置属性会返回false.
1 var o={};
2 Object.defineProperty(o,'x',{value:1,configurable:false})
3 console.log(delete o.x);//false
六、检测属性
方法一:in运算符,in运算符的左侧是属性(字符串),右侧是对象。如果对象的自有属性或者继承属性(与属性的可枚举性无关)包含这个属性,则返回true。
1 var o={
2 set y(value){
3 console.log(value);
4 },
5 get y(){
6 return 120;
7 }
8 };
9 Object.defineProperty(o,'x',{value:1,enumerable:true})
10 console.log('x' in o);//true
11 console.log("toString" in o);//true
12 console.log('y' in o);
方法二:hasOwnProperty(),这个方法可以用来检测一个对象是否含有特定的自身属性;和 in
运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
1 var o={
2 set y(value){
3 console.log(value);
4 },
5 get y(){
6 return 120;
7 }
8 };
9 Object.defineProperty(o,'x',{value:1,enumerable:true})
10 console.log(o.hasOwnProperty('x'));//true
11 console.log(o.hasOwnProperty('toString'));//false
12 console.log(o.hasOwnProperty('y'));//true
方法三:propertyIsEnumerable(),只有对象时自身的且可以枚举才返回true.
1 var o={ 2 set y(value){ 3 console.log(value); 4 }, 5 get y(){ 6 return 120; 7 } 8 }; 9 Object.defineProperty(o,'x',{value:1,enumerable:false}) 10 console.log(o.propertyIsEnumerable('x'));//false不可枚举 11 console.log(o.propertyIsEnumerable('toString'));//false 不是自身属性 12 console.log(o.propertyIsEnumerable('y'));//true
方法四:同!==运算符判断,不过当对象的属性值为undefined的时候不能判断。
1 var o={
2 set y(value){
3 console.log(value);
4 },
5 get y(){
6 return undefined;
7 }
8 };
9 Object.defineProperty(o,'x',{value:1,enumerable:false})
10 console.log(o.x!==undefined);//true
11 console.log(o.y!==undefined);//false 属性存在(特殊情况)
12 console.log(o.toString!==undefined)//true
七、枚举属性
方法一:for/in循环 遍历对象中的所有可枚举属性(包括自有属性和继承属性)
1 var o={ 2 z:10, 3 set y(value){ 4 console.log(value); 5 }, 6 get y(){ 7 return 10; 8 } 9 }; 10 Object.defineProperty(o,'x',{value:1,enumerable:false}); 11 function extend(p,o){ 12 //对于构造器属性,只会简单的赋值属性的值。 13 for(var s in o){ 14 p[s]=o[s]; 15 } 16 return p; 17 } 18 var p={}; 19 var p=extend(p,o); 20 console.log(p);//{x:10,y:10}
方法二:Object.keys() 返回一个数组,数组的元素由对象自身的可枚举属性组成。
1 var o={
2 z:10,
3 set y(value){
4 console.log(value);
5 },
6 get y(){
7 return 10;
8 }
9 };
10 Object.defineProperty(o,'x',{value:1,enumerable:false});
11 console.log(Object.keys(o));//['x','y']
方法三:Object.getOwnPropertyNames() 返回一个数组,数组的元素由对象的自有属性组成。
1 var o={
2 z:10,
3 set y(value){
4 console.log(value);
5 },
6 get y(){
7 return 10;
8 }
9 };
10 Object.defineProperty(o,'x',{value:1,enumerable:false});
11 console.log(Object.getOwnPropertyNames(o));//['x','y','z']
八、存取器属性
对象的属性由名/值和一对特性组成,es5中可以用一个或两个方法代替,这个就是setter和getter方法。由setter和getter定义的属性叫做构造器属性。它不同于数据属性,数据属性只是一个简单的值。
查询对象的存取器属性的值时,调用getter(无参数),设置对象的存取器属性的值时,调用对象的setter方法,将赋值表达式右侧的值当做参数传入setter方法。setter方法的返回值可以忽略。
和数据属性不同,存取器属性不具有可写性,如果属性同时具有setter和getter方法,那么这个对象是可读写的,如果属性只有getter方法,那么属性是只读的,如果对象只有setter方法,那么对象是只写的,读取对象的属性总是返回undefined。
存取器属性定义为一个或者两个和属性同名的函数,函数的关键字用get或者set代替,属性名和函数体之间没有冒号,函数的结束和下一个方法或者属性的开始之间用逗号隔开。
1 var o={
2 z:10,
3 set y(value){
4 console.log(value);
5 },
6 get y(){
7 return 10;
8 }
9 };
存取器的函数可以看作对象的方法,函数体内可以使用this指向当前对象。存取器属性是可以继承的 。
九、属性的特性
为了实现对象属性的设置和查询,es5中定义了一个属性描述符对象,这个对象包含了属性的四个特性,
对象的存取器属性的四个特性:get,set,enumerable,configurable,
数据属性的四个特性:value,writable,enumerable,configurable
configurable
true
当且仅当该属性描述符的类型可以被改变并且该属性可以从对应对象中删除。
默认为false
enumerable
true
当且仅当在枚举相应对象上的属性时该属性显现。
默认为false
value
- 与属性关联的值。可以是任何有效的JavaScript值(数字,对象,函数等)。
默认为undefined
. writable
true
当且仅当与该属性相关联的值可以用assignment operator改变时。
默认为false
get
- 作为该属性的 getter 函数,如果没有 getter 则为
undefined
。函数返回值将被用作属性的值。
默认为undefined
set
- 作为属性的 setter 函数,如果没有 setter 则为
undefined
。函数将仅接受参数赋值给该属性的新值。
默认为undefined
- 查询对象属性的特性
Object.getOwnPropertyDescriptor()
方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
Object.getOwnPropertyDescriptors()
方法用来获取一个对象的所有自身属性的描述符。
1 var o = {
2 z: 10,
3 set y(value) {
4 console.log(value);
5 },
6 get y() {
7 return 10;
8 }
9 };
10 console.log(Object.getOwnPropertyDescriptor(o, 'z'));
11 /*{ value: 10,
12 writable: true,
13 enumerable: true,
14 configurable: true
15 }*/
16 console.log(Object.getOwnPropertyDescriptors(o));
17 /*
18 { z:
19 { value: 10,
20 writable: true,
21 enumerable: true,
22 configurable: true },
23 y:
24 { get: [Function: get y],
25 set: [Function: set y],
26 enumerable: true,
27 configurable: true } }
28 */
设置或者新建对象的特性
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
Object.defineProperties()
方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
var obj = {};
Object.defineProperties(obj, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
});var descriptor = Object.create(null); // 没有继承的属性
// 默认没有 enumerable,没有 configurable,没有 writable
descriptor.value = 'static';
Object.defineProperty(obj, 'key', descriptor);
// 显式
Object.defineProperty(obj, "key", {
enumerable: false,
configurable: false,
writable: false,
value: "static"
});
//添加构造器属性
Object.defineProperty(o, "b", {
get : function(){
return bValue;
},
set : function(newValue){
bValue = newValue;
},
enumerable : true,
configurable : true
});
对于新创建的属性,默认值为false或者undefined,对于修改的属性,默认的属性值没有任何改变。
Object.defineProperty()和
Object.defineProperties()违反规则的使用将会报错。
1、对象不可扩展,可以编辑对象的属性,但是不能给对象添加属性。
2、对象的属性是不可以配置的,不能修改它的可配置性和可枚举性。
3、对象的属性是存取器属性且不可配置的,不可以修改其getter和setter方法,也不能将他转换成数据属性。
4、对象的属性时数据属性且不可配置的,不能将其转化成存取器属性。
5、如果数据属性是不可配置的,不能将他的可写性从false转化为true,但是可以把它从true转化成false.
6、如果数据属性是不可配置不可写的,不能修改它的值,但是可配置不可写的属性是可以修改他的值得。
1 //给Obejcet.Prototype对象添加自定义的继承函数
2 //参数对象的属性和属性描述符都会被复制。
3 //如果对象中存在同名属性,则忽略。
4 Object.defineProperty(Object.prototype, 'extend', {
5 configurable: false,
6 writable: false,
7 enumerable: false,
8 value: function (o) {
9 var arr = Object.getOwnPropertyNames(o);
10 var desc;
11 for (var i = 0; i < arr.length; i++) {
12 if (arr[i] in this) continue;
13 desc = Object.getOwnPropertyDescriptor(o, arr[i]);
14 Object.defineProperty(this, arr[i], desc);
15 }
16 }
17 })
18 var o = { x: 1 };
19 Object.defineProperty(o, 'key', {
20 value: 'static',
21 writable: true,
22 enumerable: false
23 });
24 var p = {};
25 p.extend(o);
26 console.log(Object.getOwnPropertyDescriptor(p,"key"));
27
28 /*{ value: 'static',
29 writable: true,
30 enumerable: false,
31 configurable: false }*/
十、对象的三个属性
每一个对象都有的原型、类和可扩展性。
对象的原型
对象的原型属性是用来继承属性的,原型对象在对象创建之前就设置好的。
Object.getPrototypeOf()
方法返回指定对象的原型。也可以通过对象的__proto__属性访问对象的原型。通过构造函数创建的对象和对象字面量创建的对象可以用对象的构造器constructor.prototype属性访问对象的原型。
1 function X(){
2 this.y=10,
3 this.z=11;
4 }
5 X.prototype.a=111;
6 var x=new X();
7 console.log(Object.getPrototypeOf(x));//X.prototype
8 console.log(x.__proto__);//X.prototype
9 console.log(x.constructor.prototype);//X.prototype
通过对象的构造函数创建的对象,对象的原型上的constructort指向构造函数。
检测是不是对象的原型:isPrototypeOf();
1 function X(){
2 this.y=10,
3 this.z=11;
4 }
5 X.prototype.a=111;
6 var x=new X();
7 console.log(X.prototype.isPrototypeOf(x));//true
对象的类属性
对象的类属性是一个字符串,用来表示对象的类型。只能通过toString()方法简单的获取,但是很多对象都重写了toString()方法。可以用如下的工具函数获得对象的类属性。
8 function classof(o){
9 if(o===null) return "null";
10 if(o===undefined) return "undefined";
11 return Object.prototype.toString.call(o).slice(8,-1);
12 }
13 console.log(classof(""));//'String'
对象的可扩展性
对象的可扩展性表明是否可以给对象添加属性。所有的内置对象都是可扩展的。Object.preventExtensions()可以将对象变成不可扩展的,这个过程是不可逆的。把对象转化成不可扩展的值影响对象本身,对象的原型依旧是可以扩展的,可其添加的方法对象会继承。
Object.isExtensible()
方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
Object.preventExtensions()
方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
1 function X(){
2 this.y=10,
3 this.z=11;
4 }
5 var x=new X();
6 //判断对象是不可以扩展
7 console.log(Object.isExtensible(x));//true
8 //将对象转化成不可扩展的
9 Object.preventExtensions(x);
10 //给对象添加属性
11 x.b=11;
12 console.log(x.b);//undefined
13 //给对象的原型添加方法
14 X.prototype.a=111;
15 console.log(x.a);//111
Object.seal()
方法可以让一个对象密封,并返回被密封后的对象。密封对象将会阻止向对象添加新的属性,并且会将所有已有属性的可配置性(configurable)置为不可配置(false),即不可修改属性的描述或删除属性。但是可写性描述(writable)为可写(true)的属性的值仍然可以被修改。不可逆的过程
Object.isSealed()
方法判断一个对象是否被密封。
Object.freeze()
方法可以冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。不可逆的过程。如果对象有setter方法,还可以给对象赋值。
Object.isFrozen()
方法判断一个对象是否被冻结。
十一、对象的序列化
对象的序列化是将对象转化成字符串。es5中用JSON.stringify()和JSON.parse()方法序列化和还原对象。JSON.stringify()只能序列化对象的可枚举属性,不可以枚举的属性在序列化时会被省略。可以序列化对象、数组、字符串、无穷大、true、false和null.函数、Error对象、正则和undefined不能序列化。NaN、无穷大序列化的结果是null.日期对象序列化是一个日期字符串,无法还原成对象。
1 var o={
2 now:new Date(),
3 x:null,
4 y:Infinity,
5 };
6 Object.defineProperty(o,'z',{
7 enumerable:false,
8 });
9 //序列化对象
10 var s=JSON.stringify(o);
11 console.log(s);//'{"now":"2018-03-12T19:34:38.685Z","x":null,"y":null}'
12 //反序列化
13 console.log(JSON.parse(s));//{ now: '2018-03-12T19:34:38.685Z', x: null, y: null }
十二、原型上的方法
toString();对象转化成字符串时调用。
toLocaleString()返回对象的本地化字符串。默认回调用toString();Date()和Number()对象的toLocaleString()做了定制,可以转换成本地化转化。
toJSON()。JSON.Stringify()会调用这个方法。
valueOf()对象类型转化时调用。日期对象的valueOf()做了定制。返回1970.1.1到现在的毫秒数。