Linjiayu6 / FE-Notes

[2020] Front-End Notebook
13 stars 2 forks source link

1 - JavaScript 基础 #1

Open Linjiayu6 opened 4 years ago

Linjiayu6 commented 4 years ago

JS 基础

[JS基础] 1 - 内存空间

- 基本类型, 引用类型
- 变量对象 和 堆内存

[JS基础] 2 - 执行上下文

- JS执行环境: 全局, 函数环境, eval
- 执行栈: 全局在最底下, 函数执行才入栈, 遇到return立刻出栈

[JS基础] 3 - 变量对象 Variable Object

- 函数的执行上下文 Execution Context, 分为两个阶段 创建 和 执行
- 创建阶段 VO: 变量声明,函数声明提升
- 执行阶段 AO (入栈执行)

[JS基础] 4 - 作用域与作用域链 scope / scope chain

- EC创建阶段: VO, 作用域链, this
- EC执行阶段: AO赋值, 其他code .....

[JS基础] 5 - 闭包 Closure

1. 函数执行的地方: Function Call Stack 函数调用栈
2. 在Function Call Stack 函数调用栈里面,存放着可执行上下文EC
3. 执行上下文EC, 告诉函数你周遭的环境是什么样的。
   生命周期有两个:
   - 创建: 又可以简单的理解为 你写代码的环境。主要负责VO, scopechain, this 的声明。
        声明包含函数提升声明 和 变量声明
        变量给它分配个内存空间, 函数或对象类型的是key分配个内存空间,对象类型放到堆内存中。

   - 执行: 负责AO参数赋值, 其他代码执行。

4. 可执行环境分为: 全局环境 和 函数环境。可以简单理解为作用域
   而作用域链是在你写代码阶段已确定的作用域环境。

5. 当EC在执行阶段,会push进函数调用栈,此时当前函数里参数的获取 是按照作用域链创建的来的。
6. 闭包: 我个人理解是作用域链的应用。上级函数保存当前函数执行的内容。

[JS基础] 6 - 执行机制, 同步异步, 事件循环, 宏任务, 微任务

# 事件循环 Event Loop
本质是: 
1. 作为单线程js对于异步事件的处理机制 

2. 或者可以说是 只有一个主线程js的处理逻辑

3. 如何保证主线程, 有序并高效 或非阻塞 的处理呢? => 事件循环机制 Event Loop

4. 异步任务也是有优先级的,分为 宏任务 MacroTask, 微任务 MicroTask

[JS基础] 7 - this, call/apply/bind/箭头函数

1. 在执行上下文 EC 的创建阶段this已经被创建出来了

2. 独立函数运行,this指向函数内部, 但因为非严格模式下,this 会指向window
   对象调用的函数,this指向对象

3. this绑定
   - call, apply
   - bind
   - 利用闭包和apply你自己实现个 bind方法
   - 手动绑定 例如const that = this

[JS基础] 8 - 从 Chrome 看 闭包 / this / 作用域链

闭包的产生条件:

函数a 里 写着函数b。
b执行的时候, 用到了a_EC.VO  a的执行下上文的变量对象。
(要用到人家a里的才产生, 知道window下的不算)

[JS基础] 9 - 函数式编程 Functional Programming

没有完成 TODO

[JS基础] 10 - 构造函数 原型 原型链 继承 new

创建对象
- 工厂模式? 问题是重复分配内存给相同的函数或方法,寻求共享模式。
- 构造函数? 对象私有化
- prototype: 方法共享方式 (其实也是利用引用类型)。
  __proto__: 指针, 某个对象要使用共享方法, 用指针指向该引用类型即可。
  prototype chain: 继承 想共享方法都连在一起。

- 继承? 原理是什么? prototype chain
- new都干了啥? 创建对象,私有内容赋值(this的绑定),指针__proto__指向共享的公共内容

[JS基础] 11 - Promise

[JS基础] 12 - 深拷贝 VS 浅拷贝

