js 手写代码 2

189 阅读3分钟

部1

1. jsonp

function jsonp({ url, data, success }) {
  let script = docuemnt.createElement('script')

  var str = ''
  for (let k in data) {
    str += `&${k}=${data[k]}`
  }

  let fnName = 'myJsonp' + Math.random().toString().replace('.', '')

  window[fnName] = success

  script.src = url + '?callback=' + fnName + str

  document.body.appendChild(script)
  script.onload = function () {
    document.body.removeChild(script)
  }
}


jsonp({
  url: '  ',
  data: {},
  success: function (data) {},
})

2. 栈解题

/*

给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。

有效字符串需满足:

  1.  左括号必须用相同类型的右括号闭合。
  2.  左括号必须以正确的顺序闭合。
  注意空字符串可被认为是有效字符串。


示例 1:
输入:s = "()"
输出:true

示例 2:
输入:s = "()[]{}"
输出:true

示例 3:
输入:s = "(]"
输出:false

示例 4:
输入:s = "([)]"
输出:false

示例 5:
输入:s = "{[]}"
输出:true


*/

function isValid(str) {
  if (str.length % 2 !== 0) {
    return false
  }

  let items = []

  for (let i = 0; i < str.length; i++) {
    let letter = items[items.length - 1]
    switch (str[i]) {
      case '(':
        items.push('(')
        break

      case '[':
        items.push('[')
        break

      case '{':
        items.push('{')
        break

      case ')':
        if (letter === '(') {
          items.pop()
        }
        break

      case ']':
        if (letter === '[') {
          items.pop()
        }
        break

      case '}':
        if (letter === '{') {
          items.pop()
        }

        break
    }
  }

  return items.length === 0
}

4. 实现斐波那契数列

// 递归
function fibonacci (n){
    if(n==0) return 0
    if(n==1) return 1
    return fibonacci(n-2)+ fibonacci(n-1)
}

5. 寄生组合继承

function Parent(name) {
  this.name = name;
  this.say = () => {
    console.log(111);
  };
}
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;

6. 类数组转化为数组的方法

const arrayLike=document.querySelectorAll('div')

// 1.扩展运算符
[...arrayLike]

// 2.Array.from
Array.from(arrayLike)

// 3.Array.prototype.slice
Array.prototype.slice.call(arrayLike)

// 4.Array.apply
Array.prototype.splice.call(arrayLike, 0);

// 5.Array.prototype.concat
Array.prototype.concat.apply([], arrayLike)

7. queryString

let urlStr = 'http://www.inode.club?name=koala&study=js&study=node'

function queryString(request) {
  let arr = request.split('?')[1].split('&')

  let res = {}
  arr.forEach((item) => {
    let [key, value] = item.split('=')

    if (res[key]) {
      res[key] = [].concat(res[key], value)
    } else {
      res[key] = value
    }
  })
  return res
}


console.log(queryString(urlStr))

8. 转化驼峰命名

var s1 = 'get-element-by-id'

// 转化为 getElementById

var f = function (s) {
  return s.replace(/-\w/g, function (x) {
    return x.slice(1).toUpperCase()
  })
}

9. 字符串模板

function render(template, data) {
  // 模板字符串正则
  const reg = /\{\{(\w+)\}\}/
  // 判断模板里是否有模板字符串
  if (reg.test(template)) {
    // 查找当前模板里第一个模板字符串的字段
    const name = reg.exec(template)[1]
    // 将第一个模板字符串渲染
    template = template.replace(reg, data[name])
    // 递归的渲染并返回渲染后的结构
    return render(template, data)
  }
  // 如果模板没有模板字符串直接返回
  return template
}

let template = '我是{{name}},年龄{{age}},性别{{sex}}'
let person = {
  name: '布兰',
  age: 12,
}
// 我是布兰,年龄12,性别undefined
render(template, person)

部2

1. 函数柯里化

function curry(fn) {
  return function judge(...args) {
    if (args.length >= fn.length) return fn(...args)
    
    return function (...arg) {
      judge(...args, ...arg)
    }
  }
}


