Object.defineProperty方法有什么作用,如何实现数据劫持?
object.defineProperty方法作用:
将对象属性绑定到另一个对象上,数据劫持-监听对象数据变化
实现数据变化自动更新界面
语法
Object.defineProperty(obj, prop, descriptor)
参数
obj : 要定义属性的对象。
prop:要定义或修改的属性的名称或 Symbol 。
descriptor:要定义或修改的属性描述符对象。
描述符对象的数据属性:
1.configurable:表示能否通过delete删除属性从而重新定义属性,
能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。2.enumerable:表示能否通过for in循环访问属性,默认值为false
3.writable:表示能否修改属性的值。默认值为false。
4.value:包含这个属性的数据值。默认值为undefined。
返回值
被传递给函数的对象。
ES6新增的Proxy代理对象有何作用?与Object.defineProperty的区别?
区别1:defineproperty只能监听某个属性不能全对象监听,proxy不用设置具体属性
区别2:proxy不需要借助外部的value,也有单独相配的对象即Reflect,get里面有target,key,reciver三个值
区别3:不会去污染原对象,proxy会返回一个新的代理对象,不会对原来的target进行改动
如何判断属性或方法是否属于对象自身的方法?
Object.prototype.hasOwnProperty()方法作用:通过原型链继承该方法,判断属性是否是对象自身的。
什么是深拷贝、浅拷贝,分别如何实现?
浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址(只复制一层对象)
深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址(复制得到完全不一样的对象)
实现:
1. JSON.parse(JSON.stringify(obj))
缺点: 数据类型是Function或数据值为undefined无法拷贝
2. Object.assign(obj)或展开运算符{…obj}
缺点:只能拷贝一层,如果属性值是对象,无法拷贝
3. 递归 cloneDeep()(深拷贝)
什么是闭包,形成条件及作用?
闭包概念:
1. 有一个A函数,在A函数内部返回一个B函数
2. 在B函数内部,访问A函数内部私有变量
3. 在A函数外部,有一个变量引用返回的B函数
1. 有函数嵌套,内层函数被返回
2. 内层函数访问外层函数私有变量
3. 返回的内层函数被引用
形如此:
闭包形成条件:
1. 有一个A函数,在A函数内部返回一个B函数
2. 在B函数内部,访问A函数内部私有变量
3. 在A函数外部,有一个变量引用返回的B函数
闭包特点/作用:
1. 作用域空间不销毁
优点: 因为不销毁,变量不会销毁,增加了变量的生命周期
缺点: 因为不销毁,会一直占用内存,多了以后就会导致内存溢出2. 可以利用闭包,在一个函数外部,访问函数内部的变量
优点: 可以在函数外部访问内部数据
缺点: 必须要时刻保持引用,导致函数执行栈不被销毁3. 保护私有变量
优点: 可以把一些变量放在函数里面,不会污染全局
缺点: 要利用闭包函数才能访问,不是很方便
实现一个转换函数,可以将多形参的函数f(a,b,c)转换成f(a)(b)(c)形式
function sum(a,b){
return a + b
}
// let s = sum(10,20)
// console.log(s)
/*
sum进行柯里化转换 -> f(10)(20) <=> sum(10,20)
let f = curryingSum(sum)
*/
function curryingSum(sum){
return function(a){
return function(b){
return sum(a,b)
}
}
}
let f = curryingSum(sum)
// let s = sum(10,20)
let s = f(10)(20)
console.log('s ',s)
什么是继承,es5实现继承的方式有那些?
继承 是类与类之间的关系,子类继承父类子类就拥有父类的属性和方法
继承的方式:
- 构造函数继承,继承父类构造函数属性和方法
- 原型拷贝继承,继承父类原型对象上公共的属性和方法
- 原型继承,改变原型指向
- 组合继承(原型继承+构造继承)
- es6是class类继承
什么是设计模式?单例模式的作用?
- 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。
- 单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。
写代码实现发布/订阅模式
发布/订阅模式
发布者 -> 消息事件中心 -> 订阅者
不同类型 -> 订阅者
按钮点击事件
发布者(点击按钮的人)
|
事件处理中心
|
订阅者(按钮)
三部份组成 :
发布者 : 负责发布消息事件中心
订阅者 : 订阅事件处理中心消息
事件处理中心 :
处理不同类型的消息,发送给不同的订阅者
{
消息类型: [消息事件1,消息事件2]
click: [执行订阅的消息1,执行订阅的消息2],
mousemove:[...]
}
订阅消息
向事件对象添加一条数据
发布消息
触发指定类型消息
购书
小明去书店购书,书店没有三国演义本书,小明订阅这本书,当书到达时通知我
小红啊去书店购书,书店没有西游记本书,小明订阅这本书,当书到达时通知我
订阅者 事件处理中心 发布者(出版商,书店)
购书buybook
通知订阅者书到达
<body>
<button class="btn1">订阅者1</button>
<button class="btn2">订阅者2</button>
<script>
function test1() {
const btn1 = document.querySelector('.btn1')
const btn2 = document.querySelector('.btn2')
btn1.addEventListener('click', function () {
console.log('执行订阅的消息1')
})
btn2.addEventListener('click', function () {
console.log('执行订阅的消息2')
})
}
// 事件处理中心
class EventCenter{
constructor(){
//{type:[执行消息处理函数1,执行消息处理函数2...]}
this.subs = {}
}
//订阅消息
addEvent(type,even){
// if(!this.subs[type]){
// this.subs[type] = []
// }
this.subs[type] = this.subs[type] || []
this.subs[type].push(even)
}
// 发布消息
emitEvent(type){
if(this.subs[type]){
this.subs[type].forEach(even=>{
even()
})
}
}
}
// let eventCenter = new EventCenter()
// // 订阅类型为click的事件,
// eventCenter.addEvent('click',function(){
// console.log('执行消息处理函数1');
// })
// eventCenter.addEvent('click',function(){
// console.log('执行消息处理函数2');
// })
// // 发布消息
// eventCenter.emitEvent('click')
let eventCenter = new EventCenter()
eventCenter.addEvent('buybook',function(){
console.log('你订阅的三国演义书已到');
})
eventCenter.addEvent('buybook',function(){
console.log('你订阅的西游记书已到');
})
eventCenter.emitEvent('buybook')
</script>
</body>
什么是垃圾回收机制?垃圾回收算法有哪些,如何实现回收?
垃圾回收
回收释放不在使用的"对象"占用的内存空间
内存管理
分配内存
读写内存
回收释放内存
回收释放内存
c语言中 malloc()和free()作用回收释放内存的方法
javascript语言中由"垃圾回收"程序自动处理回收释放内存
垃圾回收机制
垃圾回收算法
1. 引用计数垃圾收集
判断当前对象有无其它对象引用,如果没有,垃圾回收机制释放它占用的内存空间
(垃圾回收程序定时遍历内存中所有对象)
缺点:
不能回收相互引用的对象
2. 标记清法
垃圾回收器将定期从根开始,找所有从根开始引用的对象,如果没有引用,否被回收
单页应用与多页应用区别?如何实现单页应用路由?
单页应用
单页应用又称 SPA(Single Page Application)指的是使用单个 HTML 完成多个页面切换和功能的应用。这些应用只有一个 html 文件作为入口,一开始只需加载一次 js,css 等相关资源。使用 js 完成页面的布局和渲染。页面展示和功能室根据路由完成的。单页应用跳转,就是切换相关组件,仅刷新局部资源。
多页应用
多页应用又称 MPA(Multi Page Application)指有多个独立的页面的应用,每个页面必须重复加载 js,css 等相关资源。多页应用跳转,需要整页资源刷新。
实现单页应用路由:
- hash方式 :location.hash+hashchange事件
- history api:history.pushState()+popState事件
方式实现路由跳转
首先创建一个Router构造函数,初始化当前的url和一个routes对象。
定义了三个方法:
- route方法 — 该方法用于在实例化router对象之后,注册路由,对于不同的路由,执行不同的回调函数 。
- refresh方法 — 在路由切换时,执行该方法刷新页面。
- init方法 — 在注册完路由之后,执行该方法,该方法主要注册了两个事件,主要是添加hashchange监听事件。
history方式路由实现
html5中的history api包括两个方法history.pushState()和history.replaceState(),包含一个事件history.onpopstate。
1、history.pushState(stateObj, title, url) —向历史栈中写入数据
stateObj为一个状态对象,这个对象可以被popstate事件读取到,也可以在history对象中获取。
title为标题,但是浏览器目前还没能实现,由于其本身是一个字符串,所以我们使用‘’来代替即可。
url为路径。一般设定为相对的url,绝对路径需要保证同源。
pushState方法不会触发页面刷新,只是导致history对象发生变化,地址栏会有反应。
2、history.replaceState(stateObj, title, url)
参数和pushState一样
区别:替换修改浏览历史中当前纪录
3、popstate事件
定义:每当同一个文档的浏览历史(即history对象)出现变化时,就会触发popstate事件。
只调用pushState方法或replaceState方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者调用back、forward、go方法时才会触发。
使用:为popstate事件指定回调函数。这个回调函数的参数是一个event事件对象,它的state属性指向pushState和replaceState方法为当前URL所提供的状态对象(stateObj)