call: 简单来说就是改变执行方法当前的this,可以传入不定参数
Function.prototype.myCall = function(context, ...args) {
context.fn = this
const result = context.fn(...args)
delete context.fn
return result
}
getMessage.myCall(obj, 'name')
立即执行getMessage方法,不过是以obj.getMessage的方式,
所以这个时候getMessage内的this是obj,传入参数'name'。
(obj可能压根就没有getMessage方法)
apply: 和call作用一致,传递参数格式不同,需是数组
Function.prototype.myApply = function(context, args) {
context.fn = this
const result = context.fn(args)
delete context.fn
return result
}
getMessage.myApply(obj, ['name'])
bind: 改变指定方法的this后执行,以函数的形式返回执行结果
Function.prototype.myBind = function(context, ...args) {
return () => {
return this.myApply(context, args);
}
}
const result = getMessage.myBind(obj)
result是一个函数,再执行一次才是getMessage方法的执行结果
new:将构造函数实例化,将参数创建为对象以及赋值原型方法
function createNew(Ctor, ...args) {
const obj = Object.create(Ctor.prototype);
const ret = Ctur.apply(obj, args);
return ret instanceof Object ? ret : obj;
}
1. 将构造函数的原型赋值给新建的obj的隐式原型__proto__。
2. 在obj下执行构造函数,并传入参数,
这个时候构造函数内的this就是obj。
3. 如果这个'构造函数'没有return对象格式的结果,
返回新创建的obj。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.getName = function() {
console.log(this.name);
}
const xm = createNew(Person, 'xiaoming', 22);
instanceof: 判断一个变量是否是某个类型
function myInstanceOf(left, right) {
while(1) {
if(left.__proto__ === null) {
return false;
}
if(left.__proto__ === right.prototype) {
return true;
}
left = left.__proto__;
}
}
instanceof的原理就是通过原型链查找,
所以一直向上查找左侧的隐式原型__ptoto__是否等于右侧显式原型,
原型链的尽头是null,没找到就返回false。
myInstanceOf([1,2], Array);
forEach: 遍历数组,这个大家经常用,想必不说都懂
Array.prototype.myForEach = function(fn) {
const arr = this
for(let i = 0
fn(arr[i], i, arr)
}
}
接受一个fn回调函数,传递给回调函数三个参数:
每项的值,下标,自身。第二个参数有人用么?
const arr = ['a','b','c']
arr.myForEach(item => {
console.log(item)
})
map: 返回经过处理的数组
Array.prototype.myMap = function(fn) {
const arr = this
const ret = []
for(let i = 0
ret.push(fn(arr[i], i, arr))
}
return ret
}
和forEach类似也是接受一个fn回调,
不过会将回调处理的结果放入一个新的数组,
所以map回调内的每一项需要return,
因为要组成新的数组结果。
const arr = ['a', 'b', 'c']
const newArr = arr.myMap(item => { // a1 b1 c1
return item + 1
})
filter: 返回回调处理结果为true的新数组
Array.prototype.myFilter = function(fn) {
const arr = this
const ret = []
for(let i = 0
if(fn(arr[i], i, arr)) {
ret.push(arr[i])
}
}
return ret
}
大同小异,过滤出处理条件为true的值。
返回数组中不重复的值:
function repeat(arr) {
return arr.myFilter((v, i, a) => {
return a.indexOf(v) === a.lastIndexOf(v)
})
}
const arr = [1,2,3,4,1,2,3,5,6,8,3]
repeat(arr)
find:返回处理条件第一个为true的数组项
Array.prototype.myFind = function(fn) {
const arr =this
for(let i = 0
if(fn(arr[i], i, arr)) {
return arr[i]
}
}
}
否则返回undefined
findIndex: 返回处理条件第一个为true的数组下标
大家自己写下咯~
every:如果数组每一项都符合处理条件。返回true,否则返回false
Array.prototype.myEvery = function(fn) {
const arr = this
for(let i = 0
if(!fn(arr[i], i, arr)) {
return false
}
}
return true
}
some:只要数组有一项符合处理条件。返回true,都不满足返回false。
这个相信大家都知道怎么写了~
reduce: 一般为数组做累计结果使用。
Array.prototype.myReduce = function(fn, second) {
const arr = this
let index = 0
if(typeof second === 'undefined') { // 没传第二个参数
index = 1
second = arr[0]
}
for(let i = index
const invoked = fn(second, arr[i], i, arr)
second = invoked
}
return second
}
一般会传入第二个参数作为初始值,如果没有传入,
初始值就是数组的第一项,将处理的结果进行累计,
最后返回累计的结果。
返回数组中指定参数重复的次数:
function count(arr, value) {
return arr.myReduce((f, s) => {
return Object.is(s, value) ? f + 1 : f + 0
}, 0)
}
const arr = [1,2,3,4,1,2,3,2,1]
count(arr, 2)
map:使用reduce模仿map
Array.prototype.myMap = function (fn) {
return this.reduce((pre, cur, i) => {
return [...pre, fn(cur, i, this)]
}, [])
}
includes:传入某项检测
Array.prototype.myIncludes = function (item) {
const arr = this;
for (let i = 0; i < arr.length; i++) {
if (Object.is(arr[i], item)) {
return true;
}
}
return false;
}
debounce: 函数防抖
function debounce(fn, delay = 1000) {
let timer;
return () => {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments);
}, delay)
}
}
函数防抖指的是一定时间内没有再次触发函数,就执行该函数,否则重新计时。
wow为例:
2.5s施法的寒冰箭,再读条的过程中,
你身子抖动打断了施法,想再次触发技能时麻烦您重新读条。
throttle:函数节流
function throttle(fn, delay = 100) {
let timer;
return () => {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay)
}
}
}
函数节流指的是规定某个时间内只能执行一次函数。
wow为例:
火冲为瞬发技能,不过你规定cd为8s,
所以即使8s内按了10次,也只能来1发,节省点体力吧。
deepClone:深拷贝
一般够用型
function deepClone(source) {
if (typeof source !== 'object' || source == null) {
return source
}
const target = Array.isArray(source) ? [] : {}
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = deepClone(source[key])
} else {
target[key] = source[key]
}
}
}
return target
}
解决循环引用和symblo类型
function cloneDeep(source, hash = new WeakMap()) {
if (typeof source !== 'object' || source === null) {
return source
}
if (hash.has(source)) {
return hash.get(source)
}
const ret = Array.isArray(source) ? [] : {}
Reflect.ownKeys(source).forEach(key => {
const val = source[key]
if (typeof val === 'object' && val != null) {
ret[key] = cloneDeep(val, hash)
} else {
ret[key] = val
}
})
return ret
}
Promise:手写简易版
class MyPromise {
constructor(fn) {
this.state = 'PENDING'
this.value = null
this.resolvedCallbacks = []
this.rejectedCallbacks = []
const resolve = value => {
if (this.state === 'PENDING') {
this.state = 'RESOLVED'
this.value = value
this.resolvedCallbacks.map(cb => cb())
}
}
const reject = value => {
if (this.state === 'PENDING') {
this.state = 'REJECTED'
this.value = value
this.rejectedCallbacks.map(cb => cb())
}
}
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r }
if (this.state === 'PENDING') {
this.resolvedCallbacks.push(() => {
onFulfilled(this.value)
})
this.rejectedCallbacks.push(() => {
onRejected(this.value)
})
}
if (this.state === 'RESOLVED') {
onFulfilled(this.value)
}
if (this.state === 'REJECTED') {
onRejected(this.value)
}
}
catch(fn) {
return this.then(null, fn)
}
static resolve(val) {
return new MyPromise(resolve => {
resolve(val)
})
}
static reject(err) {
return new MyPromise((resolve, reject) => {
reject(err)
})
}
static race(promises) {
return new MyPromise((resolve, reject) => {
for (let i = 0
promises[i].then(resolve, reject)
}
})
}
static all(promises) {
const arr = []
let i = 0
function processData(index, data, resolve) {
arr[index] = data
i++
if (i === promises.length) {
resolve(arr)
}
}
return new MyPromise((resolve, reject) => {
for (let i = 0
promises[i].then(data => {
processData(i, data, resolve)
}, reject)
}
})
}
}
iterator:不使用Generator
函数创建迭代器
function myIterator(items) {
let i = 0
return {
next() {
const done = i >= items.length
const value = !done ? items[i++] : undefined
return {
done, // 是否全部迭代完成
value // 返回迭代的值
}
}
}
}
const interator = myIterator([1, 2, 3])
interator.next()
Events:事件中心管理
class Events {
constructor() {
this._evnets = Object.create(null);
}
on(event, fn) {
if (Array.isArray(event)) {
for (let i = 0; i < event.length; i++) {
this.on(evnet[i], fn);
}
} else {
(this._evnets[event] || (this._evnets[event] = [])).push(fn);
}
}
emit(event, ...args) {
const cbs = this._evnets[event];
if (cbs) {
for (let i = 0; i < cbs.length; i++) {
cbs[i].apply(this, args);
}
}
}
off(event, fn) {
if (!arguments) {
this._evnets = Object.create(null);
return this;
}
if (Array.isArray(event)) {
for (let i = 0; i < event.length; i++) {
this.off(event[i], fn);
}
return this;
}
if (!fn) {
this._evnets[event] = null;
return this;
}
const cbs = this._evnets[event];
let i = cbs.length;
while (i--) {
const cb = cbs[i];
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1);
break;
}
}
return this;
}
once(evnet, fn) {
function on() {
this.off(evnet, on);
fn.apply(this, arguments);
}
on.fn = fn;
this.on(evnet, on);
return this;
}
}
const event = new Event();
event.on('test', (name, sex) => {
console.log(`${name}:${sex}`);
})
event.emit('test', 'cc', 'man');
evnet.off();
迭代器
const queue = []
const iterator = (hook, next) => {
hook(route, currnet, to => {
next(to)
})
}
const runQueue(queue, fn, cb) {
const step = index => {
if (index >= queue.length) {
cb()
} else {
if (queue[index]) {
fn(queue[index], () => {
step(index + 1)
})
} else {
step(index + 1)
}
}
}
step(0)
}
runQueue(queue, iterator, () => {
onComplete()
})
setInterval: 使用setTimeout模拟,并随时取消
function mySetInterval(fn, delay) {
let timer;
const loop = () => {
timer = setTimeout(() => {
loop();
fn();
}, delay);
};
loop();
return () => {
clearInterval(timer)
}
}
const stop = mySetInterval(() => {
console.log('test')
}, 200);
stop()
setInterval: 使用requestAnimationFrame模拟
function mySetInterval(fn, interval) {
const now = Date.now
let startTime = now()
const loop = () => {
const timer = requestAnimationFrame(loop)
if (now() - startTime >= interval) {
startTime = now()
fn.call(this, timer)
}
}
loop()
}
一般来说是不建议使用setInterval的,
如内部函数复杂就不能保证一定在规定时间内自动执行。
一般是通过setTimeout模仿setInterval。
那为什么要实现setInterval?
因为它内部的实现是使用requestAnimationFrame实现的,
该方法自带函数节流。
如有持续的动画需要执行,
基本会保证在16.6毫秒内执行一次,
提高动画性能并延时也是精确的。
mySetInterval(timer => {
console.log('a')
// cancelAnimationFram(timer)
})
写到最后