;(function () {
// 一个函数无论我们 bind 几次, this 永远由第一次 bind 决定
const a = {}
const fn = function () {
console.log(this)
}
fn.bind().bind(a)()
// 换种写法
const fn2 = function () {
return function () {
return fn.apply()
}.apply(a)
}
fn2()
})()
// 闭包第一题
;(function () {
function createIncrement() {
let count = 0
function increment() {
count++
}
let message = `Count is ${count}`
function log() {
console.log(message)
}
return [increment, log]
}
const [increment, log] = createIncrement()
increment()
increment()
increment()
log() // => ?
})()
// 闭包第二题
;(function () {
function fn() {
a = 0
return function (b) {
return b + a++
}
}
var f = fn()
console.log(f(5)) // 5
console.log(fn()(5)) // 5
console.log(f(5)) // 6
console.log(a) // 2
})()
;(function () {
/**
* 简易版深拷贝函数
*/
const deepClone = obj => {
if (typeof obj !== 'object') {
throw new Error('发生错误')
}
const newObj = obj instanceof Array ? [] : {}
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
const value = obj[key]
newObj[key] = typeof value === 'object' ? deepClone(value) : value
}
}
return newObj
}
})()
;(function () {
// 变量提升转换前
console.log(a) // undefined
var a = 1
function b() {
console.log(a)
}
b() // 1
// 变量提升转换前后
var a = undefined
console.log(a) // undefined
a = 1
function b() {
console.log(a)
}
b() // 1
})()
;(function () {
/**
* 组合继承:子类的构造函数中通过 Parent.call(this) 继承父类的属性,
* 然后改变子类的原型为 new Parent() 来继承父类的函数
*/
function Parent(value) {
this.value = value
}
Parent.prototype.getValue = function () {
console.log(this.value)
}
function Child(value) {
Parent.call(this, value) // 构造函数可以传参,不会与父类引用属性共享
}
Child.prototype = new Parent() // 可以复用父类的函数,但是子类原型上多了不需要的父类的属性
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
/**
* Class 继承,关键点在于 extends、super
*/
class Parent {
constructor(value) {
this.value = value
}
getValue() {
console.log(this.value)
}
}
class Child extends Parent {
constructor(value) {
super(value)
this.value = value
}
}
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
})()
;(function () {
/**
* 通过自定义 set 和 get 函数的方式,在原本的逻辑中插入了我们的函数
* 逻辑(回调函数),实现了在对对象任何属性进行读写时发出通知
*/
let obj = { a: 1 }
let onWatch = (target, setCallback, getCallback) => {
return new Proxy(target, {
set(target, key, value, receiver) {
setCallback(key, value)
return Reflect.set(target, key, value, receiver)
},
get(target, key, receiver) {
getCallback(target, key)
// return target[key]
return Reflect.get(target, key, receiver)
},
})
}
let p = onWtch(
obj,
(k, v) => {
// 数据变化,响应式监听
console.log(`监测到属性${k}改变为${v}`)
},
(t, k) => {
// 数据读取,响应式派发
console.log(`'${k}' = ${t[k]}`)
}
)
p.a = 3 // 监测到属性a改变为3
p.a // 'a' = 3
})()
;(function () {
/**
* 手写 Promise
* 作用:1、消灭嵌套调用;2、合并多个任务的请求结果
* API: Promise.resolve, Promise.reject, Promise.prototype.catch,
* Promise.prototype.finally, Promise.all, Promise.race
*/
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function MyPromise(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onResolvedCallbacks = []
this.onRejectedCallbacks = []
const resolve = value => {
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onResolvedCallbacks.forEach(fn => fn())
}
}
const reject = reason => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
onResolved = typeof onResolved === 'function' ? onResolved : v => v
onRejected =
typeof onRejected === 'function'
? onRejected
: err => {
throw err
}
if (this.status === RESOLVED) {
onResolved(this.value)
}
if (this.status === REJECTED) {
onRejected(this.reason)
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
onResolved(this.value)
})
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
}
}
})()
;(function () {
function mySetInterval(callback, interval) {
let timer
const now = Date.now
let startTime = now()
let endTime = startTime
const loop = () => {
timer = window.requestAnimationFrame(loop)
endTime = now()
if (endTime - startTime >= interval) {
startTime = endTime = now()
callback(timer)
}
}
timer = window.requestAnimationFrame(loop)
return timer
}
let a = 0
mySetInterval(timer => {
console.log('1')
a++
if (a === 3) {
window.cancelAnimationFrame(timer)
}
}, 1000)
})()
;(function () {
/**
* Event loop(执行一个宏任务,执行所有微任务,再继续如此循环)
* log:1,4,8,7,3,9,6,5,2
*/
;(function () {
function test() {
console.log(1)
setTimeout(function () {
console.log(2)
}, 1000)
}
test()
setTimeout(function () {
Promise.resolve().then(() => {
console.log(9)
})
console.log(3)
})
new Promise(function (resolve) {
console.log(4)
setTimeout(function () {
console.log(5)
}, 100)
resolve()
}).then(function () {
setTimeout(function () {
console.log(6)
}, 0)
console.log(7)
})
console.log(8)
})()
})()
;(function () {
/**
* apply、call 的模拟实现,这两个方法被调用时,函数会立即执行,并返回结果
*/
Function.prototype.myCall = function (context) {
const context = context || window
context.fn = this
const args = []
for (let i = 1; i < arguments.length; i++) {
args.push('arguments[' + i + ']') // 由于后面会使用 eval 表达式,所以不能直接 push 具体的值
}
const result = eval('context.fn(' + args + ')')
delete context.fn
return result
}
Function.prototype.myApply = function (context, arr) {
const context = Object(context) || window
context.fn = this
let result
if (!arr) {
result = context.fn()
} else {
const args = []
for (let i = 0; i < arr.length; i++) {
args.push('arr[' + i + ']')
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result
}
/**
* bind 的模拟实现。bind 方法会创建一个新函数,这个函数并不会立即执行。当这个新函数被调用时,bind的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。
*/
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new Error('Function.prototype.bind - what is trying to be bound is not callable')
}
const self = this
const args = Array.prototype.slice.call(arguments, 1) // 此处的 arguments 为 bind 时传递的参数
const fNOP = function () {}
const fbound = function () {
/**
* 当作为构造函数时,this 指向实例,self 指向绑定函数,因为下面修改了 fbound.prototype 为 绑定函数的
* prototype,此时结果为 true,当结果为 true 的时候,this 指向实例。
*
* 当作为普通函数时,this 指向 window,self 指向绑定函数,此时结果为 false,当结果为 false 的时候,
* this 指向绑定的 context。
*/
self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments))) // 此处的 arguments 返回的函数执行时的参数,两处参数合并起来成为 bind 函数完整的参数
}
fNOP.prototype = this.prototype // 空函数中转,防止改变 fbound 函数的 prototype 时改变了原来函数的原型
fbound.prototype = new fNOP()
return fbound
}
})()
;(function () {
/**
* 模拟实现 new 操作。e.g. myNew(Person,18)
* @returns 新对象
*/
function myNew() {
const obj = new Object(), // 用new Object() 的方式新建了一个对象 obj
Constructor = [].shift.call(arguments) // 取出第一个参数,就是我们要传入的构造函数。此外因为 shift 会修改原数组,所以 arguments 会被去除第一个参数
obj.__proto__ = Constructor.prototype
// function Person(name, age) {
// this.strength = 60
// this.age = age
// return {
// name: name,
// habit: 'Games',
// }
// }
// var person = new Person('Kevin', '18')
// console.log(person.name) // Kevin
// console.log(person.habit) // Games
// console.log(person.strength) // undefined
// console.log(person.age) // undefined
const result = Constructor.apply(obj, arguments)
return typeof result === 'object' ? result : obj // 构造函数返回值如果是一个对象,就返回这个对象,如果不是,就该返回什么就返回什么
}
})()
;(function () {
/**
* 自定义instanceof函数
*/
function myInstanceof(left, right) {
const prototype = right.prototype
let left = left.__proto__
while (true) {
if (left === null || left === undefined) {
return false
}
if (prototype === left) {
return true
}
left = left.__proto__
}
}
})()
;(function () {
// 以下会先打印冒泡然后是捕获
node.addEventListener(
'click',
event => {
console.log('冒泡')
},
false
)
node.addEventListener(
'click',
event => {
console.log('捕获')
},
true
)
})()
;(function () {
/**
* url 链接返回的是:[callbackName]({code:0, data:[], msg: ''})
* 在执行栈这个函数会立即执行
*/
const jsonp = (url, callbackName, success) => {
let script = document.createElement('script')
script.src = url
script.async = true
script.type = 'text/script'
window[callbackName] = function (data) {
success && success(data)
}
document.body.appendChild(script)
}
jsonp('https://xxx', 'callback', function (value) {
console.log(value)
})
})()
;(function () {
/**
* 防抖:事件触发,N秒之后执行。期间再次触发,则重新计算
*/
const debounce = (fn, wait, immediate) => {
let timer
const debounced = function (...args) {
timer && clearTimeout(timer)
if (immediate) {
const callNow = !timer
if (callNow) {
fn.apply(this, args)
}
timer = setTimeout(() => {
timer = null
}, wait)
} else {
timer = setTimeout(() => {
fn.apply(this, args)
}, wait)
}
}
return debounced
}
})()
;(function () {
/**
* 节流:事件触发,马上执行,N秒之内,事件不再执行,N秒结束之时,再执行一次
*/
const throttle = (fn, wait) => {
let timer,
previous = 0
const throttled = function (...args) {
const now = +new Date()
const remaining = wait - (now - previous)
if (remaining <= 0 || remaining > wait) {
if (timer) {
clearTimeout(timer)
timer = null
}
previous = now
fn.apply(this, args)
} else if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args)
previous = +new Date()
timer = null
}, remaining)
}
}
return throttled
}
})()
;(function () {
// 通过Dep解耦属性的依赖和更新操作
class Dep {
constructor() {
this.subs = []
}
// 收集依赖
add(sub) {
this.subs.push(sub)
}
// 通知更新
notify() {
this.subs.forEach(sub => {
sub.update()
})
}
}
Dep.target = null // 全局属性,通过该属性配置watcher
class Watcher {
constructor(obj, key, callback) {
// 将 Dep.target 指向自己
// 然后触发属性的 getter 触发监听
// 最后将 Dep.target 置为空
Dep.target = this
this.callback = callback
this.obj = obj
this.key = key
this.val = obj[key]
Dep.target = null
}
update() {
// 获得最新值
this.val = this.obj[this.key]
// 调用 update 方法 更新 dom
this.callback(this.val)
}
}
// 观察、监听
function observe(obj) {
if (!obj || typeof obj !== 'object') {
return
}
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
})
}
function defineReactive(obj, key, val) {
// 递归子属性
observe(val)
let dp = new Dep()
Object.defineProperty(obj, key, {
enumerable: true, // 可枚举
configurable: true, // 可配置
// 自定义函数
set: function reactiveSetter(newVal) {
val = newVal
dp.notify() // 值变了以后才去通知
},
get: function reactiveGetter() {
if (Dep.target) {
// 只有在实例化watcher的时候依赖才会被添加进去
dp.add(Dep.target)
}
return val
},
})
}
var data = { name: 'xiaoming' }
observe(data)
function update(value) {
console.log(`watch 到新的值:${value}`)
}
new Watcher(data, 'name', update)
data.name = 'xiaowang'
})()
;(function () {
// 给 localstorage 设置过期时间
Storage.prototype.setExpire = (key, value, expire) => {
let obj = {
data: value,
time: Date.now(),
expire: expire,
}
localStorage.setItem(key, JSON.stringify(obj))
}
Storage.prototype.getExpire = key => {
let val = localStorage.getItem(key)
if (!val) {
return val
}
val = JSON.parse(val)
if (Date.now() - val.time > val.expire) {
localStorage.removeItem(key)
return null
}
return val.data
}
})()