对象的属性指对象的成员。讲解属性之前介绍一下Object对象,Object对象是所有JavaScript对象的超类(基类)。本文将会使用到Object对象的一些方法。
一、对象的属性分类
我们先通过讲属性的一些分类,来明确一些概念
1. 属性可分为自有属性和继承属性
function Person(name,age){
this.trueName=name;
this.age=age;
}
Person.prototype.species='human';
Person.prototype.run=function () {
console.log(this.trueName+' is running');
}
var p1=new Person('Lilyan',18);
console.log(p1);
对象中的属性,根据是否子属于自身的可分为自有属性和继承属性。
① 自有属性(own property) :也可叫实例属性;指对象自身的属性,上述的p1对象中的姓名、年龄等都是自有属性。
② 继承属性(inherited property):也可叫原型属性;指对象从原型中继承的属性。
我们通过在控制台输出p1来看一下:
2. 可分为属性和方法
上面的代码中run的属性是一个函数,当属性值是函数的时候,该属性被称为方法,可以说方法就是一种特殊的属性。
3. 可分为公有属性和私有属性
关于公有属性和私有属性,里面涉及到了很多名词,如私有属性、私有方法、公有属性、公有方法、特权方法,中间存在一些概念模糊的地方,,我的说法不一定是对的,但我个人就这样记忆了。
对于构造函数来说,构造函数的实例能够访问的属性和方法就是公有属性公有方法,定义在函数内部且没有通过this声明的属性和方法就是私有的属性和方法。
function Person(name,age) {
this.name=name; //公有属性
var _age=age; //私有属性,约定私有属性前加_
//私有方法
var checkAge=function(){
return (_age<18)?_age:'保密';
};
//公有方法-特权方法
this.introduce=function () {
console.log(this.name+'的年龄:'+checkAge(_age));
};
}
//公有方法
Person.prototype.species='人类';
Person.prototype.say=function () {
console.log(this.name+'的年龄:'+this._age);
};
var p=new Person('张三',19);
console.log(p);
p.introduce();
p.say();
简单的说,打印实例时,能看到的就是公有属性公有方法,定义了又看不到的是私有属性/方法。
私有属性和私有方法只能在函数内部使用,所谓特权方法其实也是一种公有方法,它能够在构造函数外,被其实例调用,并且能够访问构造函数内的私有属性和方法(如果你了解执行上下文的知识,就很好理解)。
上面的例子中,公有方法say没有访问到私有属性,特权方法introduce访问到了私有属性和私有方法。
4.静态属性和静态方法
静态属性和静态方法一般指不需要构造函数的实例化,直接将构造函数作为对象,可以访问的属性和方法。
function Person(name) {
this.name=name; //公有属性
}
Person.species='人类';
Person.say=function () {
console.log(this.name+'的年龄:'+this._age);
};
console.log(new Person());
console.dir(Person);
二、属性的访问方式
- 可用为 ’ . '点访问方式:obj.propertyName,属性名称必须为一个字符串,不能为变量。
- 可用’ [ ] '中括号方法方式 ,obj[propertyName],此方法访问属性更加的灵活,可以使用变量来访问属性
说明:访问一个不存在的属性,将返回undefined。给对象不存在的属性赋值,将会向该对象添加此属性。var p1={ "true name":"Lily", } var tn="true name"; console.log(p1[tn]); //Lily console.log(p1.gender); //undefined p1.gender='female'; //新增属性 console.log(p1.gender); //female p1.gender='male'; //修改属性值 console.log(p1.gender); //male
三、新增/修改属性
上例中,通过给一个不存在的属性赋值的方法实际上实现了新增属性;
给一个已经存在的属性重新赋值,就实现了修改属性值。
四、删除属性 delete
说明:通过delete方法可以删除对象的自有属性。
function Person(name,age){
this.trueName=name;
this.age=age;
}
Person.prototype.species='human';
Person.prototype.run=function () {
console.log(this.trueName+' is running');
}
var p1=new Person('Lilyan',18);
delete p1.age;
delete p1['species']; //species是p1的继承属性,静默失败
五、检测对象是否包含某个属性
- 使用in运算符判断对象是否包含某个属性(会检查自有属性和继承属性);
语法:属性 in 对象
返回值:如果存在,返回值为:true;不存在,则为:false。console.log('age' in p1); //true,找到自由属性age console.log('constructor' in p1); /true,在原型属性中找到constructor
- 判断对象是否拥有一个指定名称的自有属性;
语法:对象.hasOwnProperty(属性)
返回值:如果存在,返回值为:true;不存在,则为:false。console.log(p1.hasOwnProperty('age')); //true console.log(p1.hasOwnProperty('constructor')); //false,不会检查原型
- 判断指定名称的属性是否为可枚举的自有属性
语法:对象.propertyIsEnumerable(属性)
前面的例子中,我们为p1增加了一个不可枚举的属性xconsole.log(p1.propertyIsEnumerable('age')); //true console.log(p1.propertyIsEnumerable('x')); //false
六、属性的遍历
这里用一个构造函数的例子,来讲解一下属性的遍历
function Person(name,age){
this.trueName=name;
this.age=age;
}
Person.prototype.species='human';
Person.prototype.run=function () {
console.log(this.trueName+' is running');
}
var p1=new Person('Lilyan',18);
Object.defineProperty(p1,'x',{
value:'x',
enumerable:false
});
Object.defineProperty(Person.prototype,'y',{
value:'y',
enumerable:false
});
console.log(p1);
- for / in 语句 遍历对象可枚举的自有属性和继承属性
- Object对象的4个静态属性,Object.keys()、Object.values() 、Object.entries() 、Object.getOwnPropertyNames()可以实现对象的遍历
- Object.keys() 返回一个给定对象==可枚举的自有属性(键)==组成的数组
- Object.values() 返回一个给定对象可枚举的自有属性值组成的数组(ES6新增)
- Object.entries() 返回一个给定对象可枚举的自有属性键值对组成的数组(ES6新增)
- Object.getOwnPropertyNames() 返回一个给定对象对象不包括Symbol 属性全部的自有属性组成的数组。
上面的代码中,对象p1既有自有属性又有继承属性,其中还有不可枚举的属性x和y,下面就来遍历它
var forResult="for/in遍历结果(返回可枚举的自有属性和继承属性)";
for(var key in p1){
forResult+="\n"+key+":"+p1[key];
}
console.log(forResult);
console.log("Object.getOwnPropertyNames()方法的遍历结果,返回全部的自有属性组成的数组(不包括Symbol属性):");
console.log(Object.getOwnPropertyNames(p1));
console.log("Object.keys()方法的遍历结果(返回可枚举的自有属性名组成的数组):");
console.log(Object.keys(p1));
console.log("Object.values()方法的遍历结果(返回可枚举的自有属性值组成的数组):");
console.log(Object.values(p1));
console.log("Object.entries()方法的遍历结果(返回可枚举的自有属性键值对组成的数组):");
console.log(Object.entries(p1));
七、对象字面量与JSON
对象字面量与JSON 字符串形式上看起来很像,JSON 是一种独立的语言,JSON字符串现在常用于进行前后端的数据交互,JavaScript提供了一个内置对象JSON,可以实现js对象与json字符串的互转。
JSON 语法规则
- 数据为 键/值 对。
- 数据由逗号分隔。
- 大括号保存对象
- 方括号保存数组
JSON.stringify返回一个字符串,JS对象的可枚举的自有属性成为json字符串的键,属性值成为键值;
方法名和方法会被过滤掉。
这个对象p1,转成JSON字符串
console.log(JSON.stringify(p1)); //{"trueName":"Lilyan","age":18}
八 、属性描述符对象
在前面我们多次提到可枚举属性,不可枚举属性,其实能更加微观的角度去看属性,还可以有可写的属性,不可写的属性;可配置的属性不可配置的属性等等。
在ES5 中定义了一个名叫“属性描述符”的对象,用于描述对象属性的各种特征,属性的特征分为两种类型,数据属性和访问器属性。
也就是说我们给对象配置一个属性的同时,还可以配置这个属性的特征。
使用Object.getOwnPropertyDescriptor() 和Object.getOwnPropertyDescriptors() 可以返回指定对象属性的描述
- 将数据属性理解为某属性作为数据表现出的特征,如属性值是多少、能不能被修改等等;
- 访问器属性指某属性被读取或修改时表现出的特征。
- 一个描述符不能同时有(value或writable)和(get或set)关键字(将会产生一个异常),如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。
- 数据属性包含的4个属性:
数据属性 默认值 说明 configrable true 描述属性是否配置,以及可否删除 enumerable true 可枚举性描述属性是否会出现在for in 或者 Object.keys()的遍历中 writable true 可写性,表示能否修改属性的值。 value undefined 数据属性,表示属性的值 - 访问器属性包含的4个属性:
访问器属性 默认值 说明 configrable false 描述属性是否配置,以及可否删除 enumerable false 描述属性是否会出现在for in 或者 Object.keys()的遍历中 get undefined 当访问该属性时,该方法会被执行,没有参数传入,会传入this对象 set undefined 该方法将接受唯一参数,并将该参数的新值分配给该属性
使用for/in循环可以遍历对象的可枚举属性,下面我用Object对象的defineProperty方法为上面的p1对象增加一个不可枚举属性x
Object.defineProperty(p1,'x',{
value:'x',
enumerable:false
});
console.log(p1); //Person {trueName: "Lilyan", age: 18, x: "x"}
console.log(Object.getOwnPropertyDescriptor(p1, 'x')); //{value: "x", writable: false, enumerable: false, configurable: false}
for(var key in p1) {
console.log(key); //不会打印出x,x属性不可枚举
}
这里用到了Object对象的两个方法:
- Object.defineProperty(obj, propertyName, propertyDescriptor) :添加/修改对象指定属性的特性
- Object.getOwnPropertyDescriptor(object, propertyName) :返回对象属性的描述
可以跳转到 JavaScript原生对象-Object对象详解