对象

对象和属性

现实世界中的 事物 可以看作是包含很多 属性对象

比如 包含了 头、躯干、手、腿 等。

可以说 头、躯干、手、腿 是 人这个对象 的 属性。

而 属性也是对象,包含了它的属性,比如:

头还包含 眼睛、耳朵、鼻子、嘴等。


js中 对象(Object) 可以和现实世界中的对象 概念对应

就是包含了一组 属性(Property) 的数据, 属性可以是包含其它属性的对象。

创建对象: 使用字面标记

创建一个 Object 类型的数据 有多种方法,最常见的就是直接 js语言 字面标记(literal notation) 构建

写法如下:

var myCar = {
    'brand'   : '奔驰',
    'country' : '德国',
    'price'   : 300000
}

这里面的 brand/country/price 都是对象的 属性(英文叫 properties )。

属性之间用逗号隔开,最后一个属性后面可以不用加逗号。

它们的值分别是字符串 奔驰/德国 和数字 300000

有时,我们也把 属性名 称之为 对象的 key(键)


定义时,也可以省略 key的引号,如下

var myCar = {
    brand   : '奔驰',
    country : '德国',
    price   : 300000
}

如果对象的属性名中有空格 加减号 之类的字符,就必须要加上引号了,如下

var myCar = {
    brand   : '奔驰',
    country : '德国',
    'price-in-us'   : 300000
}

当然属性也可以是中文字符

var myCar = {
    品牌   : '奔驰',
    国家 : '德国',
    价格   : 300000
}

访问对象的属性,语法是 对象.属性,比如:

myCar.brand

或者 对象['属性名'],比如:

myCar['brand']

这种写法常用于属性名在写代码时不确定,是变量的情况,比如:

let prop = prompt('请输入属性名')
myCar[prop]

js中的对象经常被用作 存放查询数据,比如

var stockTable = {
  '包钢股份' : '600010',
  '保利地产' : '600048',
  '凤凰光学' : '600071',
  '东方航空' : '600115',
  '航天机电' : '600151',
  '太原重工' : '600169',
  '华微电子' : '600360'
}

let name = prompt('请输入股票名称')
console.log(name + ' 对应的代码是 ' + stockTable[name])

可以通过 key 查询到 值。


动态更改属性

添加属性

对象创建后可以随时给它添加属性

可以这样写

myCar.year = '2021-3-3'

也可以这样写

myCar['year'] =  '2021-3-3'

第二种写法方便 属性中有特殊字符(空格、加减号等)或者是一个变量的情况,

比如

myCar['year of purchase'] =  '2021-3-3'

let attrName = 'year of purchase'
let attrValue = '2021-3-3'
myCar[attrName] =  attrValue

修改属性

如果赋值语句前面的的属性名已经存在,会覆盖原来的属性值

比如:

myCar.year = '2021-3-3'

myCar.year = '2022-5-5'
console.log(myCar.year)

myCar['year'] =  '2023-6-6'
console.log(myCar.year)

删除属性

如果你要删除对象中一个属性,可以使用 操作符 delete

比如:

delete myCar.year

或者

delete myCar['year']

函数作为对象属性

对象的属性也可以是函数,比如

var myCar = {
    brand   : '奔驰',
    country : '德国',
    price   : 300000,
    showInfo : function() {
      console.log(this.brand + ' ' + this.country + ' ' + this.price)
    }
}

对象的函数属性,通常也被叫做对象的 方法(英文叫 method )。

对象 的方法其实可以看成是对象所拥有的函数

也就是说 这个方法,是 属于 这个对象的函数。

调用对象的方法,语法是 所属对象.属性()

比如:

myCar.showInfo()

注意 showInfo 函数代码中的 this

在 JavaScript 中, this 关键字 代表了 当前执行环境,术语是Context,上下文的意思。


关于this,后面还有进一步的介绍


这里,我们只要知道,当 this 出现在对象方法中,所对应的执行环境,

就是指 调用这个方法 所通过的那个对象 ,也就是 . 前面的对象

通过 myCar.showInfo() 这行代码调用的showInfo,里面的 this就是 myCar 对象

创建对象:使用构造函数

我们还可以 使用 构造函数(constructor function) 来创建对象

如下

function Car(brand, country, price) {
  this.brand = brand
  this.country = country
  this.price = price
}

var myCar1 = new Car('凯美瑞2020', '日本', 200000);
var myCar2 = new Car('特斯拉Model 3', '美国', 300000);

这里的 Car 就是一个构造函数。

构造函数其实本质上就是一个函数。任何函数都可以当作构造函数来使用。

