目录
说明一下:高级部分第一天和第二天的内容我直接上传了md文件,感兴趣的可以去资源里面下载,另外我有时间会把前两天的笔记补上的
一、工厂模式
工厂模式:批量创建对象出来(多例模式)
普通的工厂函数:
function person(name, age){
const obj = new Object()
obj.name = name
obj.age = age
return obj
}
const p1 = person('王成', 18)
const p2 = person('陈洁', 20)
我们可以把工厂模式升级为——构造函数的形式
function Person(name, age){
this.name = name
this.age = age
}
const p1 = new Person('王成', 18)
const p2 = new Person('陈洁', 20)
二、单例模式
单例模式:一个类一生中只能有一个子类
单例模式核心代码,你可以给Person构造函数添加一个静态方法
function Person(name, age){
this.name = name
this.age = age
}
Person.getInstance = function(name, age){
// 判断有没有instance这个属性,如果没有这个属性,
//就把实例化对象赋值给它,如果有就直接返回
if(!this.instance){
this.instance = new Person(name, age)
}
return this.instance
}
const p1 = Person.getInstance('王成', 18)
console.log(p1)
const p2 = Person.getInstance('崔傑瑜', 20)
console.log(p2)
console.log(p1 === p2)
可以看到,两个实例化对象都是一样的,这就是单例模式
知识点补充:
关于面向对象里面的this:
如果是实例化对象调用的函数,那么构造函数里面的this指向的就是实例化对象
如果是把构造函数当成普通函数去调用,那么里面的this指向的是window
如果是通过构造函数调用的静态方法,那么静态方法里面的this指向的是构造函数
上面是用构造函数的方法写单例模式,现在我们用class的方式写单例模式
class Person{
constructor(){}
init(name, age){
this.name = name
this.age = age
}
// 通过一个关键字static把方法变成静态方法
static getInstance(name, age){
if(!this.instance){
this.instance = new Person(name, age)
}
this.instance.init(name, age)
return this.instance
}
}
const p1 = Person.getInstance('王成', 18)
console.log(p1)
const p2 = Person.getInstance('陈洁', 28)
console.log(p1 === p2) // true
console.log(p2)
可以看到,这里逐步打印p1和p2,会得到不一样的结果,这是因为在静态方法里面调用了init()函数,但若是把console.log(p1)放到console.log(p2)之后,打印的结果都是与p2的结果一样的
三、垃圾回收机制
1.原理
js的垃圾回收,是执行环境在运行代码时,同时进行管理的,这为开发者减轻很大的负担.
通过自动内存管理实现内存的分配和闲置资源的回收.
基本思路是这样的:
确定那个变量不再使用,然后释放其占用的内存,这个回收过程是周期行的,每隔一段时间就会执行一次
当运行一个函数时,栈或堆就会分配空间保存相应的值.当变量使用完成后,就不再被需要,它占用的内存就要被释放,供后面的变量使用.但很多时候变量的使用与否不会这么简单.垃圾回收程序必须标记那个变量还会使用,那个变量不再使用,便于回收
在浏览器发展历史上,有两种标记策略: 标记清理和引用计数.
2.标记清理
垃圾回收程序运行时,会标记内存中存储的所有变量(标记方法有多种).然后他会将所有在作用域的变量,以及被变量引用的变量的标记去掉.在此之后,被标记的变量就等待被删了.随后垃圾回收机制会运行一次,清理标记的值且收回被占用的内存
3.引用计数
它的思路是对每个值都记录它被引用的次数,但是弊端太多已经被放弃
如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露。
四、数据劫持
1.浅拷贝
浅拷贝:对象的复制只是复制对象的引用,可以改变原始数据的值
常见的浅拷贝的方式:=、...、for in 遍历、
2.深拷贝
深拷贝:彻底复制一个对象,而不是简单的拷贝对象的引用,拷贝后不能修改原始数据的值
最常用的方式是:JSON.parse(JSON.stringify())
注意:单层数据使用 Object.assign() 和ES6的 ... 为深拷贝,多层数据为浅拷贝
3.Object.defineProperty()
数据劫持:在操作对象的时候,可以设置一些限定
这里的限定指的是:对象里面的属性不允许你遍历、对象里面的属性不允许你修改、对象里面的属性不允删除等
配置项:
value: 该属性对应的值
configurable 该属性是否能被删除
writable: 该属性是否可被重写, 默认是 false
enumerable: 该属性是否可被枚举, 默认是 false
get: 是一个函数, 叫做 getter 获取器, 可以来决定该属性的值:get 函数的返回值, 就是当前这个属性的值
set: 是一个函数, 叫做 setter 设置器, 当你需要修改该属性的值的时候, 会触发该函数
注意: get 不能和 value 和 writable 一起使用, 会报错
Object.defineProperty(参数1,参数2,参数3)
参数1表示你要劫持那个对象,参数2表示要对那个属性做限定,参数3表示的是配置项
// 被劫持的对象
const obj = {}
// 声明一个对象用来给劫持的对象设置值
const objValue = {
name: '蔡徐坤',
age:18
}
// 注意点:如果对象里面没有设置这个属性,那么通过劫持属性就相当于设置了一个属性,它的值是通过value配置项设置的,并且优先级更高
Object.defineProperty(obj, 'name', {
value: '王成',
configurable: false,
writable: false,
enumerable: false
})
//但我们大多数时候倾向于用get和set
Object.defineProperty(obj, 'name', {
get(){
return objValue.name
},
set(value){
document.querySelector('h1').innerHTML = `hello,我是<span>${value}</span>,曾经江湖中的传说人物,今天晚上8点钟在直播间不见不散!`
objValue.name = value
}
})
4.数据劫持封装
上述的数据劫持每次只能劫持一个属性,当有多个属性的时候比较麻烦,所以我们把他进行了封装
function property(setData, callback){
// 声明一个被劫持的对象,一般把这个对象称之为目标对象
const target = {}
for(let key in setData){
Object.defineProperty(target, key, {
get(){
return setData[key]
},
set(value){
setData[key] = value
callback && callback()
}
})
}
return target
}
但注意:这种数据劫持的封装只供参考,因为js给我们提供了更好了方法
5.Object.defineProperties()
数据劫持的升级方法,可以劫持多个属性
// 专门用来设置数据的
const setData = {
name: '张三',
age: 18
}
// 目标对象,被劫持的对象
const target = {}
Object.defineProperties(target, {
'name': {
get(){
return setData.name
},
set(value){
setData.name = value
}
},
'age': {
get(){
return setData.age
},
set(value){
setData.age = value
}
}
})
6.数据代理 proxy()
proxy 数据代理,为ES6提供的,就是数据劫持的封装版本的标准语法。不需要你自己去封装了,直接变成标准语法结构,内部你也不需要关注(直接用)
const setData = {name: '王成', age: 18}
const result = new Proxy(setData, {
// target目标对象(被劫持的对象)
// property表示被劫持对象要设置的属性(跟咱们封装数据劫持时遍历的key是一样的)
get(target, property){
return target[property]
},
// value表示你要修改目标对象的属性值
set(target, property, value){
target[property] = value
}
})