实现数组api
forEach
原生的forEach
方法中接收2个参数 callback
和 thisArg
,并且 callback
函数传入三个参数,数组当前项的值,索引,数组本身
注意:由于 forEach 方法没有返回值,因此 forEach 不支持链式操作
Array.prototype.forEach2 = function(callback,thisArgv){
// 判断调用该API的元素是否为null
if(this == null){
throw new TypeError('this is null or not defined')
}
if(typeof callback!=="function"){
throw new TypeError(callback + ' is not a function')
}
let self = this;
for(let i = 0;i<this.length;i++){
callback.call(thisArgv,self[i],i,self)
}
}
let arr1 = [{
name: 'ljc',
age: 19
}, {
name: 'xy',
age: 18
}]
arr1.forEach((item,index,arr)=>{
console.log(item,index,arr);
item.age += 1
})
console.log(arr1);
map
与 forEach 方法相比,map 方法有返回值而 forEach 方法没有返回值。与forEach不同的是,map会返回一个新数组
map
中的每一个元素都要执行回调函数,所以必须要有 return,因此不能采用map
对数组进行过滤
map
也叫映射,也就是将原数组映射成一个新数组
- 数组中的每一个元素都会调用一个提供的函数后返回结果。
- 会新建一个数组,需要有承载对象,也就是会返回一个新的对象
- 除非用原有数组去承载,否则原有数组不会改变
使用方法
let arr = [1, 2, 3, 4, 5]
let newArr = arr.map(item => item * 2)
console.log(newArr);
// [2, 4, 6, 8, 10]
手写代码
Array.prototype.map2 = function(callback,thisArg){
// 和forEach相同需要进行两个排除
// 与forEach不同的是,map会返回一个新数组
if (this == undefined) {
throw new TypeError('this is null or not defined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
let self = this
let result = []
for(let i = 0;i < self.length; i++){
result[i] = callback.call(thisArg,self[i],i,self)
}
return result
}
let arr = [1, 2, 3, 4, 5]
let newArr = arr.map2(item => item * 2)
console.log(newArr);
filter
filter
从名字上看可以知道是它是用来做筛选过滤的。和map
一样,会返回一个新的对象数组,并不会改变原数组
用法:
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
Array.prototype.filter2 = function (callback, thisArgv) {
if (this == null) {
throw new TypeError('this is null or not defined')
}
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function')
}
let self = this,
result = []
for(let i = 0;i< self.length;i++){
if(callback.call(thisArgv,self[i],i,self)){
result.push(self[i])
}
}
return result
}
some
some
方法用于检查数组中是否有符合条件的值,返回值是个布尔值
const array = [1, 2, 3, 4, 5]
const even = (element) => element % 2 === 0;
console.log(array.some(even));
Array.prototype.some2 = function (callback, thisArgv) {
if (this == null) {
throw new TypeError('this is null or not defined')
}
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function')
}
let self = this,
result = []
for(let i = 0;i< self.length;i++){
if(callback.call(thisArgv,self[i],i,self)){
return true
}
}
return false
}
every
与some
相比,每个成员都满足条件才返回true
,有一个不满足都返回false
const isBelowThreshold = (currentValue) => currentValue < 40;
const array1 = [1, 30, 39, 29, 10, 13];
console.log(array1.every(isBelowThreshold));
Array.prototype.every2 = function (callback, thisArgv) {
if (this == null) {
throw new TypeError('this is null or not defined')
}
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function')
}
let self = this,
result = []
for(let i = 0;i< self.length;i++){
if(!callback.call(thisArgv,self[i],i,self)){
return false
}
}
return true
}
reduce
用法:
let arr = [10, 20, 30, 40, 50];
let result = arr.reduce((x, item, index) => {
// x初始值是数组的第一项,从数组第二项开始迭代
// 第一次 x=10 item=20 index=1 返回30
// 第二次 x=30 item=30 index=2 返回60
// 第三次 x=60 item=40 index=3 返回100
// 第四次 x=100 item=50 index=4 返回150
// 迭代结束,最后一次返回的150赋值给外面的result
return x + item;
});
let result = arr.reduce((x, item, index) => {
// x初始值是传递的第二个参数,从数组第一项开始迭代
// 第一次 x=0 item=10 index=0 返回10
// ...
return x + item;
}, 0);
手写代码:
Array.prototype.reduce = function reduce(callbackFn, initialValue) {
var self = this,
result = initialValue,
index = 0;
if (typeof callbackFn !== "function") throw new TypeError('callbackFn is not a function');
if (typeof initialValue === "undefined") {
result = self[0];
index = 1;
}
// 迭代数组
for (; index < self.length; index++) {
result = callbackFn(result, self[index], index);
}
return result;
};
let arr = [10, 20, 30, 40, 50];
let result = arr.reduce((x, item) => x + item),0);
console.log(result)
join
用法:join() 方法用于把数组中的所有元素转换一个字符串, 元素是通过指定的分隔符进行分隔的。
Array.prototype._join = function (s = ',') {
let str = ''
for(let i = 0; i < this.length; i++) {
str = i === 0 ? `${str}${this[i]}` : `${str}${s}${this[i]}` }
return str
}
var fruits = ["Banana", "Orange", "Apple", "Mango"];\
var energy = fruits._join();
push
Array.prototype.push = function push(value) {
// this->arr
this[this.length] = value;
this.length++;
return this.length;
};
flat
flat()
方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat())
const arr2 = [0, 1, 2, [[[3, 4]]]];
console.log(arr2.flat(2));
Array.prototype.flat2 = function () {
let arr = this
while (arr.some(item => Array.isArray(item))) {
console.log(...arr)
arr = [].concat(...arr)
console.log(arr)
}
return arr
}
实现Function原型方法
call
const fn = function fn(x, y) {
console.log(this, x, y);
return x + y;
};
let obj = {
name: 'obj'
};
let res = fn._call(obj, 10, 20);
Function.prototype._call = function call(context, ...params) {
let self = this,
key = Symbol('KEY'),
result;
context[key] = self;
result = context[key](...params);
// delete context[key]; //新增的属性用完后记得移除
Reflect.deleteProperty(context, key); //ES6中,可以基于Reflect.deleteProperty移除对象的属性
return result;
}
apply
Function.prototype.sx_apply = function (obj, args) {
obj = obj || window // Symbol是唯一的,防止重名key
const fn = Symbol()
obj[fn] = this // 执行,返回执行值
return obj[fn](...args) }
const testobj = {
name: '林三心',
testFn(age) { console.log(`${this.name}${age}岁了`) } }
const testobj2 = { name: 'sunshine_lin' }
testobj.testFn.sx_apply(testobj2, [22]) // sunshine_lin22岁了
bind
//- 需求:点击按钮,fn方法执行,我们想让其中的this变为o
const submit = document.querySelector('#submit');
submit.onclick = fn
const obj = { name: 'obj' };
const fn = function fn(x, y, ev) {
console.log(this, x, y, ev);
};
Function.prototype.bind = function bind(context, ...params) {
// this->fn context->obj params->[10,20]
let self = this;
return function proxy(...args) {
// args->[ev] this->submit
params = params.concat(args);
self.call(context, ...params);
};
};
实现Object原型方法
new
function _new(Ctor, ...params) {
let obj = Object.create(Ctor.prototype),
result;
result = Ctor.call(obj, ...params);
if (result !== null && /^(object|function)$/.test(typeof result)) return result;
return obj;
}
instanceof
var instance_of = function instance_of(obj, Ctor) {
// 右侧必须是一个函数
if (typeof Ctor !== "function") throw new TypeError("Right-hand side of 'instanceof' is not callable");
// 原始值检测都是false
if (obj == null || !/^(object|function)$/.test(typeof obj)) return false;
// 构造函数必须具备prototype
if (!Ctor.prototype) throw new TypeError("Function has non-object prototype 'undefined' in instanceof check");
// 支持Symbol.hasInstance的使用这个方法处理
if (typeof Symbol !== "undefined") return Ctor[Symbol.hasInstance](obj);
// 不支持:自己按照原型链查找
let proto = Object.getPrototypeOf(obj);
while (proto) {
if (proto === Ctor.prototype) return true;
proto = Object.getPrototypeOf(proto);
}
return false;
};
let arr = [10, 20];
console.log(instance_of(arr, Array)); //true
console.log(instance_of(arr, RegExp)); //false
console.log(instance_of(arr, Object)); //true
String
单词首字母大写
let str = "you can you up a" let reg = /\b([a-zA-Z])[a-zA-Z]*\b/g; //=>函数被执行了五次,let str = "you can you up a"
let reg = /\b([a-zA-Z])[a-zA-Z]*\b/g;
//=>函数被执行了五次,每一次都把正则匹配信息传递给函数
//=>每一次ARG:["you","y"] ["can","c"] ["you","y"]...
str = str.replace(reg,(...arg)=>{
let [content,$1]=arg;
$1=$1.toUpperCase();
content=content.substring(1);
return $1+content;
});
console.log(str)
queryURLParams :获取URL地址问号和面的参数信息(可能也包含HASH值)
let url = 'http://www.javascriptpekkk.cn/course/1935/task/114966/show?a=b&c=d#test'
function queryURLParams() {
let obj = {};
this.replace(/([^?=&#]+)=([^?=&#]+)/g, (...[, $1, $2]) => obj[$1] = $2);
this.replace(/#([^?=&#]+)/g, (...[, $1]) => obj['HASH'] = $1);
return obj;
}
防抖
const clearTimer = function clearTimer(timer) {
if (timer !== null) clearTimeout(timer);
return null;
};
const debounce = function debounce(func, wait) {
if (typeof func !== "function") throw new TypeError("func is not a function!");
if (typeof wait !== "number") wait = 300;
let timer = null;
return function operate(...params) {
// 清楚之前设置的定时器
timer = clearTimer(timer);
timer = setTimeout(() => {
// 清楚最后一次设置的定时器
timer = clearTimer(timer);
func.call(this,...params)
}, wait);
};
};
let sub = document.getElementById('btn')
sub.onclick = debounce(()=>{
console.log('click')
},300)
func this指向问题
func 里的this应该是要指向sub
直接绑定方法,方法里的this时绑定元素本身,现在绑定是operate函数,operate函数this是元素本身,要执行的func函数this指向window ,要把func的this也改为绑定事件的元素sub,箭头函数没有this,是所属上下文的this,就是operate里的this
数组扁平化
对象扁平化
一维数组转树结构
树形结构转列表
const tree2list = (tree) => {
let list = []
let queue = [...tree]
while (queue.length) {
// 从前面开始取出节点
const node = queue.shift()
const children = node.children
// 取出当前节点的子节点,放到队列中,等待下一次循环
if (children.length) {
queue.push(...children)
}
// 删除多余的children树形
delete node.children
// 放入列表
list.push(node)
}
return list
}
// 测试
const data = [
{
"id": 1,
"name": "部门1",
"pid": 0,
"children": [
{
"id": 2,
"name": "部门2",
"pid": 1,
"children": []
},
{
"id": 3,
"name": "部门3",
"pid": 1,
"children": [
{
"id": 4,
"name": "部门4",
"pid": 3,
"children": [
{
"id": 5,
"name": "部门5",
"pid": 4,
"children": []
}
]
}
]
}
]
}
]
console.log(tree2list(data))
/*
[
{ id: 1, name: '部门1', pid: 0 },
{ id: 2, name: '部门2', pid: 1 },
{ id: 3, name: '部门3', pid: 1 },
{ id: 4, name: '部门4', pid: 3 },
{ id: 5, name: '部门5', pid: 4 }
]
*/
高阶函数
柯理化函数
实现求和
let res = fn(1, 2)(3);
console.log(res)
const fn = function fn(...params) {
// params:[1,2] 预储存
return function proxy(...args) {
// args:[3]
return params.concat(args).reduce((x, item) => x + item);
};
};
let res = fn(1, 2)(3);
console.log(res)
compose函数
const add1 = (x) => x + 1;
const mul3 = (x) => x * 3;
const div2 = (x) => x / 2;
div2(mul3(add1(add1(0)))); //=>3
样的写法可读性明显太差了,我们可以构建一个compose函数,它接受任意多个函数作为参数(这些函数都只接受一个参数),然后compose返回的也是一个函数,达到以下的效果:
const operate = compose(div2, mul3, add1, add1)
operate(0) //=>相当于div2(mul3(add1(add1(0))))
operate(2) //=>相当于div2(mul3(add1(add1(2))))
const add1 = x => x + 1;
const mul3 = x => x * 3;
const div2 = x => x / 2;
const compose = function compose(...funcs) {
// funcs:数组,依次存储要执行的函数,而且按照从右到左的顺序执行
return function operate(x) {
// x:函数执行的初始值
let len = funcs.length;
if (len === 0) return x;
if (len === 1) return funcs[0](x);
return funcs.reduceRight((x, item) => {
return item(x);
}, x);
};
};
const operate = compose(div2, mul3, add1, add1);
console.log(operate(0)); //3
console.log(operate(2)); //6
惰性函数
惰性函数:懒,执行一次可以搞定的,绝对不会执行第二次
var getCss = function (elem, attr) {
if (window.getComputedStyle) {
return window.getComputedStyle(elem)[attr];
}
return elem.currentStyle[attr];
};
console.log(getCss(document.body, 'margin'));
console.log(getCss(document.body, 'padding'));
瑕疵:浏览器没换、页面没关,第一次执行需要判断兼容性是必须的,但是第二次及以后再执行,如果还要判断兼容性,是没有必要的
var getCss = function (elem, attr) {
if (window.getComputedStyle) {
getCss = function (elem, attr) {
return window.getComputedStyle(elem)[attr];
};
} else {
getCss = function (elem, attr) {
return elem.currentStyle[attr];
};
}
return getCss(elem, attr);
};
console.log(getCss(document.body, 'margin'));
console.log(getCss(document.body, 'padding'));
数组算法
两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
输入: nums = [3,2,4], target = 6
输出: [1,2]
const twoSum = (nums, target) => {
const cacheMap = {}
for (let i = 0, len = nums.length; i < len; i++) {
const diff = target - nums[ i ]
if (cacheMap[ diff ] !== undefined) {
return [ cacheMap[ diff ], i ]
} else {
cacheMap[ nums[ i ] ] = i
}
}
}
console.log(twoSum([ 2, 7, 11, 15 ], 22))