javaScript中的对象有内置对象,宿主对象,自定义对象;
内置对象:数组,函数,日期,正则表达式;
宿主对象:javascript解析器所在的宿主,比如web浏览器,宿主环境定义的对象document,DOM对象等;
自定义对象:在js中自己创建的对象;
对象是由无序的名/值对(属性)组成;属性名可以是包括空字符串在内的任意字符串;
创建对象
有三种方式可以创建对象
1.对象字面量
2.通过new关键词来创建对象
3.通过Object.create()方法来创建对象
Object.create(prototype[,propertiesObject]);
参数
prototype:Object类型;作为新创建对象的原型对象;可以取值为null;
propertiesObject:为新创建对象绑定的属性;
返回值
返回指定原型对象上添加新属性后的对象。
例子
var o=Object.create(null);
console.info(Object.getPrototypeOf(o));//undefined;
//下面三种写法是等价的
var o=Object.create(Object.prototype);
var o={};
var o=new Object();
function Person(name){
this.name=name;
};
Person.prototype.eat = function(){
console.info("eat");
};
var p=Object.create(Person.prototype);
console.info(p.constructor);//Person;
var o=Object.create(Object.prototype,{
name:{
writable:true,
value:"zhang",
enumerable:true,
configurable:false
}
});
for(var i in o){
console.info(i);//name
};
//简单的实现原型继承
function inherit(p){
if(p==null){
throw TypeError();
}else if(typeof p !=="object"&&typeof p !=="function"){
throw TypeError();
};
if(Object.create){
return Object.create(p);
}else{
function f(){};
f.prototype=p;
f.prototype.constructor=f;
return new f();
}
}
查询属性
可以通过(.)运算符或([])方括号来获取属性的值。对于点运算符来说,右侧必须是一个以属性名称命名的简单标识符。对于方括号来说,方括号内必须是一个计算结果为字符串或可以转换为字符串的表达式,这个字符串就是属性的名字。ES3中,点运算符后面的标识符不能是保留字,只能通过方括号来访问它们,ES5对此放宽了限制,可以在点运算符后面直接使用保留字。
方括号的形式可以动态的访问属性,或者属性名中有空格出现的情况,推荐使用。
设置属性
属性设置分为几种情况:1.如果要设置的属性是对象中不存在的(原型链和对象本身都没有),则相当于给对象创建了一个自有属性;2.如果要设置的属性在原型链中存在且writable为false,则不能设置该属性;3.如果要设置的属性是自有属性,不是继承而来的,则修改这个自有属性;4.如果要设置的属性在原型链中存在且是具有setter方法的访问器属性,那么这时将调用setter方法而不是给对象创建一个自有属性,需要注意的是setter方法是由对象来调用的,而不是定义这个属性的原型对象调用的。因此setter方法定义任意属性,这个操作只是针对对象本身,而不会修改原型链。5.如果要设置的属性在原型链中,且writable为true,则相当于为对象创建一个自有属性,覆盖了原型链上的同名属性。6.如果要设置的属性在原型链中,且为访问器属性,但是没有setter方法,则不能设置该属性。
总结:属性设置的结果包括:设置不成功;为对象创建一个属性;修改对象上原有的属性;一个例外(上述第四种情况);
规律特性:设置属性时,首先判断原型链,以此判断是否允许赋值操作;
//第一种情况
var o={};
o.x="zhang";
console.info(o);//{x: "zhang"}
//第二种情况
var p={};
Object.defineProperty(p,"x",{value:"zhang",writable:false});
var o=Object.create(p);
o.x="wang";
console.info(o.x);//"zhang";
//第三种情况
var o={x:"zhang"};
o.x="wang";
console.info(o.x);//"wang";
//第四种情况
var p={
"age":25,
get x(){
return "zhang";
},
set x(name){
this.name=name;
}
};
var o=Object.create(p);
o.x="wang";
console.info(o.x);//"zhang";
console.info(o.name);//"wang";setter内部创建了一个name属性
//第五种情况
var p={};
Object.defineProperty(p,"x",{value:"zhang",writable:true});
var o=Object.create(p);
o.x="wang";
console.info(o.x);//"wang";
console.info(p.x);//"zhang";
//第六种情况
var p={
"age":25,
get x(){
return "zhang";
}
};
var o=Object.create(p);
o.x="wang";
console.info(o.x);//"zhang";
删除属性
delete操作符可以删除对象的自有属性,它的操作数应当是一个属性访问表达式。delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性;
var o={p:{x:1}};
var n=o.p;
delete o.p;
console.info(n.x)//1;
由于已经删除的属性的引用依然存在,所以这种不严谨的代码会造成内存泄漏,所以在销毁的属性是对象类型的时候,要遍历属性中的属性,依次删除。
function deleteProperty(o, p) {
function isObjectOfProperty(property) {
return (Object.prototype.toString.call(o[property]) === "[object Object]") ? true : false;
}
if (arguments.length == 1 && typeof arguments[0] == "object") {
for (var item in o) {
isObjectOfProperty(item) ? deleteProperty(o[item]) : (delete o[item]);
}
} else if (arguments.length == 2 && typeof arguments[0] == "object" && typeof arguments[1] == "string") {
isObjectOfProperty(p) ? deleteProperty(o[p]) : (delete o[p]);
} else {
return false;
}
};
注意细节
1、delete只能删除自有属性,不能删除继承过来的属性;
2、delete不能删除可配置性为false的属性,可以删除不可扩展对象的可配置属性,比如prototype对象的可配置属性constructor;
3、通过变量声明和函数声明创建的全局对象的属性是不可配置的,不能用delete操作符删除;
4、在非严格模式中,删除全局对象的可配置属性时,可以省略对全局对象的引用,直接在delete操作符后跟随要删除的属性;在严格模式下则会报错;
5.delete操作符在什么情况下返回true/false;(删除不存在的属性,删除继承过来的属性,删除configuable为true的属性;这几种情况返回true)
//不能删除继承属性
var p={x:1};
var o=Object.create(p);
console.info(o.x);//1
delete o.x;//true;
console.info(o.x);//1
//不能删除configurable为false的属性
var p=Object.create(Object.prototype,{
name:{
value:"zhang",
writable:true,
enumerable:true,
configurable:false;
}
});
delete p.name;//false;
//在全局作用域中通过var,function创建的变量和函数声明是不可配置的,不能删除
var x=1;
function test(){};
delete this.x;//false;
delete this.test;//false;
//严格模式和非严格模式使用delete
"use strict"
var p={x:1};
delete x;//报错
delete p.x;//true;
//delete后不是一个属性访问表达式
delete 1;//true
检测属性
检测某个属性是否属于指定对象;可以使用in运算符或Object.prototype.hasOwnProperty()和Object.prototype.propertyIsEnumerable();
分以下几种情况:
1.检测的属性不区别是自有的还是继承过来的;
var o={"name":"zhang"};
"name" in o;//true;
“toString" in o;//true;
2.检测自有属性
var o={"name":"zhang"};
o.hasOwnProperty("name");//true;
o.hasOwnPropery("toString");//false;
3.检测自有属性中的可枚举属性
var o={};
Object.defineProperty(o,"name",{value:"zhang",enumerable:false});
o.age=26;
o.propertyIsEnumerable("name");//false;
o.propertyIsEnumerable("age");//true;
枚举属性
有三种方式可以枚举属性:
1.for in 循环;
2.Object.keys();
3.Object.getOwnPropertyNames();
分以下几种情况
1.枚举自有属性和继承属性(enumerable为true)
var o={x:2};
for(item in o){
//打印自有属性x,因为继承的内置对象属性都是不可枚举的
console.info(item);//x;
}
2.枚举自有属性(enumerable为True)
var o={x:2};
Object.defineProperty(o,"y",{value:3});
Object.keys(o);//["x"];
3.枚举所有的自有属性,不仅仅是可枚举的属性
var o={x:2};
Object.defineProperty(o,"y",{value:3});
Object.getOwnPropertyNames(o);// ["x", "y"]
存取器属性的setter和getter
var o={
name:"zhang",
y:26,
get age(){
return this.y;
},
set age(newV){
this.y=newV;
}
}
console.info(o.age);//26;
o.age=20;
console.info(o.y);//20;
console.info(o.age);//20;
属性特性
属性分为数据属性和存取器属性;
数据属性有四个特性:
1.value(值属性);
2.writable(可写属性);------表明是否可以设置该属性的值(控制value这个属性);
3.enumerable(可枚举属性);------表明该属性是否可以通过for...in循环返回该属性;
4.configurable(可配置属性);------表明是否可以删除或修改该属性;(控制writable,enumerable属性)
用法:writable特性控制是否可以修改value的值;configurable特性控制是否可以修改enumerable和writable特性,但是如果writable为true时,可以修改为false,反之则不可;当configurable为false时,不能修改为true;
var o={};
Object.defineProperty(o,"name",{value:"zhang",writable:false,enumerable:false,configurable:true});
console.info(o.name);//"zhang";
//当writable为false时不能修改属性的值
o.name="wang";
console.info(o.name);//"zhang";
存取器属性有四个特性:
1.get属性(读取);
2.set属性(写入);
3.enumerable(可枚举属性);------表明该属性是否可以通过for..in循环返回该属性;
4.configurable(可配置属性);------表明是否可以删除或修改该属性;
对象属性
对象本身也拥有三个特性;
1.对象的原型:指向另一个对象,本对象的属性继承自它的原型对象。
2.对象的类(class):一个标识对象类型的字符串。
3.对象的扩展标记:指明了是否可以向该对象添加新属性。
对象的原型属性是实例对象创建之初就设置好的,查询原型特性可以使用Object.getPrototypeOf();在ES3中没有与之对应的方法,通常使用表达式o.constructor.prototype来检测一个对象的原型(这种方式存在问题);要检测一个对象是否是另一个对象的原型,使用isPrototypeOf();
对象的类属性用来表示对象的类型信息,ES3,ES5都未提供设置这个属性的方法,并只有一种间接的方法可以查询它。Object.prototype.toString();
toString方法大多被重写了,所以要使用Function.call()来调用Object.prototype.toString;
var a=[];
var r=Object.prototype.toString.call(a);
console.info(r);//"[object,Array]";
r.slice(8,-1);//"Array";
对象的扩展属性用以表示是否可以给对象添加新属性,内置对象和自定义对象都是默认可扩展的。宿主对象的可扩展性由javaScript引擎定义。
判断对象是否是可扩展的
Object.isExtensible();
var o={};
Object.isExtensible(o);//true;
设置对象为不可扩展的
Object.preventExtensions()
这个方法只影响对象本身的可扩展性。如果给一个不可扩展对象的原型对象添加一个属性,对象仍然可以继承这个属性。
var o={};
Object.preventExtensions(o);
console.info(Object.isExtensible(o));//false;
var p=Object.getPrototypeOf(o);//获取o的原型对象
p["name"]="zhang";
console.info(o.name);//"zhang";
密封对象(seal)
Object.seal();
Object.isSealed();
这个方法除了能够将对象设置为不可扩展的,还可以将对象的所有自有属性都设置为不可配置的。
var o={};
Object.defineProperty(o,"age",{value:26,writable:true,enumerable:true,configurable:true});
Object.seal(o);
console.info(Object.isSealed(o));//true;
console.info(Object.getOwnPropertyDescriptor(o,"age")["configurable"]);//false;
var p=Object.getPrototypeOf(o);//获取o的原型对象
p["name"]="zhang";
console.info(o.name);//"zhang";
冻结对象(frozen)
Object.freeze();
Object.isFrozen();
这个方法可以将对象设置为不可扩展的,还可以将对象的所有自有属性设置为不可配置的,还可以将对象的所有数据属性设置为只读(如果对象的存取器属性具有setter方法,存取器属性不受影响,仍然可以使用属性赋值调用他们);
var o={};
Object.defineProperty(o,"age",{value:26,writable:true,enumerable:true,configurable:true});
Object.freeze(o);
console.info(Object.isFrozen(o));//true;
console.info(Object.getOwnPropertyDescriptor(o,"age")["configurable"]);//false;
console.info(Object.getOwnPropertyDescriptor(o,"age")["writable"]);//false;
var p=Object.getPrototypeOf(o);//获取o的原型对象
p["name"]="zhang";
console.info(o.name);//"zhang";
preventExtensions(),seal(),freeze()三个方法都返回一个新对象,所以可以链式的调用
//创建一个封闭对象,包括一个冻结的原型和一个不可枚举的属性
var o=Object.seal(Object.create(Object.freeze({x:1}),{y:{value:2}}));