手写实现call
call()的作用是改变函数执行的this指向,需要传至少一个参数,第一个参数为指向的对象和单独给出的一个或多个参数来调用函数,注意: call()只会改变一次this指向
let obj = {
name: '张三',
age: 18
}//先定义一个要指向的对象
function fn(q, w, e) {
console.log(this)
console.log(q, w, e)
return 888
}//定义一个函数来调用手写的myCall
//call不能改变this的两种情况 1.箭头函数 2.bind后的新函数
Function.prototype.myCall = function(target, ...arg) {
//定义一个myCall挂在Function的原型上 让每个函数都可以调用
// this ---> fn 现在this指向调用myCall的fn这个函数
let fn = this; // 定义一个变量赋值为fn这个函数
let a = Symbol('mycall')//定义一个唯一值
target[a] = fn;//给要指向的对象新增一个属性,属性名为唯一值,属性值为fn这个函数
let res = target[a](...arg) //target调用的这个函数,所以this是这个对象
//res是fn执行结果
delete target[a] //只改变一次this指向,执行完后删除这个属性
return res //myCall的执行结果就是fn的执行结果
}
fn.myCall(obj,1,2,3)
手写实现bind
bind的作用: bind() 方法返回一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用
let obj = {
name: '李四',
age: '18'
}
Function.prototype.myBind = function(target, ...arg) {
let fn = this;//定义一个变量赋值为调用myBind的函数
return function(...arg2) {//返回一个新函数
return fn.call(target, ...arg, ...arg2)//新函数中返回fn的执行结果,使用call改变this指向
// 将两个函数形参放在一起,新函数的形参会放在myBind形参的后面
}
}
function fn(...arg) {
console.log(arguments);
console.log(this);//此处打印obj这个对象
}
let res = fn.myBind(obj, 1, 2, 3)
console.log(res( 4, 5, 6));
手写实现apply
和call的唯一区别就是 第二个参数是一个集合
let obj = {
name: 'leizai',
sex: '1',
}
Function.prototype.myapply = function(target, ...arg) {
let fn = this, //当前的this指向调用此方法的函数
a = Symbol('myapply'); //定义一个唯一值 作为属性名
target[a] = fn; //将唯一值a赋值为fn
let res = target[a](...arg); //执行这个函数并传参
delete target[a]; //调用完成删除这个属性
return res; //返回函数的执行结果
}
function fn() {
console.log(this);
console.log(arguments);
};
let res = fn.myapply(obj, 1, 2, 3, 4, 5, 6);
console.log(res);
手写实现函数防抖
函数的防抖主要应用在按钮的频繁点击以及input框的连续输入
防抖的作用: 防止用户多次点击触发多次请求,用户点击后。会有一段等待时间,在等待时间内,如果用户重复则重新定时,,并且只执行最后一次。
const debounce = (fn, ...argu) => {
let sign = null; //标记定时器
return () => {
clearTimeout(sign) //每次进来先清除定时器
sign = setTimeout(() => {
fn.apply(this, argu); //只执行最后一次
}, 200)
}
}
手写函数节流
函数的节流主要应用于onscroll和touchmove等此类触发频率高的事件
节流的作用: 减低函数的触发次数,减少栈内存的占用,以免造成页面卡顿
const throttle = (fn, ...arg) => {
let flag = true; //定义标记
return () => {
if (!flag) return; //边界判断 防止指定时间内再被执行
flag = false; //将标记赋值为false,此时无法进入此函数
setTimeout(() => {
fn.apply(this, arg); //执行函数
flag = true; //将flag重新赋值为true,以便执行下一次
}, 200)
}
}
正则匹配身份证号码
let Id = 130432199811282111,
reg = /^1[\d]{16}[\d|x]$/;
let res = reg.test(id);
console.log(res);
正则解析URL
let obj = {};
let url = 'https://baidu.com?name=aa&&id=1',
reg = /([^?&=]+)=([^?&=]+)/g;
url.replace(reg, ($1, $2, $3) => {
//$1 为整个正则捕获的内容 $2为第一个分组捕获的内容 $3为第二个分组捕获的内容
obj[$2] = $3;
});
console.log(obj);
正则匹配邮箱
let email = '3077251786@qq.com',
reg = /^\d{5,11}\@qq\.com$/, //简易匹配qq邮箱
reg1 = /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/; //完整匹配邮箱
let res = reg.test(email);
console.log(res);
正则匹配手机号
let phone = '15203204450';
let reg = /^1[3-8]\d{9}$/, //简易版
reg1 = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/;//完整版
let res = reg.test(phone);
console.log(res);
手写实现new
function myNew(target, ...arg) {
let obj = {}; //第一步先创建一个对象
obj.__proto__ = target.prototype; //第二步将对象的隐式原型指向target的原型
let res = target.call(obj, ...arg);//使用call改变this指向
//如果函数执行结果是一个引用数据类型 则返回执行结果 否则返回obj这个对象
return typeof res == 'object' ? res : obj;
};
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
};
let p1 = new Person('小明', 15, 1);
let p2 = myNew(Person, '小李', 16, 2);
console.log(p1, p2);
手写实现instanceof
instranceof使用来检测一个构造函数的prototype(显式原型)是否在某个实例对象的原型链上,是四个检测数据类型的方法中的一种。
let ary = [];
function myIstanceof(example, type) {
//存储example的隐式原型
let value = example.__proto__;
while (value && value !== type.prototype) {
//证明value不是null;而且value不等于b的显式原型
//下一步就是 看a.__proto__.__proto__
value = value.__proto__; //通过隐式原型向上查找
}
return value == null ? false : true
}
console.log(myIstanceof(ary, Array));
console.log(myIstanceof(ary, Number));
console.log(myIstanceof(ary, Date));
手写实现create
Object.create执行返回一个空对象,对象的__proto__指向传递的参数;
function myCreate(value) {
let obj = {};
// obj.__proto__ = value; //__proto__是非法属性
function Person() {};
Person.prototype = value; //原型重写 内置类的原型不可更改
let a = new Person;//利用new执行的原理 创造一个实例
return a;
}
console.log(myCreate(Array.prototype));
手写实现JSONP
JSONP是跨域的解决方案之一,利用script标签的src属性不会被浏览器的同源策略限制的机制,实现跨域。
(function () {
//工具方法
const toString = Object.prototype.toString;
//检测是否为纯粹对象
const isObject = function isObject(obj) {
let proto, Ctor;
//判断obj是否不存在 或者是否 非对象
if (!obj || toString.call(obj) !== '[object Object]') return;
//获取obj的原型
proto = Object.getPrototypeOf(obj);
if (!proto) return true;
//获取obj原型上的 函数本身
Ctor = proto.hasOwnProperty('constructor') && proto.constructor;
return typeof Ctor === "function" && Ctor === Object;
};
//将参数转为传参指定格式
const stringify = function stringify(obj) {
let str = ``;
Reflect.ownKeys(obj).forEach(key => {
//获取每一项的属性值
let value = obj[key];
str += `&${key}=#{value}`;
console.log(str);
});
//返回一个从索引1开始的字符串 删除开头的&
return str.substring(1);
};
//核心方法
const jsonp = function jsonp(url, options) {
//格式检验 & 合并默认配置项
if (typeof url !== 'string') throw new Error('url is not a string');
if (!isObject(options)) options = {};
//合并默认配置项
let {
pamas,
jsonpName
} = {
pamas: '',
jsonName: 'callback',
...options
};
/* 或者 Object.assign({
params: null,
jsonpName: "callback"
}, options); */
//返回peomise实例
return new Promise((resolve, reject) => {
//移除创建的全局函数和script标签
const cler = function cler() {
delete window.jsonName;
document.removeChild(script);
};
//先创建全局函数 不能造成全局变量污染
const name = `json${+new Date}`;
window[name] = function (data) {
// 请求成功:会把创建的全局函数执行,DATA就是从服务器获取的结果
resolve(data);
cler();
};
//处理url
if (isObject(pamas)) {
url += `${url.includes('?')?'&':"?"}${stringify(options)}`;
};
url += `${url.includes("?") ? "&" : "?"}${jsonpName}=${name}`;
//创建script标签
const script = document.createElement('script');
script.src = url;
//如果script请求失败返回失败态
script.onerror = function () {
reject();
cler();
};
//将标签插入到页面内
document.body.appendChild(script);
})
};
/* 暴露API */
if (typeof module === 'object' && typeof module.exports === 'object') module.exports = jsonp;
if (typeof window !== 'undefined') window.jsonp = jsonp;
})()