JS对象
- 是一组属性的无序集合,内容是键值对
属性的类型
用内部特性来描述属性的特征
- 数据属性(常规的属性),以下是内部特性
- [[Configurable]]: 表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true
- [[Enumerable]]: 表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true
- [[Writable]]: 表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的这个特性都是 true
- [[Value]]: 包含属性实际的值
- 修改属性的数据特征用Object.defineProperty(object,property,描述符对象):描述符对象比如 {writable: false,value: "Nicholas"},注意:把configurable设置成false之后就不能再用defineProperty()方法处理同一个属性,而且调用defineProperty(),如果没声明的数据特征会自动为false
- 访问器属性:是一种特别的属性,没有值,包含一个获取(getter)函数和一个设置(setter)函数,。在读取访问器属性时,会调用获取函数,这个函数的责任就是返回一个有效的值。在写入访问器属性时,会调用设置函数并传入新值,这个函数必须决定对数据做出什么修改
- 访问器属性有四个特性
- [[Configurable]]
- [[Enumerable]]
- [[Get]]
- [[Set]]
- 访问器属性有四个特性
js
// 定义一个对象,包含伪私有成员 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 相当于这个属性的设置可以更新对象中别的属性
对象操作
- 删除对象属性 delete obj.pro
- 定义多个属性Object.defineProperties(obj,{描述符对象})
比如{pro1:{描述符对象},pro2:{描述符对象}}
- Object.getOwnPropertyDescriptor(obj,proName): 读取对象属性的特性,返回一个对象
- Object.getOwnPropertyDescriptors(obj): 返回该对象每个自有属性的特性的对象集合
- 合并对象-> Object.assign(目标对象,一个或多个源对象),是一种浅复制:然后将每个源对象中可枚举(obj.propertyIsEnumerable()返回 true)和自有(obj.hasOwnProperty()返回 true)属性复制到目标对象
- 以字符串和符号为键的属性会被复制。对每个符合条件的属性,这个方法会使用源对象上的[[Get]]取得属性的值,然后使用目标对象上的[[Set]]设置属性的值
- 对象标识及相等判定:Object.is(a,b)
增强的对象语法
ECMAScript 6 为定义和操作对象新增了很多极其有用的语法糖特性,这些特性都没有改变现有引擎的行为,但极大地提升了处理对象的方便程度
- 属性值简写:如果属性名和变量名一样,可简写
- 可计算属性:[]中括号包围起来的js表达式做对象字面量的属性
- ES5中只能添加属性js
const nameKey = 'name'; const ageKey = 'age'; const jobKey = 'job'; let person = {}; person[nameKey] = 'Matt'; person[ageKey] = 27; person[jobKey] = 'Software engineer';
- 可计算属性与属性值简写不兼容
- ES5中只能添加属性
- 简写方法名
- 新的简写方法的语法遵循同样的模式,但开发者要放弃给函数表达式命名
- 简写方法名对获取函数和设置函数也是适用的(新的语法糖:get set可以在对象字面量中直接定义比如
let ob={a:5,get name(){},set name(){}};
中定义了name属性(访问器属性)的get和set函数) - 简写方法名与可计算属性键相互兼容
对象解构
就是使用与对象匹配的结构来实现对象属性赋值, 一种新的赋值方式
js
// 使用对象解构
let person = {
name: 'Matt',
age: 27
};
let { name: personName, age: personAge } = person;
console.log(personName); // Matt
console.log(personAge); // 27
let person = {//变量名跟属性名一样可进一步简写
name: 'Matt',
age: 27
};
let { name, age } = person;
console.log(name); // Matt
console.log(age); // 27
let { name, job='Software engineer' } = person;//可设置默认值
解构并不要求变量必须在解构表达式中声明。不过,如果是给事先声明的变量赋值,则赋值表达式必须包含在一对括号中
jslet personName, personAge; let person = { name: 'Matt', age: 27 }; ({name: personName, age: personAge} = person);
嵌套解构:原理很简单
部分解构:发生错误不回滚
参数上下文匹配:在函数参数列表中也可以进行解构赋值。对参数的解构赋值不会影响 arguments 对象,但可以在函数签名中声明在函数体内使用局部变量
jslet person = { name: 'Matt', age: 27 }; function printPerson(foo, {name, age}, bar) { console.log(arguments); console.log(name, age); } function printPerson2(foo, {name: personName, age: personAge}, bar) { console.log(arguments); console.log(personName, personAge); } printPerson('1st', person, '2nd'); // ['1st', { name: 'Matt', age: 27 }, '2nd'] // 'Matt', 27 printPerson2('1st', person, '2nd'); // ['1st', { name: 'Matt', age: 27 }, '2nd'] // 'Matt', 27
创建对象
- ECMAScript 6 开始正式支持类和继承。ES6 的类旨在完全涵盖之前规范设计的基于原型的继承模式。不过,无论从哪方面看,ES6 的类都仅仅是封装了 ES5构造函数加原型继承的语法糖而已
- 工厂模式:简单粗暴用new Object()创建空对象并手动赋值
- 构造函数(构造函数名最好大写),定义后用new关键字js
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function() { console.log(this.name); }; } let person1 = new Person("Nicholas", 29, "Software Engineer"); let person2 = new Person("Greg", 27, "Doctor");
- 新创建的对象的constructor属性指向构造函数,而且也是构造函数的实例,instanceof返回true
- 这个新对象内部的[[Prototype]]特性被赋值为构造函数的 prototype 属性
- 构造函数不一定要写成函数声明的形式。赋值给变量的函数表达式也可以表示构造函数
- 构造函数也是函数
- 构造函数的问题构造函数虽然有用,但也不是没有问题。构造函数的主要问题在于,其定义的方法会在每个实例上都创建一遍: 要解决这个问题,可以把函数定义转移到构造函数外部
- 原型模式
- 每个函数都会创建一个 prototype 属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法(实际上,这个对象就是通过调用构造函数创建的实例对象的原型。使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。原来在构造函数中直接赋给对象实例的值,可以直接赋值给它们的原型)
- 代办(红宝书p224)