对象作为六种基本类型的一种,在js中是非常常用的。但是还有许多关于对象的深入的知识了解不深。在这里总结一下关于对象的知识。
对象类型
JavaScript有3大对象,分别是本地对象、内置对象和宿主对象。
在此引用ECMA-262(ECMAScript的制定标准)对于他们的定义:
-
本地对象
与宿主无关,独立于宿主环境的ECMAScript实现提供的对象。
简单来说,本地对象就是 ECMA-262 定义的类(引用类型)。
这些引用类型在运行过程中需要通过new来创建所需的实例对象。
包含:Object、Array、Date、RegExp、Function、Boolean、Number、String等。 -
内置对象
与宿主无关,独立于宿主环境的ECMAScript实现提供的对象。
在 ECMAScript 程序开始执行前就存在,本身就是实例化内置对象,开发者无需再去实例化。
内置对象是本地对象的子集。
包含:Global和Math。
ECMAScript5中增添了JSON这个存在于全局的内置对象。 -
宿主对象
由 ECMAScript 实现的宿主环境提供的对象,包含两大类,一个是宿主提供,一个是自定义类对象。
所有非本地对象都属于宿主对象。
对于嵌入到网页中的JS来说,其宿主对象就是浏览器提供的对象,浏览器对象有很多,如Window和Document等。
所有的DOM和BOM对象都属于宿主对象。
复制对象
当复制一个对象是时,需要判断其实引用值还是原始值。
原始值可以直接复制数据,但是引用值需要复制引用的环境。
这就分成了浅度克隆和深度克隆
- 浅克隆
基本类型 值传递
引用类型 地址传递
深度克隆
var obj = {
name : 'abc',
age : 12,
card : ['visa','master'],
car : {
name : 'bwm',
BuyYear : 2013,
ID : 'Id9067'
}
}
// 1.判断是不是原始值 null
// 2.判断是数组还是对象。遍历数组,重新进行上个步骤。 toString ,instanceof ,constructor
// 3.建立相应的数组或对象
function deepClone(origin,target){
var target = target || {};
for(var prop in origin)
{
if(origin.hasOwnProperty(prop)){
if(typeof(origin[prop]) == 'object' && origin[target] !== null){
if(Object.prototype.toString.call(origin[prop]) == '[object Array]'){
//哇,原来object是小写开头的,哭了。
target[prop] = [];
}else{
target[prop] = {};
}
// 以上的if,else可以用三目运算符代替。
// target[prop] = (Object.prototype.toString.call(origin[prop]) == '[object Array]')?[]:{};
deepClone(origin[prop],target[prop]);
}
else{
target[prop] = origin[prop];
}
}
}
return target;
}
var obj1 = {};
deepClone(obj,obj1);
对象的属性
从 ES5 开始,所有的属性都具备了属性描述符。比如:
var myObject = {
a:2
};
Object.getOwnPropertyDescriptor( myObject, "a" );
// {
// value: 2,
// writable: true,
// enumerable: true,
// configurable: true
// }
如你所见,这个普通的对象属性对应的属性描述符可不仅仅只是一个 2。它还包含另外三个特性:writable(可写)、 enumerable(可枚举)和 configurable(可配置)。 在创建普通属性时属性描述符会使用默认值,我们也可以使用 Object.defineProperty(…) 来添加一个新属性或者修改一个已有属性(如果它是 configurable)并对特性进行设置。
- setter,getter
操作属性值时的隐藏函数;
setter在设置属性值时调用
getter在获取属性值时调用
var myObject = {
// 给 a 定义一个 getter
get a() {
return this._a_;
},
// 给 a 定义一个 setter
set a(val) {
this._a_ = val * 2;
}
};
myObject.a = 2;
myObject.a; // 4
对象值的遍历
在对象中我们可以使用for in语句遍历对象的可枚举属性,若要遍历属性的值,在es6中提出了for of函数。但是对象里面没有@@iteractor,不能手动遍历。
var myObject = {
a: 2,
b: 3
};
Object.defineProperty( myObject, Symbol.iterator, {
enumerable: false,
writable: false,
configurable: true,
value: function() {
var o = this;
var idx = 0;
var ks = Object.keys( o );
return {
next: function() {
return {
value: o[ks[idx++]],
done: (idx > ks.length)
};
}
};
}
} );
// 手动遍历 myObject
var it = myObject[Symbol.iterator]();
it.next(); // { value:2, done:false }
it.next(); // { value:3, done:false }
it.next(); // { value:undefined, done:true }
// 用 for..of 遍历 myObject
for (var v of myObject) {
console.log( v );
}
// 2
// 3
很巧妙的一道题。
不改变下面代码,输出o的中的所有属性值
var foo = (function(){
var o = {
a: 1,
b: 2,
/**更多属性**/
};
return function(key) {
return o[key];
}
})();
要获取o中的属性值,首先要将o提取出来。
结合上面的知识,在函数getter和setter中,this指向事件本身,因此只要在o取值的时候绑定get即可。现在给o增添一个属性,方便监听。由于对象的原型都是Object,只需在Object.prototype上编写。
Object.definePrototype(Object.prototype,"self",{
get() {
return this;
}
});
var o = foo("self"); //调用get函数,取得o
console.log(Object.keys(o));// 利用Object上的keys函数将o中的属性遍历。