首页 > 代码库 > javascript 对象(四)
javascript 对象(四)
一、对象概述
对象中包含一系列的属性,这些属性是无序的。每个属性都有一个字符串key和对应的value。
var obj={x:1,y:2};obj.x;obj.y;
1、为什么属性的key必须是字符串
重点:
可见,字符串的1和数字1访问的都是同一个属性
不管把空对象作为一个key,还是用带有x属性的对象作为key,实际上javascript都会把它转化为字符串,会toString()一下再去处理。所以最终依然指向同样一个属性。
2、对象结构
1、对象属性的结构
- 对象的属性可以动态添加和删除
- 对象的每个属性都有一些标签。
这些标签为每个属性提供访问权限的控制,比如是否可写(writable),是否可以被删除掉(configurable),是否可枚举(enumerable)。
var obj={x:1,y:2};obj.x;obj.y;
这段代码对应的结构:
2、对象的原型
每个对象都有一个原型[[proto]]
function foo(){}foo.prototype.z=3;var obj=new foo();
上面这段代码对应的obj的结构如下:
3、对象的标签
对象除了[[proto]]标签还有[[class]]标签和[[extensible]]标签。
[[class]]标签标示对象是属于哪个种类的。
[[extensible]]标签标示对象是否允许增加新的属性。
所以一个对象完整的结构如下:
二、创建对象、原型链
1、创建对象的方式有哪些
1、对象字面量创建对象
可以嵌套
var obj1={x:1,y:2}; var obj2={ x:1, y:2, o:{ z:3, n:4 } };
2、new构造器的方法创建
原型链结构
function foo(){}foo.prototype.z=3;
var obj=new foo();obj.y=2;obj.x=1;
通过new去构造对象的特点:它的原型会指向构造器的prototype属性。
这么做的作用:通过原型链继承,继承原型上的属性和方法。
当访问一个属性,这个对象上没有该属性,通过原型链向上查找,一直找到null,还没有就返回undefined。
注意:
obj.z返回undefined,不一定是没有z属性,也可能是z=undefined。也可能是向上查找原型链没找到返回undefined。一个对象是否有某个属性,可以通过in或者hasOwnProperty方法来确认。
3、Object.create创建对象
Object.create是系统内置的函数,这个函数会接收一个参数,一般是一个对象,它会返回一个新创建的对象,并且让对象的原型指向这个参数。
对应的结构为:
用字面量创建的对象{x:1}也是有原型的,它的原型指向Object.prototype,所以在obj上仍然可以用obj.toString这个方法,并且由于是从原型链上继承的,并不是对象本身的属性,所以obj.hasOwnProperty(‘x‘)会返回false。
注意:
并不是所有的对象都有toString(),因为并不是所有的对象的原型链上都有Object.prototype。
如果用Object.create(null)传入一个null的话,它的原型就直接就是null了。这样它就不会包含任何方法,
三、属性操作
1、读写对象属性
可以用.操作符或者[]。
一般情况用.操作符更好一点,有些编辑器可以自动检查。
当需要拼属性名的时候往往需要用[]。
除了用for循环拼接属性名,也可以用for in循环。for in有可能把原型链上的属性也遍历出来,并且是无序的。
2、属性异常
obj.y会查找原型链,一直找到末端是null还是没有的话就返回undefined。
obj.y.z,obj.y不存在是undefined,再去访问或者写入z,就会报错不能read或者set 一个undefined属性z。
重点:
所以在做一些出来时经常要先做些判断,比如
var yz;if(obj.y){ yz=obj.y.z}
技巧:巧用运算符的规则
var yz=obj&&obj.y&&obj.y.z;
3、删除属性
重复删除已经不存在的属性,不会做任何事情,但是因为属性已经不存在了,仍然会返回true。
注意:delete并不表示操作生效了或者成功了,只表示这个对象上已经不存在delete的这个属性。
特殊情况:
有些属性是不允许被删除的,比如Object.prototype。
顺便说一下其他坑:
用var定义的全局变量也不能被删除。
隐式声明的全局变量可以被删除。
局部变量,函数声明,函数内部的函数声明都是不可被delete掉的。
了解更多参考:javascript var变量删除
4、检测属性
<script type="text/javascript"> var cat=new Object; cat.legs=4; cat.name="Kitty"; //属性检测 console.log(‘legs‘ in cat); //true console.log(‘abc‘ in cat); //false console.log(‘toString‘ in cat); //true ,继承来的属性 console.log(cat.hasOwnProperty(‘legs‘)); //true console.log(cat.hasOwnProperty(‘toString‘)); //false //查看属性是否可枚举 console.log(cat.propertyIsEnumerable(‘legs‘)); //true console.log(cat.propertyIsEnumerable(‘toString‘)); //false //输出cat的属性和对应的值 for(p in cat){ console.log(p+" "+cat[p]); } /** legs 4 name Kitty / </script>
自定义一个不可枚举的属性。
Object.defineProperty(cat,‘price‘,{enumerable:false,value:1000});//Object {legs: 4, name: "Kitty", price: 1000}cat.propertyIsEnumerable(‘price‘);//falsecat.hasOwnProperty(‘price‘);//true
注意:
用Object.defineProperty定义的属性,默认的enumerable,writable,configurable标签都是false,所以这里的enumerable也可以不写。
5、枚举属性
var o={x:1,y:2,z:3};‘toString‘ in o//trueo.propertyIsEnumerable(‘toString‘)//falsevar key;for(key in o){ console.log(key);}/*xyz*/
for in枚举
var o={x:1,y:2,z:3}; var obj=Object.create(o); obj.a=4; var key; for(key in obj){ console.log(key);//a,x,y,z }
如果只想枚举对象上的属性,加上hasOwnProperty判断
var o={x:1,y:2,z:3}; var obj=Object.create(o); obj.a=4; var key; for(key in obj){ if(obj.hasOwnProperty(key)){ console.log(key);//a } }
四、get/set方法
另一种读写属性的方式
1、get,set
get,set方法和其他属性区别很大, 以get,set关键字开头,紧接着对应的属性的名字,后面是个函数体。get,set方法与一般属性之间仍然是用逗号隔开。
var man={ name:‘Bosn‘, weibo:‘@Bosn‘, get age(){ return new Date().getFullYear()-1988; }, set age(val){ console.log(‘Age can\‘t be set to ‘+val); }}console.log(man.age); //29man.age=100; //Age can‘t be set to 100console.log(man.age); //still 29
也可以写成:
get:function age() {};
复杂点的用法:
$age,这样一个特殊的key表示这个key我不想暴露到外面。
isNaN(x)如果x是特殊的非数字值(或者能被转换为非数字值)返回true。
如果把 NaN 与任何值(包括其自身)相比得到的结果均是 false,所以要判断某个值是否是 NaN,不能使用 == 或 === 运算符。正因为如此,isNaN() 函数是必需的。
var man = { weibo: ‘@Bosn‘, $age: null, get age() { if (this.$age == undefined) { //undefined或者null return new Date().getFullYear() - 1988; } else { return this.$age; } }, set age(val) { val = +val; //把val强制转化为数字,如果val是数字声明都不做,如果是字符串转化为数字 if (!isNaN(val) && val > 0 && val < 150) { this.$age = +val; } else { throw new Error(‘Incorrect val = ‘ + val); } } } console.log(man.age); //29 man.age = 100; console.log(man.age); //100 man.age = ‘abc‘; //error:Incorrect val =NaN
2、get/set与原型链
get ,set方法在和原型链结合的时候有一些更复杂的情况。
function foo(){} Object.defineProperty(foo.prototype,‘z‘,{get:function(){return 1;}}); var obj=new foo(); console.log(obj.z); //1 obj.z=10; console.log(obj.z); //still 1
当obj对象上没有z属性的时候,并且在原型链上查找的时候发现有get方法或者set方法的时候,那么当我们尝试去赋值的时候,会尝试走get,stet方法,而不会再去给obj添加先属性这样处理。
如何实现对z的修改呢?
如何在obj上添加一个z属性呢?
通过Object.defineProperty。
function foo() {}Object.defineProperty(foo.prototype, ‘z‘, { get: function() { return 1; }});var obj = new foo();console.log(obj.z); //1obj.z = 10;console.log(obj.z); //still 1Object.defineProperty(obj, ‘z‘, { value: 100, configurable: true});console.log(obj.z); //100delete obj.z;console.log(obj.z); //back to 1
访问对象原型链上writable为false的属性时也是不可写的,此时也需要用Object.defineProperty来添加属性,如下:
var o={};Object.defineProperty(o,‘x‘,{value:1}); //writable=false,configurable=falsevar obj=Object.create(o);console.log(obj.x);//1obj.x=200;console.log(obj.x);//still 1,can‘t change itObject.defineProperty(obj,‘x‘,{writable:true,configurable:true,value:100});console.log(obj.x);//100obj.x=500;console.log(obj.x);//500
五、属性标签
属性级的权限设置
1、怎样看一个对象的属性标签
用Object.getOwnPropertyDescriptor()第一个参数是你要判断的对象,第二个参数是一个字符串的属性名。
查看随便一个字面量对象的属性标签。
2、设置一个对象的属性标签
Object.defineProperty接收3个参数,第一个参数是添加属性的对象,第二个参数是字符串的属性的名字,第三个参数是个对象,这个对象里面就是具体的每一个标签的值。
定义一个属性type的enumerable为false,所以for in时不可枚举,此外也可以通过Object.keys(person)来获取对象上的所有的key。
给person定义多个属性
定义多个属性用Object.defineProperties,该方法接收2个参数,第一参数是你要定义属性的对象,第二个是一个复杂的对象,该对象第一级是你想定义的对象的属性,如下是title,corp和salary,属性的值等价于defineProperty里的第三个参数,即包含所有表情情况的一个对象。
下面定义属性时,标签为true的写了,不写的默认都是false。
更复杂的情况:
用标签属性访问,可以实现很多强大的功能,在编写一些复杂的程序上,帮助也是非常大的。
var person={};Object.defineProperties(person,{ title:{value:‘fe‘,enumerable:true}, corp:{value:‘BABA‘,enumerable:true}, salary:{value:50000,enumerable:true,writable:true}, luck:{ get:function(){ return Math.random()>0.5?‘good‘:‘bad‘; } }, promote:{ set:function(level){ this.salary*=1+level*0.1; } }});console.log(person.salary); //50000person.promote=2;console.log(person.salary); //60000
属性的标签可以通过Object.defineProperty重复设置。
如果writable为false,不能修改属性的值,如果此时configurable为true,可以通过Object.defineProperty设置value来修改。或者把writable改为true,再修改属性值。所以configurable为true时想通过writable阻止属性的修改没有意义。
注意:
configurable为false不允许delete,不允许修改getter/setter方法,只允许把writable从true修改为false。
六、对象标签、对象序列化
1、[[proto]]
原型链
2、[[class]]
[[class]]标签没有一个直接的方式去查看它或者是修改它。
可以间接的通过Object.prototype.toString的方式去获取class。
Object.prototype.toString.call([1,2]);//[object Array]
访问Object.prototype.toString返回的是[object 类型],用Object.prototype.toString.call(o).slice(8,-1);截取从第8个字符开始一直到最后,然后再去掉最后一个字符的字符串,便于查看类型。
3、[[extensible]]
[[extensible]]标签表示对象是否可扩展。
就是说对象上的属性是否可以被继续添加。
用Object.isExtensible(obj)来判断对象是否可扩展
var obj={x:1,y:2}Object.isExtensible(obj)//true 默认都是true
怎样不让扩展?
var obj={x:1,y:2}Object.isExtensible(obj)//trueObject.preventExtensions(obj)Object.isExtensible(obj)//false
不让扩展的后的目的或者结果:
obj.z=1;obj.z//undefined //添加新属性失败Object.getOwnPropertyDescriptor(obj,‘x‘)//Object {value: 1, writable: true, enumerable: true, configurable: true}//通过preventExtensions来阻止对象去添加新的属性 对象上的属性标签没变化
意味着属性仍然是可以删除掉 修改和枚举的。
往往我们在去阻止一个对象扩展的时候,我们不想让他们去修改属性标签,也不想让他们删除这些属性。
这个时候我们会用Object.seal方法,在Object.preventExtensions基础上又增加了一条限制,就是会把对象上的所有属性的configurable设置为false,所以我们再去查看属性标签,发现不同的是configurable变成了false。
Object.getOwnPropertyDescriptor(obj,‘x‘)//Object {value: 1, writable: true, enumerable: true, configurable: true}Object.seal(obj)Object.getOwnPropertyDescriptor(obj,‘x‘)//Object {value: 1, writable: true, enumerable: true, configurable: false}
可以通过Object.isSealed方法来判断一个对象是否被隐藏。
Object.getOwnPropertyDescriptor(obj,‘x‘)//Object {value: 1, writable: true, enumerable: true, configurable: false}Object.isSealed(obj)//true
有的时候,我们想限制更强一点,让对象不仅不可以添加新属性,不可以配置属性标签,不可删除,甚至也不可写。我们可以用Object.freeze(obj)这样一个方法。
Object.freeze(obj)Object.getOwnPropertyDescriptor(obj,‘x‘)//Object {value: 1, writable: false, enumerable: true, configurable: false}
freeze之后configurable和writable都变成了false,这样就实现了不可写,不可改,不可添加属性,不可删除属性,不可修改属性的值这样一个目的。
可以通过Object.isFrozen(obj)判断对象是否被冻结
Object.freeze(obj)Object.getOwnPropertyDescriptor(obj,‘x‘)//Object {value: 1, writable: false, enumerable: true, configurable: false}Object.isFrozen(obj);//true
注意:no affects prototype chain!!!
这里所有的方法只是针对这样一个对象的,并不会影响对象的原型链。
所以如果想对你的原型链也做一些处理的话。可以拿到对象的原型,遍历原型链层层冻结。
4、序列化
前端javascript处理结果发到后端,后端需要接收一字符串的数据格式。
用JSON.stringify(obj)做处理。
序列化的坑:
- 如果值是undefined,整个属性就不会出现在序列化的结果当中。
- 如果属性值是NaN或者是Infinity的话,那么会转化为null
- 如果是时间的话会转化为UTC的实际格式
后端返回JSON数据,怎样把它变成javascript对象?
合法的JSON属性必须以双引号引起来。
obj=JSON.parse(‘{"x":1}‘)obj.x//1
有的时候序列化会更复杂一点,可以自定义序列化
如何定制序列化的过程?
obj对象的o属性的值是个对象,o的对象序列化结果想要自定义。只需要在当前层下写一个toJSON方法,该方法的this指向当前层级o。
var obj={ x:1, y:2, o:{ o1:1, o2:2, toJSON:function (){ return this.o1+this.o2; } }}JSON.stringify(obj)//"{"x":1,"y":2,"o":3}"
再去stringfify时o就是通过toJSON方法返回的。
5、其他对象方法
1、toString()
2、valueOf()
参考:http://www.cnblogs.com/wangfupeng1988/tag/%E5%8E%9F%E5%9E%8B/
本文作者starof,因知识本身在变化,作者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/4904929.html有问题欢迎与我讨论,共同进步。
javascript 对象(四)