js常见代码的手写实现

js常见代码的手写实现

发表于 2024-08-20
更新于 2024-08-24
阅读时长:15分钟
阅读量:4
AI 总结
|

1、判断数据的类型

function typeOf(obj) {
    return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()
}

分析:

console.log(toString.call(str));  // [object String]
//这里显示绑定优先级高于隐式绑定

Object类型的每个实例都有toString()方法,返回对象的字符串表示

Object.prototype.toString最终都会返回:[Object, 具体数据类型],这是我们判断数据类型的依据

//为什么要使用call?
Object.prototype.toString();  //输出:[object Object]

这是因为Object.prototype本身也是一个对象,使用call可以让显示绑定到传入的参数执行

Object.prototype.toString.call(str);  //输出:[object String]

2、实现instanceof

说明:

1、instanceof的特点:instanceof 只能用来判断两个对象是否属于实例关系,返回布尔值

2、Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)

递归实现

//obj实例
//fn构造函数
function instanceof1(obj, fn) {
    //obj必须是一个对象
    if(obj === null || typeOf obj !== 'object') {
        return false
    }
    let proto = obj.__proto__
    if(proto === fn.prototype) { //隐式原型指向显示原型,说明存在原型链上
        return true
    } else if (proto === null) { //到了尽头
        return false
    } else {
        return instanceof1(proto, fn)
    }
}

//测试代码
function People() {}

const p1 = new People()
const obj = new Object()

console.log(instanceof1(p1, People));  // true
console.log(instanceof1(obj, People));  // false
console.log(instanceof1(obj, Object));  // true

3、实现继承的方案

继承的方案有:

  • 原型链继承
  • 借用构造函数实现继承
  • 寄生式继承
  • 组合继承
  • 寄生式组合继承
  • class实现继承

原型链继承

原理:改写构造函数的显示原型对象

function People() {
  this.name = 'linming'
}
People.prototype.running = function () {
  console.log(this.name + "在跑步")
}

//Student想要继承People
function Student() {}
Student.prototype = new People() //改写Student的原型

const stu = new Student()
console.log(stu.name) //linming
stu.running() //linming在跑步

问题1:原型中包含的引用类型属性将被所有实例共享;

问题2:子类在实例化的时候不能给父类构造函数传参

借用构造函数实现继承

function People(name, age, address) {
    this.name = name
    this.age = age
    this.address = address
}
People.prototype.running = function() {
    console.log(this.name + "在跑步")
}

function Student(name, age, address, sno) {
    // this = 所创建的实例
    People.call(this, name, age, address)
    this.sno = sno
}
//改写Student的原型对象
Student.prototype = new People()

const stu2 = new Student('小明', 223, '广州', 201803041) 
console.log(stu2)
//输出:People { name: '小明', age: 223, address: '广州', sno: 201803041 }

借用构造函数实现继承解决了原型链继承的 2 个问题:引用类型共享问题以及传参问题。但是由于方法必须定义在构造函数中,所以会导致每次创建子类实例都会创建一遍方法

寄生式继承

const person = {
    age: 23
}

function CreateSudent(name) {
    let stu = Object.create(person) //stu等于对象的隐式原型
    stu.name = name
    stu.sayHello = function() {
        console.log(this.name+ "hello~")
    }
    return stu
}

const stu = new CreateSudent('linming')
console.log(stu)

**Object.create()**方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

寄生组合式继承

function Person(name, age, firedns) {
    this.name = name
    this.age = age
    this.firedns = firedns
}

Person.prototype.running = function() {
    console.log("running~")
}
Person.prototype.eating = function() { 
    console.log("eating~")
}

function Student(name, age, friends, sno, score) {
    Person.call(this, name, age, friends)
    this.sno = sno
    this.score = score
}

// 改写Student的原型对象
// 创建一个对象,该对象的隐式原型指向Person的原型对象,赋值给Student的原型
Student.prototype = Object.create(Person.prototype)
//解决Student的原型中的constructor指向Person的问题
Object.defineProperty(Student.prototype, "constructor", {
    enumerable: false,
    configurable: true,
    writable: true,
    value: Student
})

