类型判断
主要是利用 Object.prototype.toString.call() ,其中toString 方法返回反映这个对象的字符串。
如果此方法在自定义对象中未被覆盖,toString() 返回 “[object type]”,其中 type 是对象的类型。以下代码说明了这一点:
var o = new Object();
o.toString();
具体查看:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
不直接使用 obj.toString() 的原因: 在js中所有的对象类型(Array,Date,Function等)都是Object的实例,Object有一个toString方法也被这些实例继承下来,但是这些实例都无一例外的重写了toString方法,根据原型链的知识,直接调用toString,调用的只是重写过后的toString方法,所以也就不会像Object那样返回带有类型的字符串了。
自定义方法:
function typeOf(obj) {
return Object.prototype.toString.call(obj).slice(8,-1).toLowerCase()
}
new关键字执行的五个步骤,实现new
function mynew(Fn, ...args) {
const obj = {}
Object.setPrototypeOf(obj, Fn.prototype)
const res = Fn.apply(obj, args)
return res instanceof Object ? res : obj
}
数组扁平化
let arr = [1,2,[3,4],[5,6,[7,8,9]]]
console.log(arr.flat(Infinity))
console.log(arr.toString().split(',').map(Number))
console.log(arr.join().split(',').map(Number))
简单实现flat()
Array.prototype.flatten = function () {
let res = [], len = this.length
for (let i = 0; i < len; i++) {
if (Array.isArray(this[i])) {
res = res.concat(this[i].flatten())
} else res.push(this[i])
}
return res
}
const flatten = (arr) => {
return arr.reduce(
(acc, cur) => (Array.isArray(cur) ? acc.concat(flatten(cur)) : acc.concat(cur)),
[]
)
}
深拷贝
简单的实现方式:
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj))
}
局限很大,没法clone函数、symbol等。
call、apply、bind
手写call
Function.prototype.mycall = function(context) {
const ctx = context || globalThis
ctx.fn = this
const args = [...arguments].slice(1)
const res = ctx.fn(...args)
delete ctx.fn
return res
}
foreach、map、filter
手写foreach()
因为是第一个,所以再说下foreach 的几个特性:
myMap.forEach(callback[, thisArg])
forEach 方法将对 Map 中真实存在的每一个元素执行一次参数中提供的回调函数,它不会对任何已经被删除的元素执行调用。然而,它还会对键存在而值为 undefined 的元素执行调用。
callback 函数有三个参数:
value - 元素的值key - 元素的键Map - 当前正在被遍历的对象
不多说,直接上代码:
Array.prototype.myforEach = function (fn, context) {
if (typeof fn !== 'function')
throw new Error(fn + ' is not a function!')
const ctx = context || undefined
for (let i = 0; i < this.length; i++) {
if (!(i in this)) continue
fn.call(ctx, this[i], i, this)
}
}
做个测试:
let arr = [1, , 3, 4, undefined, NaN]
arr.forEach((item, index, val) => {
console.log(item, index, val[index])
})
console.log('---------------')
arr.myforEach((item, index, val) => {
console.log(item, index, val[index])
})
手写map()
和foreach差不多,不过会返回一个执行回调函数后的新数组。这里不多说了。
Map的几个注意点:
- Map生成的是一个新数组
- 有第二参数,为指定的this,如果没有则默认this丢失
- 对于稀疏数组,返回的仍然是稀疏数组(即map会保留“不存在”的元素)
Array.prototype.myMap = function (fn, context) {
if (typeof fn !== 'function')
throw new Error(fn + ' is not a function!')
let _arr = []
const ctx = context || Object.create(null)
for (let i = 0; i < this.length; i++) {
if (!(i in this)) _arr = _arr.concat([,])
else _arr.push(fn.call(ctx, this[i], i, arr))
}
return _arr
}
做个测试:
let arr = [1, , 2, 3, undefined, NaN]
console.log( arr.map((val, index) => val * index) )
console.log( arr.myMap((val, index) => val * index) )
手写filter()
filter的注意点:
- filter同样是创建一个新函数
- 有第二参数,为指定的this,如果没有则默认this丢失
- filter会直接跳过不存在的元素
Array.prototype.myFilter = function (fn, context) {
if (typeof fn !== 'function')
throw new Error(fn + ' is not a function!')
let _arr = []
const ctx = context || Object.create(null)
for (let i = 0; i <= this.length; i++) {
if(!this[i]) continue
if(fn.call(ctx, this[i], i, this)) _arr.push(this[i])
}
return _arr
}
做个测试:
let arr = [1, , 2, 3, undefined, NaN]
console.log(arr.filter((item, index) => item > index))
console.log(arr.myFilter((item, index) => item > index))
防抖
function debounce(fn, delay = 500) {
let timeout = null
return function () {
clearTimeout(timeout)
const ctx = this
const args = arguments;
timeout = setTimeout(function () {
fn.apply(ctx, args)
}, delay)
}
}
节流
一个普通版本的:
function throttle(fn, delay = 500) {
let timeout = null
return function () {
if (timeout) return
timeout = setTimeout((...args) => {
fn.apply(this, args)
timeout = null
}, delay)
}
}
|