对象
- 对象的定义:属性的无序集合
- JS中一切皆对象,原始值也有对应的包装类对象
- 大致可分为 内置对象 宿主对象 自定义对象
1. 创建自定义对象
同样,有两种方式:字面量创建、构造函数创建
// 字面量(常用)
> let obj1 = {
... name: "JS",
... getName() { return this.name; }
... }
// 构造函数
> let obj2 = new Object(); // {} 空对象
> obj2.name="JS"; // 可以添加属性和方法
> Obj2.getName=function() { return this.name; }
因为JS中一切皆对象,所以引用类型都可为其添加属性和方法:
> let arr = new Array(1,2); // [ 1, 2 ]
> arr.name = "JS"; // 添加属性和方法
> arr.getName() = function() { return this.name; }
> arr // [ 1, 2, name: 'JS', getName: [Function] ]
> for( let v in arr) { console.log(v); } // 添加的属性和方法可遍历
0
1
name
getName
2. 内置对象
如 String Array Function Date RegExp。。。
- 实际上,内置对象不过是拥有自己特有属性,且在其原型对象上定义了自己的特有方法而已。
所以,对应实例对象可以共享原型中的方法!
> console.dir(new String()); // 特有属性length
String
length: 0
__proto__: String
[[PrimitiveValue]]: ""
> console.dir([]); // 特有属性length
length: 0
__proto__: Array(0)
- 原型对象其实都是Object的实例对象
所以内置对象可以使用Object原型中的方法
(new RegExp()).__proto__ instanceof Object // true
- 原型对象的源头:Object原型对象
> console.dir({});
Object
__proto__: // Object的原型对象
constructor: ƒ Object() // 构造函数
hasOwnProperty: ƒ hasOwnProperty() // 判断自有属性
isPrototypeOf: ƒ isPrototypeOf() // 判断原型
propertyIsEnumerable: ƒ propertyIsEnumerable() // 判断属性的枚举特性
toLocaleString: ƒ toLocaleString() // 字符串字面量本地化
toString: ƒ toString() // 字符串字面量
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__() // 拓展中解释
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
3. 宿主对象
依赖于具体的JS运行环境,如 浏览器中的全局上下文对象 —— window对象。
对象属性、方法
1. 实例属性
JS中不同特性的对象会预置不同的实例属性:
// 预置属性
> [].length // 0 可见数组对象天生就有length属性
> "".length // 0 同理
> {} // Object实例对象则没有length属性
> [].__proto__ instanceof Object && // 原型对象都是Object的实例
"".__proto__ instanceof Object; // true
> let arr = [1,2];
> Object.getOwnPropertyDescriptor(arr, "length");
{value: 2, writable: true, enumerable: true, configurable: true}
// 自定义属性
arr.name = "JS";
Object.getOwnPropertyDescriptor(arr, "name");
{value: "JS", writable: true, enumerable: true, configurable: true}
属性的内部特性
- 数据属性
对象中的每个属性都有属性特性(描述符对象)来记录可进行的操作:
点语法添加自定义属性时,描述符对象默认为可配置、可枚举、可写!
均为布尔值,value值类型任意!
- configurable 可配置性
是否可delete、是否可修改特性、是否可改为访问器属性- enumerable 可枚举性
for-in是否可返回- writable 可写性
value是否可修改- value
属性值特别注意:
- 设为不可配置后,则不能再改为可配置了
- 点语法添加自定义属性时,默认为可配置、可枚举、可写!
- defineProperty()方法添加自定义属性时,特性默认为 false!
可通过 Object.defineProperty() 方法修改属性的内部特性:
> let obj = new String("abc");
> obj.name = "JS";
// 自定义属性
> Object.getOwnPropertyDescriptor(obj,"name"); // 获取属性的描述符
{ value: 'JS',writable: true,enumerable: true,configurable: true }
// 字符串元素属性
> Object.getOwnPropertyDescriptor(obj,"0");
{value: "a", writable: false, enumerable: true, configurable: false}
// 修改属性的描述符
> Object.defineProperty(obj,"name",{enumerable:false}); // 修改配置性
> Object.getOwnPropertyDescriptor(obj,"name"); // 获取属性的描述符
{ value: 'JS',writable: true,enumerable: false,configurable: true }
> delete obj.name; // false 不可delete
> Object.defineProperty(obj,"name",{writable:false}); // 修改可写性
> obj.name = "ES";
> obj.name // 'JS' 不可修改
// 特别注意(严格模式下):
// 不可配置后不能再修改:
Object.defineProperty(obj,"0",{configurable: true}); // 报错
TypeError: Cannot redefine property: 0 at Function.defineProperty
// 点语法添加,默认可写可枚举可配置
> Object.getOwnPropertyDescriptor(obj,"name"); // 获取属性的描述符
{ value: 'JS',writable: true,enumerable: true,configurable: true }
// defineProperty()方法添加,默认为false
> Object.defineProperty(obj, "age",{});
> Object.getOwnPropertyDescriptor(obj,"age");
{value: undefined, writable: false, enumerable: false, configurable: false}
- 访问器属性
重点是 set get 设置和获取函数。
前两个均为布尔值(也是数据属性),后两个默认为undefined!
- configurable 可配置性
是否可delete、是否可修改特性、是否可改为数据属性- enumerable 可枚举性
for-in是否可返回- get 获取函数
读取属性时自动调用- set 设置函数
设置属性时自动调用,可修改其他属性特别注意:
- get set方法是可选的
- 只写get方法则属性是只读的
- 只写set方法则属性是只写的
不可直接定义,必须通过Object.defineProperty()方法定义:
// 实现功能:月份超出向年份进位
> birth = {
year: 2000,
month_:1
}
> Object.defineProperty(birth, "month", { // 添加 month 访问器属性
get() {
return this.month_;
},
set(value) {
this.month_ = (value-1)%12 + 1;
let temp = parseInt((value-1)/12);
if(temp) {
this.year += temp;
}
}
});
> birth.month = 26;
> console.log(birth.year, birth.month); // 2002 2 自动进位
> Object.getOwnPropertyDescriptor(birth,"month"); // 默认为false
{enumerable: false, configurable: false, get: ƒ, set: ƒ}
> Object.getOwnPropertyNames(birth);// ["year", "month_", "month"]
month_: 1
year: 2001
month: (...)
get month: ƒ get()
set month: ƒ set(value)
__proto__: Object
- 拓展:
前面看到的函数:
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
ES5之前,使用这两个非标准的属性来设置访问器属性,现在已经弃用了。
2. 原型方法
不同类型,JS预置了一些不同原型方法,以便实例对象共享,如Array:
// 数组的原型方法:
concat: ƒ concat() // 数组连接
copyWithin: ƒ copyWithin() // 复制数组元素替换
entries: ƒ entries() // 生成[key,value]迭代器
every: ƒ every() // 任意性
fill: ƒ fill() // 填充
filter: ƒ filter() // 过滤
find: ƒ find() // 条件查找,返回值
findIndex: ƒ findIndex() // 条件查找,返回索引
flat: ƒ flat() // 扁平化,浏览器环境实现了
flatMap: ƒ flatMap() // 映射后扁平化,浏览器环境实现了
forEach: ƒ forEach() // 遍历
includes: ƒ includes() // 判断是否存在
indexOf: ƒ indexOf() // 返回目标的索引
join: ƒ join() // 元素拼接为字符串
keys: ƒ keys() // 生成key的迭代器
lastIndexOf: ƒ lastIndexOf() // 返回目标的索引,由后向前查找
map: ƒ map() // 映射
pop: ƒ pop() // 出栈
push: ƒ push() // 入栈
reduce: ƒ reduce() // 归并
reduceRight: ƒ reduceRight() // 归并,由后向前
reverse: ƒ reverse() // 数组翻转
shift: ƒ shift() // 出队
slice: ƒ slice() // 获取子串
some: ƒ some() // 存在性
sort: ƒ sort() // 排序
splice: ƒ splice() // 可删除、添加、替换元素
toLocaleString: ƒ toLocaleString() // 字符串字面量本地化
toString: ƒ toString() // 字符串字面量化
unshift: ƒ unshift() // 入队
values: ƒ values() // 生成value迭代器
Symbol(Symbol.iterator): ƒ values()
Symbol(Symbol.unscopables): {copyWithin: true, entries: true, fill: true, find: true, findIndex: true, …}
Array专题:https://blog.csdn.net/qq_45668041/article/details/115706018
不同类型的原型对象共有的部分:
// 内置类型的原型对象都是Object实例,自然由Object构造函数创建出来的
length: 0 // 创建实例时传入的参数个数,默认使用Object()创建
__proto__: Object// 指向原型对象的原型
3. Object原型方法
> console.dir({});
Object
__proto__: // Object的原型对象
constructor: ƒ Object() // 构造函数
hasOwnProperty: ƒ hasOwnProperty() // 判断自有属性
isPrototypeOf: ƒ isPrototypeOf() // 判断原型
propertyIsEnumerable: ƒ propertyIsEnumerable() // 判断属性的枚举特性
toLocaleString: ƒ toLocaleString() // 字符串字面量本地化
toString: ƒ toString() // 字符串字面量
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__() // 访问器属性(非标准)
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
那么,前面用到的方法哪来的呢?
其实还是Object构造函数的实例属性,只是不可枚举!
// getOwnPropertyDescriptor是不可枚举属性
> Object.getOwnPropertyDescriptor(Object, "getOwnPropertyDescriptor");
{writable: true, enumerable: false, configurable: true, value: ƒ}
// 所以,下面方法不能判断是否为自身属性:
> Object.hasOwnProperty(Object, "getOwnPropertyDescriptor");// false
// 应该通过下面的方法查看所有的自身属性:
> Object.getOwnPropertyNames(Object);
["length", "name", "prototype", "assign", "getOwnPropertyDescriptor", "getOwnPropertyDescriptors", "getOwnPropertyNames", "getOwnPropertySymbols", "is", "preventExtensions", "seal", "create", "defineProperties", "defineProperty", "freeze", "getPrototypeOf", "setPrototypeOf", "isExtensible", "isFrozen", "isSealed", "keys", "entries", "fromEntries", "values"]
下图可能有助于理解构造函数和原型对象的关系:
对象解构
ES6新语法,null undefined不支持对象解构!
- 解构的优点:
可快速取出对象中的属性!
// 解构
> let person = {
name: "JS",
age:18
};
// 部分解构
> let { name: personName, age: personAge } = person;
> console.log(personName, personAge); // JS 18
// 简写形式
> let { name,age } = person;
> console.log(name, age); // JS 18
// 嵌套解构
> let body = {
name: "JS",
job: {
title: "Dev",
},
};
> let { job:{title:Title} } = body;
> console.log(Title); // Dev
// 参数上下文匹配:不影响arguments对象
> function printPerson(foo,{ name, age }, bar) {
console.log( arguments, name, age );
}
> printPerson('1st',person,'2nd');
[Arguments] { '0': '1st', '1': { name: 'JS', age: 18 }, '2': '2nd' }
JS 18
对象序列化
用于和后台进行数据传递,为后台提供JSON字符串。
> let obj = {
name: "JS",
age: 18
}
> let str = JSON.stringify(obj); // '{"name":"JS","age":18}'
> JSON.parse(str); // { name: 'JS', age: 18 }