9.对象基础(总) - JS

认识对象

  1. 对象是数据(方法也是数据)的集合,以键值对(键: 值)的形式体现,键就是属性名,值就是属性值。
  2. 对象字面量使用 {} 包围。
  3. 空对象是没有任何数据的对象。
const empty = {  }
const robot = {
	name: 'balance car',
  	priceExpected: 899,
	isChecked: true,
	parts: ['balanceSys', 'dynamicalSys', 'model'],
 	agency: { warranty: '2Y', address: ['A city', 'B city'] },
  	forward: function () {
    	console.log('前进');
  	},
  	backward: function () {
    	console.log('后退');
  	},
  	brake: () => { 
		console.log('刹车')
	},
  	turnLeft() {
  		console.log('左转');
  	},
  	turnRight() {
		console.log('右转');
	}
};

属性和属性值

属性是属性名与属性值的结合(属性=属性名+属性值)。

  1. 属性名只能是 StringSymbol 两种类型;
    • 属性名没有关键字的限制,除 Symbol 外都被转成字符串了,任何对象作为属性名时,被转成字符串 [object Object]
    • 没有空格的属性不需要被引号包围;
    • 有空格的属性名需要被引号包围;
    • 赋值器(getter)、取值器(setter)等例外。
  2. 属性值可以是任何数据(数、字符串、数组、对象、函数等);
  3. 判断属性是否存在,使用运算符 in,语法是:key in object

属性值的访问

属性值的访问(获取、更新等):

  1. obj.key 点链式;
  2. obj['key'] 方括号式,具有数字特征的属性名,只能使用此式。
let person1 = {
	1: 'id',
	name: 'Peter',
	gender: 'Man',
	habit: { sport: 'jog', game: 'LOL' }
	_age: 28,
	'birth year': 2000,		// 存在空格,需要引号
	get age() { return this.age; },
	set age(age) { this.age = age; },
}
'name' in person1				// true
person1.1						// 报错 person1[1]/person1['1']是允许的
person1['name']					// 'Peter'	--- [] 式
person1.age						// 28		--- . 式
person1.gender = 'Woman'		// 将属性更新
person1.habit.sport = 'football'

属性的定义

  1. 在对象字面量里以键值对形式定义;
  2. 通过 . 式添加属性;
  3. 通过 [] 式添加属性。
let a = 10, b = 20, c = 30;
let obj = { 
	a: a,		// 第一个 a是属性名,第二个是属性值(=10、引用)
	sum() { let s = 0; for (let k in this) if(k != 'sum') s += this[k]; return s; } 
}
obj.sum			// 10
obj.b = b;
obj['c'] = c
obj.sum			// 60

计算属性 - 属性表达式

  1. 属性名的书写可以是一个表达式,计算出的值是最终的属性名;
  2. 在对象字面量的书写中,使用 [] 将其包围。
let what = prompt("输入你要的属性名?", "what");
let value = prompt("输入你要的属性值?", "1");
let o = {
	[1 + 2]: '三',		// 1+2是一个表达式, 计算结果3 是最终属性名
	[what]: value		// what 是一个变量, 它的右边是表达式, 输入的提示词是最终的属性名
}
/* 如果what==name,value=joe */
o		// { 3: '三', name: 'joe' }
o.name	// 'joe'

Symbol 作为属性名

  1. 使用 Symbol(描述字符串) 创建唯一标识符
  2. 就算相同描述,内存指向也不同,是不同的标识;
  3. 在对象字面量中,作为属性表达式进行添加,需要 [] 包围;
  4. 可以通过方括号式 obj[key] 添加,不能通过点链式 obj.key 进行添加;
  5. 在一些遍历操作中被忽略。
let id1 = Symbol('ID');
let id3 = Symbol('ID');
let o = {
	a: 1,
	b: 2,
	[id1]: 1,
	[Symbol('id')]: 2
}
o[id3] = 3
o	// { a: 1, b: 2, Symbol('ID'): 1, Symbol('ID'): 2, Symbol('ID'): 3 }

属性的描述器

  1. 每个属性都有一个描述器(Descriptor),用来控制该属性的行为;
  2. 通过 Object.getOwnPropertyDescriptor(object, key) 进行读取;
  3. 描述了属性的值以及是否可写、是否可枚举、是否可配置(决定前二者)等。
let o = { a: 1, b: 2 };
Object.getOwnPropertyDescriptor(o, 'a')
/*
	{
	   value: 123,			// 值
	   writable: true,		// 可写
	   enumerable: true,	// 可枚举
	   configurable: true	// 可配置
	}
*/

属性的可枚举性和遍历

  1. 根据可枚举性,至少有四个操作会忽略不可枚举的(enumerable: false)的属性:
    • for...in 循环遍历:只遍历对象自身的和继承的可枚举的属性(不含Symbol属性);
    • Object.keys():返回对象自身的所有可枚举的属性的键名(不含Symbol属性);
    • JSON.stringify():只串行化对象自身的可枚举的属性(不含Symbol属性);
    • Object.assign():ES6、只拷贝对象自身的可枚举的属性。
  2. 属性的遍历:
    • for in 循环遍历,遍历(对象自身的继承的)可枚举的属性名;
    • Object.keys(obj),返回一个数组,包含(对象自身的)可枚举的属性名;
    • Object.getOwnPropertyNames(obj),数组,包含(对象自身的)除 Symbol 外的属性名;
    • Object.getOwnPropertySymbols(obj),数组,包含(对象自身的)Symbol 属性名;
    • Reflect.ownKeys(obj),返回一个数组,包含(对象自身的)所有属性名。
  3. 遍历的次序规则:
    • 首先遍历所有数值键,按照数值升序排列;
    • 其次遍历所有字符串键,按照加入时间升序排列;
    • 最后遍历所有 Symbol 键,按照加入时间升序排列。
