阅读(1234) (0)

JavaScript学习笔记整理(4):对象

2017-06-19 11:58:34 更新

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)它的属性。 属性包括名字(键名)和值(键值)。
属性名可以是包含空字符串在内的任意字符串,但对象中不能存在两个同名的属性。 3、属性特性
可写(writable attribute):可设置该属性的值。
可枚举(enumerable attribute):可通过for/in循环返回该属性。
可配置(configurable attribute):可删除或修改属性。

4、读取属性
读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。

var o = {

  name : 'a'

}

o.name  // "a"

o['name']  //"a"

注意:数值键名不能使用点运算符(因为会被当成小数点),只能使用方括号运算符。

JavaScript对象是动态的,可新增属性也可删除属性。但注意,我们是通过引用而非值来操作对象。

5、属性的查询和设置

在JavaScript中,我们可以通过点(.)或方括号([])运算符来获取属性的值。运算符左侧应当是一个表达式,它返回一个对象。


for...in

for...in循环用来遍历一个对象的全部属性。

var o = {

  name : 'a',

  age : 12

}

for(var i in o){

  console.log(o[i]

}

// "a"

// 12


查看所有属性

查看一个对象本身的所有属性,可以使用Object.keys方法,返回一个数组。

var o = {

  name : 'a',

  age : 12

}


Object.keys(o)  //['name','age']


删除属性

delete运算符可以删除对象的属性。

var o={

  name : 'a'

}

delete o.name  //true

o.name  //undefined

注意:delete运算符只能删除自有属性,不能删除继承属性。

删除一个不存在的属性,delete不报错,而且返回true。

只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除。

检测属性

在JavaScript中,有多种方法检测某个属性是否存在于某个对象中。


用“!==”来判断一个属性是否是undefined


hasOwnPreperty()方法


propertyIsEnumerable()方法

只有检测到是自有属性且这个属性的可枚举性为true时才返回true。


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)


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