你还没搞懂 this 指向?

发布于 2023-11-05 02:39:39 字数 7588 浏览 32 评论 0

一、前言

this 关键字是 JavaScript 中最复杂的机制之一。它是一个很特别的关键字,被自动定义在所有函数的作用域中。对于那些没有投入时间学习 this 机制的 JavaScript 开发者来说,this 的绑定一直是一件非常令人困惑的事。

二、了解 this

学习 this 的第一步是明白 this 既不指向函数自身也不指向函数的词法作用域,你也许被这样的解释误导过,但其实它们都是错误的。随着函数使用场合的不同,this 的值会发生变化。但总有一条原则就是JS 中的 this 代表的是当前行为执行的主体,在 JS 中主要研究的都是函数中的 this,但并不是说只有在函数里才有 this,this 实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。如何的区分 this 呢?

三、this 到底是谁

这要分情况讨论,常见有五种情况:

1、函数执行时首先看函数名前面是否有".",有的话,"."前面是谁,this 就是谁;没有的话 this 就是 window

function fn(){
  console.log(this);
}
var obj={fn:fn};
fn();//this->window
obj.fn();//this->obj
function sum(){
     fn();//this->window
}
sum();
var oo={
 sum:function(){
 console.log(this);//this->oo
       fn();//this->window
  }
};
oo.sum();

2、自执行函数中的 this 永远是 window

  (function(){ //this->window })();
  ~function(){ //this->window }();

3、给元素的某一个事件绑定方法,当事件触发的时候,执行对应的方法,方法中的 this 是当前的元素,除了 IE6~8 下使用 attachEvent(IE 一个著名的 bug)

  • DOM 零级事件绑定
  oDiv.onclick=function(){
     //this->oDiv
  };
  • DOM 二级事件绑定
  oDiv.addEventListener("click",function(){
     //this->oDiv
  },false);
  • 在 IE6~8 下使用 attachEvent,默认的 this 就是指的 window 对象
  oDiv.attachEvent("click",function(){
       //this->window
  });

我们大多数时候,遇到事件绑定,如下面例子这种,对于 IE6~8 下使用 attachEvent 不必太较真

function fn(){
  console.log(this);
}
document.getElementById("div1").onclick=fn;//fn 中的 this 就是#divl
document.getElementById("div1").onclick=function(){
console.log(this);//this->#div1
fn();//this->window
};

4、在构造函数模式中,类中(函数体中)出现的 this.xxx=xxx 中的 this 是当前类的一个实例

function CreateJsPerson(name,age){
//浏览器默认创建的对象就是我们的实例 p1->this
this.name=name;//->p1.name=name
this.age=age;
this.writeJs=function(){
console.log("my name is"+this.name +",i can write Js");
   };
//浏览器再把创建的实例默认的进行返回
}
var p1=new CreateJsPerson("尹华芝",48);

必须要注意一点:类中某一个属性值(方法),方法中的 this 需要看方法执行的时候,前面是否有".",才能知道 this 是谁。大家不妨看下接下来的这个例子,就可明白是啥意思。

function Fn(){
this.x=100;//this->f1
this.getX=function(){
console.log(this.x);//this->需要看 getX 执行的时候才知道
   }
}
var f1=new Fn;
f1.getX();//->方法中的 this 是 f1,所以 f1.x=100
var ss=f1.getX;
ss();//->方法中的 this 是 window ->undefined

5.call、apply 和 bind

我们先来看一个问题,想在下面的例子中 this 绑定 obj,怎么实现?

var obj={name:"浪里行舟"};
function fn(){
console.log(this);//this=>window
}
fn();
obj.fn();//->Uncaught TypeError:obj.fn is not a function

如果直接绑定 obj.fn(),程序就会报错。这里我们应该用 fn.call(obj)就可以实现 this 绑定 obj,接下来我们详细介绍下 call 方法:

  • call 方法的作用:

①首先我们让原型上的 call 方法执行,在执行 call 方法的时候,我们让 fn 方法中的 this 变为第一个参数值 obj;然后再把 fn 这个函数执行。

②call 还可以传值,在严格模式下和非严格模式下,得到值不一样。

//在非严格模式下
var obj={name:"浪里行舟 "};
function fn(num1,num2){
console.log(num1+num2);
console.log(this);
}
fn.call(100,200);//this->100 num1=200 num2=undefined
fn.call(obj,100,200);//this->obj num1=100 num2=200
fn.call();//this->window
fn.call(null);//this->window
fn.call(undefined);//this->window
//严格模式下 
fn.call();//在严格模式下 this->undefined
fn.call(null);// 在严格模式 下 this->null
fn.call(undefined);//在严格模式下 this->undefined
  • apply 和 call 方法的作用是一模一样的,都是用来改变方法的 this 关键字并且把方法
    执行,而且在严格模式下和非严格模式下对于第一个参数是 null/undefined 这种情况的规
    律也是一样的。

两者唯一的区别:call 在给 fn 传递参数的时候,是一个个的传递值的,而 apply 不是一个个传,而是把要给 fn 传递的参数值统一的放在一个数组中进行操作。但是也相当子一个个的给 fn 的形参赋值。总结一句话:call 第二个参数开始接受一个参数列表,apply 第二个参数开始接受一个参数数组

fn.call(obj,100,200);
fn.apply(obj,[100,200]);
  • bind:这个方法在 IE6~8 下不兼容,和 call/apply 类似都是用来改变 this 关键字的,但是和这两者有明显区别:
fn.call(obj,1,2);//->改变 this 和执行 fn 函数是一起都完成了
fn.bind(obj,1,2);//->只是改变了 fn 中的 this 为 obj,并且给 fn 传递了两个参数值 1、2,
                     但是此时并没有把 fn 这个函数执行
var tempFn=fn.bind(obj,1,2);
tempFn(); //这样才把 fn 这个函数执行

bind 体现了预处理思想:事先把 fn 的 this 改变为我们想要的结果,并且把对应的参数值也准备好,以后要用到了,直接的执行即可。

call 和 apply 直接执行函数,而 bind 需要再一次调用。

  var a ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b)
        }
    }
  var b = a.fn;
  b.bind(a,1,2)

