开一个帖子记录一下学习js的时候一些知识点的代码实现,准备持续更新
1.浅拷贝的实现
浅拷贝只拷贝一层,深层次的引用共享内存地址(属性是基本类型拷贝属性的值,属性是引用类型拷贝属性的内存地址)
// 浅拷贝
let shallowClone = function(obj) {
const newobj = {}
for(let i in obj) {
if(obj.hasOwnProperty(i)) {
newobj[i] = obj[i]
}
}
return newobj
}
let student = {
name: 'zhou'
}
console.log(shallowClone(student));
2.深拷贝的实现
深拷贝开辟一个新的栈,两个对象的属性完全相同,但是对应不同的地址,修改一个对象的属性不会修改另一个对象的属性
// 深拷贝-使用Json.parse(Json.stringify)的方式
let student = {
name: 'zhou',
hobby:['打球','睡觉']
}
let newstudent = JSON.parse(JSON.stringify(student))
// 深拷贝-使用循环递归的方式
function deepClone(obj, hash = new WeakMap()) {
// 如果对象为 null 或 undefined,则原样返回(不需要克隆)
if (obj === null) return obj;
// 如果对象是 Date 类型,则创建一个具有相同值的新 Date 对象
if (obj instanceof Date) return new Date(obj);
// 如果对象是 RegExp 类型,则创建一个具有相同模式和标志的新 RegExp 对象
if (obj instanceof RegExp) return new RegExp(obj);
// 如果对象不是对象(例如原始类型或函数),则原样返回
if (typeof obj !== "object") return obj;
// 如果对象已经被克隆过,则返回已经克隆的版本以避免循环引用
if (hash.get(obj)) return hash.get(obj);
// 使用与原始对象相同的构造函数创建一个新对象
let cloneObj = new obj.constructor();
// 在 WeakMap 中存储原始对象到克隆对象的映射关系
hash.set(obj, cloneObj);
// 遍历原始对象的属性并递归深度克隆它们
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 递归克隆每个属性
cloneObj[key] = deepClone(obj[key], hash);
}
}
// 返回克隆的对象
return cloneObj;
}
hash
参数在这个函数中的作用是为了在深度克隆对象的过程中,避免处理循环引用导致的无限递归,并且有效地跟踪已经被克隆的对象。
3.柯里化函数
柯里化的目的在于避免频繁调用具有相同参数函数的同时,又能够轻松的重用
// 函数柯里化,避免频繁调用具有相同参数的函数
// 计算矩形的面积
let getArea = function(width,height) {
return width * height
}
// 如果我们需要计算的矩形的宽是固定的
let kery = function(width) {
return (height) => {
return width * height
}
}
let getkeryArea = kery(20)
console.log(getkeryArea(1));
console.log(getkeryArea(2));
console.log(getkeryArea(3));
4.利用闭包创建私有变量
// 利用闭包创建私有变量
// 创建一个计数器函数
function Count() {
// 私有变量
let count = 0
// 私有方法
function increatment() {
count++
}
return {
value() {
return count
},
increatCountment() {
increatment()
}
}
}
let counter = Count()
console.log(counter.value());
console.log(counter.increatCountment());
console.log(counter.value());
5.继承的实现
让 Child
的原型指向一个 Parent
的实例,实现了原型链继承。这种方式有一个缺点,两个实例使用的是同一个原型对象,内存空间是共享的
5.1 原型链继承
// 原型链继承
function Parent() {
this.name = 'tom'
}
function Child() {
this.age = 11
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
let child = new Child()
console.log(child);
console.log(child.name);
5.2 构造函数继承
过这种方式避免了引用类型属性的共享,但是无法继承父类原型上的方法。
// 构造函数继承
function Parent() {
this.name = 'tom'
}
function Child() {
Parent.call(this)
this.age = 20
}
let child = new Child()
console.log(child);
5.3 组合继承
// 组合式继承
function Parent() {
this.hobby = ['洗澡','睡觉']
}
Parent.prototype.pushHobby = function(item) {
this.hobby.push(item)
}
function Child() {
Parent.call(this)
this.age = 20
}
Child.prototype = new Parent()
let child = new Child()
console.log(child);
child.pushHobby('看书')
console.log(child);
5.4 原型式继承
Object.create
是 JavaScript 中用于创建一个新对象,并指定该对象的原型的方法。
// 原型式继承
let parent = {
name: 'tom',
age: 11,
hobby:['洗澡','睡觉']
}
let child1 = Object.create(parent)
let child2 = Object.create(parent)
child1.hobby.push('看书')
console.log(child2);
5.5 寄生式继承
寄生式继承在上面继承基础上进行优化,利用这个浅拷贝的能力再进行增强,添加一些方法。其优缺点也很明显,跟上面讲的原型式继承一样
// 寄生式继承
let parent = {
name: 'tom',
age: 11,
hobby:['洗澡','睡觉']
}
function jisheng(oldobj) {
let newobj = Object.create(parent)
newobj.pushhobby = function() {
this.hobby.push('看书')
}
return newobj
}
let child = jisheng(parent)
console.log(child.hobby);
child.pushhobby()
console.log(child.hobby);
5.6 寄生组合继承
寄生组合式继承,借助解决普通对象的继承问题的Object.create
方法,在前面几种继承方式的优缺点基础上进行改造,这也是所有继承方式里面相对最优的继承方式。
也避免了组合式继承中属性会出现多次的问题
// 寄生组合继承
function Parent() {
this.name = 'tom'
this.hobby = ['洗澡','睡觉']
}
Parent.prototype.pushHobby = function() {
this.hobby.push('看书')
}
function Child() {
Parent.call(this)
}
Child.prototype = Object.create(Parent.prototype)
let child = new Child();
console.log(child);
6.instanceof的实现
Object.getPrototypeOf()
是 JavaScript 中用于获取对象的原型的方法。
function myinstanceof(left,right) {
if(typeof left !== 'object') {
return false
}
let proto = Object.getPrototypeOf(left)
while(true) {
if(proto === null) {
return false
}
if(proto === right.prototype) {
return true
}
proto = Object.getPrototypeOf(proto)
}
}
let arr = new Array()
console.log(myinstanceof(arr,Array));
7.new的实现
function mynew(Fun,...args) {
// 创建一个对象
let obj = {}
// 对象的原型指向构造函数的原型对象
obj.__proto__ = Fun.prototype
// 改变this指向
let res = Fun.call(obj,args)
// 判断构造函数的返回值
if(typeof res === 'object') {
return res
}else {
return obj
}
}
8.实现一个ajax
// 先写一个例子
ajax({
type: 'post',
dataType: 'json',
data: {},
url: 'https://xxxx',
success: function(text,xml){//请求成功后的回调函数
console.log(text)
},
fail: function(status){////请求失败后的回调函数
console.log(status)
}
})
//封装一个ajax请求
function ajax(options) {
//创建XMLHttpRequest对象
const xhr = new XMLHttpRequest()
//初始化参数的内容
options = options || {}
options.type = (options.type || 'GET').toUpperCase()
options.dataType = options.dataType || 'json'
const params = options.data
//发送请求
if (options.type === 'GET') {
xhr.open('GET', options.url + '?' + params, true)
xhr.send(null)
} else if (options.type === 'POST') {
xhr.open('POST', options.url, true)
xhr.send(params)
//接收请求
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
let status = xhr.status
if (status >= 200 && status < 300) {
options.success && options.success(xhr.responseText, xhr.responseXML)
} else {
options.fail && options.fail(status)
}
}
}
}
9.实现一个axios
10.实现bind、call、apply
Function.prototype.Mybind = function(context) {
// 拿到函数实例
const self = this
// 拿到参数
const args = [...arguments].slice(1)
return function() {
return self.apply(context,args)
}
}
// 给一个例子
let obj = {
name:'zhou'
}
function greeting(element) {
return `${element},${this.name}`
}
let BindFnc = greeting.Mybind(obj,'hello')
let res = BindFnc()
console.log(res);
Function.prototype.call1 = function () {
// 初始化,获取传入的this对象和后续所有参数
const [context, ...args] = [...arguments];
// 在传入的对象上设置属性为待执行函数
context.fn = this;
// 执行函数
const res = context.fn(args);
// 删除属性
delete context.fn;
// 返回执行结果
return res;
};
Function.prototype.apply1 = function (context, args) {
// 给传入的对象添加属性,值为当前函数
context.fn = this;
// 判断第二个参数是否存在,不存在直接执行,否则拼接参数执行,并存储函数执行结果
let res = !args ? context.fn() : context.fn(...args)
// 删除新增属性
delete context.fn;
// 返回函数执行结果
return res;
}
const obj = {
value: 1,
};
function fn() {
console.log(this.value); // 1
return [...arguments]
}
console.log(fn.apply(obj, [1, 2])); // [1, 2]
11.实现函数缓存
实现函数缓存主要依靠闭包、柯里化、高阶函数
// 利用闭包、柯里化函数、高级函数实现函数缓存
// 设计一个函数用于计算矩形的宽高
function area(width,height) {
return width * height
}
// 根据以上函数实现函数缓存
function curyArea() {
const cache = {}
return function(...args) {
if(cache[args]) {
console.log('这里的数据从缓存中得到');
return cache[args]
}else {
let res = 1
args.forEach(item => {
res = res * item
})
cache[args] = res
return res
}
}
}
let getArea = curyArea()
let p1 = getArea(1,2,3,4)
let p2 = getArea(1,2,3,4)
console.log(p1);
console.log(p2);
12.实现防抖
<button class="click">点击触发防抖</button>
<script>
// 防抖的实现
function fangdou(fn,delay) {
let timer = null
return function() {
clearTimeout(timer)
timer = setTimeout(() => {
fn()
}, delay);
}
}
// 防抖实例
function hello() {
console.log('hello');
}
let fangdouHello = fangdou(hello,2000)
const fangdouButton = document.querySelector('.click')
fangdouButton.addEventListener('click',fangdouHello)
</script>
13.实现节流
//节流的时间戳写法
<button class="click">点击触发节流</button>
<script>
//节流的事件戳写法
function throttled1(fn,delay) {
let oldtime = new Date()
return function() {
let newtime = new Date()
if(newtime-oldtime >= delay) {
fn()
oldtime = new Date()
}
}
}
function log() {
console.log('触发节流');
}
const throttledLog = throttled1(log,1000)
const button = document.querySelector('.click')
button.addEventListener('click',throttledLog)
</script>
//节流的定时器写法
<button class="click">点击触发节流</button>
<script>
//节流的定时器写法
function throttled1(fn,delay) {
let timer = null
return function() {
if(!timer) {
timer = setTimeout(()=>{
fn()
timer = null
},delay)
}
}
}
function log() {
console.log('触发节流');
}
const throttledLog = throttled1(log,1000)
const button = document.querySelector('.click')
button.addEventListener('click',throttledLog)
</script>