• Javascript中的this指向问题


    this,真的折磨了我蛮久,躺在了我的笔记本中也挺久....今天总结下,this的一系列问题吧.

    1.this永远指向一个对象;

    2.this的指向完全取决于函数调用的位置.

    针对上面的两点,我们可以保证到一个,不管在什么地方使用this,它必然会指向某个对象;确定了第一点后,也引出了一个问题,就是this使用的地方到底在哪里,而this就是函数运行时所在的对象.这本来并不会让我们糊涂,但是在Javascript支持运行环境动态切换,也就是说,this的指向是动态的,很难事先会确定到底指向哪个对象,这才是最让我们感到困惑的地方.

    先看原理吧:

    function fun(){
    console.log(this.s);
    }
    
    var obj={
    s:'1',
    f:fun
    }
    
    var s='3';
    
    obj.f();//1
    fun();//3

    上述代码中,fun函数被调用了两次,但是两次执行的结果是不一样的.大多数的理解的话,是obj.f()的调用中,因为运行环境在obj对象内,因此函数中的this指向对象obj;而在全局作用域下调用fun(),函数中的this就会指向全局作用域对象window;但是,最开始还是并没有了解到this的指向为啥改变,this指向的改变是什么时候发生的.

    首先,我们知道在JS中,数组函数和对象都是引用类型,在参数传递时也就是引用传递.比如上述的代码中,obj对象有两个属性,但是属性的值类型是不同的,在内存中的表现也是不同的.

    调用的时候,就会变成

    因为函数在JavaScript中,既可以当作值传递与返回,也可以当作对象与构造函数,所有函数在运行时需要确定其当前的运行环境,因此就引出了this,this会根据运行环境的改变而改变,同时,函数中的this也只能在运行时才能最终确定运行环境.

    接下来再看个例子,有助于理解运行环境的动态切换规则:

    var C={
    name:"王五",
    f:function(){
    console.log("姓名" + this.name);
    }
    
    var D={
    name:"李四"};
    
    D.f = C.f;
    
    D.f();//姓名李四
    C.f();//姓名王五

    上述代码中,C.f中的属性被赋给D.f,也就是C对象将匿名函数的地址赋值给了D对象;在调用时,函数分别根据运行环境的不同,指向不同的对象.

    function baz(){
    console.log(this.a);
    }
    
    var obj2={
    a:2,
    fn:baz
    };
    
    var obj1={
    a:1,
    o1:obj2};
    obj1.o1.fn(); //2

    obj1对象的o1属性值是obj2对象的地址,而obj2对象的fn属性的值是函数baz的地址;函数baz的调用环境始在obj2中的,因此this指向对象obj2.

    接下来,再叙述下几种常用的this情况.

    首先,我们先看下事件绑定情况下的,事件绑定的话一共有三种方式,分别为行内绑定、动态绑定、事件监听;

    行内绑定的两种情况:

    <input type='button' value="ljy的按钮"  onclick="btnclick()">
    <script>
    function btnclick(){
    this//此函数的运行环境为window上,因此this指向window;}
    </script>
    <input type="button" value="ljy的button" onclick="this">
    //运行环境在节点对象中,因此this指向本节点对象

    当事件触发时,属性值就会作为JS代码被执行,当前运行环境没有btnclick函数,因此浏览器就需要跳出当前运行环境,在整个环境中寻找一个btnclick的函数并执行,所以函数内部的this指向了全局对象window;若不是一个函数调用,直接在当前节点对象环境下使用this,那么this显然指向本节点对象.

    动态绑定与事件监听:

    <input type="button" value="ljy的按钮" id="btn">
    <script>
    var btn= document.getElementById('btn');
    btn.onclick= function(){
    this;//this指向本节点对象}
    </script>

    因为动态绑定的事件本就是为节点对象的属性,重新复制一个匿名函数,因此函数在执行时就是在节点对象环境下,this自然就指向了本节点对象.

    构造函数中的this:

    function Person(){
    this.x="11";
    this.y = function(){};
    }
    
    var person=new Person();

    new一个构造函数并执行函数内部代码的过程:

    1.创建一个空对象;2.将本对象的原型指向Person.prototype(对象继承Pesron.prototype);3.将构造函数的this指向本对象;4.执行构造函数的代码为对象赋值;5.返回本对象地址;

    当Javascript引擎指向到第三步的时候,会强制的将this指向新创建出来的这个对象.所以这里的this指向person.

    定时器中的this:

    var obj={
    fun:function(){
    this;}
    }
    setInterval(obj.fun,2000);//指向window
    setInterval('obj.fun()',2000);//指向obj

    setInterval()/setTimeout()是window对象下内置的一个方法,接受两个参数,第一个参数,可以是一个函数或者一段js代码,第二个参数实质性前面函数或者代码的时间间隔.

    在上述代码中,setInterval(obj.fun,2000)的第一个参数是obj对象的fun,因为Javascript中函数可以被当作值来做引用传递,实际就是将这个函数的地址当作参数传递给了setInterval,那么2000毫秒后,函数的运行就已经是再window对象下了,也就是函数的调用者成为了window,因此指向window.那么下面一个,中的第一个参数,实际则是传入了一段JS代码,2000毫秒后,当JS引擎来执行这段代码的时候,则是通过obj对象来找到fun函数并执行调用的,那么函数依旧执行在对象obj内,所以函数内部的this指向了obj对象.

    再来一个定时器的例子!

    var name='My name is window';
    var obj={
    name:'I am obj.";
    fun:function(){
    var timer=null;
    clerInterVal(timer);
    timer=setInterval(function(){
    console.log(this.name);//My name is window.
    },1000)
    }
    }

    在这里,从this.name可以看出this的指向是window.如果没有特殊指向,setInterval和setTimeout的回调函数中this的指向是window.那么怎么修改this的指向,这里总结了点:

    在外部函数中,将this存为一个变量,回调函数中使用该变量,而不是直接使用this.

    var name='My name is window';
    var obj={
    name:'I am obj.";
    fun:function(){
    var that=this;
    var timer=null;
    clerInterVal(timer);
    timer=setInterval(function(){
    console.log(that.name);//I am obj.
    },1000)
    }
    }

    还有一个非常妙的用法,箭头函数:

    首先,先介绍下箭头函数中的this指向问题,箭头函数本身是没有自己的this的,它的this继承自外部函数的作用域.我们来举两个例子:

    const obj={
    num:10,
    hello:function(){
    console.log(this);//obj
    setTimeout(()=>{
    console.log(this);//obj
    });
    }
    }
    obj.hello()

    这个箭头函数的上一级为hello函数,它是属于obj的,因此其this指向obj.

    const obj={
    radius:10;
    diameter(){
    return this.radius*2;
    },
    pre:()=>2*Math.PI*this.radius
    }
    console.log(obj.diameter())//20
    console.log(obj.pre())//NaN

    diameter的原因就不说了,pre是箭头函数,this应指向上下文函数this的指向,这里上下文没有函数对象,所以就默认为window,window里面没有radius属性,因此返回NaN.

    那么,我们通过箭头函数也可以非常ok的修改setTimeInterval中的this指向.

    var name='My name is window';
    var obj={
    name:'I am obj.";
    fun:function(){
    var that=this;
    var timer=null;
    clerInterVal(timer);
    timer=setInterval(()=>{
    console.log(this.name);},1000)//I am obj
    }
    }
  • 相关阅读:
    二叉索引树
    将rst转换为markdown格式
    LeetCode电话号码的字母组合题
    根据文件名列表批量复制文件
    VMware+Alpine代替Docker部署Gogs服务
    wsl安装Ubuntu16.04无法ssh提示Permission denied (publickey)
    json二位数组根据指定键名去重
    thinkphp6 关联模型如何查询已经软删除的数据
    DB2数据库配置步骤(图解)
    基于SpringBoot2.3.1进行AOP原理源码阅读
  • 原文地址:https://www.cnblogs.com/ljylearnsmore/p/14488646.html
Copyright © 2020-2023  润新知