牛骨文教育服务平台(让学习变的简单)

对象

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方法

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