部分JS原生方法手写
Array
let arr = [{
name: '科比',
num: 24
},
{
name: '詹姆斯',
num: 23
},
{
name: '保罗',
num: 3
},
{
name: '威少',
num: 1
}
]
forEach
对数组每一项元素进行操作,改变原数组
Array.prototype.myForeach = function (fn) {
for (let i = 0; i < this.length; i++) {
fn(this[i], i)
}
}
arr.myForeach((val, index) => {
console.log('val', val, 'index', index);
})
arr.forEach((val, index) => {
console.log('val', val, 'index', index);
})
map
对数组每一项元素进行操作,返回新数组,不改变原数组
Array.prototype.myMap = function (fn) {
let a = []
for (let i = 0; i < this.length; i++) {
a.push(fn(this[i], i, this))
}
return a
}
console.log(arr.myMap((item, index, arr) => item.num * 2));
console.log(arr.map((item, index, arr) => item.num * 2));
filter
返回所有满足条件元素组合的新数组,不改变原数组
Array.prototype.myFilter = function (fn) {
let a = []
for (let i = 0; i < this.length; i++) {
fn(this[i], i, this) ? a.push(this[i]) : ''
}
return a
}
console.log(arr.filter(item => item.num > 4));
console.log(arr.myFilter((item, index, arr) => item.num > 4))
every
当所有元素满足条件时,返回true,否则false,不改变原数组
Array.prototype.myEvery = function (fn) {
let a = 0
for (let i = 0; i < this.length; i++) {
fn(this[i], i, this) ? a++ : ''
}
return a === this.length
}
console.log(arr.every((item, index, arr) => item.num > 0));
console.log(arr.myEvery((item, index, arr) => item.num > 0));
some
只要有一项元素满足条件时,返回true,否则false,不改变原数组
Array.prototype.mySome = function (fn) {
//方法1
let a = 0
for (let i = 0; i < this.length; i++) {
fn(this[i], i, this) ? a++ : ''
}
return !!a
//方法2
for (let i = 0; i < this.length; i++) {
if (fn(this[i], i, this)) return true
}
return false
}
console.log(arr.some((item, index, arr) => item.num > 4))
console.log(arr.mySome((item, index, arr) => item.num > 4))
reduce
累加器,返回最终结果,不改变原数组
Array.prototype.myReduce = function (fn, val) {
for (let i = 0; i < this.length; i++) {
val = fn(val, this[i], i, this)
}
return val
}
console.log(arr.reduce((val, item, index, arr) => val + item.num, 0))
console.log(arr.myReduce((val, item, index, arr) => val + item.num, 0))
findIndex
数组中有满足条件的元素时,返回该元素的索引,否则返回-1,不改变原数组
indexOf
需要具体值
Array.prototype.myFindIndex = function (fn) {
for (let i = 0; i < this.length; i++) {
if (fn(this[i], i, this)) return i
}
return -1
}
console.log(arr.findIndex((item, index, arr) => item.name === '威少'))
console.log(arr.myFindIndex((item, index, arr) => item.name === '威少'))
find
返回满足条件的元素,否则返回undefinded,不改变原数组
Array.prototype.myFind = function (fn) {
for (let i = 0; i < this.length; i++) {
if (fn(this[i], i, this)) return this[i]
}
return undefined
}
console.log(arr.find((item, index, arr) => item.name === '科比'));
console.log(arr.myFind((item, index, arr) => item.name === '科比'));
fill
用指定元素填充数组中指定的元素,改变原数组
Array.prototype.myFill = function (val, start, end) {
//初始值为负时,返回原数组
if (start < 0) return this
//超出长度时,设置为末元素
let final = end < this.length ? end : this.length
for (let i = start; i < final; i++) {
this[i] = val
}
return this
}
console.log(arr.fill('jzy', 1, 3))
console.log(arr.myFill('jzy', 1, 3))
includes
判断数组中是否有给出的元素,若有则返回true,否则为false,能够判断NaN
,不改变原数组
Array.prototype.myIncludes = function (val) {
for (let i = 0; i < this.length; i++) {
if (String(this[i]) === String(val)) return true
}
return false
}
console.log([1, 2, 3, 4, NaN].includes(NaN))
console.log([1, 2, 3, 4, NaN].myIncludes(NaN))
join
用指定元素将数组拼接为字符串,不改变原数组
Array.prototype.myJoin = function (val) {
let str = ''
for (let i = 0; i < this.length; i++) {
i == this.length - 1 ? str += String(this[i]) : str += String(this[i]) + val
}
return str
}
console.log([1, 2, 3, 4, NaN].join('*'));
console.log([1, 2, 3, 4, NaN].myJoin('*'));
flat
展开多重数组,展开次数为传入参数,不改变原数组
Array.prototype.myFlat = function (val) {
//默认次数为1
val = Number(val) ? Number(val) : 1
let arr = []
//当参数大于需要展开次数时,返回完全展开的数组
while (!this.some(item => Array.isArray(item))) {
return this
}
//展开一次数组
this.forEach(item => {
Array.isArray(item) ? arr = arr.concat(item) : arr.push(item)
})
//递归实现指定次数展开
return val === 1 ? arr : arr.myFlat(--val)
}
console.log([1, 2, 3, 4, [5, 6, [7, 8, [9, 0]]]].flat(2))
console.log([1, 2, 3, 4, [5, 6, [7, 8, [9, 0]]]].myFlat(2))
splice
截取指定长度的元素,返回截取元素组成的数组,在原数组中删除截取元素,并插入指定元素
Array.prototype.mySplice = function (x = this.length + 1, y = this.length, ...args) {
//处理x,y
x = Math.floor(x)
y = Math.floor(y)
x = x < 0 ? this.length + x : x
x = x < 0 ? 0 : x
y = x + y <= this.length ? y : this.length - x
let arr = []
if (x < this.length && y >= 0) {
let arr_ = []
for (let i = 0; i < this.length; i++) {
//原数组插入
if (x === i && !y) {
for (let i = 0; i < args.length; i++) {
arr_.push(args[i])
}
}
//原数组保留保留
if (x > i || !y) arr_.push(this[i])
//删除、返回对象
if (x === i && y) {
arr.push(this[i])
x++
y--
}
}
//清空this,重新赋值
this.length = 0
for (let i = 0; i < arr_.length; i++) {
this[i] = arr_[i]
}
}
return arr
}
let arr = [...str]
let arr2 = [...str]
console.log(arr.mySplice());
console.log(arr);
console.log(arr2.splice());
console.log(arr2);
Object
Tips:
对对象使用forin
方法时,会遍历原型链上的自定义属性
let obj = {
name: 'J1nzy',
age: 22,
sex: '男',
hobby: {
food: '糯米藕'
}
}
entries
将对象拆分为键值对的数组形式
Object.prototype.myEntries = function (obj) {
let arr = []
for (const key in obj) {
(Object.hasOwnProperty.call(obj, key)) && arr.push([key, obj[key]])
}
return arr
}
console.log(Object.entries(obj))
console.log(Object.myEntries(obj))
fromEntries
将键值对的数组转化为对象形式
Object.prototype.myFromEntries = function (arr) {
let obj = {}
for (let i = 0; i < arr.length; i++) {
let [key, value] = arr[i]
obj[key] = value
}
return obj
}
console.log(Object.fromEntries([
['name', 'J1nzy'],
['age', '22']
]))
console.log(Object.myFromEntries([
['name', 'J1nzy'],
['age', '22']
]))
keys
返回以对象每个键组成的数组
Object.prototype.myKeys = function (obj) {
let arr = []
for (const key in obj) {
Object.hasOwnProperty.call(obj, key) && arr.push(key)
}
return arr
}
console.log(Object.keys(obj))
console.log(Object.myKeys(obj))
values
返回以对象每个值组成的数组
Object.prototype.myValues = function () {
let arr = []
for (const key in obj) {
Object.hasOwnProperty.call(obj, key) && arr.push(obj[key])
}
return arr
}
console.log(Object.values(obj))
console.log(Object.myValues(obj))
instanceOf
若Father.prototype
在person
的原型链上,返回true,否则为false
function Father(){}
function Son() {}
Son.prototype.__proto__ = Father.prototype
let person = new Son()
function myInstanceOf(father,son) {
while (son.__proto__) {
if (son.__proto__ === father.prototype) return true
son = son.__proto__
}
return false
}
console.log(myInstanceOf(Father,person))
console.log(person instanceof Father)
is
判断两个对象是否相等,相等则返回true,否则false
Object.prototype.myIs = function (x, y) {
if (x === y) {
//处理+0和-0
return x !== 0 || 1 / x === 1 / y;
} else {
//处理NaN
return x !== x && y !== y;
}
}
let a = {
i: 1
}
let b = {
i: 1
}
let c = a
console.log(Object.myIs(a, b));//false
console.log(Object.myIs(a, c));//true
assign
将第一个对象改变为一个拼接后的对象,若有相同属性,以后面为准,并作为返回值
Object.prototype.myAssign = function (arr, ...arrs) {
if (arr === null || arr === undefined) throw ('添加类型错误')
for (let i = 0; i < arrs.length; i++) {
for (const key in arrs[i]) {
if (arrs[i].hasOwnProperty(key)) {
arr[key] = arrs[i][key]
}
}
}
return arr
}
let a = {
a: 1,
b: 2
}
let b = {
a: 3,
c: 4
}
let c = {
a: 4,
d: 5
}
console.log(Object.myAssign(a, b, c) === a);//true
console.log(Object.myAssign(a, b, c));
console.log(a);
Function
let mei = new Person('小美')
let li = new Person('小丽')
function Person(name) {
this.name = name
}
Person.prototype.showName = function (age,a) {
console.log(this.name + age + '岁了' + a);
}
call
改变this的指向,默认为window,传参为队列
Function.prototype.myCall = function (caller, ...args) {
caller = caller || window
//唯一,避免与属性重复
let fn = Symbol()
caller[fn] = this
return caller[fn](...args)
}
mei.showName.myCall(li, 16)
li.showName.myCall(mei, 18)
apply
改变this的指向,默认为window,传参为数组
Function.prototype.myApply = function (caller, args) {
caller = caller || window
//唯一,避免与属性重复
let fn = Symbol()
caller[fn] = this
return caller[fn](...args)
}
mei.showName.apply(li, [16,12])
mei.showName.myApply(li, [16,12])
bind
返回一个改变this指向后函数,该函数返回值不变
Function.prototype.myBind = function (binder, ...args) {
binder = binder || window
let fn = Symbol()
binder[fn] = this
return function () {
return binder[fn](...args)
}
}
Object.prototype.test = function (...args) {
console.log(this.name);
console.log(args);
return '美'
}
let mei = {
name: '小美'
}
let li = {
name: '小丽'
}
console.log(mei.test.bind(li, 1, 2, 3)());
console.log(mei.test.myBind(li, 1, 2, 3)());
String
let str = '1234567890'
substr
截取指定长度的字符串,不改变原字符串
String.prototype.mySubstr = function (x = 0, y = this.length) {
//处理下x,y
x = Math.floor(x)
y = Math.floor(y)
x = x < 0 ? this.length + x : x
x = x < 0 ? 0 : x
y = y < 0 ? 0 : y
let str = ''
//截取字符并不超出字符串
while (y && x < this.length) {
str += this[x]
x++
y--
}
return str
}
console.log(str.substr(-3, 5));
console.log(str.mySubstr(-3, 5));
substring
截取指定位置的元素并返回其组成的数组,x>y
时,对调,负值置零,不改变原数组
String.prototype.mySubstring = function (x = 0, y = this.length) {
//处理下x,y
x = Math.floor(x)
y = Math.floor(y)
[x, y] = x > y ? [y, x] : [x, y]
x = x < 0 ? 0 : x
let str = ''
//当y大于字符串长度时,设置为字符串长度,y<0时返回空字符串
while (x < (y = y > this.length ? this.length : y) && y >= 0) {
str += this[x]
x++
}
return str
}
console.log(str.substring(4, -1));
console.log(str);
console.log(str.mySubstring(-2, -1));
slice
截取指定位置的元素并返回其组成的数组,负值会处理,不改变原数组
String.prototype.mySlice = function (x = 0, y = this.length) {
//处理下x,y
x = Math.floor(x)
y = Math.floor(y)
x = x < 0 ? this.length + x : x
y = y < 0 ? this.length + y : y
x = x < 0 ? 0 : x
y = y < 0 ? 0 : y
let str = ''
//当y大于字符串长度时,设置为字符串长度
while (x < (y = y > this.length ? this.length : y)) {
str += this[x]
x++
}
return str
}
console.log(str.slice(1, 10));
console.log(str.mySlice(1, 13));