js算法整理

117 阅读6分钟

开一个帖子记录一下学习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>