function add(a, b, c) {
  return a + b + c
}
add(1, 2, 3)
let addCurry = curry(add)
addCurry(1)(2)(3)

2. Object.is 实现

//  Object.is不会转换被比较的两个值的类型,这点和===更为相似,他们之间也存在一些区别。
//     1. NaN在===中是不相等的,而在Object.is中是相等的
//     2. +0和-0在===中是相等的,而在Object.is中是不相等的

// 实现代码如下:
Object.is = function (x, y) {
  if (x === y) {
    // 当前情况下,只有一种情况是特殊的,即 +0 -0
    // 如果 x !== 0,则返回true
    // 如果 x === 0,则需要判断+0和-0,则可以直接使用 1/+0 === Infinity 和 1/-0 === -Infinity来进行判断
    return x !== 0 || 1 / x === 1 / y
  }

  // x !== y 的情况下,只需要判断是否为NaN,如果x!==x,则说明x是NaN,同理y也一样
  // x和y同时为NaN时,返回true
  return x !== x && y !== y
}

数组排序

1. 冒泡排序

function bubble(arr) {
  for (let i = arr.length - 1; i > 0; i--) {
    for (let j = 0; j < i; j++) {
      if (arr[j+1] < arr[j]) {
        let temp = arr[j + 1]
        arr[j + 1] = arr[j]
        arr[j] = temp
      }
    }
  }

  return arr
}

2. 选择排序

function selection(arr) {
  for (let i = 0; i < arr.length - 1; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[j] < arr[i]) {
        ;[arr[i], arr[j]] = [arr[j], arr[i]]
      }
    }
  }

  return arr
}

3. 插入排序

function insertionSort(arr) {

  let len = arr.length
  let preIndex, current

  for (let i = 1; i < len; i++) {
    preIndex = i - 1
    current = arr[i]

    while (preIndex >= 0 && arr[preIndex] > current) {
      arr[preIndex + 1] = arr[preIndex]
      preIndex--
    }

    arr[preIndex + 1] = current
  }

  return arr
}

图片懒加载

1.clientHeight, scrollTop, offsetTop

let imgs = document.getElementsByTagName('img')
let count = 0
// 首次加载
lazyLoad()
// 通过监听 scroll 事件来判断图片是否到达视口,别忘了防抖节流
window.addEventListener('scroll', throttle(lazyLoad, 160))

function lazyLoad() {
  let clientHeight = document.documentElement.clientHeight //视口高度
  //滚动条卷去的高度
  let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
  for (let i = count; i < imgs.length; i++) {
    // 元素现在已经出现在视口中
    if (imgs[i].offsetTop < scrollTop + clientHeight) {
      if (imgs[i].getAttribute('src') !== 'default.jpg') continue
      imgs[i].src = imgs[i].getAttribute('data-src')
      count++
    }
  }
}

2.getBoundingClientRect

dom 元素的 getBoundingClientRect().top 属性可以直接判断图片是否出现在了当前视口。

// 只修改一下 lazyLoad 函数
function lazyLoad() {
  for (let i = count; i < imgs.length; i++) {
    if (
      imgs[i].getBoundingClientRect().top <
      document.documentElement.clientHeight
    ) {
      if (imgs[i].getAttribute('src') !== 'default.jpg') continue
      imgs[i].src = imgs[i].getAttribute('data-src')
      count++
    }
  }
}

3. IntersectionObserver

IntersectionObserver 浏览器内置的 API,实现了监听 window 的 scroll 事件、判断是否在视口中 以及 节流 三大功能。该 API 需要 polyfill。

let imgs = document.getElementsByTagName('img')
const observer = new IntersectionObserver((changes) => {
  for (let i = 0, len = imgs.length; i < len; i++) {
    let img = imgs[i]
    // 通过这个属性判断是否在视口中,返回 boolean 值
    if (img.isIntersecting) {
      const imgElement = img.target
      imgElement.src = imgElement.getAttribute('data-src')
      observer.unobserve(imgElement) // 解除观察
    }
  }
})
Array.from(imgs).forEach((item) => observer.observe(item)) // 调用