只是,用来构造的函数里面,通常会 为将来所要构造对象 设置一些属性。

是通过如下的语法定义的

  this.brand = brand
  this.country = country
  this.price = price

函数里面的 this 就代表了要构建的对象,

上面的语句,就给对象添加了 brand、country、price 这些属性。


通常构造函数的首字母大写


构造函数定义好以后,后续要构造对象,通过如下语句

let myCar1 = new Car('奔驰', '德国', 500000);
let myCar2 = new Car('特斯拉Model 3', '美国', 300000);

使用 new 操作符 后面调用构造函数,并传入参数

这样构建的对象就会拥有这些属性

> myCar1
Car {brand: '凯美瑞2020', country: '日本', price: 200000}

通常,我们可以说,myCar1、myCar2 就是一个 Car 类型的 对象 , 或者 对象实例

原型 和 原型链

内置构造函数 Object

js 有个内置的构造函数 Object

Object 翻译成中文,也叫 对象,为方便区分,我直接用 英文 Object 称呼它,不给他起中文名

前面说过的直接 字面标记语法 创建的对象

var myCar = {
    brand   : '奔驰',
    country : '德国'
}

其实, js引擎对这样的写法会自动调用Object构造函数,等同于下面的写法

var myCar = new Object()
myCar.brand = '奔驰'
myCar.country = '德国'

所以,所有 用 字面标记 创建的对象 都是 Object 类型的 对象实例。

函数本身也是对象

js中函数也是对象,也可以为其动态的添加属性。

比如

function func1(){
  console.log('in func1')
}

func1.name = 'func1'
func1.desc = 'func1 函数的作用是...'

我们可以把函数看作 内置一段代码的 特殊对象。

使用原型创建对象

内置函数 Object 有个 create 方法(函数对象也可以有自己的方法属性)

可以用它直接创建对象。

看如下示例

// 汽车类型
var Car = {
    type       : '汽车',
    hasTire    : true
}

// 电动车类型
var ElectricCar = Object.create(Car)
ElectricCar.type = '电动车'
ElectricCar.hasBattery = true //有电池

// 汽油车类型
var GasolineCar = Object.create(Car)
GasolineCar.type = '汽油车'
GasolineCar.hasEngine  = true //有发动机

var myCar1 = Object.create(ElectricCar)
myCar1.owner = '白月黑羽'

var myCar2 = Object.create(GasolineCar)
myCar2.owner = '白月黑羽'

Object.create 返回一个对象, 而create 的 参数 对象 就是 返回对象的 原型(prototype)

上面的示例:

ElectricCar 的 原型是 Car

myCar1 的 原型是 ElectricCar

myCar1 --->  ElectricCar ---> Car
myCar2 --->  GasolineCar ---> Car

这样就形成一个 原型链


对象 有这个原型 到底 有什么用 呢?

对象会 继承(inherit) 原型链 对象 里面的属性。


怎么个 继承 法?

js有个规则:

对象.属性 的方式访问一个属性,

优先访问自身的属性,如果自身没有该属性,就尝试访问原型里面的这个属性;

如果原型对象里面也没有该属性, 就访问其原型的原型;

如果还没有,就在其原型链一直访问下去

所以可以像下面这样写代码:

// hasBattery 是 myCar1 原型 ElectricCar 里面的属性
console.log(myCar1.hasBattery) 

// hasTire  是 myCar1 原型的原型 Car 里面的属性
console.log(myCar1.hasTire)       

// owner  是 myCar1 自身 的属性
console.log(myCar1.owner)  

注意:

myCar1 对象本身的属性并没有 hasBattery、type ,

所以

console.log(myCar1)

结果是 是 { owner: '白月黑羽' }


如果对象自身属性和 原型链上的属性 同名,使用哪个,按原型链次序,首先会优先使用自身属性

var Car = {
    type       : '汽车',
    hasTire    : true
}

var ElectricCar = Object.create(Car)
ElectricCar.type = '电动汽车'
ElectricCar.hasTire  = '4个轮子'

var myCar1 = Object.create(ElectricCar)
myCar1.type = '白月黑羽的电动汽车'

//使用自己的属性
console.log(myCar1.type)    

//使用原型链最靠近自身的属性
console.log(myCar1.hasTire) 

Object.prototype

其实上面原型链还少了最后一环

myCar1 --->  ElectricCar ---> Car ---> Object.prototype
myCar2 --->  GasolineCar ---> Car ---> Object.prototype

因为Car 是 字面标记 创建,其实是从 Object 对象创建的,

它的原型是 Object 的属性 prototype


