JavaScript高级程序设计 第八章---对象,类与面向对象编程

第八章(一)

内容

  • 理解对象
  • 理解对象创建过程

ECMA-262 将对象定义为一组属性的无序集合。严格来说,这意味着对象就是一组没有特定顺序的值。对象的每个属性或方法都由一个名称来标识,这个名称映射到一个值。正因为如此(以及其他还未讨论的原因),可以把ECMAScript 的对象想象成一张散列表,其中的内容就是一组名/值对,值可以是数据或者函数。

8.1 理解对象

创建自定义对象的通常方式是创建Object 的一个新实例,然后再给它添加属性和方法,如下例所示:

let person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function() {
   
	console.log(this.name);
};

sayName()方法会显示this.name 的值,这个属性会解析为person.name

8.1.1 属性的类型

对象的属性通过一些内部特性来进行描述,。这些特性是由为JavaScript 实现引擎的规范定义的。因此,开发者不能在JavaScript 中直接访问这些特性了将某个特性标识为内部特性,规范会用两个中括号把特性的名称括起来,比如[[Enumerable]]

属性分两种:数据属性访问器属性

1.数据属性
数据属性包含一个保存数据值的位置。值会从这个位置读取,也会写入到这个位置。

在像前面例子中那样将属性显式添加到对象之后,[[Configurable]]、[[Enumerable]]和[[Writable]]都会被设置为true,而[[Value]]特性会被设置为指定的值。比如:

let person = {
   
	name: "Nicholas"
};

这里,我们创建了一个名为name 的属性,并给它赋予了一个值"Nicholas"。这意味着[[Value]]特性会被设置为"Nicholas",之后对这个值的任何修改都会保存这个位置。

修改属性的默认特性,就必须使用Object.defineProperty()方法。这个方法接收3 个参数:

  • 要给其添加属性的对象
  • 属性的名称
  • 一个描述符对象

最后一个参数,即描述符对象上的属性可以包含:configurable、enumerable、writable 和value,跟相关特性的名称一一对应。根据要修改的特性,可以设置其中一个或多个值。比如:

let person = {
   };
Object.defineProperty(person, "name", {
   
	writable: false,
	value: "Nicholas"
});
console.log(person.name); // "Nicholas"
person.name = "Greg";
console.log(person.name); // "Nicholas"

这个例子创建了一个名为name 的属性并给它赋予了一个只读的值"Nicholas"。这个属性的值就不能再修改了,在非严格模式下尝试给这个属性重新赋值会被忽略。在严格模式下,尝试修改只读属性的值会抛出错误。

此外,一个属性被定义为不可配置configurable: false之后,就不能再变回可配置的了,再次调用Object.defineProperty()并修改任何非writable 属性会导致错误:

let person = {
   };
Object.defineProperty(person, "name", {
   
	configurable: false,
	value: "Nicholas"
});
// 抛出错误
Object.defineProperty(person, "name", {
   
	configurable: true,
	value: "Nicholas"
});

因此,虽然可以对同一个属性多次调用Object.defineProperty(),但在把configurable 设置为false 之后就会受限制了。

在调用Object.defineProperty()时,configurable、enumerable 和writable 的值如果不指定,则都默认为false。多数情况下,可能都不需要Object.defineProperty()提供的这些强大的设置,但要理解JavaScript 对象,就要理解这些概念。

2.访问器属性

访问器属性不包含数据值。相反,它们包含一个获取(getter)函数和一个设置(setter)函数,不过这两个函数不是必需的。在读取访问器属性时,会调用获取函数,这个函数的责任就是返回一个有效的值。在写入访问器属性时,会调用设置函数并传入新值,这个函数必须决定对数据做出什么修改。

访问器属性是不能直接定义的,必须使用Object.defineProperty()。下面是一个例子:

// 定义一个对象,包含伪私有成员year_和公共成员edition
let book = {
   
	year_: 2017,
	edition: 1
};
Object.defineProperty(book, "year", {
   
	get() {
   
		return this.year_;
	},
	set(newValue) {
   
		if (newValue > 2017) {
   
			this.year_ = newValue;
			this.edition += newValue - 2017;
		}
	}
});
book.year = 2018;
console.log(book.edition); // 2

year_中的下划线常用来表示该属性并不希望在对象方法的外部被访问。另一个属性year 被定义为一个访问器属性,其中获取函数简单地返回year_的值,而设置函数会做一些计算以决定正确的版本(edition)。因此,把year 属性修改为2018 会导致year_变成2018,edition 变成2

即修改会进行传递。

这是访问器属性的典型使用场景,即设置一个属性值会导致一些其他变化发生

在对象内部定义访问器属性:

dest = {
   
	set id(x) {
   
		console.log(x);
	}
	get id() {
   
		return 'foo';
	}
};

如上所示,定义了设置id属性和获取id属性的方法。

8.1.2 定义多个属性

ECMAScript 提供了Object.define-Properties()方法。这个方法可以通过多个描述符一次性定义多个属性。它接收两个参数:要为之添加或修改属性的对象和另一个描述符对象,其属性与要添加或修改的属性一一对应。

let book = {
   };
Object.defineProperties(book, {
   
	year_: {
   
		value: 2017
	},
	edition: {
   
		value: 1
	}
});

所有属性都是同时定义的,并且数据属性的configurable、enumerable 和writable 特性值都是false(使用Object.defineProperties这个方法定义就会默认为false,直接定义会默认为true)。

使用Object.getOwnPropertyDescriptor()方法可以取得指定属性的属性描述符。

8.1.4 合并对象

ECMAScript 6 专门为合并对象提供了Object.assign()方法。这个方法接收一个目标对象和一个或多个源对象作为参数,然后将每个源对象中可枚举(Object.propertyIsEnumerable()返回true)和自有(Object.hasOwnProperty()返回true)属性复制到目标对象。以字符串和符号为键的属性会被复制。对每个符合条件的属性,这个方法会使用源对象上的[[Get]]取得属性的值,然后使用目标对象上的[[Set]]设置属性的值。

let dest, src, result;
/**
* 简单复制
*/
dest = {
   };
src = {
    id: 'src' };
result = Object.assign(dest, src);
// Object.assign 修改目标对象
// 也会返回修改后的目标对象
console.log(dest === result); // true
console.log(dest !== src); // true
console.log(result); // { id: src }
console.log(dest); // { id: src }

对于Object.assign(dest, src)这个复制过程是先调用src的获取方法,获取属性后,调用dest的设置方法进行设置。
因此,Object.assign()实际上对每个源对象执行的是浅复制。如果多个源对象都有相同的属性,则使用最后一个复制的值。

**/**
* 对象引用
*/
dest = {
   };
src = {
    a: {
   } };
Object.assign(dest, src);
// 浅复制意味着只会复制对象的引用
console.log(dest); // { a :{} }
console.log(dest.a === src.a); // true**

通过最后一行的全等符合可以看出它们两个的a指向的是同一个对象,因此复制的只是对象的引用。

8.1.5 对象标识及相等判定

ECMAScript 6 规范新增了Object.is(),这个方法与===很像,但改善了一些特殊情况的判定。

// 这些情况在不同JavaScript 引擎中表现不同,但仍被认为相等
console.log(+0 === -0); // true
console.log(+0 === 0); // true
console.log(-0 === 0); // true
// 要确定NaN 的相等性,必须使用极为讨厌的isNaN()
console.log(NaN === NaN); // false
console.log(isNaN(NaN)); // true

// 正确的0、-0、+0 相等/不等判定
console.log(Object
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值