js基础-各种手写的实现

如果想看基础总结,查看xmind文件夹下的js文件

js脑图

Object.create的实现

将创建的对象a的隐式原型指向传入的显示原型

function create(obj) {
  function F() {}
  F.prototype = obj
  return new F()
}
1
2
3
4
5

instanceof 的实现

右侧构造函数的prototype是否在左侧原型链中能够找到

function myInstance (left, right) {
  let proto = Object.getPrototypeOf(left)
  let prototype = right.prototype
  while(true) {
    if(!proto) return false
    if(proto === prototype) return true
    proto = Object.getPrototypeOf(proto)
  }
}
1
2
3
4
5
6
7
8
9

new 操作符实现原理

  1. 使用Object.create创建一个空对象,并且隐式原型指向传入的显示原型
  2. 使用apply执行构造函数和确定this指向
  3. 返回空对象或者执行后拿到的对象
function myNew(fn, ...rest) {
  let obj = Object.create(fn.prototype)
  let res = fn.apply(obj, ...rest)
  if(res && (typeof res ==='object'|| typeof res === 'function')) {
    return res
  }
  return obj
}
1
2
3
4
5
6
7
8

防抖函数实现

在n秒内触发事件后,如果再次触发会重新计算时间

function debounce(fn, wait) {
  let timer = null
  return function() {
    args = arguments
    if(timer) {
      clearTimeout(timer)
      timer = null
    }
    timer = setTimeout(() => {
      fn.apply(this, args)
    }, wait);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

节流函数的实现

n秒内只会执行一次事件

function throttle(fn, delay) {
  let curTime = Date.now()
  return function() {
      args = arguments,
      nowTime = Date.now()
    if(nowTime - curTime >= delay) {
      curTime = Date.now()
      return fn.apply(this, args)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11

call 和 apply的实现

callapply实现是一样的,只是传入的参数一个是数组,一个是单个的

Function.prototype.myCall = function(context, ...args) {
  if(!context || context === null) {
    context = window
  }
  let fn = Symbol()
  context[fn] = this
  return context[fn](...args)
}
1
2
3
4
5
6
7
8

bind 实现

  1. bind可以被new调用,调用后this指向当前实例
  2. 返回的是一个函数
Function.prototype.myBind = function(context, ...args) {
  if(!context || context === null) {
    context = window
  }
  let fn = Symbol()
  context[fn] = this
  let _this = this
  const result = function(...innerArgs) {
    // 说明是用new了 是 否早函数
    if(this instanceof _this === true) {
      this[fn] = _this // 重新赋值函数
      this[fn](...[...args, ...innerArgs])
    }else {
      // 如果是普通函数调用 this指向就是传入的context
      context[fn](...[...args, ...innerArgs])
    }
  }
  result.prototype = Object.create(this.prototype)
  return result
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

实现一个compose函数

用法如下

function fn1 (x) {
  return x + 1
}
function fn2 (x) {
  return x + 2
}
function fn3 (x) {
  return x + 3
}
function fn4 (x) {
  return x + 4
}
const a = compose(fn1, fn2, fn3, fn4)

console.log(a(1)) // 1+4+3+2+1=11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

实现

function compose(...fn) {
  if(!fn.length) return v=>v
  if(fn.length === 1) return fn[0]
  return fn.reduce((pre, cur) => (...args) => pre(cur(...args)))
}
1
2
3
4
5

用 settimeout 模拟 setinterval

为什么要这么做?

setinterval中,因为主线程执行时间不确定

  1. 可能多个定时器会连续执行
  2. 某些间隔可能会跳过
function mySetinterval(fn, t) {
  let timer = null
  function interval() {
    fn()
    timer = setTimeout(interval, t);
  }
  interval()
  return {
    cancel: () => clearTimeout(timer)
  }
}
1
2
3
4
5
6
7
8
9
10
11

实现发布订阅模式

class EventEmitter {
  constructor() {
    this.events = {}
  }
  on(type, callback) {
    if(!this.events[type]) {
      this.events[type] = [callback]
    } else {
      this.events[type].push(callback)
    }
  }
  emit(type, ...rest) {
    this.events[type] && this.events[type].forEach(fn => fn.apply(this, rest))
  }
  off(type, callback) {
    if(!this.events[type]) return
    this.events[type] = this.events[type].filter(item => item !== callback)
  }
  once(type, callback) {
    function fn() {
      callback()
      this.off(type, fn)
    }
    this.on(type, fn)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

数组去重

function uniqueArr(arr) {
  return [...new Set(arr)]
}
1
2
3

数组扁平化

function flatter(arr) {
  if(!arr.length) return 
  return arr.reduce((pre, cur) => (Array.isArray(cur) ? [...pre, flatter(cur)]: [...pre, cur]), [])
}
1
2
3
4

迭代的方法

function flatter2(arr) {
  if(!arr.length) return
  if(arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr)
  }
  return arr
}
1
2
3
4
5
6
7

es5 模拟 es6的 继承

function Parent (name) {
  this.name = name
  this.say = () => {
    console.log(this.name)
  }
}

Parent.prototype.play = () => {
  console.log(222)
}
function Children (name) {
  Parent.call(this)
  this.name = name
}
Children.prototype = Object.create(Parent.prototype)
Children.prototype.constructor = Children
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

深拷贝

function isObject() {
  return typeof val === 'object' && val !== null
}
function deepClone(obj, hash = new WeakMap()) {
  if(!isObject(obj)) return obj
  // 已存在
  if(hash.hah(obj)) {
    return hash.get(obj)
  }
  let target = Array.isArray(obj) ? [] : {}
  hash.set(obj, target)
  Reflect.ownKeys(obj).forEach(item => {
    if (isObject(obj[item])) {
      target[item] = deepClone(obj[item], hash)
    } else {
      target[item] = obj[item]
    }
  })
  return target
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

柯里化

柯里化实现,部分求值,吧接收多个参数的函数转变成接收一个参数的函数 用法如下: const add = (a, b, c) => a + b + c; const a = currying(add, 1); console.log(a(2,3))

function currying(fn, ...args) {
  const length = fn.length
  let allArgs = [...args]
  const res = (...newArgs) => {
    allArgs = [...allArgs, ...newArgs]
    if(allArgs.length === length) {
      return fn(...allArgs)
    } else {
      return res
    }
  }
  return res
}
1
2
3
4
5
6
7
8
9
10
11
12
13

升级版:add(1)(2)(3)...(n)

function add(...args) {
  let allArgs = [...args]
  function fn(...newArgs) {
    allArgs = [...allArgs, ...newArgs]
    return fn
  }
  fn.toString = function () {
    if(!allArgs.length) return
    return allArgs.reduce((pre, cur) => pre+cur)
  }
  return fn
}
1
2
3
4
5
6
7
8
9
10
11
12

版本号排序

有一组版本号如下['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5']。 现在需要对其进行排序,排序的结果为 ['4.3.5','4.3.4.5','2.3.3','0.302.1','0.1.1']

arr.sort((a, b) => {
  let i = 0
  const arr1 = a.split('.')
  const arr2 = b.split('.')

  while (true) {
    const s1 = arr1[i]
    const s2 = arr2[i]
    i++
    if (s1 === undefined || s2 === undefined) {
      return arr2.length - arr1.length
    }
    if (s1 === s2) continue

    return s2 - s1
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

列表转树形结构

[ { id: 1, text: '节点1', parentId: 0 //这里用0表示为顶级节点 }, { id: 2, text: '节点1_1', parentId: 1 //通过这个字段来确定子父级 } ... ]

转成 [ { id: 1, text: '节点1', parentId: 0, children: [ { id:2, text: '节点1_1', parentId:1 } ] } ]

function listToTree(data) {
  let temp = {}
  treeData = []
  for(let i=0;i<data.length;i++) {
    temp[data[i].id] = data[i]
  }
  for(let i in temp) {
    if(+temp[i].parentId != 0) {
      if(!temp[temp[i].parentId].children) {
        temp[temp[i].parentId].children = []
      }
      temp[temp[i].parentId].children.push(temp[i])
    }
    treeData.push(temp[i])
  }
  return treeData
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function treeToList(data) {
  let res = []
  const dfs = (tree) => {
    tree.forEach(item => {
      if(item.children) {
        dfs(item.children)
        delete item.children
      }
      res.push(item)
    })
  }
  dfs(data)
  return res
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

ajax

const SERVER_URL = "/server";
let xhr = new XMLHttpRequest();
// 创建 Http 请求
xhr.open("GET", url, true);
// 设置状态监听函数
xhr.onreadystatechange = function() {
  if (this.readyState !== 4) return;
  // 当请求成功时
  if (this.status === 200) {
    handle(this.response);
  } else {
    console.error(this.statusText);
  }
};
// 设置请求失败时的监听函数
xhr.onerror = function() {
  console.error(this.statusText);
};
// 设置请求头信息
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
// 发送 Http 请求
xhr.send(null);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

原型修改、重写

function Person(name) {
    this.name = name
}
// 修改原型
Person.prototype.getName = function() {}
var p = new Person('hello')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // true
// 重写原型
Person.prototype = {
    getName: function() {}
}
var p = new Person('hello')
console.log(p.__proto__ === Person.prototype)        // true
console.log(p.__proto__ === p.constructor.prototype) // false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

下面p的原型被修改了,p.constructor 不指向Person。那么只要修改回来就好了

Person.prototype = {
    getName: function() {}
}
var p = new Person('hello')
p.constructor = Person
console.log(p.__proto__ === Person.prototype)        // true
console.log(p.__proto__ === p.constructor.prototype) // true
1
2
3
4
5
6
7

原型链指向

要牢记 __proto__是浏览器的实现,它是用来访问prototype的。

p.__proto__  // Person.prototype
Person.prototype.__proto__  // Object.prototype
p.__proto__.__proto__ //Object.prototype
p.__proto__.constructor.prototype.__proto__ // Object.prototype
Person.prototype.constructor.prototype.__proto__ // Object.prototype
p1.__proto__.constructor // Person
Person.prototype.constructor  // Person
1
2
3
4
5
6
7

如何获得对象非原型链上的属性?

function iterate(obj){
  var res=[];
  for(var key in obj){
      if(obj.hasOwnProperty(key))
          res.push(key+': '+obj[key]);
  }
  return res;
} 
1
2
3
4
5
6
7
8

继承的几种方式

  • 原型链继承

缺点: 无法实现多继承

//父类型
function Person(name, age) {
   this.name = name,
   this.age = age,
   this.play = [1, 2, 3]
   this.setName = function () { }
}
Person.prototype.setAge = function () { }
//子类型
function Student(price) {
   this.price = price
   this.setScore = function () { }
}
Student.prototype = new Person() // 子类型的原型为父类型的一个实例对象
var s1 = new Student(15000)
var s2 = new Student(14000)
console.log(s1,s2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 借用构造函数继承

缺点:无法继承原型上的方法

function Person(name, age) {
  this.name = name,
  this.age = age,
  this.setName = function () {}
}
Person.prototype.setAge = function () {}
function Student(name, age, price) {
  Person.call(this, name, age)  // 相当于: this.Person(name, age)
  this.price = price
}
var s1 = new Student('Tom', 20, 15000)
1
2
3
4
5
6
7
8
9
10
11
  • 组合继承

使用了es5的语法,Object.create(),将子类的原型的原型指向了父类的原型,并且它的构造函数重新指向自己 没有副作用的方式

function Person(name, age) {
  this.name = name,
  this.age = age
}
Person.prototype.setAge = function () {
  console.log("111")
}
function Student(name, age, price) {
  Person.call(this, name, age)
  this.price = price
  this.setScore = function () {}
}
Student.prototype = Object.create(Person.prototype)//核心代码
Student.prototype.constructor = Student//核心代码
var s1 = new Student('Tom', 20, 15000)
console.log(s1 instanceof Student, s1 instanceof Person) // true true
console.log(s1.constructor) //Student
console.log(s1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Last Updated: