对象

最后更新于: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方法返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上。
';