实现let
// 加_for(var _i=0;_i<5;_i++){}// 立即函数表达式(for(var i=0;i<5;i++){})()
实现const
// 实现一个constfunction myconst(key, value) {let desc = {value,writable: false,};Object.defineProperty(window, key, desc);}myconst("name", { a: 1 });
实现call、apply、bind
// 实现一个function.call(thisArg, arg1, arg2, ...)Function.prototype.mycall = function (thisArg, ...args) {thisArg.fn = this;return thisArg.fn(...args);};// applyFunction.prototype.myapply = function (thisArg, args) {thisArg.fn = this;return thisArg.fn(...args);};// bindFunction.prototype.mybind = function (thisArg, ...args) {return this.call(thisArg, ...args);};
实现new
// 模拟实现newfunction myNew(foo, ...args) {// Object.create()函数的含义:创建一个对象,并让该对象的__proto__指向参数,// 这里的参数是foo.prototype,意思是指向foo的原型对象let obj = Object.create(foo.prototype);let result = foo.apply(obj, args); // 这里obj调用了foo函数,就会被加上foo函数里带有this.xxx的属性,于是就有obj.name, obj.age等return typeof result === "object" ? result : obj; // 这里的result是调用foo函数所返回的结果,如果该结果是对象,则返回该对象,否则返回上面所创建的对象obj} // 相当于 let str = new foo();
防抖debounce函数
// 防抖函数function debounce(fn, wait) {let timeout = null;return function () {let context = this;let args = arguments;if (timeout) clearTimeout(timeout);else {timeout = setTimeout(() => {fn.apply(context, args);}, wait);}};}// 防抖测试:function debounceTest(age) {console.log("防抖测试", this.name, age);}let person = {name: "oneper",};debounce(debounceTest, 4000).call(person, 18); // 4秒后:防抖测试, oneper, 18
节流throttle函数
// 节流函数function throttle(fn, wait) {let timeout = null;return function () {const thisArg = this;const args = arguments;if (!timeout) {setTimeout(() => {fn(thisArg, args);timeout = null;}, wait);}};}
数组扁平化
const arr = [1, [2, 3], [4, [5, 6]]];// 1.flat(Infinity) -- <(^-^)>arr.flat(Infinity);// 2.序列化后正则去全部括号,添左右括号,反序列化 -- <(^-^)>const str = `[${JSON.stringify(arr).replace(/(\[|\])/g, "")}]`;JSON.parse(str);// 3.递归 -- <(^-^)>function myflat(arr) {let result = [];for (const item of arr) {item instanceof Array? (result = result.concat(flat(item))): result.push(item);}return result;}// 4.自创 -- <(^-^)>JSON.parse(`[${arr.toString()}]`);// 5.reduce()递归function myflatSec(arr) {return arr.reduce((result, cur) => {return result.concat(cur instanceof Array ? myflatSec(cur) : cur);}, []);}// 6.迭代+展开运算符function myflatThr(arr) {while (arr.some(Array.isArray)) {arr = [].concat(...arr);}}
数组去重
// 1. 原始办法:双重循环function unique(arr = []) {let result = [];for (let i = 0, arrLen = arr.length; i < arrLen; i++) {for (let j = 0, resLen = result.length; j < resLen; j++) {if (arr[i] === result[j]) break;}if (j === resLen) {res.push(arr[i]);}}return res;}// 2. 数组方法indexOffunction unique2(arr = []) {let result = [];for (let item of arr) {if (result.indexOf(item) === -1) result.push(item);}return result;}// 3. 排序后,后不等前则添加function unique3(arr = []) {let result = [];let sortedArr = [...arr];sortedArr.sort((a, b) => a - b);for (let i = 0; i < sortedArr.length; i++) {if (i === 0) result.push(sortedArr[i]);else if (sortedArr[i] !== sortedArr[i - 1]) result.push(sortedArr[i]);}return result;}
数组乱序
- arr.sort(()=>Math.random()-0.5),但不是真正的乱序
- 利用洗牌算法思路:
- 从数组末尾开始,向前推进,每次随机和数组中一个元素进行交换
数组最大、最小值
深拷贝
```javascript // 1.递归 function clone(obj) { // 深拷贝的实现, 递归实现 // 是不是有点N叉树遍历的感觉?! let buf; if (obj instanceof Array) { // 若为数组 buf = [] //创建一个空数组来存 let i = obj.length while (i—) { buf[i] = clone(obj[i]) } return buf } else if (obj instanceof Object) { // 若为对象 buf = {} // 创建一个空对象来存 for (let i in obj) { buf[i] = clone(obj[i]) } return buf } else { // 基本数据类型 return (obj) } }
- 从数组末尾开始,向前推进,每次随机和数组中一个元素进行交换
// 2. JSON, 只能用于对象内部没有方法时 JSON.parse(JSON.stringify(obj))
<a name="5ahlZ"></a>## 浅拷贝<a name="ynuOs"></a>## 遍历1. 数组的遍历:```javascriptlet arr = [1, 2, 3];/1. 原始for方法for (let i = 0; i < arr.length; i++) {console.log(arr[i]);}/2. for...in 也会将数组的属性遍历出来arr.name = "joy";for (let item in arr) {console.log(item); // 0, 1, 2, nameconsole.log(arr[item]); // 1, 2, 3, joy}/3. for...ofarr.name = "joy";for (let item of arr) {console.log(item); // 1, 2, 3}
- 对象的遍历: ```javascript let obj = { name: “joy”, age: 18, school: “fzu”, };
/1. for…in for (let item in obj) { console.log(item); // name, age, school console.log(obj[item]); // “joy”, 18, “fzu” }
/2. Object.keys(obj), Object.values(obj), Object.entries(obj) Object.keys(obj).map((item)=>{ console.log(item); // name, age, school })
3. 类数组的遍历```javascript/ for...of// for...of语句在可迭代对象(包括 Array, Map, Set, String, TypedArray,arguments 对象等等)上创建一个迭代循环
创建对象的几种模式
参考资料:
- 《JS高级程序设计》
- 面向对象的程序设计
工厂模式
构造函数模式
与其它函数不同,函数内部使用this,且通过new操作符进行调用,new操作符做了两件事:1. 创建一个对象并将其proto指向该(构造)函数;2. 通过apply方法将该(构造)函数的this指向该对象。注意构造函数和普通函数没有区别,只是任何函数通过new操作符调用就被称为构造函数。
原型模式
原型模式即将属性和方法下放到函数的原型对象中:如Function.prototype.name = “mode”
原型+构造函数模式组合
ES5实现继承的4种方式
谈到继承,那么自然就会想到谁继承谁,于是就有子类和父类。至少两个以上的角色才能谈“继承”。
1. 原型链继承
直接让子类的原型对象指向父类实例(即可以调用父类的属性、方法,及其原型上的方法),当子类实例找不到对应的属性和方法时,就会往它的原型对象,也就是父类实例上找,从而实现对父类的属性和方法的继承。
Child.prototype = new Parent();Child.prototype.constructor = Child;
缺点:
- 所有Child实例原型都指向同一个Parent实例,因此对某个Child实例的父类引用类型变量修改会影响所有的Child实例。
- 在创建子类实例时无法向父类构造传参, 即没有实现
super()的功能
2. 构造函数继承
构造函数继承,即在子类的构造函数中执行父类的构造函数,并为其绑定子类的**this**,让父类的构造函数把成员属性和方法都挂到子类的this上去,这样既能避免实例之间共享一个原型实例,又能向父类构造方法传参。
function Parent(name){this.name = name;}function Child(){Parent.apply(this, args);}
3. 组合式继承
组合使用原型链继承和构造函数继承
function Parent(name){this.name = name;}function Child(){Parent.apply(this, args);}Child.prototype = new Parent();Child.prototype.constructor = Child;
缺点:期间调用了2次父类,分别是Parent.apply和new Parent()。
4. 寄生组合式继承
对组合式继承的改进:
Child.prototype = Object.create(Parent.prototype)
ES5实现继承与ES6中class对比
Sleep
// 使用定时器, 是异步的, 所以无法堵塞function sleep(delay) {setTimeout(function () {}, delay * 1000)}console.log('a')sleep(10)console.log('b')// 使用伪死循环堵塞JS单线程function sleep(delay) {var start = (new Date()).getTime();while ((new Date()).getTime() - start < delay * 1000) {continue}}console.log('a')sleep(2)console.log('b')// await / async// await则是进行执行顺序控制,每次执行一个await,// 程序都会暂停等待await返回值,然后再执行之后的awaitfunction sleep(time) {return new Promise((resolve) => {setTimeout(resolve, time)})}(async function output() {console.log(2)let out = await sleep(2000);console.log(1);})()//Promisevar sleep = (time) => {return new Promise((resolve) => {setTimeout(resolve, time)})}sleep(2000).then((res) => {console.log(res + ' ' + 1)})
函数柯里化
特点:有参数时,返回一个函数;没有参数时,执行这个函数,得到计算的结果
// 函数的柯里化// 需求:要实现一个这样的加法函数,使得:add(1,2,3)(1)(2)(3)(4,5,6)(7,8)() === 42// 分析这个函数的特点: 当这个函数有参数的时候,返回的是一个函数// 如果没有参数的时候,就会去执行这个函数,得到计算的结果function add() {let list = [];list = list.concat([...arguments]); // 类数组先转为数组return function () {// 如果传了参数,则length不为0if (arguments.length) {list = list.concat([...arguments]);return arguments.callee;} else {// 否则开始遍历数组,计算结果return list.reduce((a, b) => {return a + b;});}};}console.log(add(1, 2, 3)(1, 2, 4, 5, 6)());
JS中sort排序的用法
基本用法:arr.sort(),数组中的数字将会转换成字符进行比较,按从小到大排列。默认排序顺序是根据字符串Unicode码点
sort提供一个比较函数function(a,b)。
- 不传任何参数,按照字符串Unicode码点进行排序。
- 传入参数,可实现数字的升序、降序:
- 升序:arr.sort((a,b)=>a-b)
- 降序:arr.sort((a,b)=>b-a)
- 根据数组中对象的某个属性值进行排序:arr.sort((a,b)=>a.id-b.id)
- 根据数组对象中多个属性值进行排序、多条件排序:
var arr6 = [{id:10,age:2},{id:5,age:4},{id:6,age:10},{id:9,age:6},{id:2,age:8},{id:10,age:9}];arr6.sort(function(a,b){if(a.id === b.id){//如果id相同,按照age的降序return b.age - a.age}else{return a.id - b.id}})
将扁平数据转为树状格式
https://blog.csdn.net/qq_37746973/article/details/78662177
假设后端同学通过接口向前端返回了行业信息列表,为了取用方便,我们希望可以将其转换为树状格式,例如:
let list = [{parent_ind: "女装",name: "连衣裙",},{name: "女装",},{parent_ind: "女装",name: "半身裙",},{parent_ind: "女装",name: "A字裙",},{parent_ind: "电脑配件",name: "内存",},{name: "数码",},{parent_ind: "数码",name: "电脑配件",},];
转化为:
{"数码": {"电脑配件": {"内存" : {}}},"女装" : {"连衣裙": {},"半身裙": {},"A字裙": {}}}
function convert_format(data = []) {const index = {undefined: {},};data.forEach((item) => {index[item.name] = {};});data.forEach((item) => {index[item.parent_ind][item.name] = index[item.name]; // 对于没有parent_id的项来说, parent_id是undefined});return index.undefined;}
// 解法2function convert_format(data) {let groups = {} // 生成个体对象let tree = {} // 存储最终结果const getGroup = key => {if (!Reflect.has(groups, key)) {Reflect.set(groups, key, {})}return Reflect.get(groups, key)}for (let item of data) {let group = getGroup(item.name)if (Reflect.has(item, 'parent_ind')) { // 有parent_ind属性let parent = getGroup(item.parent_ind)Reflect.set(parent, item.name, group)// 存放有parent_ind属性的对象} else { // 无parent_ind属性Reflect.set(tree, item.name, group)}}return tree}
将对象转数组
let items = {"3/28/20": 81999,"3/29/20": 82122}// 转化为[{time: "3/28/20",number: 81999},{time: "3/29/20",number: 82122}]
使用Object.entries:
Object.entries(items).map((entry)=>{const [time, number] = entry;return {time, number}; //用到了ES6的简写规则})
深度遍历转换
[{codeName: "name1",codeValue: "value1",nodeList: [{codeName: "name11",codeValue: "value11",nodeList: [{codeName: "name111",codeValue: "value111"}]},{codeName: "name12",codeValue: "value12"}]}, {codeName: "name2",codeValue: "value2",nodeList: [{codeName: "name21",codeValue: "value21"},{codeName: "name22",codeValue: "value22"}]}]
转换为:
[{label: "name1",value: "value1",children: [{label: "name11",value: "value11",children: [{label: "name111",value: "value111"}]},{label: "name12",value: "value12"}]}, {label: "name2",value: "value2",children: [{label: "name21",value: "value21"},{label: "name22",value: "value22"}]}]
我的解法:
const dfsBasedTransform = (arr, type) => {let obj = {};let result = [];arr.map((item) => {if (item.codeName) obj.label = item.codeName;if (item.codeValue) obj.value = item.codeValue;if (item.nodeList)obj.children = dfsBasedTransform(item.nodeList, "object");result.push(obj);});if (type === "array") return result;else if (type === "object") return obj;};
数据过滤与转换
[ [true, 'value1'], [false, 'value2']]==>[{ value: 'value1' }]
我的解法:
const normalizeData = (arr = []) => {return arr.reduce((pre, cur) => {const [key, value] = cur;if (key) return pre.concat({ value });return pre;}, []);};