1. 值类型 基本类型 or 引用类型?
2. 遍历对象的三个方法及不同? (for - in , Object.keys() ,  Object.getOwnPropertyNames())
3. 浅拷贝?
4. 浅拷贝 + 迭代? code? 问题? 解决?
5. 深拷贝? 
   三种 (JSON, 浅拷贝+循环, 浅拷贝+循环+校验)
6. 会遇到什么问题? 怎么解决?

[JS基础] 13 - 其他 JS 基础

[JS基础] 14 - V8的回收机制 ♻️ / 内存泄露

[JS基础] 15 - 节流 防抖 / Event Bus / new实现

Linjiayu6 commented 4 years ago
实现caller

npm 版本规则
websocket polling
笔试 css九宫格 异步任务并发数限制
js array string api https://www.jianshu.com/p/5477e281c30e

1. 懒加载与动态 import 语法的坑
3. 说说怎么本地调试 npm 包,考察 npm link
6. Tree-shaking 原理
7. 微前端了解多少,如果让你做微前端的技术选型,你怎么考虑
8. 疯狂问项目,思考和看法

11. Node 框架用的什么, Koa 与 Express 相比有什么不同
12. Node 服务部署,运维 /  node容灾处理  🔥  / node如何捕获异常 🔥
13. 懒加载怎么做 / 懒加载原理实现方法 🔥 🔥
15. Node 底层了解多少
17. 用户弱网环境问题排查与优化
19. SSO 鉴权流程
24. 如何自己实现一个组件按需加载
30. Node Stream 是干什么的
34. async 的异常捕获
42. 聊聊尾递归 / 尾递归函数优化 🔥
47. 扫码登录的实现逻辑
56. 算法,最大连续子序列(dp) 🔥
67. 将json字符串'{"a": 1, "b": "str", "c":[2, 3], "d":{"e": 4}}'
69. 给定一个数组,求该数组所有的自子数组
78. 买卖股票一次获得的最大利润(动态规划)  🔥
82. 对flutter的了解 🔥

1. 单链表的对折 🔥
2. 块级作用域  IIFE 🔥 🔥 🔥
3. 你项目怎么接入ci的,整个流水线是怎样【描述】
git提交 => lint执行 => 触发hook => 读取yml文件执行命令 => 部署 => 调用机器人接口发布企业微信群周知
4. web worker 🔥
https://leetcode-cn.com/problems/add-two-numbers/
Linjiayu6 commented 4 years ago

面试可参考 Google面试 2020年前端面试复习必读精选文章

Linjiayu6 commented 4 years ago

1 - this / apply / call / bind 实现

1.1 - apply / call

/**
 * fn.apply(context, [xxx])
 * fn.call(context, a, b, c, ...)
 * fn.bind(context)(a, b, c)
 */
Function.prototype._call_ = function (context) {
  var context = Object(context) || window // Object() 自动包装对象
  // let args = [...arguments].slice(1);
  const args = Array.prototype.slice.call(arguments, 1)
  // 放在对象或window去执行, this是 调用该方法的函数 eg: A.call(xxx), this 是 A
  context.fn = this

  var result = context.fn(...args)
  delete context.fn
  return result
}

function fn (...args) { console.log(this.a, args) }
const obj = { a: 1, b: 2}

fn._call_(obj, '参数')
Function.prototype._apply_ = function (context) {
  var context = context || window
  const args = Array.prototype.slice.call(arguments, 1)
  context.fn = this
  context.fn(...args)
  delete context.fn
}
fn._apply_(obj, [1, 2, 3])

1.2 - bind

基础版本

Function.prototype._bind_ = function (context) {
  context = context || window

  let args = Array.prototype.slice.call(arguments, 1)
  const fn = this
  return function () {
    const bindArgs = Array.prototype.slice.call(arguments, 0)
    return fn.apply(context, args.concat(bindArgs))
    // 或 fn.call(context, ...argList)
  }
}

const obj = { name: 1 }
function executor (age, unv) {
  console.log(this.name)
  console.log(age)
  console.log(unv)
}
executor._bind_(obj)(18, 'hkbu')

进阶版本 - new this指向会丢失 🔥

https://github.com/sisterAn/JavaScript-Algorithms/issues/81

