对象
对象其实可以看做有许多键值对组合成的一个无序集合,可以通过键俩访问值。值一般由,基本数据类型 和 object 组合。
对象的创建
有三种方式:
1. new构造函数
-
使用new来创建一个对象
var person = new Object();
甚至可以简写成为:var person = new Object;
-
Object参数如果是一个对象,则直接返回对象本身
var o = { a : 1};
var p = new Object(o);
alert(o === p); // true
- 传入原始数据类型,则返回包装对象
var o1 = new Object("123");
console.log(o1); // String{0:"1",1:"2",2:"3",length:3,__proto__: String, [[PrimitiveValue]]: "123"}
var o2 = new Object(123);
console.log(o2) // Number{123, __proto__: Number, [[PrimitiveValue]]: 123}
var o3 = new Object(true);
console.log(o3)
// Boolean {__proto__: Boolean, [[PrimitiveValue]]: true}
// ------------ 下面是空对象 和不传参数一样。
var o4 = new Object(null)
console.log(o4) // Object{__proto__: Object}
var o5 = new Object(undefined)
console.log(o5) // Object{__proto__: Object}
var o6 = new Object()
console.log(o6) // Object{__proto__: Object}
2. 对象字面量
这是我们 最熟悉的一种,也是经常使用的一种,其实使用字面量只是隐藏了与使用new操作符相同的基本过程,于是也可以叫做语法糖。
简单示例:
var person = {
age : 24,
name: 'xiaoming',
1: true,
}
注意,使用对象字面量方法来定义对象的时候,属性名会自动转化为字符串。
就是上面示例,自动变成下面的。
var person = {
'age' : 24,
'name': 'xiaoming',
'1': true,
}
Object.create()
ES5定义了一个名为Object.create()的方法,它创建一个新对象,第一个参数就是这个对象的原型,第二个可选参数用以对对象的属性进行进一步描述
var o = Object({a:1})
console.log(o) // Object{a:1,__proto__:object}
注意:传入参数null来创建一个没有原型的新对象
var o1 = Object.create(null);
var o2 = {};
此时o1不会继承任何东西,甚至不包括基础方法。比如toString()和valueOf()。
如果想要创建一个普通的空对象(例如,通过{}或new Object()创建的对象),需要传入Object.prototype, 这就是因为,这个函数的第一个参数就是原型对象。
var o3 = Object.create(Object.prototype);
var o4 = {};
对象的引用
如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。
var o = {
a:1,
}
var p = o;
console.log(p.a) // 1
p.a = 0;
console.log(o.a) // 0
对象的属性
对象的属相操作,有两种 [] 和 点号(.)。
点号(.), 在使用的时候,属性名必须符合标识符的命名规则。
不存在的属性取值返回undefined。
var o = {
a : 0,
1 : true,
}
o.b = 1; // 可以增加属性,并赋值
console.log(o.a) // 可以取值操作
console.log(o.1) // 报错
console.log(o.b) // undefined 不存在的变量
o.a = 2; // 可以赋值操作
console.log(o.a);
[] 方括号中的值, 若是字符串,则直接访问。 如果非字符串类型且不符合标识符命名,则转成字符串访问,如果非字符串类型且符合标识符命名,则需要识别变量的值,变量未定义,则报错。
在[] 访问属性的时候 是可以计算属性的
var c = 1;
var o = {}
console.log(o[0]) // undefined o[0] --> o['0'] 访问。
console.log(o[a]) // 报错,a符合变量的命名规则,a未被定义,而报错
console.log(o['a']) // undefined o['a'] 访问。
console.log(o[null]) // undefined , o[null] --> o['null']访问
console.log(o[c + 1]) // undefined o[c+1] --> o['4']访问
注意 null和undefined不是对象,给它们设置属性会报错
属性的删除
使用delete运算符可以删除对象属性(包括数组元素)。
delete是有返回值的,删除成功则返回true,删除不存在的属性或非左值时,返回false。当使用delete操作符删除不可配置的属性时,返回false,严格模式下会抛出TypeError错误
将属性赋值为 null 和undefined 只是修改了属性的值。
var o = {
a:1
}
console.log(delete o.a);//true
console.log(o.a);//undefined
console.log('a' in o);//false in 操作符 可以判断对象是否具有该属性,但是无法判断是否是继承来的
使用delete删除数组元素时,不会改变数组长度
var a = [1,2,3];
delete a[2];
2 in a;//false
a.length;//3
属性的检测
每一个javascript对象都和另一个对象相关联。“另一个对象”就是我们熟知的原型对象,每一个对象都会从原型对象继承属性。
对象本身具有的属性叫自有属性,从原型对象继承而来的属性叫继承属性
in: in操作符可以判断对象是否可以使用某一属性,但无法区别自有还是继承属性
for-in: for-in循环可以遍历出该对象中所有可枚举属性
hasOwnProperty(): 可以确定该属性是自有属性还是继承属性
Object.keys(): 返回所有可枚举的自有属性
Object.getOwnPropertyNames(): Object.keys()方法不同,Object.getOwnPropertyNames()方法返回所有自有属性(包括不可枚举的属性)
属性描述符
对象属性描述符分为, 数据属性 访问器属性
数据属性
数据属性(data property)包含一个数据值的位置,在这个位置可以读取和写入值。
-
可配置configuable
- 可配置性决定是否可以使用delete删除属性,以及是否可以修改属性描述符的特性,默认值为true
var o = {a:1}; Object.defineProperty(o,'a',{ configurable:false, }); delete o.a; //false 严格模式下报错 console.log(o.a);//1
- 设置Configurable:false后,将无法再使用defineProperty()方法来修改属性描述符
var o = {a:1}; Object.defineProperty(o,'a',{ configurable:false }); // 报错不能修改属性 Object.defineProperty(o,'a',{ configurable:true });
- writable的状态 只能是从true变为false
-
可枚举enumerable
可枚举性决定属性是否出现在对象的属性枚举中,具体来说,for-in循环、Object.keys方法、JSON.stringify方法是否会取到该属性。var o = {a:1}; Object.defineProperty(o,'a',{enumerable:false}); for(var i in o){ console.log(o[i]);//undefined }
-
可写性writable
可写性决定是否可以修改属性的值,默认值为truevar o = {a:1}; Object.defineProperty(o,'a',{ writable:false }); console.log(o.a);//1 o.a = 2;//由于设置writable为false,所以o.a=2这个语句会默认失效,严格模式会报错 console.log(o.a);//1
-
属性值value
属性值包含这个属性的数据值,读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。默认值为undefined
访问器属性
访问器属性不包含数据值;它们包含一对儿getter和setter函数(不过,这两个函数都不是必需的)。在读取访问器属性时,会调用 getter函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据。
- 可配置性Configurable
表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为 true - 可枚举性Enumerable
可枚举性决定属性是否出现在对象的属性枚举中,比如是否可以通过for-in循环返回该属性,默认值为true - getter 读
在读取属性时调用的函数。默认值为undefined - setter 写
在写入属性时调用的函数。默认值为undefined
和数据属性不同,访问器属性不具有可写性(Writable)。如果属性同时具有getter和setter方法,那么它是一个读/写属性。如果它只有getter方法,那么它是一个只读属性。如果它只有setter方法,那么它是一个只写属性。读取只写属性总是返回undefined
get是一个隐藏函数,在获取属性值时调用。set也是一个隐藏函数,在设置属性值时调用,它们的默认值都是undefined。Object.definedProperty()中的get和set对应于对象字面量中get和set方法
[注意]getter和setter取代了数据属性中的value和writable属性
通常这里的get和set是用来 拦截数据访问和修改的。
var o = {
get a(){
return 0;
}
}
console.log(o.a) // 0
o.a = 1 // 没有set方法, 默认失效
console.log(o.a) // 0
// ---------------------------------------
Object.defineProperty(o,'b',{
get: function(){
return 2;
}
})
console.log(o.b);//2
//由于没有设置set方法,所以o.a=3的赋值语句会静默失败
o.b = 1;
console.log(o.b);//2
只有set
var o = {}
Object.defineProperty(o,'a',{
set: function(){
return 0;
}
})
o.a = 1;
console.log(o.a);//undefined
通常情况下 get 和set是成对出现的, 并且你会发现,这样做,我们还需要空间来存储值。 这也是数据属性的不同之处吧。
var o = {};
Object.defineProperty(o,'a',{
get: function(){
return this._a;
},
set :function(val){
this._a = val*2;
}
})
o.a = 1;
console.log(o.a);//2
对象属性的获取和设置
- Object.getOwnPropertyDescriptor(对象,属性名)
查看对象的属性,返回一个对象,包含了属性的特性。,当查看不存在的属性时,返回undefined。 并且只适用于自身的属性。
var o = {a : 0};
// Object {value: 0, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(o,'a'));
// undefined
console.log(Object.getOwnPropertyDescriptor(o,'b'));
- Object.defineProperty(对象,属性名, 属性描述)
返回配置后的对象。
使用该方法创建或配置对象属性的描述符时,如果不针对该属性进行描述符的配置,则该项描述符默认为false
var o = {};
Object.defineProperty(o,'a',{
value: 0,
writable: true,
enumerable: true,
});
// /{value: 1, writable: true, enumerable: true, configurable: false} 没有配置由于没有配置configurable,所以它的值为false
console.log(Object.getOwnPropertyDescriptor(o,'a'))
- Object.defineProperties(o, 属相描述)
用于创建或配置对象的多个属性的描述符,返回配置后的对象。
var o = {
a : 1,
}
Object.defineProperties(o,{
a:{enumerable:false},
b:{value:2}
})
//{value: 1, writable: true, enumerable: false, configurable: true}
console.log(Object.getOwnPropertyDescriptor(o,'a'));
//{value: 2, writable: false, enumerable: false, configurable: false}
console.log(Object.getOwnPropertyDescriptor(o,'b'));
- Object.create(原型对象,描述)
使用指定的原型和属性来创建一个对象。
var o = Object.create(Object.property,{
a:{
value: 0,
writable: false,
enumerable: true,
},
b{
value: 1,
}
})
//{value: 0, writable: false, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(obj,'a'));
// {value: 1, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(obj,'b'))
对象的拷贝
浅拷贝
使用for-in将对象的所有属性复制到新建的空对象中,并将器返回
function simpleClone(o,cloneObj){
// 判断参数
for(var i in obj){
cloneObj[i] = obj[i];
}
return cloneObj;
}
使用属性描述符
function simpleClone(o){
// 判断参数
var cloneObj = Object.create(Object.getPrototypeOf(o)); // 根据o对象的原型对象,创建新对象。
// 获取自身属性并迭代。
Object.getOwnPropertyNames(o).forEach(function(propKey){
// 根据自身属性名 获取属相描述。
var desc = Object.getOwnPropertyDescriptor(o,propKey);
// 给克隆对象设置 属性描述
Object.defineProperty(cloneObj,propKey,desc);
});
return cloneObj;
}
深拷贝
复制对象的属性时,对其进行判断,如果是数组或对象,则再次调用拷贝函数;否则,直接复制对象属性
function deepClone(o,cloneObj){
if(typeof o != 'object'){
return false;
}
var cloneObj = cloneObj || {}; // 保证cloneObj不是null和undefined
for(var i in o){
if(typeof o[i] === 'object'){ // 是对象继续递归拷贝
cloneObj[i] = (o[i] instanceof Array) ? [] : {};
arguments.callee(o[i],cloneObj[i]);
}else{
cloneObj[i] = o[i];
}
}
}
使用 json.
先是对象转成字符串,然后在转成对象返回。
function jsonDeepClone(o){
return JSON.parse(JSON.string(o))
}