/* 只有 for in 循环遍历,会遍历继承的属性 */
const obj = { c: 3 };
let o = { a: 1, b: 2, [Symbol('id')]: 1 }
o.__proto__ = obj
o.c								// 3
for (let k in o) console.log(k)	// 1, 2, 3
Object.getOwnPropertySymbols(o)	// [Symbol('id')]

对象方法与 this

对象方法

  1. 对象方法,就是属性值是函数表达式的属性;
  2. 一种简写:省略 :function
  3. 语法:函数表达式,参考开头的第一个例子。

this 的含义与指向

  1. 关键字 this 指向的是,当前代码运行时的对象;
  2. 谁调用就指向谁。
let dog1 = {
	name: 'Kitty';
	bark() { console.log(`Dog ${this.name} is barking!`) }	// this 指向 dog1
}
let dog2 = {
	name: 'Gimmy';
	bark() { console.log(`Dog ${this.name} is barking!`) } // this 指向 dog2
}

构造函数

  1. 目的是使用函数构造对象,即函数的返回值是一个对象;
  2. 解决了对象一些初始化的问题;
  3. 通过 newthis 引导实现:new 开辟一个对象空间,this 指向并绑定对象,调用时需要 new 引导;
  4. 注意事项:
    • 调用时,没有使用 new 引导,没有开辟对象空间,则 this 可能指向全局对象,所有创建的属性也归属之;
    • 函数体有 return 语句,则返回对应的值。
function Pet(type, name) {
	// this = {}	// 隐式实现
  	this.type = type;	// 'dog'? or 'cat'? or anyelse
  	this.name = name;
  	this.bio = function () {
    	console.log(`${this.name} is my ${this.type} pet!`);
  	};
  	// return this;	// 隐式实现
  	// return { a: 1, b: 2 }	// 撒子或疯子会这样写
}
let pet = new Pet('dog', '二哈');	// pet = { type: 'dog', .... }
let p = Pet('cat', '咪咪');			// p = undefined 需要new开辟对象空间

/* 传统的构造对象的函数 */
function Pet(type, name) {
  	const pet = {};
  	pet.type = type;	// 'dog'? or 'cat'? or anyelse
  	pet.name = name;
  	pet.bio = function () {
    	console.log(`${pet.name} is my ${pet.type} pet!`);
  	};
  	return pet;
}

对象的引用和赋值

  1. 原始类型(数字等),只有单一的值,一个实例就是一个值,不用建立复杂的引用关系;
  2. 对象是“引用类型”,创建或更新对象,是将索引()关联到数据()的过程;
  3. 对象的基本操作(赋值等),通过 指定对应的 (就像指针指向地址)实现;
  4. 一些对象赋值的基本结论:
    • 将对象简单赋值给变量,这个变量只是拿到了数据的索引,进而可以引用或更新数据,但是不复制数据,不开辟新空间以存储复制的数据;
    • 拷贝赋值,方法之一就是使用 for in 遍历以拷贝到数据进行赋值,另外就是使用 Object.assign 将源对象的数据拷贝赋值给目标对象,两方法近似等价;
    • 深层拷贝赋值,为了解决属性值是一个对象的问题(一层拷贝不够),方法之一是使用 lodash 库的 _.cloneDeep(obj)
let o1 = { a: 1, b: 2 }
let o2 = o1
o1 == o2	// true
o1 === o2	// true

let obj1 = { a: 0 }, obj2 = { a: 0 };
obj1 == obj2	// false 不是同一个数据,尽管内容一样

/* 拷贝赋值 Object.assign(dest, [src1, src2, ...]) src个数0或1或多个 */
let obj3 = { a: 1, b: 2 };
let obj = Object.assign(obj1, obj3);
obj		// { a: 1, b: 2 } 相同的属性被覆盖
/* 深层拷贝赋值 */
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

可选链 - “?.”

  1. 当“不确定属性是否存在”时,一般可通过条件语句判断或逻辑表达式来避免错误,这样的工作量大(语句冗余、方法调用多等),特别是很多不确定的场景。一个基本要求是,尽量存在不确定的场景使用。
  2. 一个很棒的选择就是,使用可选链 ?.。如果有实在的属性(!=null,undefined),就继续引用;否则停止并返回 undefined
  3. 尽管 ?. 使得其前面的数据作为判断的主体,判断主体需要事先声明,主体不存在(未定义或为空等情况),会“短路”马上停止。
let resident = {};
user.address ? user.address.street ? user.address.street.name : null : null	// 通过条件判断
resident.address && resident.address.street && resident.address.street.name	// 通过逻辑表达式

/* 下面使用可选链 */
resident?.address?.street?.name
resident.address?.street?.name		// address 这个属性可以确定

可选链的变体

  1. ?.(),面向方法(函数),不确定是否可调用;
  2. ?.[] 方括号式,和点链式 ?. 一样访问不确定的属性。
let o = {
	a: 1,
	b: 2,
	s() { return this.a + this.b; }
}

o.s?.()	// 3,s 是o的方法,可以被调用
o.x?.()	// undefined,o.x===undefined
o.a?.() // 报错,a是确定的,不可被调用的属性

o?.['a']	// 1
o?.['c']	// undefined
  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值