JavaScript中的对象
对象的概念
JS 中的对象其实是无序属性的集合,属性包括基本数据、函数与对象,每个属性有属性名,属性名与属性值以键值对的形式保存在对象内部。1
2
3
4
5
6
7
8// JS中的一个对象
var person = {
name: "syz",
job: "Software Engineer",
sayHello: function(){
console.log("Hello");
}
}
对象的属性
为了JavaScript引擎可以操作对象中的属性,每个属性定义了对应的两种属性类型:
1. 数据属性。 2. 访问器属性。
数据属性
是对一个对象属性值的操作说明,共有四类说明:configurable、enumerable、writable及value。
可通过Object.defineProperty实现不同属性的值操作限定,接收三个参数,第一个是操作对象、第二个是操作对象的属性第三个是对该属性的操作限定。
1 | var person = { |
访问器属性
访问器属性是对访问对象属性值的操作的说明,共四类:configurable、enumerable、get及set。
可通过Object.defineProperty实现不同属性的值操作限定,接收三个参数,第一个是操作对象、第二个是操作对象的属性第三个是对该属性的操作限定。
1 | var person = { |
数据属性其它知识
- 通过Object.defineProperties()方法一次性为对象的多个属性设置属性类型。
1 | var book = {}; |
- 通过Object.getOwnPropertyDescriptor()方法获得指定对象指定属性的属性类型说明对象。第一个参数接收查询对象,第二个参数接收对应对象的查询属性的字符串表示。该方法只能获取实例对象的属性类型描述对象,对于原型对象,需要调用Person.prototype。
1 | var descriptor = Object.getOwnPropertyDescriptor(book, "year"); |
创建对象
JavaScript中自定义的对象又是怎么创建来的呢?从前面知识,我们了解到,可以通过构造函数或者对象字面量的形式创建,如下:1
2
3
4
5
6
7
8
9// 默认的Object构造函数
var person = new Object({
name : "syz"
});
// 对象字面量形式
var person = {
name : "syz"
}
首先,通过对象字面量形式创建的对象只能是Obejct类型实例,无法满足特定实例的要求。
然后,再看通过构造函数创建的方式。
构造函数
构造函数其实就是函数的一种,任何函数都可以是构造函数,只要在函数前面使用 new 关键字,就使该函数成为了构造函数。
1 | // 构造函数通常大写 |
前面提到,函数其实也是一种Funcation类型的实例对象,加入了new关键字后,函数执行四个步骤:
- 创建一个新对象。
- 将构造函数的作用域赋给对象,即this的主体换为新对象。
- 执行构造函数的语句,对新对象执行初始化操作。
- 返回该对象。
构造函数也可以当做普通函数使用,如下:1
2
3
4
5// 此时函数的作用域是全局执行环境,因此也就是给windows对象赋予了name及age属性
Person("syz", 12);
//通过调用call()方法在指定作用域执行,相当于为person对象赋予了姓名与年龄属性
var person = {};
Person.call(person, "syz", 12);
通过构造函数创建的对象实例可以通过关键字 instanceof来判断其是否为某自定义引用类型的对象。1
person instanceof Person; // => true
原型对象
然而,仅通过构造函数创建对象存在弊端。即一些属于类公共部分的属性(尤其是引用类型值)仍需要在每个实例创建过程中重新创建一份副本。1
2
3
4
5
6
7
8
9
10
11function Person(name) {
this.name = name;
this.sayHello = function (){
console.log("Hello");
}
}
var person1 = new Person("syz");
var person2 = new Person("xj");
console.log(person1.sayHello===person2.sayHello); // =>false
因为 sayHello() 方法是 Function 类型的实例,尽管其作用在每个对象实例中是相同的,但是在每个对象创建过程中,每个sayHello() 都重新创建一份副本,导致内存资源的浪费。
JS通过构造函数的原型对象来解决。
原型对象其实是构造函数的属性之一,可以通过 Person.prototype 访问 Person 构造函数原型对象,其中保存了 Person 类共享的属性与方法。
原型对象在每个函数的创建过程中自动生成,即每个函数自动生成一个 prototype 属性,指向一个原型对象,该原型对象自动生成一个 constructor属性,指向构造函数,其属性类型是不可枚举的。
1 | function Person(){} |
在一个实例中访问某个属性,首先在实例本身搜索,如果没找到,再通过内部属性[[Property]]找到指向的原型对象,再在其内部寻找。
如果实例内部定义了与原型对象中重名的属性,则读取到该属性即停止搜索。
1 | function Person(){} |
- 相关函数
- isPrototypeOf(): 判断某个原型对象是否是某个实例对应的原型对象
1
Person.prototype.isPrototypeOf(person); // => ture
- Object.getPrototypeOf(): 获得某个实例的原型对象
1
var proto = Object.getPrototypeOf(person);
- hasOwnProperty(): 判断某个属性是来自实例还是原型对象,来自实例返回true
1
console.log(persom.hasOwnProperty("height")); //=> true
- in: 判断某个实例是否包含某个属性,无论是位于实例本身还是原型对象
1
2console.log("job" in person); //=> true
console.log("height" in person); // => true - for in: 遍历所有可枚举属性,无论是位于实例本身还是原型对象
1
2
3for(var item in person){
console.log(item); // name,height,age,job,sayName
} - Object.keys(): 以字符串数组形式返回实例本身可枚举的属性
1
2console.log(Object.keys(Person.prototype)); //[ 'name', 'age', 'job', 'sayName' ]
console.log(Object.keys(person)); //[ 'name', 'height' ] - getOwnPropertyNames(): 以字符串数组形式返回实例本身所有的属性(包括不可枚举)
1
2console.log(Object.getOwnPropertyNames(Person.prototype)); //[ 'constructor', 'name', 'age', 'job', 'sayName' ]
console.log(Object.getOwnPropertyNames(person)); //[ 'name', 'height' ]
- isPrototypeOf(): 判断某个原型对象是否是某个实例对应的原型对象
混合模式创建对象
通过原型模式可以设置共享的对象类型,通过构造器模式可以为对象的创建赋予初始值,结合二者的混合模式是最常见的创建对象的方法。
1 | function Person(name, age){ |