上述代码没有执行,bind 返回改变了上下文的一个函数,我们必须要手动去调用:

 b.bind(a,1,2)() //3

必须要声明一点:遇到第五种情况(call apply 和 bind),前面四种全部让步。

四、箭头函数 this 指向

箭头函数正如名称所示那样使用一个“箭头”(=>)来定义函数的新语法,但它优于传统的函数,主要体现两点:更简短的函数并且不绑定 this

var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = function () {
            return new Date().getFullYear() - this.birth; // this 指向 window 或 undefined
        };
        return fn();
    }
};

现在,箭头函数完全修复了 this 的指向,箭头函数没有自己的 this,箭头函数的 this 不是调用的时候决定的,而是在定义的时候处在的对象就是它的 this

换句话说,箭头函数的 this 看外层的是否有函数,如果有,外层函数的 this 就是内部箭头函数的 this,如果没有,则 this 是 window

    <button>测试箭头函数 this_1</button>
    <button>测试箭头函数 this_2</button>
    <script type="text/javascript">   
        let btn1 = document.getElementById('btn1');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: function () {
                btn1.onclick = () => {
                    console.log(this);//obj
                };
            }
        };
        obj.getName();
    </script>

上例中,由于箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this。其实可以简化为如下代码:

   let btn1 = document.getElementById('btn1');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: function () {
                console.log(this)
            }
        };
   obj.getName();

那假如上一层并不存在函数,this 指向又是谁?

    <button>测试箭头函数 this_1</button>
    <button>测试箭头函数 this_2</button>
    <script type="text/javascript">   
        let btn2 = document.getElementById('btn2');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: () => {
                btn2.onclick = () => {
                    console.log(this);//window
                };
            }
        };
        obj.getName();
    </script>

上例中,虽然存在两个箭头函数,其实 this 取决于最外层的箭头函数,由于 obj 是个对象而非函数,所以 this 指向为 Window 对象

由于 this 在箭头函数中已经按照词法作用域绑定了,所以,用 call()或者 apply()调用箭头函数时,无法对 this 进行绑定,即传入的第一个参数被忽略

var obj = {
    birth: 1990,
    getAge: function (year) {
        var b = this.birth; // 1990
        var fn = (y) => y - this.birth; // this.birth 仍是 1990
        return fn.call({birth:2000}, year);
    }
};
obj.getAge(2018); // 28

参考文章

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

等你爱我

暂无简介

0 文章
0 评论
568 人气
更多

推荐作者

13886483628

文章 0 评论 0

流年已逝

文章 0 评论 0

℡寂寞咖啡

文章 0 评论 0

笑看君怀她人

文章 0 评论 0

wkeithbarry

文章 0 评论 0

素手挽清风

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文