Student.prototype.studying = function() {
    console.log("studying~");
}

var stu = new Student("why", 18, ['kobe'], 111, 100)
console.log(stu) 
stu.studying() //studying~
stu.running() //running~
stu.eating() //eating~

class实现继承

class People {
    constructor(name) {
        this.name = name
    }
    getName() {
        return this.name
    }
}
class Student extends People {
    constructor(name, age) {
        super(name)
        
    }
}

4、vue响应式原理

proxy实现

class Depend {
  constructor() {
    // 收集依赖
    this.reactiveFns = new Set()
  }
  //添加依赖
  addDepend() {
    if(activeEffectFn) {
      this.reactiveFns.add(activeEffectFn)
    }
  }
  //执行依赖
  notify() {
    this.reactiveFns.forEach(item => {
      item()
    })
  }
}
//封装获取dep的函数
const weakMap = new WeakMap()
function getDep(obj, key) {
  let map = weakMap.get(obj)
  if(!map) {
    map = new Map()
    weakMap.set(obj, map)
  }
  let dep = map.get(key)
  if(!dep) {
    dep = new Depend()
    map.set(key, dep)
  }
  return dep
}

let activeEffectFn = null
const watchFn = function(fn) {
  activeEffectFn = fn
  fn() //调用时触发get
  activeEffectFn = null
}

const reactive = function (obj) {
  return new Proxy(obj, {
    get(target, key) {
      let dep = getDep(target, key)
      dep.addDepend() //收集依赖
      return target[key]
    },

    set(target, key, newValue) {
      let dep = getDep(target, key)
      target[key] = newValue
      dep.notify()
    }
  })
}

Object.defineProperty实现方式

const reactive = function (obj) {
  Object.keys(obj).forEach(key => {
    let dep = getDep(obj, key)
    let value = obj[key]
    Object.defineProperty(obj, key, {
      get() {
        dep.addDepend()
        return value
      },
      set(newValue) {
        value = newValue
        dep.notify()
      }
    })
  })
  return obj
}

5、手写Promise

const Promise_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECT = 'reject'

class MyPromise {
    constructor(executor) {
        this.status = Promise_STATUS_PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilled = []
        this.onRejected = []
        const resolve = (value) => {
            queueMicrotask(() => {
                if(this.status === Promise_STATUS_PENDING) {
                    this.status = PROMISE_STATUS_FULFILLED
                    this.value = value
                    this.onFulfilled.forEach(item => {
                        item(this.value)
                    })
                }
            })
        }
        const reject = (reason) => {
            queueMicrotask(() => {
                if(this.status === Promise_STATUS_PENDING) {
                    this.status = PROMISE_STATUS_REJECT
                    this.reason = reason
                    this.onRejected.forEach(item => {
                        item(this.reason)
                    })
                }
            })
        }
        executor(resolve, reject)
    }
    // then方法
    then(onFulfilled, onRejected) {
        this.onFulfilled.push(onFulfilled)
        this.onRejected.push(onRejected)
    }

    // 类方法——resolve
    static resolve(value) {
        return new MyPromise(function(resolve) {
            resolve(value)
        })
    }

    // 类方法——reject
    static reject(reason) {
        return new MyPromise((resolve, reject) => {
            reject(reason)
        })
    }

    // 类方法——all
    static all(promises) {
        return new MyPromise((resolve, reject) => {
            const values = []
            promises.forEach(promise => {
                promise.then(res => {
                    values.push(res)
                    if(values.length === promises.length) {
                        resolve(values)
                    }
                }, err => {
                    reject(err)
                })
            })
        })
    }

    // 类方法-race
    static race(promises) {
        return new MyPromise((resolve, reject) => {
            promises.forEach(promise => {
                promise.then(res => {
                    resolve(res)
                }, err => {
                    reject(err)
                })
            })
        })
    }
}

