对象添加删除检测
let Object ={}
//添加
Object.name=8
Object['age']=3
//删除
delete Object.name
//检测本身
console.log(Object.hasOwnProperty('name')); //布尔值
//数组对象检测本身加原型
console.log('constructor' in Object); //布尔值
计算属性给对象定义键名
const lessons = [
{
title:'任1务',
cons:'cs1s'
},
{
title:'任2务',
cons:'css2',
},
{
title:'任3务',
cons:'cs4s'
}
]
let obj = {}
let res = lessons.map((a,b)=>obj[`${a.cons}-${b}`]=a)
console.log(obj);
利用系统API获取对象属性与值
const hd = {
name: "后盾人",
age: 10
};
console.log(Object.keys(hd)); //["name", "age"]
console.log(Object.values(hd)); //["后盾人", 10]
console.table(Object.entries(hd)); //[["name","后盾人"],["age",10]]
遍历对象
for/in 用于遍历对象属性
const hd = {
name: "后盾人",
age: 10
};
for (let key in hd) {
console.log(key, hd[key]);
} // 后盾人 10
for/of 用于遍历迭代对象,不能直接操作对象。但Object
对象的keys/
方法返回的是迭代对象。通常用作遍历数组
const hd = {
name: "后盾人",
age: 10
};
for (const [key, value] of Object.entries(hd)) {
console.log(key, value); //name 后盾人
} //age 10
拷贝
对象拷贝:对象赋值时复制的内存地址,所以一个对象的改变直接影响另一个
let obj = {
name: '后盾人',
user: {
name: 'hdcms'
}
}
let a = obj;
let b = obj;
a.name = 'lisi';
console.log(b.name); //lisi
浅拷贝:改变新数据,不改变原数据,个人理解为只是针对一层数据的拷贝有效,多层嵌套结构的数据则无效。
- 使用
for/in
执行对象拷贝
let obj = {name: "后盾人"};
let hd = {};
for (const key in obj) {
hd[key] = obj[key];
}
hd.name = "hdcms";
console.log(hd);
console.log(obj);
Object.assign
函数可简单的实现浅拷贝,它是将两个对象的属性叠加后面对象属性会覆盖前面对象同名属性。- 使用es6扩展符也可以实现浅拷贝
**深拷贝:**改变新数据,不改变原数据
- 使用
for/in
执行深拷贝
let obj = {
name: '后盾人',
user: {
name: 'hdcms'
},
use:[4]
}
function ob(obj){
let res = ob instanceof Array ?[]:{}
for(const [k,v] of Object.entries(obj)){
typeof( res[k] = v) ==='Object'?ob(v):res[k] = v
}
return res
}
console.log(ob(obj));
JSON.parse(JSON.stringify())
函数可实现深拷贝
属性特征
JS中可以对属性的访问特性进行控制
查看特征:使用 Object.getOwnPropertyDescriptor
查看对象属性的描述
const user = {
name: "向军",
age: 18
};
// 查看所有属性特征
let desc = Object.getOwnPropertyDescriptors(user);
//查看一个属性特征
let desc = Object.getOwnPropertyDescriptor(user, "name"`)
属性包括以下四种特性
特性 | 说明 | 默认值 |
---|---|---|
configurable | 能否使用delete、能否需改属性特性、或能否修改访问器属性 | true |
enumerable | 对象属性是否可通过for-in循环,或Object.keys() 读取 | true |
writable | 对象属性是否可修改 | true |
value | 对象属性的默认值 | undefined |
**设置特征:**使用Object.defineProperty
方法修改属性特性,通过下面的设置属性name将不能被遍历、删除、修改。
"use strict";
const user = {
name: "向军"
};
Object.defineProperty(user, "name", {
value: "后盾人",
writable: false,
enumerable: false,
configurable: false
});
禁止添加:Object.preventExtensions
禁止向对象添加属性
const user = {
name: "向军"
};
Object.preventExtensions(user);
user.age = 18; //Error
Object.isExtensible
判断是否能向对象中添加属性
const user = {
name: "向军"
};
Object.preventExtensions(user);
console.log(Object.isExtensible(user)); //false
封闭对象:Object.seal()
方法封闭一个对象,阻止添加删除新属性并将所有现有属性标记为 `configurable: false,可以修改属性
const user = {
name: "后盾人",
age: 18
};
Object.seal(user);
delete user.name; //Error
Object.isSealed
如果对象是密封的则返回 true
,属性都具有 configurable: false
。
"use strict";
const user = {
name: "向军"
};
Object.seal(user);
console.log(Object.isSealed(user)); //true
**冻结对象:**Object.freeze冻结对象后不允许添加、删除、修改属性,writable、configurable都标记为
false
const user = {
name: "向军"
};
Object.freeze(user);
user.name = "后盾人"; //Error
Object.isFrozen()
方法判断一个对象是否被冻结
const user = {
name: "向军"
};
Object.freeze(user);
console.log(Object.isFrozen(user)); //true
注意:封闭对象可以修改对象值,冻结不可修改
访问器
**gerter/setter:**getter方法用于获得属性值,setter方法用于设置属性,这是JS提供的存取器特性即使用函数来管理属性。
const user = {
data: { name: '后盾人', age: null },
//给属性赋值
set age(value) {
if (typeof value != "number" || value > 100 || value < 10) {
throw new Error("年龄格式错误");
}
this.data.age = value;
},
//获取值
get age() {
return `年龄是: ${this.data.age}`;
}
};
user.age = 99; //走set age
console.log(user.age); //走get age
下面通过设置站网站名称与网址体验getter/setter
批量设置属性的使用
const web = {
name: "后盾人", //后盾
url: "houdunren.com", //hdcms
get site() {
return `${this.name} ${this.url}`;
},
set site(value) {
[this.name, this.url] = value.split(",");
}
};
web.site = "后盾,hdcms";
console.log(web.site); //后盾hdcms
访问器描述符:使用 defineProperty
可以模拟定义私有属性,从而使用面向对象的抽象特性。
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 = "向军1";
console.log(hd.name);
上面的代码也可以使用语法糖 class
定义
const DATA = Symbol();
class User {
constructor(name, age) {
this[DATA] = { name, age };
}
get name() {
return this[DATA].name;
}
set name(value) {
if (value.trim() == "") throw new Error("无效的用户名");
this[DATA].name = value;
}
get age() {
return this[DATA].name;
}
set age(value) {
if (value.trim() == "") throw new Error("无效的用户名");
this[DATA].name = value;
}
}
let hd = new User("后盾人", 33);
console.log(hd.name);
hd.name = "向军1";
console.log(hd.name);
console.log(hd);
代理拦截
**代理对象:**代理(拦截器)是对象的访问控制,setter/getter
是对单个对象属性的控制,而代理是对整个对象的控制。严格模式set必须返回布尔值
"use strict";
const hd = { name: "后盾人" };
const proxy = new Proxy(hd, {
get(obj, property) { //obj是hd对象,property随proxy.age = 10;属性名age改变而改变
return obj[property];
},
set(obj, property, value) { //value随proxy.age = 10;值改变而改变
obj[property] = value;
return true;
}
});
proxy.age = 10;
console.log(hd);
**代理函数:**如果代理以函数方式执行时,会执行代理中定义 apply
方法。
下面使用 apply
计算函数执行时间
function factorial(num) {
return num == 1 ? 1 : num * factorial(num - 1);
}
let proxy = new Proxy(factorial, {
apply(func, obj, args) { //参数说明:函数,上下文对象,参数
console.time("run");
func.apply(obj, args);
console.timeEnd("run");
}
});
proxy.apply(this, [1000]);
**截取字符串:**下例中对数组进行代理,用于截取标题操作
const lessons = [
{
title: "媒体查询响应式布局",
category: "css"
},
{
title: "FLEX 弹性盒模型",
category: "css"
},
{
title: "MYSQL多表查询随意操作",
category: "mysql"
}
];
let proxy = new Proxy(lessons,{
get(array,key){
const title= array[key].title
array[key].title= title.length>5?title.substr(0,5):title //改变原数组
return array[key]
}
})
console.log(proxy[0]);
数据双向绑定
下面通过代理实现vue
等前端框架的数据绑定特性特性。
<body>
<input type="text" v-model="title" />
<input type="text" v-model="title" />
<div v-bind="title"></div>
</body>
<script>
//构造函数
function View() {
//设置代理拦截
let proxy = new Proxy(
{},
{
get(obj, property) {},
set(obj, property, value) {
obj[property] = value;
document
.querySelectorAll(
`[v-model="${property}"],[v-bind="${property}"]`
)
.forEach(el => {
el.innerHTML = value;
el.value = value;
});
}
}
);
//初始化绑定元素事件
this.run = function() {
const els = document.querySelectorAll("[v-model]");
els.forEach(item => {
item.addEventListener("keyup", function() {
proxy[this.getAttribute("v-model")] = this.value;
}); //getAttribute("v-model")通过class名获取class值
});
};
}
let view = new View().run();
表单验证
<style>
body {
padding: 50px;
background: #34495e;
}
input {
border: solid 10px #ddd;
height: 30px;
}
.error {
border: solid 10px red;
}
</style>
<body>
<input type="text" validate rule="max:12,min:3" />
<input type="text" validate rule="max:3,isNumber" />
</body>
<script>
"use strict";
//验证处理类
class Validate {
max(value, len) {
return value.length <= len;
}
min(value, len) {
return value.length >= len;
}
isNumber(value) {
return /^\d+$/.test(value);
}
}
//代理工厂
function makeProxy(target) {
return new Proxy(target, {
get(target, key) {
return target[key];
},
set(target, key, el) {
const rule = el.getAttribute("rule"); //获取class值
const validate = new Validate(); //创建构造函数
let state = rule.split(",").every(rule => { //max:12,min:3
const info = rule.split(":"); ['max','12 ']'
return validate[info[0]](el.value, info[1]); //max(输入框值,12)
});
el.classList[state ? "remove":"add"]("error"); //给标签添加class
return true;
}
});
}
const nodes = makeProxy(document.querySelectorAll("[validate]"));//把需要验证的元素传给函数
//给元素添加事件
nodes.forEach((item, i) => {
item.addEventListener("keyup", function() {
//主要触发set
nodes[i] = this;
});
});
</script>
json
序列化是将 json
转换为字符串,一般用来向其他语言传输使用。
根据第二个参数指定保存的属性
第三个是参数用来控制TAB数量,如果字符串则为前导字符。
let hd = {
title: "后盾人",
url: "houdunren.com",
teacher: {
name: "向军大叔",
}
}
console.log(JSON.stringify(hd, null, 4));//全部转换保存
console.log(JSON.stringify(hd, ['title', 'url'])); //只保存title,url
为数据添加 toJSON
方法来自定义返回格式
let hd = {
"title": "后盾人",
"url": "houdunren.com",
"teacher": {
"name": "向军大叔",
},
"toJSON": function () {
return {
"title": this.url,
"name": this.teacher.name
};
}
}
console.log(JSON.stringify(hd)); //{"title":"houdunren.com","name":"向军大叔"}
反序列化
使用 JSON.parse
将字符串 json
解析成对象,使用第二个参数函数来对返回的数据二次处理
let hd = {
title: "后盾人", //[推荐]后盾人
url: "houdunren.com",
teacher: {
name: "向军大叔"
}
};
let jsonStr = JSON.stringify(hd);
console.log(
JSON.parse(jsonStr, (key, value) => { //keys
if (key == "title") {
return `[推荐] ${value}`;
}
return value;
})
);
turn {
“title”: this.url,
“name”: this.teacher.name
};
}
}
console.log(JSON.stringify(hd)); //{“title”:“houdunren.com”,“name”:“向军大叔”}
## 反序列化
使用 `JSON.parse` 将字符串 `json` 解析成对象,使用第二个参数函数来对返回的数据二次处理
```js
let hd = {
title: "后盾人", //[推荐]后盾人
url: "houdunren.com",
teacher: {
name: "向军大叔"
}
};
let jsonStr = JSON.stringify(hd);
console.log(
JSON.parse(jsonStr, (key, value) => { //keys
if (key == "title") {
return `[推荐] ${value}`;
}
return value;
})
);