一文让你拿捏 this、call、apply、bind

79 阅读4分钟

前言:前端现在越来越卷,找工作拿高薪更是不易,因为想要在这个特别卷的时代,我们要想进大厂,拿高薪,首先最重要的就是,知识点得掌握扎实,做到举一反三。

因此 this、call、apply、bind 还不是很清楚的,文章干货很多,请先收藏,慢慢阅读,让你直接 拿捏 这些重要经典知识点。

1、this的指向

this的指向是面试中必考且比较难得一个点,本文以最简单最好理解的方式来解读this指向的问题。

ES5中,this的指向是原则:永远指向最后调用它的那个对象 这一点非常关键:理解this,必须牢牢记住这句话。

看看一些例子:

例1:

 var name = 'windowName';
 function example(){
     var name = 'object';
     console.log(this.name);  //windowName
 }
 example()  

这里为什么输出的是 window 而不是object ,原因在于: 函数 example 在调用时,直接调用 example() ,前面没有对象,相对于这样调用 window.example();又:this 永远指向最后调用它的那个对象 ,所以输出 window。

例2:

 var name = "windowName";
 var o = {
    name : "object",
    fn : function(){
        console.log(this.name);  //object
    }
 }
 o.fn()
 //window.a.fn()

在这里打印的是 object ,因为函数 是 对象 o 调用的。

这里换成 window.a.fn() 也是同样的结果,因为最后还是 对象a 调用的,印证了那句话 :this永远指向它最后调用的对象

例3:面试中很坑的一个例子。

 var name = "windowName";
 var o = {
    name : "object",
    fn : function(){
        console.log(this.name);  //windowName
    }
 }
 const f = o .fn;
 f()

这里就让人很懵,大家肯定都很好奇原因,首先要明白 const f = o .fn; 这一步是声明变量f 并赋值的操作,在这里只是将对象 o 的方法 fn 赋值给 f 并没有调用,而最后调用f() ,(实际是调用o .fn()) ,是在window对象下调用的,因此this 指向的是 window , 所有输出的是 :windowName

2、改变this的指向?

通常改变this的方法有一下几种方法:

  • 使用call、apply、bind。
  • ES 6 中的箭头函数。
  • 函数内部一般使用 that = this
  • new 实例化一个对象。
  • 隐式绑定

2.1 使用 apply、call、bind

1、使用call

 var person = {
     name:'王涵宇'
 }
 var o = {
    name : "object",
    fn : function(){
        console.log(this.name);  //王涵宇
    }   
 }
 o.fn.call(person)

2、使用apply

 /*
     以call实例为例
 */
 o.fn.apply(person)  //王涵宇

3、使用bind

 /*
     以call实例为例
 */
 o.fn.bind(person)()  // 王涵宇
 //注意这里的调用方式与上面的不同

2.2 使用 ES 6 中的箭头函数。

使用箭头函数:

 var name = "windowName";
 var o = {
    name : "object",
    fn1 : function(){
        console.log(this.name);  //object
    },
     fn2 : function(){
         setTimeout(()=>{
             this.fn1()
         },1000)
     }
 }
 o .fn2();

不使用箭头函数:

 var name = "windowName";
 var o = {
    name : "object",
    fn1 : function(){
        console.log(this.name);  //Uncaught TypeError: this.fn1 is not a function
    },
     fn2 : function(){
         setTimeout(function(){
             this.fn1()
         },1000)
     }
 }
 o .fn2();

会报错,因为 最后是 window对象 调用 setTimeOut ,而 window对象上没有 fn1 这个方法。

2.3 函数内部使用 that = this

 var name = "windowName";
 var o = {
    name : "object",
    fn1 : function(){
        console.log(this.name);  //  object
    },
     fn2 : function(){
         let that = this;  //保存this
         setTimeout(()=>{
             that.fn1()
         },1000)
     }
 }
 o .fn2();

2.4 new 实例化一个对象

 var name = "windowName";
 function getName(){
     this.name = '王涵宇';
     console.log(this); //getName {name: '王涵宇'}  this指向这个函数(对象)
 }
 new getName()

2.5 隐式绑定

说白了:就是谁调用指向谁。

 ​
 const obj = {
     name :'王涵宇',
     getName : function(){
         console.log(this);
         console.log(this.name)   //王涵宇
     }
 }
 obj.getName() 

3、实现call、apply、bind

1、实现call

1、这三种方法都在函数的 原型上 接受两个参数

 Function.prototype.myCall = function(obj,...args){
     //这里默认不传obj。就是给 window
     obj = obj || window;
     args = args ? args : [];
     //给obj添加一个独一无二的属性,防止与调用函数中有相同的属性变量
     const fn = Symbol();
     obj[fn] = this;
     //通过隐式绑定的方式调用函数
     //在这里用一个变量保存调用的结果,完了后删除这个属性,防止造成影响
     const res = obj[fn](...args);
     delete obj[fn](...args);
     return res;
 }
 const obj1 = {
     name:'ustinian',
     getName:function(age){
         console.log(`${this.name}今年${age}了!`)   //王涵宇今年22了!
     }
 }
 const obj2 = {
     name:'王涵宇',
 }
 obj1.getName.myCall(obj2,22)  

2、实现apply

 Function.prototype.myApply = function(obj,args){
     //这里默认不传obj。就是给 window
     obj = obj || window;
     args = args ? args : [];
     //给obj添加一个独一无二的属性,防止与调用函数中变量属性重名
     const fn = Symbol();
     obj[fn] = this;
     //隐式绑定的方式调用函数
     const res = obj[fn](args);
     delete obj[fn](args);
     return res;
 }

3、实现bind

bind 极其重要,以及涉及 的知识点很多,在下一文章中将重点且详细的介绍 bind 以及实现bind。

\