6、防抖 / 节流

防抖

function debounce(fn, delay) {
    let timer = null
    return function(...args) {
        if(timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, delay)
    }
}

    const input = document.querySelector('input')
    let counter = 0
    let fn = function(event) {
      console.log(`第${++counter}次触发`, this, event);
    }

    let newFn = debounce(fn, 1000)
    input.oninput = newFn

节流

function throttle(fn,interval) {
  let lastTime = 0
  return function(...args) {
    let nowTime = new Date().getTime()
    const remainTime = interval - (nowTime - lastTime)
    if(remainTime <= 0) {
      fn.apply(this, args)
      lastTime = nowTime
    }
  }
}

7、柯里化

function lmCurrying(fn) {
    function curried(...args) {
        if(args.length >= fn.length) {
            return fn(...args)
        } else {
            //当传入参数小于原函数的参数个数时
            function curried2(...args2) {
                return curried(...args, ...args2) //递归调用
            }
            return curried2
        }
    }
    return curried
}

8、深拷贝

function deepClone (originValue, cache = new WeakMap()) {
  //判断基本类型
  let type = typeof originValue
  if(originValue === null) return originValue
  if(type !== 'object') return originValue

  //判断是否函数
  if(type === 'function') return originValue

  // 判断是否map和set
  if(originValue instanceof Map) return new Map([...originValue])
  if(originValue instanceof Set) return new Set([...originValue])

  //递归时,判断map中是否有值
  if(cache.has(originValue)) return cache.get(originValue)
    
  let newObj = Array.isArray(originValue) ? [] : {}

  // 避免循环引用
  cache.set(originValue, newObj)

  for(let key in originValue) {
    newObj[key] = deepClone(originValue[key], cache)
  }
  return newObj
}
// 测试代码
const obj = {
  name: 'lin',
  friends: {
    name: '小强',
    age: 22,
    hobbies: ['看小说', '看综艺', '看球赛']
  },
  run: function() {
    console.log('running')
  },
  map: new Map(),
  girlFriend: null,
}
obj.self = obj

const newObj = deepClone(obj)
console.log(newObj)
newObj.friends.name = '小婷'
console.log(obj)

9、call/apply/bind

1、call

Function.prototype.myCall = function(thisArg, ...args) {

    var fn = this //this就是调用了lmcall的函数

    thisArg = (thisArg !== null && thisArg !== undefined)? thisArg : window

    thisArg.fn = fn
    thisArg.fn(...args)
    delete thisArg.fn
}

测试代码

function sum(num1, num2) {
  console.log(num1, num2, this);
}

sum.myCall({ name: 'linming' }, 10, 20)

2、apply

Function.prototype.lmapply = function(thisArg, argArray = []) {
    var fn = this

    thisArg = (thisArg !== null && thisArg !== undefined)? thisArg : window
   
    thisArg.fn = fn
    thisArg.fn(...argArray)
    delete thisArg.fn
}

3、bind

Function.prototype.myBind = function(thisObj, ...args1) {
  const fn = this
  thisObj = (thisObj === null || thisObj === undefined)? window : thisObj

  return function(...args2) {
    thisObj.fn = fn
    const finallyArgs = [...args1, ...args2]
    thisObj.fn(...finallyArgs)
    delete thisObj.fn
  }
}

10、高阶函数

1、forEach

Array.prototype.myForEach = function(fn) {
  const arr = this
  for(let i in arr) {
    fn(arr[i], i, arr)
  }
}

const arr = [1,23,4,5,6,7]
arr.myForEach((item, index) => {
  // console.log(item)
  console.log(index)
})

forEach不能跳出循环,使用了return只会return掉当前函数的执行,无法终止剩下的函数的执行

2、filter

Array.prototype.myFilter = function(fn) {
  const arr = this
  let newArr = []
  for(let key in arr) {
    if(fn(arr[key])) {
      newArr.push(arr[key])
    }
  }
  return newArr
}

