function DeleteObj(list) {
return (deletaKey) => {
let resList = [];
if (!Array.isArray(list)) throw new TypeError(`list is not a Array`);
if (typeof deletaKey !== 'string') throw new TypeError(`deletaKey is not a string`);
if (typeof deletaKey !== 'undefined' && typeof deletaKey === 'string') {
resList = list.map(item => {
delete item[deletaKey];
return item;
});
}
return resList;
}
}
// 源数据
let array = [
{ list: 'list', object: 'object', array: 'array', string: 'string', boolean: 'boolean' },
{ list: 'list', object: 'object', array: 'array', string: 'string', boolean: 'boolean' }
],
// 使用闭包存储数据
deleteObj = new DeleteObj(array),
// 需要删除的属性
deleteArr = ['list', 'array'],
// 结果
result = [];
for (let i = 0; i < deleteArr.length; i++) {
deleteObj(deleteArr[i]);
// 获取最后一次返回值,并且传空值,否则报错
if (i == deleteArr.length - 1) result = deleteObj('');
}
console.log(result);
// (2) [{…}, {…}]
function groupingSorting(list) {
if (!Array.isArray(list)) throw new TypeError(`list is not a Array`);
if (list.length <= 0) throw new TypeError(`list length cannot be equal to 0`);
let grouping1 = [],
grouping2 = [],
grouping3 = [],
grouping4 = [],
grouping5 = [];
list.forEach(item => {
let title = item.title,
str2 = title.substring(title.length - 2),
str3 = title.substring(title.length - 3);
if (str2 === '公司' && str3 !== '分公司') {
grouping1.push(item);
} else if (str3 === '分公司' && str2 === '公司') {
grouping2.push(item);
} else if (str2 == '部门') {
grouping3.push(item);
} else if (str2 === '半晨') {
grouping4.push(item);
} else {
grouping5.push(item);
}
});
return [...grouping1, ...grouping2, ...grouping3, ...grouping4, ...grouping5];
}
// 源数据
let list = [
{ id: 1, title: '1xxx公司' },
{ id: 2, title: '2xxx分公司' },
{ id: 3, title: '3xxx公司' },
{ id: 4, title: '4xxx部门' },
{ id: 5, title: '5xxx分公司' },
{ id: 6, title: '6xxx部门' },
{ id: 7, title: '7xxx' },
{ id: 8, title: '8xxx半晨' },
{ id: 9, title: '9xxx舒冬' },
{ id: 10, title: '10xxx半晨' },
{ id: 11, title: '11xxx李清照' },
{ id: 12, title: '12xxx李白' },
];
console.log(groupingSorting(list));
// (12) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
无序数组
通过遍历,依次求出每个元素值和目标值的差,比较更新。
时间复杂度:o(n)
源数据
const arr = [1, 3, 5, 6, 10];
方法一
function getClosestNumber(array, target) {
let result = array[0];
for (let i = 0; i < array.length; i++) if (Math.abs(array[i] - target) < Math.abs(result - target)) result = array[i];
return result;
}
console.log(getClosestNumber(arr, 7));
// 6
console.log(getClosestNumber(arr, 3));
// 3
方法二
function getClosestNumber(array, target) {
return array.reduce((pre, cur) => Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur);
}
console.log(getClosestNumber(arr, 7));
// 6
console.log(getClosestNumber(arr, 3));
// 3
有序数组
二分查找法
1、取left和right两个索引,每次取出中间索引位mid的值与目标值
如果中间位的值大于目标值,则想要寻找的值在左侧
如果中间位的值小于目标值,则想要寻找的值在右侧
2、动态更新left和right,直到指针停留在相邻的两个数
3、进行最好一次计算,得到与目标值最近的数
时间复杂度:o(log(n))const arr = [1, 3, 5, 8, 10, 11, 14, 16, 18]; function getClosestNumber(array, target) { let left = 0, right = array.length - 1, mid; while (right - left > 1) { mid = Math.floor((left + right) / 2); if (array[mid] < target) { left = mid; } else { right = mid; } } return Math.abs(array[left] - target) > Math.abs(array[right] - target) ? array[right] : array[left]; } console.log(getClosestNumber(arr, 13)); // 14 console.log(getClosestNumber(arr, 0)); // 1
单例模式
概念
保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。
适用场景
一个单一对象。比如:弹窗,无论点击多少次,弹窗只应该被创建一次。
代码实现
class CreateUser {
constructor(name) {
this.name = name;
this.getName();
};
getName() {
console.log(this.name);
};
};
// 代理实现单例模式
let ProxyMode = (function () {
let instance = null;
return function (name) {
if (!instance) instance = new CreateUser(name);
return instance;
};
})();
// 测试单体模式的实例
let a = new ProxyMode("aaa");
console.log(a);
// CreateUser {name: "aaa"}
let b = new ProxyMode("bbb");
console.log(b);
// CreateUser {name: "aaa"}
// 因为单体模式是只实例化一次,
// 所以下面的实例是相等的
console.log(a === b);
// true
代理模式
概念
为一个对象提供一个代用品或占位符,以便控制对它的访问。
常用的虚拟代理形式
某一个花销很大的操作,可以通过虚拟代理的方式延迟到这种需要它的时候才去创建(例:使用虚拟代理实现图片懒加载)。
图片懒加载的方式
先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面。
解释
使用代理模式实现图片懒加载的优点还有符合单一职责原则。减少一个类或方法的粒度和耦合度。
代码实现
let imgFunc = (function() {
// 创建一个img标签
let imgNode = document.createElement('img');
// 把标签放到body上
document.body.appendChild(imgNode);
// 返回setSrc函数
return {
setSrc: function(src) {
imgNode.src = src;
}
};
})();
let proxyImage = (function() {
// 创建一个img标签
let img = new Image();
// 给img标签添加自执行函数
img.onload = function() {
imgFunc.setSrc(this.src);
};
return {
setSrc: function(src) {
imgFunc.setSrc('/img/loading02.gif');
setTimeout(() => {
img.src = src;
}, 1000);
}
};
})();
console.log(proxyImage);
// {setSrc: ƒ}
proxyImage.setSrc('/img/08.jpg');
装饰者模式
概念
在不改变对象自身的基础上,在程序运行期间给对象动态地添加方法。
例如
现有4种型号的自行车分别被定义成一个单独的类,如果给每辆自行车都加上前灯、尾灯、铃铛这3个配件,如果用类继承的方式,需要创建4 * 3 = 12个子类。但如果通过装饰者模式,只需要创建3个类。
适用的场景
原有方法维持不变,在原有方法上再挂载其他方法来满足现有需求;函数的解耦,将函数拆分成多个可复用的函数,再将拆分出来的函数挂载到某个函数上,实现相同的效果但增强了复用性。
用AOP装饰函数实现装饰者模式
Function.prototype.before = function(beforefn) {
// 保存原函数引用
let self = this;
// 返回包含了原函数和新函数的'代理函数'
return function() {
// 执行新函数,修正this
beforefn.apply(this, arguments);
// 执行原函数
return self.apply(this, arguments);
};
};
Function.prototype.after = function(afterfn) {
let self = this;
return function() {
let ret = self.apply(this, arguments);
afterfn.apply(this, arguments);
return ret;
};
};
let func = function() {
console.log('2');
};
// func1和func3为挂载函数
let func1 = function() {
console.log('1');
};
let func3 = function() {
console.log('3');
};
func = func.before(func1).after(func3);
func();
// 1 2 3
中介者模式
概念
通过一个中介者对象,其他所有的相关对象都通过该中介者对象来通信,而不是相互引用,当其中的一个对象发生改变时,只需要通知中介者对象即可。通过中介者模式可以解除对象与对象之间的紧耦合关系。
例如
现实生活中,航线上的飞机只需要和机场的塔台通信就能确定航线和飞行状态,而不需要和所有飞机通信。同时塔台作为中介者,知道每架飞机的飞行状态,所以可以安排所有飞机的起降和航线安排。
适用的场景
例如购物车需求,存在商品选择表单、颜色选择表单、购买数量表单等,都会触发change事件,那么可以通过中介者来转发处理这些事件,实现各个事件间的解耦,仅仅维护中介者对象即可。
html
<div>
<div>
<span>选择颜色: span>
<select id="colorSelect">
<option value="">请选择option>
<option value="red">红色option>
<option value="blue">蓝色option>
select>
div>
<div>
<span>选择内存: span>
<select id="memorySelect">
<option value="">请选择option>
<option value="32G">32Goption>
<option value="16G">16Goption>
select>
div>
<div>
<span>输入购买数量: span>
<input type="text" id="numberInput" />
div>
<div>
<span>您选择了颜色: span>
<span id="colorInfo">span>
div>
<div>
<span>您选择了内存: span>
<span id="memoryInfo">span>
div>
<div>
<span>您输入了数量: span>
<span id="numberInfo">span>
div>
<div>
<button id="nextBtn" disabled="true">请选择手机颜色、内存和购买数量button>
div>
div>
<script src="./index.js">script>
JavaScript
// 手机库存数据
let goods = { "red|32G": 3, "red|16G": 0, "blue|32G": 1, "blue|16G": 6 };
// 中介者对象
let mediator = (function () {
let colorSelect = document.getElementById('colorSelect'),
memorySelect = document.getElementById('memorySelect'),
numberInput = document.getElementById('numberInput'),
colorInfo = document.getElementById('colorInfo'),
memoryInfo = document.getElementById('memoryInfo'),
numberInfo = document.getElementById('numberInfo'),
nextBtn = document.getElementById('nextBtn'),
regNumber = /^[1-9]{1}[0-9]{0,2}$/;
return {
changed: function (obj) {
// 颜色
let color = colorSelect.value,
// 内存
memory = memorySelect.value,
// 数量
number = numberInput.value,
// 颜色和内存对应的手机库存数量
stock = goods[`${color}|${memory}`];
// 如果改变的是选择颜色下拉框
if (obj === colorSelect) {
colorInfo.innerHTML = color;
} else if (obj === memorySelect) {
memoryInfo.innerHTML = memory;
} else if (obj === numberInput) {
numberInfo.innerHTML = number;
}
if (!color) return (nextBtn.disabled = true, nextBtn.innerHTML = '请选择手机颜色');
if (!memory) return (nextBtn.disabled = true, nextBtn.innerHTML = '请选择内存大小');
if (!regNumber.test(number)) return (nextBtn.disabled = true, nextBtn.innerHTML = '请输入正确的购买数量');
if (number > stock) return (nextBtn.disabled = true, nextBtn.innerHTML = '库存不足');
nextBtn.disabled = false;
nextBtn.innerHTML = '放入购物车';
}
}
})();
// 事件函数
// id选择器可以直接绑定事件,不需要特意获取。
colorSelect.onchange = function () {
mediator.changed(this);
};
memorySelect.onchange = function () {
mediator.changed(this);
};
numberInput.oninput = function () {
mediator.changed(this);
};
相关链接
1、掘金-原文
html
<div>
<span>选择颜色:span>
<select id="colorSelect">
<option value="">请选择option>
<option value="red">红色option>
<option value="blue">蓝色option>
select>
div>
<div>
<span>输入购买数量: span>
<input type="text" id="numberInput" />
div>
<div>
<span>您选择了颜色: span>
<span id="colorInfo">span>
div>
<div>
<span>您输入了数量: span>
<span id="numberInfo">span>
div>
<div>
<button id="nextBtn" disabled="true">请选择手机颜色和购买数量button>
div>
<script src="./index.js">script>
JavaScript
var colorSelect = document.getElementById('colorSelect'),
numberInput = document.getElementById('numberInput'),
colorInfo = document.getElementById('colorInfo'),
numberInfo = document.getElementById('numberInfo'),
nextBtn = document.getElementById('nextBtn');
// 手机库存数据
var goods = {
"red": 3,
"blue": 6
};
colorSelect.onchange = function () {
// 获取选中的颜色值
var color = this.value,
// 数量
number = numberInput.value,
// 该颜色手机对应的当前库存
stock = goods[color];
console.log(color, number);
// 赋值
colorInfo.innerHTML = color;
if (!color) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请选择手机颜色';
return;
}
// 用户输入的购买数量是否为正整数
if (((number - 0) | 0) !== number - 0) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请输入正确的购买数量';
return;
}
// 当前选择数量超过库存量
if (number > stock) {
nextBtn.disabled = true;
nextBtn.innerHTML = '库存不足';
return;
}
nextBtn.disabled = false;
nextBtn.innerHTML = '放入购物车';
};
numberInput.oninput = function () {
// 颜色
var color = colorSelect.value,
// 数量
number = this.value,
// 该颜色手机对应的当前库存
stock = goods[color];
numberInfo.innerHTML = number;
if (!color) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请选择手机颜色';
return;
}
// 输入购买数量是否为正整数
if (((number - 0) | 0) !== number - 0) {
nextBtn.disabled = true;
nextBtn.innerHTML = '请输入正确的购买数量';
return;
}
// 当前选择数量没有超过库存量
if (number > stock) {
nextBtn.disabled = true;
nextBtn.innerHTML = '库存不足';
return;
}
nextBtn.disabled = false;
nextBtn.innerHTML = '放入购物车';
};
观察者模式
// 有一家猎人工会,
// 其中每个猎人都具有发布任务(publish),
// 订阅任务(subscribe)的功能
// 他们都有一个订阅列表来记录谁订阅了自己
// 定义一个猎人类
// 包括姓名,级别,订阅列表
function Hunter(name, level) {
this.name = name;
this.level = level;
this.list = [];
};
// 在Hunter原型上添加publish方法
Hunter.prototype.publish = function(money) {
console.log(this.level + '猎人' + this.name + '寻求帮助');
this.list.forEach(function(item, index) {
item(money);
});
};
// 在Hunter原型上添加subscribe方法
Hunter.prototype.subscribe = function(targrt, fn) {
console.log(this.level + '猎人' + this.name + '订阅了' + targrt.name);
targrt.list.push(fn);
};
// 猎人工会走来了几个猎人
let hunterMing = new Hunter('小明', '黄金');
let hunterJin = new Hunter('小金', '白银');
let hunterZhang = new Hunter('小张', '黄金');
let hunterPeter = new Hunter(' Peter ', '青铜');
// Peter等级较低,
// 可能需要帮助,
// 所以小明,小金,小张都订阅了Peter
hunterMing.subscribe(hunterPeter, function(money) {
console.log('小明表示:' + (money > 200 ? '' : '暂时很忙,不能给予帮助'));
});
hunterJin.subscribe(hunterPeter, function() {
console.log('小金表示:给予帮助');
});
hunterZhang.subscribe(hunterPeter, function() {
console.log('小金表示:给予帮助');
});
// Peter遇到困难,赏金198寻求帮助
hunterPeter.publish(198);
发布订阅模式
版本一
// 定义一家猎人工会
// 主要功能包括任务发布大厅(topics),
// 以及订阅任务(subscribe),
// 发布任务(publish)
let HunterUnion = {
// 任务发布大厅
topics: Object.create(null),
// 发布任务(publish)
publish: function (topic, money) {
if (!this.topics[topic]) return false;
for (let fn of this.topics[topic]) fn(money);
},
// 订阅任务(subscribe)
subscribe: function (topic, fn) {
if (!this.topics[topic]) this.topics[topic] = [];
this.topics[topic].push(fn);
},
};
// 定义一个猎人类
// 包括姓名,级别
function Hunter(name, level) {
this.name = name;
this.level = level;
}
// 订阅
Hunter.prototype.subscribe = function (topic, fn) {
console.log(
this.level + "猎人" + this.name + "订阅了狩猎" + topic + "的任务。"
);
HunterUnion.subscribe(topic, fn);
};
// 发布
Hunter.prototype.publish = function (topic, money) {
console.log(
this.level + "猎人" + this.name + "发布了狩猎" + topic + "的任务。"
);
HunterUnion.publish(topic, money);
};
// 猎人工会走来了几个猎人
let hunterMing = new Hunter("小明", "黄金");
let hunterJin = new Hunter("小金", "白银");
let hunterZhang = new Hunter("小张", "黄金");
let hunterPeter = new Hunter("Peter", "青铜");
// Peter发布了狩猎tiger的任务
hunterPeter.publish("tiger", 198);
// 小明,小金,小张分别订阅了狩猎tiger的任务
hunterMing.subscribe("tiger", function (money) {
console.log("小明表示:" + (money > 200 ? "" : "不") + "接取任务。", money);
});
hunterJin.subscribe("tiger", function (money) {
console.log("小金表示:接取任务。", money);
});
hunterZhang.subscribe("tiger", function (money) {
console.log("小张表示:接取任务。", money);
});
// 猎人们发布 (发布者) 或订阅 (观察者/订阅者)
// 任务都是通过猎人工会 (调度中心) 关联起来的,
// 他们没有直接的交流。
版本二
class PublishAndSubscribe {
constructor() {
// 收集订阅信息,调度中心
this.eventList = {};
}
// 收集打包传入的数据
// attributeName属性名
// datas数据
// fn 事件
collects(attributeName, datas, fn) {
if (!(this.eventList[attributeName] instanceof Array)) this.eventList[attributeName] = [];
this.eventList[attributeName].push({ datas, fn });
}
// 通过属性名触发对应的事件
// attributeName属性名
release(attributeName) {
if (!this.eventList[attributeName].length) throw "出错啦!";
this.eventList[attributeName].forEach((item) => item.fn(item.datas));
}
// 通过属性名找到对应的属性下的数组
// 通过id移出对应的数组项
off(attributeName, id) {
this.eventList[attributeName].forEach((item, index) => {
if (item.datas.id == id) this.eventList[attributeName].splice(index, 1);
});
}
}
let publishAndSubscribe = new PublishAndSubscribe();
// 收集属性
// attributeName1,attributeName2,attributeName3
// 并且,给它们绑定值和事件
publishAndSubscribe.collects(
"attributeName1",
{ id: 1, content: "A4" },
function (datas) {
console.log("接收发布的数据:", datas);
}
);
publishAndSubscribe.collects(
"attributeName2",
{ id: 2, content: { a: 1, b: 6, c: 4 } },
function (datas) {
console.log("接收发布的数据:", datas);
}
);
publishAndSubscribe.collects(
"attributeName3",
{
id: 3,
content: [1, 9, 6, 3],
},
function (datas) {
console.log("接收发布的数据:", datas);
}
);
publishAndSubscribe.collects(
"attributeName4",
{
id: 4,
content: "array",
},
function (datas) {
console.log("接收发布的数据:", datas);
}
);
// 通过属性触发对应的事件
publishAndSubscribe.release("attributeName1");
publishAndSubscribe.release("attributeName2");
publishAndSubscribe.release("attributeName3");
console.log("publishAndSubscribe:", publishAndSubscribe.eventList);
// 移除
publishAndSubscribe.off("attributeName4", 4);
console.log("publishAndSubscribe:", publishAndSubscribe.eventList);
// publishAndSubscribe.release("attributeName4");
// Error: Failed to resolve async component default: 出错啦!
版本三
let publishAndSubscribe = {
eventList: {},
// 订阅
add(propertyName, datas, listener) {
if (!this.eventList[propertyName]) this.eventList[propertyName] = [];
this.eventList[propertyName].push({ datas, listener });
},
// 发布
triggle(propertyName) {
this.eventList[propertyName] &&
this.eventList[propertyName].forEach((item) => item.listener(item.datas));
},
// 移除
removes(propertyName, fn) {
if (!this.eventList[propertyName]) return false;
let index = this.eventList[propertyName].findIndex((listener) => listener === fn);
this.eventList[propertyName].splice(index, 1);
},
};
let event1 = (data) => {
console.log("数据:", data);
};
let event2 = (data) => {
console.log("数据:", data);
};
let event3 = (data) => {
console.log("数据:", data);
};
let datas = [1, 2, 3, 4, 5];
// 订阅
publishAndSubscribe.add("property1", datas, event1);
publishAndSubscribe.add("property2", [9, 8, 6, 7, 3], event2);
publishAndSubscribe.add("property3", [], event3);
// 移除
publishAndSubscribe.removes("property2", event2);
// 发布
publishAndSubscribe.triggle("property1");
publishAndSubscribe.triggle("property2");
publishAndSubscribe.triggle("property3");
策略模式
概念
定义一系列的算法,把他们一个个封装起来,并且使他们可以相互替换。
目的
策略模式的目的就是将算法的使用和算法的实现分离开。
解释
一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类(可变),策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context(不变),Context接受客户的请求,随后将请求委托给某一个策略类。要做到这一点,说明Context中要维持对某个策略对象的引用。
代码实现
// 策略类
let levelOBJ = {
funA: function(money) {
return money * 5;
},
funB: function(money) {
return money * 3;
},
funC: function(money) {
return money * 2;
}
};
// 环境类
let calculateBouns = function(level, money) {
return levelOBJ[level](money);
};
console.log(calculateBouns('funA', 10));
// 50
console.log(calculateBouns('funB', 20));
// 60
console.log(calculateBouns('funC', 30));
// 60
Array.prototype.myjoin
概念
join()
方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。
MDN链接地址
MDN - join
示例代码
let arrayData = [1, null, [2], 'string', [], { sname: 3 }, null, {}];
Array.prototype.myjoin = function(separator) {
// 如果 separator 是字符串类型,
// 赋值为 separator ;
// 否则,赋值为 , 。
separator = typeof separator === 'string' ? separator : ',';
// 获取 this 的长度。
let len = this.length;
// 初始化一个字符串
let str = '';
// 如果 len 等于 0 ,
// 返回空字符串
if (!len) return str;
// 初始化 while 循环条件
let i = 1;
// 如果 this 的长度等于 1 ,
// 直接返回且不加 , 。
str = this[0] ? this[0].toString() : '';
while (i < len) {
str += separator + (this[i] ? this[i].toString() : '');
i++;
};
return str;
};
console.log(arrayData.myjoin());
// 1,,2,string,,[object Object],,[object Object]
console.log(arrayData.myjoin(','));
// 1,,2,string,,[object Object],,[object Object]
console.log(arrayData.myjoin('_'));
// 1__2_string__[object Object]__[object Object]
console.log(arrayData.myjoin(':'));
// 1::2:string::[object Object]::[object Object]
Array.prototype.myfindIndex
概念
findIndex()
方法返回数组中满足提供的测试函数的第一个元素的索引(下标)。若没有找到对应元素则返回-1。
MDN链接地址
MDN - findIndex
示例代码
let arrayData = [4, 6, 8, 12];
Array.prototype.myfindIndex = function(callback, context) {
// 获取第二个参数,
// 即this指向。
// 如果有直接使用,
// 否则,指向window
context = context || window;
// 获取this的长度。
let len = this.length;
// 初始化while的值。
let i = 0;
while (i < len) {
// 调用函数
if (callback.call(context, this[i], i, this)) return i;
i++;
};
return -1;
};
let fun = function(item) {
return item + this.svalue > 10;
};
console.log(arrayData.myfindIndex(fun, { svalue: 5 }));
// 1
Array.prototype.myreduce
概念
reduce()
方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
MDN链接地址
MDN - reduce
功能函数
Array.prototype.myreduce = function(callback, initialValue) {
const len = this.length;
let k = 0;
// 如果this的长度等于0,
// 抛出错误
if (len == 0) {
throw new TypeError('this is null or not defined');
};
// 如果callback的类型不是function函数类型,
// 抛出错误
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
};
// 如果initialValue的值为undefined
// 拿出this里面的第一个值作为累加值
// 同时把k++ ,
// 因为this[k]需要返回两个值
// 第一个值是initialValue
// 第二个值是this[k]
// 如果initialValue的值不为undefined
// 直接返回initialValue和this[k]
// 此时this[k] => k = 0;
if (initialValue === undefined) {
initialValue = this[k++];
};
// 如果initialValue的值不是数字或者字符串类型的数字,
// 抛出错误
if (!/^(-?\d+)(\.\d+)?$/.test(initialValue)) {
throw new TypeError(initialValue + ' is not number');
};
while (k < len) {
// 如果this中的值不是数字或者字符串类型的数字,
// 抛出错误
if (!/^(-?\d+)(\.\d+)?$/.test(this[k])) {
throw new TypeError(this[k] + ' is not number');
};
// 如果k的值在this中有对应的下标,
// 就继续执行,
// 否则退出
if (k in this) {
// Number(initialValue)把字符串类型的数字转为纯数字
// Number(this[k])把字符串类型的数字转为纯数字
// 回调函数的作用是将循环出来的数据返回到外面
initialValue = callback.call(undefined, Number(initialValue), Number(this[k]), k, this);
};
k++;
};
return initialValue;
};
函数调用
// 纯数组求和
let sumNumF = function(item, num) {
return item + num;
},
dataNum = ['2', 3.1, '2.2'];
console.log(dataNum.myreduce(sumNumF, '3.1'));
// 10.399999999999999
// 数组对象求和
function sumObjF(item, num) {
// Math.round(num)四舍五入
return item + Math.round(num);
};
let sumObj = [{
id: 1,
value: 2
}, {
id: 2,
value: 1
}, {
id: 3,
value: '1.4'
}, {
id: 4,
value: '2.6'
}];
console.log(sumObj.map((item) => {
return item.value;
}).myreduce(sumObjF)); // 7
Array.prototype.myforEach
概念
forEach()
方法对数组的每个元素执行一次给定的函数。
MDN链接地址
MDN - forEach
功能函数
Array.prototype.myforEach = function() {
const len = this.length;
// 获取传入的第一参数
// 回调函数
let callback = arguments[0] || this;
// 获取传入的第二个参数
// 需要指向的this值
let thisArg = arguments[1] || this;
// 如果this的长度为0,抛出错误
if (len == 0) {
throw new TypeError('this is null or not defined');
};
// 如果传入的callback不是函数,抛出错误
if (typeof callback !== "function") {
throw new TypeError(callback + 'is not a function');
};
let k = 0;
while (k < len) {
// if in this对象中是否含有k属性
// if('age' in data) data对象中是否含有age属性
if (k in this) {
// this[k] --- item
// k --- i
// this --- data
// 循环调用传进来的函数
// call改变this的指向
// 回调函数的作用是将循环出来的值返回到外面
callback.call(thisArg, this[k], k, this);
};
k++;
};
};
函数调用
[10, 50, 90].myforEach((item, i, data) => {
console.log(item);
console.log(i);
console.log(data);
// 注意:使用箭头函数时,
// this.a的值为undefined
console.log(this.a);
}, {
a: 1
});
[10, 50, 90].myforEach(function(item, i, data) {
console.log(item);
console.log(i);
console.log(data);
// 注意 : 不使用箭头函数时,
// this.a的值为传入的值
console.log(this.a);
}, {
a: 1
});
Array.prototype.mymap
概念
map()
方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
MDN链接地址
MDN - map
功能函数
Array.prototype.mymap = function(callback, thisPointTo) {
const len = this.length;
if (len == 0) {
throw new TypeError('this is null or not defined');
};
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
};
// 定义返回数组
let result = [],
i = 0;
// 使用for循环遍历数据
for (; i < len; i++) {
if (i in this) {
// 调用回调函数并传入新数组
result[i] = callback.call(thisPointTo, this[i], i, this);
};
};
// 返回新数组
return result;
};
函数调用
let returnMap = [10, 50, 90].mymap((item, i, data) => {
console.log(item);
console.log(i);
// 原始值不变
console.log(data); // [10, 50, 90]
// 注意 : 使用箭头函数时,
// this.a 的值为 undefined
console.log(this.thisPointTo);
item = 100;
return item;
}, {
thisPointTo: 1
});
console.log('returnMap:', returnMap); // returnMap: (3) [100, 100, 100]
let returnMap = [10, 50, 90].mymap(function(item, i, data) {
console.log(item);
console.log(i);
// 原始值不变
console.log(data); // [10, 50, 90]
// 注意 : 不使用箭头函数时,
// this.a的值为传入的值
console.log(this.thisPointTo);
// 返回值
item = 100;
return item;
}, {
thisPointTo: 1
});
console.log('returnMap:', returnMap); // returnMap: (3) [100, 100, 100]
Array.prototype.mypush
概念
push()
方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
MDN链接地址
MDN - push
功能函数
Array.prototype.mypush = function() {
// 初始化被push的数组长度,
// 如果长度不是0,
// 给len赋值为this.length;
// 否则,赋值为0
let len = this.length ? this.length : (this.length = 0) && 0;
// 作用:逐一获取传进来的值
let i = 0;
while (i < arguments.length) {
// 通过长度赋值给this数组。
// 也就是向数组末尾添加元素。
// 自带返回值
this[len] = arguments[i];
++i;
// 同时this数组的长度也要++;
++len;
}
// 给this数组的length属性重新赋值
this.length = len;
// 返回长度
return this.length;
};
函数执行
let arrayData = [3, 'string'];
console.log(arrayData);
// [3, "string"]
arrayData.mypush(1, '字符串', { sname: 'object 对象' }, ['array 数组']);
console.log(arrayData);
// [3, "string", 1, "字符串", {…}, Array(1)]
Array.prototype.myevery
概念
every()
方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
注意:若收到一个空数组,此方法在一切情况下都会返回true。
MDN链接地址
MDN - every
功能函数
Array.prototype.myevery = function(callback) {
// 初始值为true
let isEvery = true;
// 获取this的长度
let len = this.length;
// 初始化index
let i = 0;
// 获取第二个参数,
// this是防止报错。
// 第二个参数是一个对象,
// 作用:改变this指向。
let context = arguments[1] || this;
while (i < len) {
if (!callback.call(context, this[i], i, this)) {
isEvery = false;
break;
};
i++;
}
return isEvery;
};
传入函数
let fun = function(item, i) {
console.log(item > this.svaleu);
// 输出3次true
return item > this.svaleu;
};
执行
let arrayData = [2, 3, 5];
console.log(arrayData.myevery(fun, { svaleu: 1 }));
// true