Object.prototype本身没有原型了, 所以原型链到此结束。

其实:js中所有的对象 的原型链 最后都是终结于 Object.prototype

所以,js 所有的对象都可以访问 Object.prototype 里面的属性

比如

myCar1.toString()
myCar1.hasOwnProperty('owner')
myCar1.hasOwnProperty('type')

toStringhasOwnProperty 都是 Object.prototype 里面的属性

toString 是把对象转化为字符串表示的,对象可以自己添加该属性方法,重新实现该方法。在打印结果和字符串格式化拼接时有用。

hasOwnProperty 是用来判断参数 是否是 对象自身的属性

原型的好处

对象和其原型的这种特性, 我们可以说对象 继承(Inheritance) 了其原型的 属性。

这种继承有什么好处?

为什么我们不直接就都使用对象自身的属性 ,而是要放一部分到原型中呢?

对象自身的属性 和 其原型链里面的属性 使用起来有什么区别 呢?


如果我们要开发一个游戏,里面有大量的汽车对象。

如果没有原型继承的特性, 我们每创建一个对象就要指明它所有的属性,比如:类型、是否有轮胎、是否有发动机、是否有电池等等。

如下

var myCar1 = {
    type       : '电动车',
    hasTire    : true,
    hasBattery : true,
    hasEngine  : false,
    owner      : '白月黑羽',
    ... // 其它属性
}


var myCar2 = {
    type       : '汽油车',
    hasTire    : true,
    hasBattery : false,
    hasEngine  : true,
    owner      : '白月黑羽',
    ... // 其它属性
}

而很多同一类型的车,这些属性都是相同的。

重复赋予对象这些属性,一个明显的缺点是:代码写起来麻烦。


而且另外一个缺点是:浪费内存空间。

自身的属性 是 每个构造对象 独有 的, 有多少个对象,就有多少份

而原型属性 是 所有构造对象 共有 的, 只有一份。

看下面这个例子

var Car = {
    type       : '汽车',
    hasTire    : true
}

var ElectricCar = Object.create(Car)
ElectricCar.hasBattery = true

var myCar1 = Object.create(ElectricCar)
myCar1.owner = '白月黑羽'

var yourCar1 = Object.create(ElectricCar)
yourCar1.owner = '麦克'

// 改变对象自身属性,只影响对象本身
yourCar1.owner = '麦克2'
// 不影响其它对象,输出还是 '白月黑羽'
console.log(myCar1.owner) 

// 而改变原型,影响的是全部使用该原型的对象
Car.type = '汽车2'
// 之后,所有使用该原型的对象 其属性都会改变
console.log(myCar1.type)       
console.log(yourCar1.type)       

上面这个例子可以看出,原型属性 是 其创建的 对象 共享的,只有一份。

我们可以把公共的不变的属性(也就是这个类型的公共属性,比如 type/hasTire)放到原型对象中。

这样从原型创建出来的对象就继承了这些属性,可以使用。代码既简洁,又节省内存。

构造函数 和 原型

构造函数 定义

上面的原型示例中

myCar1 = Object.create(ElectricCar)
myCar1.owner = '白月黑羽'

我们新建对象要设置本身的属性,得在创建好对象后,单独再指定。这样比较麻烦


如果,我们 要创建的对象 需要原型,并且希望在创建时,就指定其自身的属性(比如通过参数传入属性值)

可以使用 函数构建 ,并设置函数的 prototype 属性


每个js函数 都自动有一个 prototype属性

比如

function Car() {}

console.log(Car.prototype)

可以发现这个 prototype 对应的值也是一个对象。

注意:这个prototype对象里面有一个 constructor 属性指向其函数自身。

这个 prototype 就是 这个函数 所 构建出来的 对象的 原型

注意这个原型 不是该函数 自身的原型, 而是 这个函数 所构建出来的 对象的原型。


所以 函数构建出来的对象 会 继承(inherit) prototype 里面的属性。

我们可以在这个prototype 里面放一些属性。


来看个例子

// 汽车类型
function Car(price, owner) {
  this.price = price
  this.owner = owner
}
// 给函数的 prototype  对象里面添加属性
Car.prototype.type = '汽车'
Car.prototype.hasTire = true
Car.prototype.price = '未知'
Car.prototype.showInfo = function (){
   console.log('车型:' + this.type + ' - 车主:' + this.owner + ' - 售价:'+ this.price)
}

/* 注意
不要 这样设置prototype属性 ,这样就重新创建了一个prototype对象
Car.prototype = {type:'汽车', hasTire:true}
*/

