对this的理解 以及实现一个apply、call、bind

533 阅读4分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

this

this 的理解也是 在js 中必备的,毕竟这是js中必须要掌握的东西

全局的 this

  • 这个this指向全局 windows
console.log(this, 'this')

object中的this

  • 第一种是obj自己本身调用的指向obj
  • 第二种是在赋值给了一个变量,这个时候调用的这个时候指向windows
  • 第三种是箭头函数他会找他最近的this, 对象不构成单独的作用域(全局作用域/局部函数作用据),所以他指向了window
let obj = {
   name: '我是obj的name',
   fn() {
     console.log(this,'this')
   },
   fn1: () => { console.log(this,'箭头函数的this')}
}

obj.fn()
//调用:obj.fn()
//输出:{name: "我是obj的name", fn: ƒ}



let a = obj.fn;
a();
//输出:this 指向 windows


//调用: obj.fn1()
obj.fn1();
//输出:this 指向 windows

函数内部的this

 //1、普通函数
 function fn() {
   console.log(this, 'this')
 }
 //指向 window
 
 //2、闭包函数
 function fn() {
   return function() {
     console.log(this,'this')
   }
 }
 
 let obj = {
   f: fn()
 }
 
 //调用 fn()() 指向window
 //调用:obj.f()  指向 obj本身  
 
 //fn() return () => {console.log(this, 'this')} 
 //箭头函数在上面两种情况都是指向 window
 
 
 //3、构造函数
 
 function Fn() {
   console.log(this, 'this')
 }
 
 let f = new Fn();
 //这个构造函数的this 指向他自己本身

改变this

有3种办法能改变this: apply、bind、call; 说一下他们的不同点:

  1. apply 立即执行上下文(就是立即触发该函数) 参数是用数组列表接收
let obj = {
  text: '掘金'
}


function fn(...args) {
  console.log(this,'this', args)
  return '返回值'
}

//调用fn()默认指向window;

//apply 改变this

let f = fn.apply(obj)

//1、会调用fn函数;
//2、改变this (这个时候 this指向obj)
//3、传参数是数组列表:fn.apply(obj, [1,2,3,4,5])
//4、返回值是调用的返回结果 f = '返回值'

2、call 立即执行上下文(就是立即触发该函数) 参数是用列表接收用‘逗号隔开

let obj = {
  text: '掘金'
}

function fn(...args) {
  console.log(this,'this', args)
  return '返回值'
}

//调用fn()默认指向window;

//apply 改变this

let f = fn.call(obj)

//1、会调用fn函数;
//2、改变this (这个时候 this指向obj)
//3、传参数是:fn.call(obj, 1,2,3,4,5)
//4、返回值是调用的返回结果 f = '返回值'

3、bind 不会立即执行上下文 ,返回函数自己本身,传参数是用列表接收用‘逗号隔开’跟call一样

let obj = {
  text: '掘金'
}

function fn(...args) {
  console.log(this,'this', args)
  return '返回值'
}

//调用fn()默认指向window;

//apply 改变this

let f = fn.bind(obj);
f();
//1、不会立即调用fn函数,需要手动调用下f() / fn.bind(obj)();
//2、改变this (这个时候 this指向obj)
//3、预备传参数是:fn.bind(obj, 1,2,3,4,5)
//4、直接传参就跟函数一样了fn.bind(obj)(1,2,3,4,5)
//5、返回值是fn本身

实现一个自己的call、apply

共同点: 1、立即调用 2、实现 this指向 3、实现多个参数传参 4、null 指向window

call

//实现一个自己call
//没有考虑兼容了 这样更容易理解,也更好实现

  Function.prototype.myCall = function(target, ...args) {
       //给obj添加一个fn函数 也就 this
        const obj = target || window;
        obj.fn = this;
        //**这个时候调用的是obj上的fn**
        let result = obj.fn(...args);
        //最后删除fn
        delete obj.fn
        return result
   }


   let obj = {
       name: 'call'
   }

   function fn(...args) {
       console.log(this,'this', ...args)
   }

   fn.myCall(obj, 1,2,3,4,5)
   fn.myCall(null, 1,2,3,4,5)
   //输出验证通过

apply

function fn1(...args) {
  console.log(this,'thisFn1', ...args, args)
}


let obj = {
   name: 'apply'
 }
 
 Function.prototype.myApply = function(target, ...args) {
       //给obj添加一个fn函数 也就 this
        const obj = target || window;
        if(args && args.length > 1) {
            throw new Error('apply Invalid parameter')
        }
        obj.fn = this;
        //这个时候调用的是obj上的fn
        let result = obj.fn(...args[0]);
        //最后删除fn
        delete obj.fn

        return result
   }

 fn1.myApply(obj, [1,2,3,4])
 
 
 //验证通过 输出正确

实现一个自己的bind

 //1、不是立刻调用 返回一个他自己的函数
   //2、实现 this指向
   //3、实现多个预参数传参 和实际参数传参合并 
   //4、null 指向window


   Function.prototype.myBind = function(target, ...args) {
     //给obj添加一个fn函数 也就 this
        const obj = target || window;
        let than = this; 
        return function(...args1) {
            
            obj.fn = than;
            //这个时候调用的是obj上的fn
            let result = obj.fn(...([...args, ...args1]));
            //最后删除fn
            delete obj.fn
        }

   }


   let obj = {
       name: 'call'
   }

   function fn(...args) {
       console.log(this,'this', ...args, args)
   }

   fn.myBind(obj, 1,2,3)(4,5,6)
   
   
   //验证通过 输出正确

结束语

大家好,我是三原。我会继续保持学习总结的