const arr = [3,4,5,6,66,2,123,6,77,23,23,32]

const res = arr.filter((item) => {
  return item > 20
})
console.log(res)

3、map

Array.prototype.myMap = function(fn) {
  const arr = this
  let newArr = []
  for(let value of arr) {
    newArr.push(fn(value))
  }
  return newArr
}

const arr = [2,3,4,5,6,7,8]
const newArr = arr.myMap((item) => {
  return item * 10
})
console.log(newArr)

4、every

Array.prototype.myEvery = function(fn) {
  const arr = this
  let result = true
  for(let value of arr) {
    result = fn(value)
    if(!result) {
      break
    }
  }
  return result
}

const arr = [2,3,4,5,6,7,8]
const res = arr.myEvery((item) => {
  return item < 8
})
console.log(res)

5、some

Array.prototype.mySome = function(fn) {
  const arr = this
  let result = false
  for(let value of arr) {
    result = fn(value)
    if(result) {
      break
    }
  }

  return result
}

const arr = [2,3,4,5,6,7,8]
const res = arr.mySome((item) => {
  return item < 3
})
console.log(res)

11、迭代器

// 定义遍历器生成函数
function makeIterator (array) {
    let nextIndex = 0;
    return {
        next: function () {
            return nextIndex < array.length? 
            {value: array[nextIndex++], done: false} :
            {value: undefined, done: true}
        }
    }
} 
let example = makeIterator(["a","b"])
console.log(example.next()); //{value: "a", done: false}
console.log(example.next()); //{value: "b", done: false}
console.log(example.next()); //{value: undefined, done: true}

12、数组去重

1、Set方法

        let arr = [1,0,0,2,9,8,3,1,3,3];
        console.log(...new Set(arr)); //输出:1 0 2 9 8 3

2、利用数组的indexOf方法去重

        let arr = [1, 5, 6, 0, 7, 3, 0, 5, 9,5,5];

        function unique(arr) {
            let newArr = [] //创建一个新数组
            for(let i = 0;i < arr.length;i++) {
                if(newArr.indexOf(arr[i]) === -1) {
                    newArr.push(arr[i])
                }
            }
            return newArr
        }

        console.log(unique(arr));

3、利用数组sort方法去重

        let arr = [1, 5, 6, 0, 7, 3, 0, 5, 9, 5, 5];

        function unique(arr) {
            arr.sort() //升序排序

            let newArr = []
            newArr.push(arr[0])
            for(let i = 1;i < arr.length;i++) {
                if(arr[i] !== arr[i-1]) {
                    newArr.push(arr[i])
                }
            }
            return newArr   
        }
        console.log(unique(arr));

13、数组扁平化

数组扁平化其实就是将多维数组转换为一维数组

//利用简单递归实现功能
function myFlat(arr) {
  let result = []
  arr.forEach((value, index, arr) => {
    if(!Array.isArray(arr[index])) {//该元素不是数组
      result.push(value)
    } else {//该元素是数组
      result = result.concat(myFlat(arr[index])) //递归调用
    }
  })
  return result
}

14、并发控制

const concurrencyControl = (urls, maxNum) => {
    return new Promise((resolve) => {
        if (!urls.length) return resolve([]);

        const result = [];
        let index = 0;
        let finishCount = 0;

        const request = async () => {
            if (index >= urls.length) return;

            const i = index;
            const url = urls[index];
            index++;
            try {
                console.log(url);
                const res = await fetch(url);
                result[i] = res;
            } catch (error) {
                console.log(error);
            } finally {
                finishCount++;
                if (finishCount === urls.length) {
                    resolve(result);
                }
                request();
            }
        };

        // 限制并发数
        const times = Math.min(maxNum, urls.length);
        for (let i = 0; i < times; i++) {
            request();
        }
    });
};
评论
  • 支持 Markdown 格式
  • 评论需要登录
  • 邮箱会回复提醒(也许会在垃圾箱内)
0 /400 字