// 构造对象
var myCar1 = new Car(300000, '白月黑羽')

  • 注意点1:构造函数里面通过关键字 this 指代 构造的对象
  this.price = price
  this.owner = owner

这样就可以给 要构建的对象添加属性。


  • 注意点2:构造对象 是通过关键字操作符 new 来创建的
new Car(300000, '白月黑羽')

new 后面加上 构造函数名 和 括号。

括号里面的参数是传递给 构造函数 的。


  • 注意点3:我们可以把 构造函数 称之为一种 类型

通过构造函数 构建的对象 都是 这个类型的 具体 实例

实例的英文叫 instance

js 中 有个关键字操作符 instanceof 用来判定一个对象 是否是 一个类型对象 的实例

// 下面表达式的返回值为true, 表示是一个Car类型的实例
myCar1 instanceof Car
  • 注意点4: 函数对象的 prototype

给函数的prototype添加属性后,根据前面讲的规则:

所有用 Car 构造的对象,它们的原型就是 Car.prototype 对应的对象

比如

// 对象本身没有该属性,使用 prototype里面的 '汽车'
myCar1.type

// 对象本身和prototype里面都有
// 优先使用对象本身的,结果是300000
myCar1.price

// 可以使用prototype里面的函数属性
myCar1.showInfo()

即使先创建对象,后添加prototype属性,对已经创建的对象一样有效

构造函数原型链

我们可以创建函数原型链,如下所示

// 汽车类型
function Car(price, owner) {
  this.price = price
  this.owner = owner
}
// 给函数的 prototype  对象里面添加属性
Car.prototype.type = '汽车'
Car.prototype.hasTire = true
Car.prototype.showInfo = function (){
   console.log('车型:' + this.type + ' - 车主:' + this.owner + ' - 售价:'+ this.price)
}

// 电动车类型
function ElectricCar(price, owner) {
  Car.call(this, price, owner)
}
// 设定 ElectricCar 原型为 Car
ElectricCar.prototype = Object.create(Car.prototype);
ElectricCar.prototype.type = '电动车'
ElectricCar.prototype.hasBattery = true
ElectricCar.prototype.hasEngine  = false

// 汽油车类型
function GasolineCar(price, owner) {
  Car.call(this, price, owner)
}
// 设定 GasolineCar 的原型为 Car
GasolineCar.prototype = Object.create(Car.prototype);
GasolineCar.prototype.type = '汽油车'
GasolineCar.prototype.hasBattery = false
GasolineCar.prototype.hasEngine  = true

// 构建实例
var myCar1 = new ElectricCar(300000, '白月黑羽')
var myCar2 = new GasolineCar(500000, '白月黑羽')

上面的代码形成的原型链如下:

myCar1 --->  ElectricCar.prototype ---> Car.prototype ---> Object.prototype
myCar2 --->  GasolineCar.prototype ---> Car.prototype ---> Object.prototype

所以

myCar1.price      // 结果是 300000

myCar1.type    // 结果是 '电动车'

myCar1.hasBattery // 结果是 true

myCar1.hasTire // 结果是 true

一个对象,是其原型链上所有 构造类型 的 实例

所以

// 原型链上的,是一个实例,结果是 true
myCar1 instanceof ElectricCar

// 原型链上的,是一个实例,结果是 true
myCar1 instanceof Car

// 不是原型链上的,不是是一个实例,结果是 false
myCar1 instanceof GasolineCar

对象常见操作

检查对象是否有某个属性

前面讲过,如果要检查对象是否自身有某个属性,可以使用 hasOwnProperty 方法

比如

myCar1.hasOwnProperty('owner') 

如果,不仅仅是自身属性,还要检查原型链上是否有某个属性,直接用关键字 in 即可

比如

'owner' in myCar1

得到对象所有属性

Object.keys 方法可以返回 对象的 所有属性名 一个数组

var obj1 = {
  a: 'somestring',
  b: 42
};

// 以 obj1 为原型创建 obj2 对象
var obj2 = Object.create(obj1)
obj2.c = 33
obj2.d = 44


Object.keys(obj2) // 返回 [ 'c', 'd' ]

得到对象所有属性和值

Object.entries 方法可以返回 对象的 所有属性和值 到一个数组,方便我们遍历

var obj1 = {
  a: 'somestring',
  b: 42
};

// 以 obj1 为原型创建 obj2 对象
var obj2 = Object.create(obj1)
obj2.c = 33
obj2.d = 44

Object.entries(obj2)  // 返回 [ [ 'c', 33 ], [ 'd', 44 ] ]

您需要高效学习,找工作? 点击咨询 报名实战班

点击查看学员就业情况