- 实现call方法,思路如下:
const obj = {
name:'zhangsan',
getName(){
console.log(this.name)
}
}
obj.getName() // zhangsan
此时,我们使getName 内部this指向obj,根据这个思路,我们只需要在执行之后delete getName,具体代码如下:
Function.prototype.myCall = function(ctx){
// 首先判断this,即需要改变this的方法对象是否为function
if(typeof this !== 'function') throw new Error('Type err')
// 是否传入指定this,没有指定则为window
ctx = ctx || window
// 获取传入的参数
const args = [...arguments].slice(1)
// 在ctx上添加fn,即为需要改变this指向的对象方法,避免fn重名,使用symbol类型
const uniFn = Synbol()
ctx[uniFn] = this
// 将参数传入,执行fn
const result = ctx[uniFn](...args)
// 删除临时添加的fn
delete ctx[uniFn]
// 返回最终结果
return result
}
- 实现apply方法,如上实现了call,基于call和apply的区别,即传入参数的结构不同,apply 传入的是数组,call传入多个参数,所以直接上代码:
Function.prototype.myApply = function(ctx){
if(typeof this !== 'function') throw new Error('Type err')
ctx = ctx || window
const args = arguments[1]
const uniFn = Synbol()
ctx[uniFn] = this
const result = ctx[uniFn](...args)
delet ctx[uniFn]
return result
}
- 实现bind方法,bind和apply的区别是bind返回一个方法,而不是直接返回执行结果,代码如下:
Function.prototype.myBind = function(ctx){
if(typeof this !== 'function') throw new Error('Type err')
ctx = ctx || window
const args = [...arguments].slice(1)
const uniFn = Symbol()
ctx.[uniFn] = this
const result = ()=> ctx.[uniFn](...args,...arguments)
return result
}
4.非递归方式实现二叉树先序遍历
function preOrder(head){
if(head!==null){
console.log('satrt')
// 利用栈先进后出的特性
const stack = [head]
while(stack.length){
head = stack.pop()
console.log(head.value)
// 由于栈先进后出,所以先处理右节点,后处理左节点
if(head.right) stack.push(head.right)
if(head.left) stack.push(head.left)
}
console.log('end')
}
}
5.非递归方式利用双栈实现二叉树后序遍历
function posOrder(head){
if(head!==null){
console.log('start')
const s1 = [head]
const s2 = []
while(head){
const head = s1.pop()
s2.push(head)
if(head.left) s1.push(head.left)
if(head.right) s1.push(head.right)
}
while(s2.length){
console.log(s2.pop().value)
}
}
}
6.非递归方式实现二叉树中序遍历
fonction inOrder(head){
const stack = []
while(head!==null || stack.length){
if(head!==null){
// 找到左节点的尽头
stack.push(head)
head = head.left
}else{
// 如果左节点为null,则回到head找到右节点,继续查找左节点
head = stack.pop()
// 处理节点
console.log(head.value)
head = head.right
}
}
}
7.非递归方式按层遍历二叉树
fonction layerOrder(head){
// 利用队列先进先出特性,只要队列不为空,则出列打印,接着将下一个节点入列
const queue = [head]
while(queue.length){
const cur = queue.shift()
console.log(cur)
if(cur.left) queue.push(cur.left)
if(cur.right) queue.push(cur.right)
}
}
8.实现防抖,支持立即执行
// 防抖的定义是在规定时间内连续做出同一个动作时,只执行最后一次或者执行第一次和最后一次,
// 例如获取验证码按钮一直点击,通常情况只需要最后一次触发生效
function debounce(fn, wait, immediate){
// 利用闭包维护一个变量timer,debounce再次执行时,timer为上一次缓存的值
let timer = null
return function(){
// 如果timer存在,则清除定时器,中止执行
if(timer) clearTimeout(timer)
// 暂存arguments与this
const args = arguments
const ctx = this
if(immediate){
// 如果需要立即执行
let callNow = !timer
// 在定时器里将timer置为null
timer = setTimeout(()=> timer = null,wait)
// 第一次触发的时候,timer为null,所以callNow为true,fn可立即执行,
// 执行之后timer已经赋值定时器,规定时间内定时器内timer没有重新赋值为null,
// 则不会再次执行fn
callNow && fn.apply(ctx, args)
}else{
// 首次不立即执行
timer = setTimeout(()=>{
fn.apply(ctx, args)
},wait)
}
}
}
9.实现节流(时间戳)
// 节流的定义是,在连续做出同一个动作时,在规定时间内只触发一次,例如监听页面的滚动以及
// 支持远程搜索的下拉选择
function throttle(fn, wait){
// 初次为0,否则第一次不触发
let preTime = 0
return function(){
// 暂存arguments与this
const args = arguments
const ctx = this
const now = new Date()
if(now - preTime < wait) return
fn.apply(ctx, args)
preTime = now
}
}
10.实现节流(定时器)
function(fn, wait){
let timer = null
if(timer) cleartTimeout(timer)
return function(){
// 暂存arguments与this
const args = arguments
const ctx = this
timer = setTimeout(()=>{
fn.apply(ctx, args)
timer = null
})
}
}
11.实现instanceof
// 首先得了解原型链的基本知识,instanceof 是判断origin是否在target的原型链上,即有一句话
// 这样描述,实例的__proto__指向构造函数的prototype,如果存在
// target.__proto__===origin.prototype,或者x=target.__proto__,
// x.__proto__===origin.prototype,即说明origin在target的原型链上,也就是
// target instanceof origin === true
// 综上,可以使用while实现
function myInstance(target, origin){
// 由于js自身存在的一个遗留bug,即 typeof null === 'object',而target类型必须是
// 引用类型的,所以需要处理
if(typeof target !=='object'||target === null) return false
if(typeof origin!=='function') return false
// js 规范不建议直接使用.__proto__,所以用getPrototypeOf(target)代替
let proto = Object.getPrototypeOf(target)
// 利用while沿着原型链查找,知道原型链尽头null
while(proto){
if(proto === origin.prototype) return true
// 一直找下去
proto = Object.getPrototypeOf(proto)
}
return false
}