实现原生js方法

apply & call

call/apply实现原理:让当前的obj(做为this, this是函数被调用的对象)去调用函数

1
2
3
4
5
6
7
8
Function.prototype.myCall=function(context){
context = context || window
context.fn = this // 调用myCall的函数
let args = [...arguments].slice(1)
let res = context.fn(...args) // 执行调用myCall的参数,this指向context
delete context.fn // 删除context上的临时属性fn
return res
}

个人粗糙版

1
2
3
4
5
6
Function.prototype.myCall=function(){
let args = [...arguments]
let caller = args.shift()
caller.func = this
return caller.func(...args)
}

myApply的处理与myCall类似,区别是对参数处理,如果没有参数不能传入空数组,有参数则按参数数组传入

1
2
3
4
5
6
7
8
Function.prototype.myApply=function(context){
context = context || window
context.fn = this // 调用myCall的函数
let args = [...arguments].slice(1)
let res = args.length>1? context.fn(args): context.fn() // 执行调用myCall的参数,this指向context
delete context.fn // 删除context上的临时属性fn
return res
}

个人粗糙版

1
2
3
4
5
6
7
Function.prototype.myApply = function(){
let args = [...arguments]
let caller = args.shift()
caller.func = this
let funcArgs = args[0]?args[0]:[]
return caller.func(...funcArgs)
}

bind

bind返回是个新函数,这个函数已经绑定了this作用域,注意这个新函数可以被直接调用或者作为构造函数调用,如果是new调用,则不会被任何方式改变 this,所以对于这种情况我们需要忽略传入的 this。

1
2
3
4
5
6
7
8
9
10
11
12
Function.prototype.myBind = function(context){
context = context || window
let args = [...arguments].slice(1)
let _this = this
return function f (){
if(this instanceof f){ // 作为new调用, 调用时this指向实例
return new _this(...args, ...arguments)
}else{
return _this.apply(context,args.concat([...arguments]) )
}
}
}

个人粗糙版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Function.prototype.myBind = function(context, ...args){
let originalFunc = this
let caller = context

return function f(){
if(this instanceof f){
return new originalFunc(arguments)
}else{
caller.func = originalFunc
return caller.func(...args)

//同上 return originalFunc.apply(caller, args)
}

}
}

防抖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function debounce(fn, wait, immediate) {
let timer = null

return function() {
let args = arguments
let context = this

if (immediate && !timer) {
fn.apply(context, args)
}

if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
}

节流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function throttle(fn, wait, immediate) {
let timer = null
let callNow = immediate

return function() {
let context = this,
args = arguments

if (callNow) {
fn.apply(context, args)
callNow = false
}

if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args)
timer = null
}, wait)
}
}
}

new

  1. 创建一个空对象obj
  2. 链接到原型,这个对象的proto指向构造函数的prototype
  3. 执行构造函数,为这个对象obj增加属性
  4. 如果构造函数返回了一个对象,则返回这个对象,否则返回obj
1
2
3
4
5
6
7
function createFactory(){
let Con = [].shift.call(arguments)
let obj = {}
obj.__proto__ = Con.prototype
let res = Con.apply(obj, [...arguments])
return res instanceof Object? res: obj // 如果构造函数有返回则返回,没有则返回obj
}

深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function deepClone(obj){
console.log('clone obj', obj)
if(!isObj(obj)){
throw new Error('parameters error, should be an obj')
}

let result = Array.isArray(obj)?[...obj]:{...obj}
// 先浅拷贝 如果键值是对象,继续调用函数深克隆
Object.keys(result).forEach(key=>{
if(isObj(result[key])){ // 键值是不对象
result[key] = deepClone(result[key])
}
})
console.log('result is',result)
return result

}

实现一个EventEmitter

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class EventEmitter{

constructor(){
this.events = {}
}

// 监听
on(eventName, callback){
// 回调这里应该是个列表
if(!this.events[eventName]){
this.events[eventName] = [callback]
}else{
this.events[eventName].push(callback)
}
}

// 触发 注意传参数
emit(eventName,...args){
for(let func of this.events[eventName]){
func.apply(this, args)
}
}

// 取消
off(eventName, fn){
if(fn){
for(let func of this.events[eventName]){
if(func === fn){
let index = this.events[eventName].indexOf(fn)
this.events[eventName].splice(index,1)
break
}
}
}else{
Reflect.deleteProperty(this.events, eventName)
}
}

}


var events = new EventEmitter()
var clickHandler = function(){console.log('u click')}
events.on('click',clickHandler)
event.emit('click')
events.off('click',clickHandler)
event.emit('click')

插入大量dom节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var maxTimes= 4
var count = 1
// 隔1s 新增20个dom
function createDom(){
var frag = document.createDocumentFragment()
for(let i=1;i<=20;i++){
var dom = document.createElement('div')
dom.innerText = i
frag.appendChild(dom)
}
document.body.appendChild(frag)
count++
if(count<4){
window.requestAnimationFrame(createDom)
}

}

window.requestAnimationFrame(createDom)

async await

核心:顺次调用迭代器的next方法,将上一个next的返回值传给下个next作为参数,如果调用结果X.done=true,最后的promise resolve(X.value),否则继续调用next, 中间有异常的话直接抛出

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 spawn(genF){
return new Promise((resolve, reject)=>{
var iterator = genF()

function step(fn){
let res
try{
res = fn()

}catch(e){
reject(e)
}

if(res.done){
resolve(res.value)
}else{
Promise.resolve(res.value).then(
value => {step(()=>{return iterator.next(value)})},
err => {step(()=>{return iterator.throw(err)})}
)
}
}

step(()=>{return iterator.next(undefined)})// 封装了next方法,这个函数被调用是执行迭代器的next方法
})
}