对象
最后更新于:2022-04-01 23:53:19
## 对象
JavaScript是面向对象编程(Object Oriented Programming,OOP)语言。
面对对象编程的核心思想就是是将真实世界中各种复杂的关系,抽象成一个个对象,然后由对象之间分工合作,完成对真实世界的模拟。
**何为对象?**
对象是单个实物的抽象。
一本书、一辆汽车、一个人都可以是“对象”,一个数据库、一张网页也可以是“对象”。世界上所有的对象都可以是“对象”。
对象是一个容器,封装了“**属性**”(property)和“**方法**”(method)。
属性,就是对象的状态,而方法,就是对象的行为。比如:我们可以把一辆汽车抽象成一个对象,它的属性就是它的颜色、重量等,而方法就是它可以启动、停止等。
**1、对象**
在Javascript中,对象是一个基本数据类型。
对象是一种复合值:它将很多值聚合子啊一起,可通过名字访问这些值。对象也可看做一种无序的数据集合,由若干个“键值对”(key-value)构成。
```
var o={
name:'a'
}
```
上面代码中,大括号定义了一个对象,它被赋值给变量o。这个对象内部包含一个键值对(又称为“成员”),name是“键名”(成员的名称),字符串a是“键值”(成员的值)。
键名与键值之间用冒号分隔。如果对象内部包含多个键值对,每个键值对之间用逗号分隔。
**键名**:对象的所有键名都是字符串,所以加不加引号都可以。如果键名是数值,会被自动转为字符串。
对象的每一个“键名”又称为“属性”(property),它的“键值”可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为“方法”,它可以像函数那样调用。
```
var o = {
go: function(x){
return x+1;
}
};
o.go(2) // 3
```
如果键名不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),也不是数字,则必须加上引号,否则会报错。
```
var o = {
'1a' : 'a'
}
```
上面的代码中,如果键名'1a'不用引号引起来,就会报错。
注意:为了避免这种歧义,JavaScript规定,如果行首是大括号,一律解释为语句(即代码块)。如果要解释为表达式(即对象),必须在大括号前加上圆括号。
**2、创建对象**
在JavaScript中,有三种方法创建对象
- 对象直接量: var o={};
- 关键字new: var o=new Object();
- Object.create()函数: var o=Object.create(null)
**2.1对象直接量**
对象直接量是由若干名/值对组成的映射表。键名与键值之间用冒号分隔。如果对象内部包含多个键值对,每个键值对之间用逗号分隔。整个映射表用花括号括起来。
在ECMAScript 5中,保留字可以用做不带引号的属性名。
注意:对象直接量中的最后一个属性后的逗号可有可无,但是在ie中,如果多了一个逗号,会报错。 2.2通过new创建对象
new运算符创建并初始化一个新对象。关键字new后跟随一个函数调用,这个函数称做构造函数(constructor)。
例子:
```
var o1 = {};
var o2 = new Object();
var o3 = Object.create(null);
```
上面三行语句是等价的。
对象最常见的用法是创建(create)、设置(set)、查找(query)、删除(delete)、检测(test)和枚举(enumerate)它的属性。 属性包括名字(键名)和值(键值)。
属性名可以是包含空字符串在内的任意字符串,但对象中不能存在两个同名的属性。
**提取方法**
如果对对象中的方法进行提取,则会失去与对象的连接。
```
var obj = {
name: 'a',
get: function() {
console.log(this.name);
}
};
console.log(obj.get()); // "a"
var func = obj.get;
console.log(func()); // undefined
```
在上面的例子中,object对象中有一个方法get(),用来获取obj对象中的name,而当get()方法赋值给一个变量func,再调用func()函数时,此时的this是指向window的,而非obj的。
注意:如果在严格模式下,this会是undefined。
**3、属性特性**
- 可写(writable attribute):可设置该属性的值。
- 可枚举(enumerable attribute):可通过for/in循环返回该属性。
- 可配置(configurable attribute):可删除或修改属性。
**4、读取属性**
读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。
```
var o = {
name : 'a'
}
o.name // "a"
o['name'] //"a"
```
注意:数值键名不能使用点运算符(因为会被当成小数点),只能使用方括号运算符。
JavaScript对象是动态的,可新增属性也可删除属性。但注意,我们是通过引用而非值来操作对象。
**5、属性的查询和设置**
在JavaScript中,我们可以通过点(.)或方括号([])运算符来获取属性的值。运算符左侧应当是一个表达式,它返回一个对象。
**(1)for...in**
for...in循环用来遍历一个对象的全部属性。
```
var o = {
name : 'a',
age : 12
}
for(var i in o){
console.log(o[i]
}
// "a"
// 12
```
**(2)查看所有属性**
查看一个对象本身的所有属性,可以使用Object.keys方法,返回一个数组。
```
var o = {
name : 'a',
age : 12
}
Object.keys(o) //['name','age']
```
**(3)删除属性**
delete运算符可以删除对象的属性。
```
var o={
name : 'a'
}
delete o.name //true
o.name //undefined
```
注意:delete运算符只能删除自有属性,不能删除继承属性。
删除一个不存在的属性,delete不报错,而且返回true。
只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除。
**(4)检测属性**
在JavaScript中,有多种方法检测某个属性是否存在于某个对象中。
用“!==”来判断一个属性是否是undefined
**(5)hasOwnPreperty()方法**
用于判断一个对象自身(不包括原型链)是否具有指定名称的属性。如果有,返回true,否则返回false。
**(6)propertyIsEnumerable()方法**
只有检测到是自有属性且这个属性的可枚举性为true时才返回true。
**(7)in运算符**
in运算符左侧是属性名(字符串),右侧是对象。如果对象的自有属性或继承属性中包含这个属性就返回true。
```
var o = {
name : 'a'
}
'name' in o //true
```
**6、对象的三个属性**
每一个对象都有与之相关的原型(prototype)、类(class)和可扩展性(extensible attribute)
将对象作为参数传入Object.getPrototypeOf()可以查询它的原型。
检测一个对象是否是另一个对象的原型,可以使用isPrototypeOf()方法。
**7、序列化对象**
对象序列化是指将对象的状态转换为字符串,也可将字符串还原为对象。
在JavaScript中,提供了内置函数**JSON.stringify()**和**JSON.parse()**用来序列化和还原JavaScript对象。
NaN、Infinity和-Infinity序列化的结果是null
```
var o = {
name : 'a',
age : 12,
intro : [false,null,'']
}
s= JSON.stringify(o) // s {"name":"a","age":12,"intro":[false,null,""]}
p=JSON.parse(s) // p是o的深拷贝
```
注意:JSON.stringify()只能序列化对象可枚举的自有属性。对于一个不能序列化的属性来说,在序列化后的输出字符串中会将这个属性省略掉。
**8、构造函数**
构造函数,是用来生成“对象”的函数。一个构造函数可生成多个对象,这些对象都有相同的结构。
构造函数的特点:
- 函数体内使用了this关键字,代表了所要生成的对象实例
- 生成对象时,必需用new命令
- 构造函数名字的第一个字母通常大写。
例子:
```
function Car(){
this.color = 'black';
}
var c = new Car();
```
上面的代码生成了Car的实例对象,保存在变量c中。
构造函数也可以传入参数:
```
function Car(color){
this.color = color;
}
var c = new Car('red');
```
new命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号。下面两行代码是等价的。
```
var c = new Car();
var c = new Car;
```
每一个构造函数都有一个prototype属性。
**8.1 this关键字**
this总是返回一个对象,简单说,就是返回属性或方法“当前”所在的对象。
this.property
上面的代码中,this就代表property属性当前所在的对象。
由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即this的指向是可变的。
```
var A = {
name: '张三',
describe: function(){
return this.name;
}
};
var B = {
name: '李四'
};
B.describe = A.describe;
B.describe();
// "李四"
```
注意:如果一个函数在全局环境中运行,那么this就是指顶层对象(浏览器中为window对象)。
**8.1.1 改变this指向**
在JavaScript中,提供了call、apply、bind三种方法改变this的指向。
**(1)funciton.prototype.call()**
```
call(obj, arg1, arg2, ...)
```
第一个参数obj是this要指向的对象,也就是想指定的上下文;arg1,arg2..都是要传入的参数。
注意:如果参数为空、null和undefined,则默认传入全局对象。
**(2)funciton.prototype.apply()**
```
apply(obj,[arg1,arg2....])
```
apply()和call()方法原理类似,只不过,它第二个参数一个数组,里面的值就是要传入的参数。
**(3)function.prototype.bind()**
bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。
```
bind(obj)
```
更多:[JS中的call、apply、bind方法](http://ghmagical.com/article/page/id/UPLfoGI9vJ91)
**9、原型**
**9.1 原型**
每一个JavaScript对象(null除外)都和另一个对象相关联,也可以说,继承另一个对象。另一个对象就是我们熟知的“原型”(prototype),每一个对象都从原型继承属性。只有null除外,它没有自己的原型对象。
所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过JavaScript代码Object.prototype获得对原型对象的引用。
通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性的值。比如:通过new Object()创建的对象继承自Object.prototype;通过new Array()创建的对象的原型就是Array.prototype。
没有原型的对象为数不多,Object.prototype就是其中之一,它不继承任何属性。
所有的内置构造函数都具有一个继承自Object.prototype的原型。
**9.2 原型链**
对象的属性和方法,有可能是定义在自身,也有可能是定义在它的原型对象。由于原型本身也是对象,又有自己的原型,所以形成了一条原型链(prototype chain)。比如,a对象是b对象的原型,b对象是c对象的原型,以此类推。
如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性指向的那个对象。那么,Object.prototype对象有没有它的原型呢?回答可以是有的,就是没有任何属性和方法的null对象,而null对象没有自己的原型。
**“原型链”的作用**:
当读取对象的某个属性时,JavaScript引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。
**继承**
JavaScript对象具有“自有属性”,也有一些属性是从原型对象继承而来的。
当查询一个不存在的属性时,JavaScript不会报错,返回undefined。
如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overiding)。
**9.2.1 contructor属性**
prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。
**9.3 操作符**
**(1)instanceof运算符**
instanceof运算符返回一个布尔值,表示指定对象是否为某个构造函数的实例。
```
var c = new Car();
c instanceof Car //true
```
instanceof运算符的左边是实例对象,右边是构造函数。它的运算实质是检查右边构建函数的原型对象,是否在左边对象的原型链上。
**(2)Object.getPrototypeOf()**
Object.getPrototypeOf方法返回一个对象的原型。这是获取原型对象的标准方法
```
Object.getPrototypeOf(c) === Car.prototype //true
```
**(3)Object.setPrototypeOf()**
Object.setPrototypeOf方法可以为现有对象设置原型,返回一个新对象。Object.setPrototypeOf方法接受两个参数,第一个是现有对象,第二个是原型对象。
**(4)Object.create()**
Object.create方法用于从原型对象生成新的实例对象,可以替代new命令。
它接受一个对象作为参数,返回一个新对象,后者完全继承前者的属性,即原有对象成为新对象的原型。
**(5)Object.prototype.isPrototypeOf()**
对象实例的isPrototypeOf方法,用来判断一个对象是否是另一个对象的原型。
```
Object.prototype.isPrototypeOf({}) //true
```
**(6)Object.prototype.__proto__**
`__proto__`属性(前后各两个下划线)可以改写某个对象的原型对象。
**(7)Object.getOwnPropertyNames()**
Object.getOwnPropertyNames方法返回一个数组,成员是对象本身的所有属性的键名,不包含继承的属性键名。
**(8)Object.prototype.hasOwnProperty()**
对象实例的hasOwnProperty方法返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上。
';