call
Function.prototype.myCall = function(context){// 1.判断有没有传入要绑定的对象,没有默认是window,如果是基本类型的话通过Object()方法进行转换context = Object(context)||window/**在指向的对象obj上新建一个fn属性,值为this,也就是fn()相当于obj变成了{value: 'hdove',fn: function fn() {console.log(this.value);}}*/context.fn = this// 取出传递的参数,第一个参数是this,截取除第一个参数之外剩余参数const args = [...arguments].slice(1)let result = ''result = context.fn(...args)delete context.fnreturn result}const obj = {value: 'hdove'}function fn(name, age) {return {value: this.value,name,age}}fn.myCall(obj, 'LJ', 25); // {value: "hdove", name: "LJ", age: 25}
apply
apply与call基本一致,只不过就是传递的参数不同。
Function.prototype.myApply = function(context, args) {var context = Object(context) || window;context.fn = this;let result = '';//4. 判断有没有传入argsif(!args) {result = context.fn();}else if(args instanceof Array) { //判断是否传递数组参数,如果不是则报错result = context.fn(...args);}else{throw new TypeError()}delete context.fn;return result;}const obj = {value: 'hdove'}function fn(name, age) {return {value: this.value,name,age}}fn.myApply(obj, ['LJ', 25]); // {value: "hdove", name: "LJ", age: 25}
bind
Function.prototype.myBind = function(context){let self = this// 第一个参数是this,截取掉let args1 = [...arguments].slice(1)//柯里化解决参数丢失的问题let bindFn = function(){// 获取调用时传入的参数let args2 = [...arguments]/**这里我们通过打印this,我们可以看出来。当这个绑定函数被当做普通函数调用的时候,this其实是指向window。而当做构造函数使用的时候,却是指向这个实例,所以this instanceof bindFn为true,这个实例可以获取到fn()里面的值。我们可以再fn里面添加一个属性test.如果按照之前的写法 打印出来的是undefined,正好验证了我们上面所说的this指向的问题。所以解决方法就是添加验证,判断当前this如果 this instanceof bindFn 说明这是new出来的实例,指向这个实例, 否则指向context*/console.log(this);return self.apply(this instanceof bindFn?this:context,args1.concat(args2))}// 绑定原型function proFn(){} //创建新方法proFn.prototype = self.prototype //继承原型bindFn.prototype = new proFn() //绑定原型return bindFn}const obj = {value: 'hdove'}function fn(name, age) {this.test = '我是测试数据';}fn.prototype.pro = '原型数据';var bindFn = fn.myBind(obj, 'LJ', 25);var newBind = new bindFn();console.log(bindFn.__proto__ === fn.prototype); // falseconsole.log(bindFn.pro); // "undefined"console.log(fn.prototype.pro); // "原型数据"
防抖
//模拟一段ajax请求function ajax(content) {console.log('ajax request ' + content)}function debounce(fun,delay){return function(args){let that = thislet _args = argsclearTimeout(fun.id)fun.id = setTimeout(function(){fun.call(that,_args)},delay)}}let inputb = document.getElementById('debounce')let debounceAjax = debounce(ajax, 500)inputb.addEventListener('keyup', function (e) {debounceAjax(e.target.value)})
运行结果:
加入了防抖以后,当你在频繁的输入时,并不会发送请求,只有当你在指定间隔内没有输入时,才会执行函数。如果停止输入但是在指定间隔内又输入,会重新触发计时。
节流
function throttle(fun, delay) {let last, deferTimerreturn function (args) {let that = thislet _args = argumentslet now = +new Date()if (last && now < last + delay) {clearTimeout(deferTimer)deferTimer = setTimeout(function () {last = nowfun.apply(that, _args)}, delay)}else {last = nowfun.apply(that,_args)}}}let throttleAjax = throttle(ajax, 1000)let inputc = document.getElementById('throttle')inputc.addEventListener('keyup', function(e) {throttleAjax(e.target.value)})
运行结果:
在不断输入时,ajax会按照我们设定的时间,每1s执行一次。
深拷贝
function deepClone(value) {if (value == null) return value;if (typeof value !== 'object') return value;if (value instanceof RegExp) return new RegExp(value);if (value instanceof Date) return new Date(value);// 要判断 value 是对象还是数组 如果是对象 就产生对象 是数组就产生数组let obj = new value.constructor;for(let key in value){obj[key] = deepClone(value[key]); // 看一看当前的值是不是一个对象}return obj;}
new
function _new(fn, ...arg) {const obj = Object.create(fn.prototype);const ret = fn.apply(obj, arg);// 如果函数返回 非空并是对象 返回 value 否则 返回 newObjreturn ret instanceof Object ? ret : obj;}
