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.fn
return 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. 判断有没有传入args
if(!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); // false
console.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 = this
let _args = args
clearTimeout(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, deferTimer
return function (args) {
let that = this
let _args = arguments
let now = +new Date()
if (last && now < last + delay) {
clearTimeout(deferTimer)
deferTimer = setTimeout(function () {
last = now
fun.apply(that, _args)
}, delay)
}else {
last = now
fun.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 否则 返回 newObj
return ret instanceof Object ? ret : obj;
}