Linjiayu6 commented 4 years ago

2 - debounce 防抖 / throttle 节流

/**
 * 【debounce 防抖】
 *   场景: 输入框输入查询内容,接口请求,新内容都需要每次都重新请求接口
 *   触发后, 如果有重复触发的, 都再次重新执行

 * 【throttle 节流】 
 *   场景: 避免多次触发 例如已经提交过的, 在处理中的不允许再次提交
 *   eg: 往下拉刷新接口等,需要锁住 防止多次下拉刷新提交
 *   正在执行的, 不允许触发执行
 */

const throttle = (fn, delay) => {
  let flag = true
  return (...args) => {
    if (flag === false) return
    flag = false
    setTimeout(() => { 
      fn.call(this, ...args)  // 这里容易错
      flag = true
    }, delay)
  }
}

const debounce = (fn, delay) => {
  let timer = null
  return (...args) => {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn.apply(this, args)
    }, delay)
  }
}
Linjiayu6 commented 4 years ago

3 - 深拷贝 浅拷贝

[类型判断] Object.prototype.toString.call(value) [object Object] 类型判断

[队列从头pop] array.shift()


### 浅拷贝
```javascript
const shallowClone = target => {
  // for in + hasOwnProperty 或 Object.keys()
  const result = {}
  for (let key in target) {
    if (target.hasOwnProperty(key)) {
      result[key] = target[key]
    }
  }
  return result
}

深拷贝I: 递归 + 浅拷贝

const deepClone1 = target => {
  const result = {}

  Object.keys(target).forEach(key => {
    const value = target[key]
    // Object.prototype.toString.call(value) 类型判断
    if (Object.prototype.toString.call(value) === '[object Object]') {
      result[key] = deepClone1(value)
    } else {
      result[key] = value
    }
  })
  return result
}

深拷贝II: JSON.parse(JSON.stringify(xxxx))

JSON.parse(JSON.stringify(obj))

深拷贝III: 浅拷贝 + 迭代 + 重复引用处理 => N叉🌲 遍历

const deepClone2 = source => {
  const dictionary = {}
  const result = {}

  const stack = [{ source, result }]
  while (stack && stack.length !== 0) {
    const { source, result } = stack.shift() // 从头移除一个 或用 pop也行

    Object.keys(source).forEach(key => {
      const value = source[key]

      if (dictionary[key] === value) {
        console.error('err 重复引用类型')
        return 
      }

      dictionary[key] = value
      if (Object.prototype.toString.call(value) === '[object Object]') {
        result[key] = {}
        stack.push({ source: value, result: result[key] })
      } else {
        result[key] = value
      }
    })
  }
  return result
}
var target = {
  a: {
    b: {
      c: 123
    }
  },
  d: {
    e: 456
  },
  f: 111
}

function deepClone (target) {
  var result = {}
  var map = new Map()
  // [{ key: a, val: { ... }, result }, { key: d, val: { ... }, result }, { key: f, val: 111, result }]
  var stack = Object.keys(target).map((key) => ({ key, val: target[key], obj: result }))

  while (stack && stack.length > 0) {
    var { key, val, obj } = stack.shift()

    // 重复引用过滤
    if (map.get(key) === val) return
    map.set(key, val)
    if (Object.prototype.toString.call(val) === '[object Object]') {
      var temp = Object.keys(val).map(i => {
        obj[key] = {}
        return { key: i, val: val[i], obj: obj[key] }
      })
      stack = stack.concat(temp)
    } else {
      obj[key] = val
    }
  }

  return result
}

deepClone(target)
Linjiayu6 commented 4 years ago

4 - EventBus


class EventEmitter {
  constructor () {
    this.queueObj = {}
  }

  on (key, fn) {
    if (typeof key !== 'string' && typeof fn !== 'function') {
      return
    }

    if (key in this.queueObj) {
      this.queueObj[key].push(fn)
    } else {
      this.queueObj[key] = [fn]
    }
  }

  emit (key, ...args) {
    if (key in this.queueObj) {
      this.queueObj[key].forEach((fn) => {
        if (args.length > 0) {
          fn.apply(this, args)
        } else {
          fn.call(this)
        }
      })
    } else {
      console.error('没有该key值')
    }
  }

 delete (key) {
    if (key in this.queueObj) {
      delete this.queueObj[key]
    }
  }

  remove (key, fn) {
    if (this.queueObj[key]) {
      const index = this.queueObj[key].indexOf(fn)  // 数组.indexOf(查询内容位置)
      if (index > -1) {
        this.queueObj[key].splice(index, 1) // splice 删除第index的结点开始 1个
      }
    }
  }

  get (key) {
    if (key in this.queueObj) {
      console.log(this.queueObj[key])
    } else {
      console.error('error get失败')
    }
  }
}

const event = new EventEmitter()
event.on('key', function (x) { console.log('x', x) })
event.on('key', function (y) { console.log('y', y) })
event.on('key', function (z) { console.log('z', z) })

event.emit('key', [1, 2, 3])
event.get('key')
Linjiayu6 commented 4 years ago

5 - instanceOf

/*
   instanceObj instanceOf Class 实例对象是否属于某个类
   eg: p instanceof Person

   instanceObj = new Class()
   instanceObj._proto_ = Class.prototype 🔥
   Class.prototype.constructor = Class

  只需要判断, 实例对象 _proto_ 指针是否指向 类的原型
*/

function _instanceOf (instanceObj, Class) {
  const target = Class.prototype   
  const proto = instanceObj._proto_
  while (proto) {
    if (proto === target)  return true 
    proto = proto._proto_
  }

  return false
}
Linjiayu6 commented 4 years ago

6 - new

function _new (Class, ...args) {
 // 1. 创建对象
  const obj = {}

 // 2. 公共方法或属性指向
 // 或 obj = Object.create(Class.prototype)
  obj.__proto__ = Class.prototype

 // 3. 私有属性或方法执行
  const result = Class.apply(obj, args)

 // 4. 如果返回的不是一个对象, 则直接返回obj
  return result instanceof Object ? result : obj
}
Linjiayu6 commented 4 years ago

7 - Object.create() 实现

目的: 在原型链上层 再封装个proto


xxx = {
  _proto_: { 
     prototype
   }
}

// Object.create(原型对象) 封装个指针指向目标值
const objectCreate = prototype => {
  // 目标 obj._proto_ = prototype
  function F () {}
  F.prototype = prototype
  return new F()
}
Linjiayu6 commented 4 years ago

8 - 继承

1. 构造函数继承  
Parent.apply(this, argsArr) 默认返回this
Parent.call(this, a, b, c) 默认返回this

2. 原型方法继承  I  
# 本质: 包一层_proto_ 指向公共
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child

3. 原型方法继承  II 
# 本质: 包一层_proto_ 指向公共
Child.prototype = new Parent()
Child.prototype.constructor = Child

4. ES6
class A extends React.Component {
    constructor (props) {
       super(...props)
    }
}
// 1. 继承构造器里的内容 直接执行Class就行
// 2. 继承原型的方法, 原型链的创建
function Parent (surname, home) {
  this.surname = surname
  this.home = home
}

Parent.prototype = {
  construtor: Parent,
  getParent: function () { console.log(this.surname + this.home) }
}

// 🔥  继承构造器内容
function Child (surname, home, givename) {
  Parent.call(this, surname, home)

  // const args = Array.prototype.slice.call(arguments) 类数组转为数组
  // Parent.apply(this, args)
  this.givename = givename 
}

//  🔥  原型链继承方式 Child.prototype = { _proto_: { Parent.prototype } }
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
Child.prototype.getChild = function () {
  console.log(this)
}

// 或者 
Child.prototype = new Parent()
Child.prototype.constructor = Child

const child = new Child('林', '中国', '😃')
child.getChild()
child.getParent()

console.log(child instanceof Child)
console.log(child instanceof Parent)
Linjiayu6 commented 4 years ago

9 - JSONP

function JSONP (options = {}) {
    const { url = '', paramsObj = {}, callbackName = '' } = options
    if (typeof(url) !== 'string' || typeof(callbackName) !== 'string') {
        console.error('url or callbackName formatting error')
        return
    }

    if (Object.prototype.toString.call(paramsObj) !== '[object Object]') {
        console.error('paramsObj formatting error')
        return
    }

    // 处理 paramsObj 重点是这里, 一定要是callback结尾
    paramsObj.callback = callbackName
    let paramList = []
    for (let key in paramsObj) {
        const value = paramsObj[key]
        paramList.push(key + '=' + value)
    }
    const paramstr = paramList.join('&')
    const reqUrl = url + '?' + paramstr

    // 处理标签
    const el = document.createElement('script')
    el.setAttribute('src', reqUrl)
    document.body.appendChild(el)

    // 回调处理 🔥
    return new Promise((resolve, reject) => {
        window[callbackName] = (resp) => {
            resolve(resp)
            delete window[callbackName]
            el.parentNode.removeChild(el)
        }
    })
}

JSONP({
    url: 'http://photo.sina.cn/aj/index',
    paramsObj: {
        page: 1,
        cate: 'recommend'
    },
    callbackName: 'jsoncallback'
}).then(data => {
    console.log(data)
})
Linjiayu6 commented 4 years ago

10 - 数组拍平

const array = [1, [2], [3, [4, [5]]]]

// 拍平
function flatten (array, result = []) {
    for (let i = 0; i < array.length; i ++) {
        // 递归完成
        // Array.isArray(array[i])
        if (Object.prototype.toString.call(array[i]) === '[object Array]') {
            flatten(array[i], result)
        } else {
            result.push(array[i])
        }
    }
    return result
}
console.log(flatten(array, []))

// 用reduce实现
function flatten_reduce (array) {
    array.reduce((accumulator, current) => {
        if (Array.isArray(current)) {
            return accumulator.concat(flatten_reduce(current)) // 需要再次递归完成
        } else {
            return accumulator.concat(array) // 直接连接
        }
    }, [])
}
flatten_reduce(array)
Linjiayu6 commented 4 years ago

11 - Promise 实现

Linjiayu6 commented 4 years ago

12 - Iterator 迭代器实现 (单链表结构)

/**
 * 迭代器 Iterator
 * 
 * 本质: 单链表 SingleLinkList
 * 引用: Generator -> async await
 * 描述: [666, 888, 000]
 *      指针默认指向0位置, 当执行 返回 { done: 是否到头标识, value: 当前值 }
 *      例如: 
 *      it = iterator([666, 888, 000])
 *      it.next() // { done: false, value: 666 }
 *      it.next() // { done: false, value: 888 }
 *      it.next() // { done: true, value: 000 } / 到头了 不允许再往下了
 */

function _iterator_ (queue) {
  let pointer = 0
  return {
    next: function () {
      // 迭代器 到头了
      if (pointer === queue.length - 1) {
        return { done: true, value: queue[pointer] }
      }
      pointer ++ // 指针指向
      return { done: false, value: queue[pointer - 1] }
    }
  }
}

let it = _iterator_([666, 888, 000])
console.log(it.next()) // {done: false, value: 666}
console.log(it.next()) // {done: false, value: 888}
console.log(it.next()) // {done: true, value: 0}
Linjiayu6 commented 4 years ago

13 - Symbol.iterator 实现对象 for of 可遍历

Obj.prototype[Symbol.iterator] 原型链上写该方法即可

class Obj {
  constructor(value) {
    this.value = value
    this.next = null
  }

  // Obj.prototype[Symbol.iterator] 写迭代器方法
  [Symbol.iterator] = function () {
    let current = this
    return {
      next: function () {
        if (current) {
          const value = current.value
          current = current.next // 指向下一个指针
          return { done: false, value }
        }

        return { done: true, value: undefined }
      }
    }
  }
}

var one = new Obj(1)
var two = new Obj(2)
var three = new Obj(3)

one.next = two
two.next = three
console.log(one)

for (let p of one) {
  console.log(p)
}

image

mayuelei66 commented 3 years ago

good jiayu!

Dragon-Rider commented 2 years ago

nice jaiyu!