对象
基本概念
- 对象是属性和方法的集合
- 将复杂功能隐藏在内部,更改对象内部的复杂逻辑不会对外部调用造成影响即 抽象
- 继承 是通过代码复用减少冗余代码
- 根据不同形态的对象产生不同结果即 多态
// 字面量形式声明
let obj = {
AAA: '萧俊介',
BBB: function () {
return this.name;
}
}
// 属性与方法简写
let AAA = "萧俊介";
let objB = {
AAA,
BBB() {
return this.name;
}
};
对象方法
- 定义在对象中的函数我们称为方法
- 对象和函数、数组一样是引用类型,即复制只会复制引用地址。
- 对象做为函数参数使用时也不会产生完全赋值,内外共用一个对象。
- 对象的比较是对内存地址的比较所以使用
==
或===
一样。 this
指当前对象的引用,始终建议在代码内部使用this
而不要使用对象名- 不同对象的
this
只指向当前对象 - 和函数一样可以使用
...
语法
解构赋值
- 解构是一种更简洁的赋值特性,可以理解为分解一个数据的结构。
let { name: n, url: u } = { name: '俊介 A', url: 'www.alrcly.com' }
let { name, url } = { name: '俊介 B', url: 'www.alrcly.com' };
const hd = {
name: '后盾人',
lessons: {
title: 'JS'
}
}
const { name, lessons: { title: title } } = hd;
const { name, url, user = '向军大叔' } = { name: '后盾人', url: 'houdunren.com' };
操作属性
- 可以使用点语法获取,也可以使用
[]
获取 - 如果属性名不是合法变量名就必须使用扩号的形式
- 对象和方法的属性可以动态的添加或删除
obj.name
obj["name"]
obj.show = function () {
return `show`;
};
delete obj.show;
// hasOwnProperty 检测对象自身是否包含指定的属性,不检测原型链上继承的属性
obj.hasOwnProperty('name')
// 使用 in 可以在原型对象上检测是否包含指定的属性
"XXX" in obj
// 使用 Object.getOwnPropertyNames 可以获取对象的属性名集合
Object.getOwnPropertyNames(obj)
// 使用 Object.assign 静态方法从一个或多个对象复制属性,到第一个对象参数
// 可以使用展开语法代替 Object.assign
Object.assign(obj, { f: 1 }, { m: 9 })
// 对象属性可以通过表达式计算定义,这在动态设置属性或执行属性方法时很好用。
let id = 0;
const user = {
[`id-${++id}`]: id,
[`id-${++id}`]: id,
[`id-${++id}`]: id
};
遍历对象
- 使用系统提供的 API 可以方便获取对象属性与值
const obj = {}
Object.keys(obj); // 获取对象里的 Key
Object.values(obj); // 获取对象里的 Value
Object.entries(obj); // 获取对象里的键值对
// in 循环
for (let key in obj) {
console.log(key, hd[key]);
}
// of 循环,不能操作对象,只能通过 keys、values、entries 间接实现
for (const item of Object.values(obj)) {
console.log(item);
}
对象拷贝
- 对象赋值时复制的内存地址,所以一个对象的改变直接影响另一个
- 浅拷贝:对象本身和第一层是值传递,第二层还是复制的内存地址
- 深拷贝:整个都是新的
// 使用 for/in 执行对象拷贝 (浅拷贝)
let oldObj = { name: "俊介" };
let newObj = {};
for (const key in oldObj) {
newObj[key] = oldObj[key];
}
// 使用 Object.assign({}, oldObj) 执行对象拷贝 (浅拷贝)
let oldObjC = { name: "俊介" };
let newObjD = Object.assign({}, oldObjC)
// 使用 { ...oldObj } 执行对象拷贝 (浅拷贝)
let oldObjE = { name: "俊介" };
let newObjF = { ...oldObjE };
// 使用递归函数(深拷贝)
function copy(object) {
let obj = object instanceof Array ? [] : {};
for (const [k, v] of Object.entries(object)) {
obj[k] = typeof v == "object" ? copy(v) : v;
}
return obj;
}
函数构建
工厂函数
- 在函数中返回对象的函数称为工厂函数
- 减少重复创建相同类型对象的代码
- 修改工厂函数的方法影响所有同类对象
- 使用字面量创建对象需要复制属性与方法结构
function user(name) {
return {
name,
show() {
console.log(this.name);
}
};
}
const alrcly = user("萧俊介");
alrcly.show();
构造函数
- 和工厂函数相似构造函数也用于创建对象,它的上下文为新的对象实例
- 构造函数名每个单词首字母大写即 Pascal 命名规范
this
指当前创建的对象- 不需要返回
this
系统会自动完成 - 需要使用 new 关键词生成对象
- 在严格模式下方法中的
this
值为undefined
,这是为了防止无意的修改window
对象
function Student(name) {
this.name = name;
this.show = function () {
console.log(this.name);
};
//不需要返回,系统会自动返回
// return this;
}
const lemen = new Student("柠檬酱");
lemen.show();
const alrcly = new Student("俊介君");
alrcly.show();
内置构造
- JS 中大部分数据类型都是通过构造函数创建的
- 在 JS 中函数也是一个对象
- 函数是由系统内置的
Function
构造函数创建的
let obj = new Object()
let num = new Number()
let str = new String()
let bool = new Boolean()
let data = new Data()
let reg = new RegExp()
- 字面量创建的对象,内部也是调用了
Object
构造函数
// 设置一个函数
function hd() { }
// constructor 是对象上的一个属性,指出对象的构造函数是什么
// 打印发现这个函数是由 Function() 构造的
console.log(hd.constructor)
// 可以直接使用 Function() 来构建匿名函数
// 参数 A 为变量,B 为代码块
let UserA = new Function("name", `return name`)
// 实例化一个对象 hd
// 然后用 constructor 调用 hd 的构造函数方法
// 加上()等于是直接在使用 constructor 返回的 hd 的构造函数方法了
let UserB = new hd.constructor("name", `return name`)
抽象特性
- 通过闭包特性将对象进行抽象处理,而不是通过关键字
function User(name, age) {
// 封装成对象变量,使其不挂载到 this 上,屏蔽了外界访问
let data = { name, age };
// 利用闭包特性访问到 data 的数据
this.show = function () {
return `${data.name}是${this.info()}`;
};
// 封装成对象变量,使其不挂载到 this 上,屏蔽了外界访问
let info = function () {
return data.age > 50 ? "中年人" : "年轻人";
};
}
let jj = new User("萧俊介", 22);
console.log(jj.show());
属性特征
JS 中可以对属性的「属性描述符」进行控制,「属性描述符」共有六个。
- configurable: true:属性是否可以删除或重新配置
- enumerable: true:属性是否可遍历/隐藏
- value: 123:该属性对应的值
- writable: true:属性的值是否可以写修改
- get() :属性的 getter 函数
- set():属性的 setter 函数
查看对象某个属性的描述
- 使用
Object.getOwnPropertyDescriptor
查看对象某个属性的描述
let obj = {
aaa:123,
bbb:456
}
Object.getOwnPropertyDescriptor(obj,"aaa")
查看对象所有属性的描述
- 使用
Object.getOwnPropertyDescriptors
查看对象所有属性的描述
let obj = {
aaa:123,
bbb:456
}
Object.getOwnPropertyDescriptors(obj)
修改属性特性
- 使用
Object.defineProperty
方法修改属性特性
let obj = {
aaa: 123,
bbb: 456
}
Object.defineProperty(obj, 'aaa', {
value: 987,
writable: false,
enumerable: false,
configurable: false
})
Object.getOwnPropertyDescriptors(obj)
一次修改多个属性
- 使用
Object.defineProperties
可以一次设置多个属性
let obj = {
aaa: 123,
bbb: 456
}
Object.definePropertys(obj, {
'aaa': {
value: 987,
writable: false,
enumerable: false,
configurable: false
},
'bbb': {
value: 789,
writable: false,
enumerable: false,
configurable: false
}
})
Object.getOwnPropertyDescriptors(obj)
禁止向对象添加新属性
- 使用
Object.preventExtensions()
禁止向对象添加新属性
判断是否能向对象中添加属性
- 使用
Object.isExtensible()
判断是否能向对象中添加属性
let obj = {
aaa: 123,
bbb: 456
}
Object.preventExtensions(obj) // 禁止向对象添加属性
obj.ccc = "394" // 会报错
let isAdd = Object.isExtensible(obj) // 判断是否能向对象中添加属性
console.log(isAdd)
封闭对象(可以改值)
- 使用
Object.seal()
方法封闭一个对象,阻止添加新属性并将所有现有属性标记为configurable: false
- 使用
Object.isSealed()
如果对象是密封的则返回true
,属性都具有configurable: false
let obj = {
aaa: 123,
bbb: 456
}
Object.seal(obj) // 封闭对象
let ddd = Object.isSealed(obj) // 对象是否封闭
console.log(ddd);
冻结对象(不能改值)
- 使用
Object.freeze
冻结对象后不允许添加、删除、修改属性,writable
、configurable
都标记为false
- 使用
Object.isFrozen()
方法判断一个对象是否被冻结
let obj = {
aaa: 123,
bbb: 456
}
Object.freeze(obj) // 冻结对象
let ddd = Object.isFrozen(obj) // 对象是否冻结
console.log(ddd);
获得/设置属性值
- get Ter() 获得属性值,set Ter() 设置属性,这是 JS 提供的存取器特性即使用函数来管理属性
- 用于避免错误的赋值
- 需要动态监测值的改变
- 属性只能在访问器和普通属性任选其一,不能共同存在
- 访问器的优先级高于普通的操作方式
let Request = {
get token() {
let con = localStorage.getItem('token');
if (!con) {
alert('请登录后获取 token')
} else {
return con;
}
},
set token(con) {
localStorage.setItem('token', con);
}
};
Request.token = 'houdunren'
console.log(Request.token);
利用私有属性模拟抽象特性
使用 defineProperties
可以模拟定义私有属性,从而使用面向对象的抽象特性。
function User(name, age) {
let data = { name, age };
Object.defineProperties(this, {
name: {
get() {
return data.name;
},
set(value) {
if (value.trim() == "") throw new Error("无效的用户名");
data.name = value;
}
},
age: {
get() {
return data.name;
},
set(value) {
if (value.trim() == "") throw new Error("无效的用户名");
data.name = value;
}
}
});
}
let hd = new User("萧俊介", 33);
console.log(hd.name);
hd.name = "俊介 12";
console.log(hd.name);
代理拦截
代理(拦截器)是对象的访问控制,setter/getter 是对单个对象属性的控制,而代理是对整个对象的控制。
- 读写属性时代码更简洁
- 对象的多个属性控制统一交给代理完成
- 严格模式下 set 必须返回布尔值
使用方法
语法
new Proxy(target,handler)
参数说明
target:
即目标对象
handler:
handler 是一个对象,声明了代理 target 的指定行为
代理对象
如果代理以对象方式执行时,会执行代理中定义 get/set 方法。
const alrlcy = { name: "萧俊介", age: '24' }
const proxy = new Proxy(alrlcy, {
get(target, propKey, receiver) {
//target:原始目标对象
//propKey:当前的 Key
//receiver:表示原始操作行为所在对象
switch (propKey) {
case 'name':
console.log(target[propKey])
break;
case 'age':
console.log(target[propKey])
break;
}
},
set(target, propKey, value, receiver) {
//target:原始目标对象
//propKey:当前的 Key
//value:设置的值
//receiver:表示原始操作行为所在对象
obj[property] = value
return true
}
})
代理函数
如果代理以函数方式执行时,会执行代理中定义 apply 方法。
function factorial(num) {
return num == 1 ? 1 : num * factorial(num - 1);
}
let factorialProxy = new Proxy(factorial, {
apply(target, object, args) {
//target:原始目标对象
//object:this 指针
//args:数组形式的参数
return target.call(object, args)
}
});
console.log(factorialProxy(3))
JSON
- JSON 是一种轻量级的数据交换格式,易于人阅读和编写。
- 使用 JSON 数据格式是替换 xml 的最佳方式,主流语言都很好的支持 JSON 格式。所以 JSON 也是前后台传输数据的主要格式。
- JSON 标准中要求使用双引号包裹属性,虽然有些语言不强制,但使用双引号可避免多程序间传输发生错误语言错误的发生。
序列化
语法
JSON.stringify(value[, replacer[, space]])
参数说明
value:
必需,要转换的 JavaScript 值(通常为对象或数组)。
replacer:
可选,用于转换结果的函数或数组。
如果 replacer 为函数,则 JSON.stringify 将调用该函数,并传入每个成员的键和值。使用返回值而不是原始值。如果此函数返回 undefined,则排除成员。根对象的键是一个空字符串:""。
如果 replacer 是一个数组,则仅转换该数组中具有键值的成员。成员的转换顺序与键在数组中的顺序一样。
space:
可选,文本添加缩进、空格和换行符,如果 space 是一个数字,则返回值文本在每个级别缩进指定数目的空格,如果 space 大于 10,则文本缩进 10 个空格。space 也可以使用非数字,如:\t。
返回值
返回包含 JSON 文本的字符串。
自定义 toJSON
对象里可以声明 toJson 函数来实现自定义 JSON
let hd = {
"title": "俊介历险记",
"url": "www.alrcly.com",
"teacher": {
"name": "萧俊介",
},
toJSON(){
return {
"title": this.url,
"name": this.teacher.name
};
}
}
console.log(JSON.stringify(hd));
反序列化
语法
JSON.parse(text[, reviver])
参数说明
text:
必需,一个有效的 JSON 字符串。
reviver:
可选,一个转换结果的函数,将为对象的每个成员调用此函数。
返回值
返回包含 JSON 文本的字符串。