如何在 JS 中“深冻结”对象
- 如果咱们想要确保对象被深冻结,就必须创建一个递归函数来冻结对象类型的每个属性:
2. 没有深冻结
let person = {
name: "Leonardo",
profession: {
name: "developer"
}
};
Object.freeze(person);
person.profession.name = "doctor";
console.log(person); //output { name: 'Leonardo', profession: { name: 'doctor' } }
3. 深冻结
function deepFreeze(object) {
let propNames = Object.getOwnPropertyNames(object)
for (let name of propNames) {
let value = object[name]
object[name] = value && typeof value === "object" ? deepFreeze(value) : value
}
return Object.freeze(object)
}
let person = {
name: "Leonardo",
profession: {
name: "developer"
}
}
deepFreeze(person)
person.profession.name = "doctor"
手写call()
export function call (fn, obj, ...args) {
if (obj===null || obj===undefined) {
obj = obj || window
}
obj.tempFn = fn
const result = obj.tempFn(...args)
delete obj.tempFn
return result
}
5.3. 手写apply()
export function apply (fn, obj, args) {
if (obj===null || obj===undefined) {
obj = obj || window
}
obj.tempFn = fn
const result = obj.tempFn(...args)
delete obj.tempFn
return result
}
手写bind()
import {call} from './call'
export function bind (fn, obj, ...args) {
if (obj===null || obj===undefined) {
obj = obj || window
}
return function (...args2) {
call(fn, obj, ...args, ...args2)
}
}
5.5. 手写一个防抖函数
export function debounce(callback, delay) {
return function () {
const that = this
const args = arguments
if (callback.timeoutId) {
clearTimeout(callback.timeoutId)
}
callback.timeoutId = setTimeout(function () {
callback.apply(that, args)
delete callback.timeoutId
}, delay)
}
}
手写一个节流函数
export function throttle(callback, delay) {
let start = 0
return function () {
console.log('throttle 事件')
const current = Date.now()
if (current - start > delay) {
callback.apply(this, arguments)
start = current
}
}
}
手写一个深拷贝函数
export function deepClone1(target) {
return JSON.parse(JSON.stringify(target))
}
function getType(data) {
return Object.prototype.toString.call(data).slice(8, -1)
}
export function deepClone2(target) {
const type = getType(target)
if (type==='Object' || type==='Array') {
const cloneTarget = type === 'Array' ? [] : {}
for (const key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = deepClone2(target[key])
}
}
return cloneTarget
} else {
return target
}
}
export function deepClone3(target, map = new Map()) {
const type = getType(target)
if (type==='Object' || type==='Array') {
let cloneTarget = map.get(target)
if (cloneTarget) {
return cloneTarget
}
cloneTarget = type==='Array' ? [] : {}
map.set(target, cloneTarget)
for (const key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = deepClone3(target[key], map)
}
}
return cloneTarget
} else {
return target
}
}
export function deepClone4(target, map = new Map()) {
const type = getType(target)
if (type==='Object' || type==='Array') {
let cloneTarget = map.get(target)
if (cloneTarget) {
return cloneTarget
}
if (type==='Array') {
cloneTarget = []
map.set(target, cloneTarget)
target.forEach((item, index) => {
cloneTarget[index] = deepClone4(item, map)
})
} else {
cloneTarget = {}
map.set(target, cloneTarget)
Object.keys(target).forEach(key => {
cloneTarget[key] = deepClone4(target[key], map)
})
}
return cloneTarget
} else {
return target
}
}
自定义instanceof工具函数
export function myInstanceOf(obj, Type) {
let protoObj = obj.__proto__
while(protoObj) {
if (protoObj === Type.prototype) {
return true
}
protoObj = protoObj.__proto__
}
return false
}
自定义new工具函数
export function newInstance(Fn, ...args) {
const obj = {}
const result = Fn.apply(obj, args)
if (result instanceof Object) {
return result
}
obj.__proto__.constructor = Fn
return obj
}
手写axios函数
function axios({
url,
method='GET',
params={},
data={}
}) {
return new Promise((resolve, reject) => {
method = method.toUpperCase()
let queryString = ''
Object.keys(params).forEach(key => {
queryString += `${key}=${params[key]}&`
})
if (queryString) {
queryString = queryString.substring(0, queryString.length-1)
url += '?' + queryString
}
const request = new XMLHttpRequest()
request.open(method, url, true)
if (method==='GET') {
request.send()
} else if (method==='POST' || method==='PUT' || method==='DELETE'){
request.setRequestHeader('Content-Type', 'application/json;charset=utf-8')
request.send(JSON.stringify(data))
}
request.onreadystatechange = function () {
if (request.readyState!==4) {
return
}
const {status, statusText} = request
if (status>=200 && status<=299) {
const response = {
data: JSON.parse(request.response),
status,
statusText
}
resolve(response)
} else {
reject(new Error('request error status is ' + status))
}
}
})
}
axios.get = function (url, options) {
return axios(Object.assign(options, {url, method: 'GET'}))
}
axios.delete = function (url, options) {
return axios(Object.assign(options, {url, method: 'DELETE'}))
}
axios.post = function (url, data, options) {
return axios(Object.assign(options, {url, data, method: 'POST'}))
}
axios.put = function (url, data, options) {
return axios(Object.assign(options, {url, data, method: 'PUT'}))
}
export default axios
自定义事件总线
/*
* 自定义事件总线
*/
const eventBus = {}
/*
{
add: [callback1, callback2]
delete: [callback3]
}
*/
let callbacksObj = {}
/*
绑定事件监听
*/
eventBus.on = function (eventName, callback) {
const callbacks = callbacksObj[eventName]
if (callbacks) {
callbacks.push(callback)
} else {
callbacksObj[eventName] = [callback]
}
}
/*
分发事件
*/
eventBus.emit = function (eventName, data) {
const callbacks = callbacksObj[eventName]
if (callbacks && callbacks.length > 0) {
callbacks.forEach(callback => {
callback(data)
})
}
}
/*
移除事件监听
*/
eventBus.off = function (eventName) {
if (eventName) {
delete callbacksObj[eventName]
} else {
callbacksObj = {}
}
}
export default eventBus
自定义消息订阅与发布
const PubSub = {}
let callbacksObj = {}
let id = 0
PubSub.subscribe = function (msgName, callback) {
const token = 'token_' + ++id
const callbacks = callbacksObj[msgName]
if (!callbacks) {
callbacksObj[msgName] = {
[token]: callback
}
} else {
callbacks[token] = callback
}
return token
}
PubSub.publish = function (msgName, data) {
let callbacks = callbacksObj[msgName]
if (callbacks) {
setTimeout(() => {
Object.values(callbacks).forEach(callback => {
callback(data)
})
}, 0)
}
}
PubSub.publishSync = function (msgName, data) {
const callbacks = callbacksObj[msgName]
if (callbacks) {
Object.values(callbacks).forEach(callback => {
callback(data)
})
}
}
PubSub.unsubscribe = function (flag) {
if (flag === undefined) {
callbacksObj = {}
} else if (typeof flag === 'string') {
if (flag.indexOf('token_') === 0) {
const callbacks = Object.values(callbacksObj).find(callbacks => callbacks.hasOwnProperty(flag))
if (callbacks) {
delete callbacks[flag]
}
} else {
delete callbacksObj[flag]
}
} else {
throw new Error('如果传入参数, 必须是字符串类型')
}
}
export default PubSub
自定义数组声明式系列方法
export function map (array, callback) {
const arr = []
for (let index = 0; index < array.length; index++) {
arr.push(callback(array[index], index))
}
return arr
}
export function reduce (array, callback, initValue) {
let result = initValue
for (let index = 0; index < array.length; index++) {
result = callback(result, array[index], index)
}
return result
}
export function filter(array, callback) {
const arr = []
for (let index = 0; index < array.length; index++) {
if (callback(array[index], index)) {
arr.push(array[index])
}
}
return arr
}
export function find (array, callback) {
for (let index = 0; index < array.length; index++) {
if (callback(array[index], index)) {
return array[index]
}
}
return undefined
}
export function findIndex (array, callback) {
for (let index = 0; index < array.length; index++) {
if (callback(array[index], index)) {
return index
}
}
return -1
}
export function every (array, callback) {
for (let index = 0; index < array.length; index++) {
if (!callback(array[index], index)) {
return false
}
}
return true
}
export function some (array, callback) {
for (let index = 0; index < array.length; index++) {
if (callback(array[index], index)) {
return true
}
}
return false
}
export function test() {
console.log('test()222')
}
手写Promise
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function Promise(excutor) {
const self = this
self.status = PENDING
self.data = undefined
self.callbacks = []
function resolve(value) {
if (self.status !== PENDING) return
self.status = RESOLVED
self.data = value
if (self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(cbsObj => {
cbsObj.onResolved(value)
})
})
}
}
function reject(reason) {
if (self.status !== PENDING) return
self.status = REJECTED
self.data = reason
if (self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(cbsObj => {
cbsObj.onRejected(reason)
})
})
}
}
try {
excutor(resolve, reject)
} catch (error) {
console.log('-----')
reject(error)
}
}
Promise.prototype.then = function (onResolved, onRejected) {
const self = this
onResolved = typeof onResolved === 'function' ? onResolved : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason
}
return new Promise((resolve, reject) => {
function handle(callback) {
try {
const result = callback(self.data)
if (!(result instanceof Promise)) {
resolve(result)
} else {
result.then(
value => resolve(value),
reason => reject(reason)
)
}
} catch (error) {
reject(error)
}
}
if (self.status === RESOLVED) {
setTimeout(() => {
handle(onResolved)
})
} else if (self.status === REJECTED) {
setTimeout(() => {
handle(onRejected)
})
} else {
self.callbacks.push({
onResolved(value) {
handle(onResolved)
},
onRejected(reason) {
handle(onRejected)
}
})
}
})
}
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected)
}
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(resolve, reject)
} else {
resolve(value)
}
})
}
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let resolvedCount = 0
const values = new Array(promises.length)
promises.forEach((p, index) => {
p.then(
value => {
resolvedCount++
values[index] = value
if (resolvedCount === promises.length) {
resolve(values)
}
},
reason => reject(reason)
)
})
})
}
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach(p => {
p.then(resolve, reject)
})
})
}
Promise.resolveDelay = function (value, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (value instanceof Promise) {
value.then(resolve, reject)
} else {
resolve(value)
}
}, time)
})
}
Promise.rejectDelay = function (reason, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(reason)
}, time)
})
}
export default Promise
自定义数组扁平化
export function flatten1 (array) {
return array.reduce((pre, item) => {
if (Array.isArray(item)) {
return pre.concat(flatten1(item))
} else {
return pre.concat(item)
}
}, [])
}
export function flatten2 (array) {
let arr = [].concat(...array)
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr)
}
return arr
}