这里写目录标题
- 1 函数节流`throttle`
- 2 函数防抖`debounce`
- 3 深拷贝
- 4 数组扁平化
- 5 单例模式
- 6 数组去重
- 6.1 只剩下不重复的
- 7 手写`promise.all()` 和 `race()`,`resolve()`,`reject()`
- 8 模拟实现`new`
- 9 手写`call`、`apply`、`bind`
- 10 模拟Object.create()实现
- 11 千分位分隔符
- 12 柯里化 curry
- 13 实现`instanceof`
- 14 `url`参数解析
- 15 实现数组元素求和
- 16 现数组的`push`方法
- 17 实现数组的`filter`方法
- 18 实现数组的`map`方法
- 19 实现字符串的`repeat`方法
- 20 实现字符串翻转
- 21 判断数组的方式
1 函数节流throttle
throttle.js
创建一个节流函数,在 wait 毫秒内最多执行 callback 一次
const input = document.getElementById("text");
function throttle(fn, wait=100) {
# 定义开始时间
let start = 0;
# 返回结果是一个函数
return function (e) {
// 获取当前时间戳
let now = Date.now();
// 判断
if (now - start >= wait) {
// 修正this指向问题
fn.call(this, e);
// 修改开始时间
start = now
}
}
}
function onPut() {
console.log(222);
console.log(this);
}
input.addEventListener("keyup", throttle(onPut, 1000));
这种方法,可以保证第一次一定被触发,后面就是间隔指定时间触发一次
2 函数防抖debounce
const button = document.querySelector("input");
防抖函数
function debounce(fn, time) {
# 定时器变量
var timeId = null;
// 返回一个函数
return function () {
# 清空定时器
clearTimeout(timeId);
# 重新启动定时器
timeId = setTimeout(() => {
fn.apply(this);
}, time);
};
}
function onSubmit() {
console.log(111);
console.log(this);
}
button.addEventListener("click", debounce(onSubmit, 1000));
3 深拷贝
1 深拷贝乞丐版 函数(方法)属性会丢失,循环引用会出错
let obj3 = [1,2,3,'4565'];
let obj4 = JSON.parse(JSON.stringify(obj3));
2 递归深拷贝
function deepclone(obj) {
if (typeof obj === "object" && obj !== null) {
#1. 创建一个容器
let cloneObj = Array.isArray(obj) ? [] : {};
#2. 遍历obj
for (let item in obj) {
if (typeof obj[item] === "object") {
cloneObj[item] = deepclone(obj[item]);
} else {
cloneObj[item] = obj[item];
}
}
}
return cloneObj;
}
4 数组扁平化
1 es6
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
# 判断arr里有没有子数组
while (arr.some((item) => Array.isArray(item))) {
arr = [].concat(...arr);
} //ES6新方法
return arr;
}
console.log(flatten(arr));
2 reduce
const arr = [1, [2, [3, 4, { a: 1 }], null], undefined];
function flatten(arr) {
return arr.reduce((prev, next) => {
return prev.concat(Array.isArray(next) ? flatten(next) : next);
}, []);
}
console.log(flatten(arr));
3 递归
var arr = [[1, 2, 8, [6, 7]], 3, [3, 6, 9], 4];
function flatten(arr) {
let newarr = [];
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] === "object") {
newarr = newarr.concat(flatten(arr[i]));
} else {
newarr.push(arr[i]);
}
}
return newarr;
}
4 扁平化
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
return arr.flat(Infinity);
}
console.log(flatten(arr)); // [1, 2, 3, 4,5]
5 单例模式
在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。
单例模式有 3 个特点:
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点;
利用函数闭包,定义一个变量,保存第一个实例对象的结果, 如果再次通过new调用时,此时就可以判断这个变量是否存在,如果存在就直接返回这个对象.
let CreateGirlFriend = (function () {
let instance = null;
return class {
constructor(name, age) {
if (instance) return instance;
this.name = name;
this.age = age;
return (instance = this);
}
};
})();
let g1 = new CreateGirlFriend("毕小胖", 21);
let g2 = new CreateGirlFriend("丛乎乎", 22);
console.log('---------------------------');
console.log(g2);
console.log(g1 === g2);
6 数组去重
1 HashMap
var arr = [1, 2, 1, 1, '1', 3, 3];
const unique = function(arr){
let res = []
let tmp = []
for(let item of arr) {
if(!tmp[item]) {
tmp[item] = 1
res.push(item)
}
}
return res
}
console.log(unique(arr));
2 ES5 filter
function method(arr) {
return arr.filter((value, index, self) => {
return self.indexOf(value) === index
})
}
3 ES6 set
let arr1 = [1, 1, 1, 2, 3, 6, 5, 5, 8, 7, 7, 7, 8];
let arr2 = Array.from(new Set(arr1));
// return [...new Set(arr)]
console.log(arr2);
6.1 只剩下不重复的
var array = [0,0,1,1,1,2,3,4,4,5]
var result = []
array.filter((item,index)=>{
return array.indexOf(item) === array.lastIndexOf(item)
})
7 手写promise.all()
和 race()
,resolve()
,reject()
接收的是一个promise数组promises
当传入的所有promise都成功
,则返回一个成功
的promise,值为所有成功promise的值的数组【注意顺序】
当传入的promises中有一个不成功
,则返回一个失败的promise,其值为第一个失败的promise的值
promise.all
用法:
const p1 = Promise.resolve('a');
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('b');
},500)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('b');
},2000)
})
const result = Promise.all([p1, p2, p3]);
console.log(result);
result.then(
value => { console.log('成功了', value) },
reason => { console.log('失败了', reason)}
)
手写promise.all
Promise.all =function(promises){
return new Promise((resolve,reject)=>{
let count =0;
const values=[]; //结果
for(let i=0;i<promises.length;i++){
Promise.resolve(promises[i]).then(value=>{
count++;
values[i]=value;
if(count=== promises.length){
resolve(values)'
}
},reason=>reject(reason)
)
}
}
}
promise.race
接收的是一个promise数组promises
返回一个promise,状态和值都取决于第一个
改变状态的promise
promise.race
用法
const p1 = Promise.resolve('a');
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('b');
},500)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('b');
},2000)
})
const result = Promise.race([p1, p2, p3]);
console.log(result);
result.then(
value => { console.log('成功了', value)},
reason => { console.log('失败了', reason)}
)
//成功了 a
promise.race
手写
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i=0; i<promises.length;i++){
Promise.resolve(promises[i]).then(
value=>resolve(value),
reason=>reject(reason)
)
}
})
}
Promise.resolve()
手写
说明:用于快速返回一个状态为fulfilled或rejected的Promise实例对象
备注:传入值可能为:(1)非Promise类型、(2)Promise 对象
如果Promise对象是失败的,返回就失败的
Promise.resolve = function(value){
return new Promise((resolve,reject)=>{
if(value instanceof Promise)
value.then(resolve,reject);
else
resolve(value);
})
}
Promise.reject()
手写
用于快速返回一个状态必为rejected的Promise实例对象
Promise.reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason);
})
}
8 模拟实现new
new
操作符具体做了什么
- 创建了一个空的对象
- 将空对象的原型,指向于构造函数的原型 ** Foo.protoType=new Foo()proto
- 将 空对象 作为构造函数的 上下文(改变this指向)
- 对构造函数有返回值的处理判断
function newObject(Fn, ...args){
// 1.创建了一个空的对象
let obj={};
// 2.将空对象的原型,指向于构造函数的原型
obj.__proto__ = Fn.prototype;
// 3 将 空对象 作为构造函数的 上下文(改变this指向)
let result = Fn.call(obj, ...args);
// 4. 返回新对象
//与new保持一直,如果构造函数有返回值,返回值是对象a就返回对象a,否则返回实例对象
return result instanceof Object ? result : obj;
}
测试
function Person(name,age){
this.name=name;
this.age=age;
}
let o1 = newObject(Person, "liming", 22);
console.log(o1);
9 手写call
、apply
、bind
apply()
和call()
都是在特定的作用域中调用函数,用于扩充函数赖以运行的作用域。区别仅在于传入参数的形式的不同。
apply()
第一个参数是对象,第二个参数是参数数组;call()
第一个参数是对象,其余参数必须逐个列举出来;bind()
通过传入一个对象,返回这个对象绑定的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。
call 方法比 apply 快的原因:call 方法的参数格式正是内部方法所需要的格式。
函数bind两次后this指向:在第一次bind完this就已经确定了,结果返回一个函数,这个函数体内不存在this问题,后续无论bind多少次,this都指向第一次bind传入的context,但是后面bind再传入的参数会生效。
1.手写call
Function.prototype.myCall=function(context,...args){
let context= context||window;
//改变指针
context.fn = this;
args=args?args:[];
//有参数,则传参
const result= args.length>0? context.fun(...args):context.fun();
delete context.fn;
return result;
}
2.手写apply
Function.prototype.myApply=function(context,args=[]){
let context= context||window;
//改变指针
context.fn = this;
//有参数,则传参
const result= args.length>0? context.fun(...args):context.fun();
delete context.fn;
return result;
}
3.手写bind
Function.prototype.myBind=function(context,...args){
let context= context||window;
//新建一个变量赋值为this,表示当前函数
const fn = this;
args=args?args:[];
//把第二次的参数也一起加入到总的参数数组中
return function {
args.push(...arguments);
return fn.apply(context,agrs)
//return fn.call(context,...args,...arguments)
}
}
10 模拟Object.create()实现
function myCreate(obj){
//声明一个函数
function F(){};
//将函数的原型指向obj
F.prototype = obj;
//返回这个函数的实例化对象
return new F();
}
let obj1 = {
name: "小芳",
age: 18,
};
let obj2 = obj1;
obj2.age = 22;
console.log(obj2);
console.log(obj1);
console.log("------------------------");
let obj3 = myCreate(obj1);
obj3.age = 25;
console.log(obj3);
console.log(obj1);
console.log('------------------------');
let obj4 = Object.create(obj1);
console.log(obj4)
11 千分位分隔符
const formatPrice=number=>{
number=''+number;
const [integer,decimal=''] = number.split('.');
return integer.replace(/\B(?=(\d{3})+$)/g,',')+(decimal?'.'+decimal:'')
}
console.log(formatPrice(123456789.3343)) // 123,456,789.3343
+匹配前面的子表达式一次或多次(大于等于1次)
$匹配输入行尾
12 柯里化 curry
柯里化是指将一个接受多个参数的函数转换为固定部分参数,然后返回一个接受剩余参数的函数。
function.length 属性指明函数的形参个数。
function curry(fn){
return function recursize(...args){
if(fn.length>args.length)
return (...rest)=>recursize(...args,...rest);
else return fn(...args);
}
}
13 实现instanceof
function myInstanceof(left,right){
let letVal=Object.getPrototypeOf(left);
const rightVal=right.prototype;
while(leftVal!==null){
if(leftVal===rightVal) return true;
leftVal= Object.getPrototypeOf(leftVal);
}
return false;
}
14 url
参数解析
function parseUrlParams(url){
url = decodeURI(url);
let params=url.split('?')[1];
let obj={};
if(!params) return obj;
let arr=params.split('&');
for(let i=0;i<arr.length;i++){
let key =arr[i].split('=')[0];
let val=arr[i].split('=')[1]==undefined?true:arr[i].split('=')[1];
if(!obj[key]){
obj[key]=val;
}else if(obj[key] && typeof obj[key]==='string'){
obj[key] = [obj[key]];
obj[key].push(val);
}else{
obj[key].push(val)
}
}
return obj;
}
let url='http://www.4399.com/?use=sss&od=www&od=ww&jjs=sss&shsh'
console.log(parseUrlParams(url));
{ use: 'sss', od: [ 'www', 'ww' ], jjs: 'sss', shsh: true }
15 实现数组元素求和
let arr=[1,2,3,4,5,6,7,8,9,10]
let arr=[1,2,3,4,5,6,7,8,9,10]
let sum = arr.reduce( (total,i) => total += i,0);
console.log(sum);
var = arr=[1,2,3,[[4,5],6],7,8,9]
var = arr=[1,2,3,[[4,5],6],7,8,9]
let arr= arr.toString().split(',').reduce( (total,i) => total += Number(i),0);
console.log(arr);
递归实现:
let arr = [1, 2, 3, 4, 5, 6]
function add(arr) {
if (arr.length == 1) return arr[0]
return arr[0] + add(arr.slice(1))
}
console.log(add(arr)) // 21
16 现数组的push
方法
let arr = [];
Array.prototype.push = function() {
for( let i = 0 ; i < arguments.length ; i++){
this[this.length] = arguments[i] ;
}
return this.length;
}
17 实现数组的filter
方法
Array.prototype._filter = function(fn) {
if (typeof fn !== "function") {
throw Error('参数必须是一个函数');
}
const res = [];
for (let i = 0, len = this.length; i < len; i++) {
fn(this[i]) && res.push(this[i]);
}
return res;
}
18 实现数组的map
方法
Array.prototype._map = function(fn) {
if (typeof fn !== "function") {
throw Error('参数必须是一个函数');
}
const res = [];
for (let i = 0, len = this.length; i < len; i++) {
res.push(fn(this[i]));
}
return res;
}
19 实现字符串的repeat
方法
输入字符串s,以及其重复的次数,输出重复的结果,例如输入abc,2,输出abcabc。
function repeat(s, n) {
return (new Array(n + 1)).join(s);
}
递归:
function repeat(s, n) {
return (n > 0) ? s.concat(repeat(s, --n)) : "";
}
20 实现字符串翻转
String.prototype._reverse = function(a){
return a.split("").reverse().join("");
}
var obj = new String();
var res = obj._reverse ('hello');
console.log(res); // olleh
21 判断数组的方式
- 通过原型链做判断
obj.__proto__ === Array.prototype;
- 通过instanceof做判断
obj instanceof Array
- 通过Array.prototype.isPrototypeOf
Array.prototype.isPrototypeOf(obj)
- 通过ES6的Array.isArray()做判断
Array.isArrray(obj);