• JavaScript 你真的了解this指向吗


    前言

      终于开始写this指向了,相信这对很多JavaScript的学习者来说是一个非常恐怖的环节,个人认为也算是JavaScript中最难理解的一个知识点,this非常的方便但是在你不熟悉它的情况下可能会出现很多坑。

      本篇文章将带你充分了解this指向,用最精炼简短的语句阐述不同情况下的this指向。

    详解this指向

    window对象

      window是一个全局的对象,里面存了很多方法。

      当我们使用var进行变量命名时,变量名会存入到window对象中,以及当我们使用标准函数定义方法时函数名也会存入window对象中。

    <script>var username = "云崖";
    
            function show(){
                    console.log("show...");
            };
    
            console.log(window.username);  // 云崖
            window.show();  // show...
    </script>

    全局环境

      在全局环境中,this的指向就是window对象。

      但是我们一般不这么用。

    <script>var username = "云崖";
    
            function show(){
                    console.log("show...");
            };
    
            console.log(this.username);  // 云崖
            this.show();  // show...
    // 依旧可以执行,代表全局环境下this就是window对象
            // 如果你不相信,可以打印它看看。
    
            console.log(this);  //  Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
    </script>

    普通函数

      非严格模式下,普通函数中this的指向为window对象。

    <script>// "use strict"; // 在非严格模式下,普通函数中this的指向是window对象
    function show() {
    
                    console.log("show...");
                    console.log(this);  // window
    
            };
    
            show()
    
    </script>

      但是在严格模式下,普通函数中this指向为undefined

    <script>"use strict"; // 在严格模式下,普通函数中this指向为undefined
    function show() {
    
                    console.log("show...");
                    console.log(this);  // undefined
    
            };
    
            show()
    
    </script>

    构造函数

      当一个函数能被new时,该函数被称之为构造函数。

      一般构造函数中包含属性与方法,函数中的上下文指向到实例对象。

      在构造函数中的this一般指向为当前对象。对于其方法而言this指向同样为当前对象。

      你可以这么认为,在用Function定义类时,类中的方法指向当前类。

      这样是不是好理解多了?

    <script>"use strict"; 
    
            function User(username) {  // 在没有class语法出现之前,这种构造函数我们通常会将它当做类来看待。
    this.username = username;  // 可以称之为类属性
    
                    console.log(this);  // User {username: "云崖"}  代表指向当前对象
    this.show = function () {  // 可以称之为类方法
    
                            console.log(this.username);  // 云崖
                            console.log(this);  // {username: "云崖", show: ƒ}  代表指向当前对象
                    }
    
            }
    
            let user = new User("云崖");
            user.show();
    
    </script>

    对象字面量

      在对象中的this指向即为当前对象,同样的在对象中的函数(方法)this指向也是当前对象本身。

      这与构造函数如出一辙。

    <script>"use strict"; 
    
            let obj = {
                    username:"云崖",  // 最终的key都会转为String类型,但是Symbol类型不会转换。
    
                    show:function(){ // 这里也可以将show称作为方法,而username即为属性
    
                            console.log(this.username); // 云崖
                            console.log(this);  // {username: "云崖", show: ƒ}
    
                    },
            }
    
            obj.show();
    
    </script>

    方法中的普通函数

      首先聊方法中的普通函数之前,要先知道什么情况下的函数常被称之为方法。

      结合本章前面介绍的内容,以下环境中的函数将被称之为方法:

      在构造函数中的函数可以将其称之为方法

      在对象中的字面量函数也可以将其称之为方法

      那么,在方法中的普通函数即是这样的:

      在构造函数中的函数中的函数可以称其为方法中的普通函数

      在对象中的字面量函数中的函数也可以将其称为方法中的普通函数

      有点绕哈,看代码你就懂了。

      在方法中的普通函数的this指向非严格模式下为window对象,严格模式下为undefined

      值得一提的是,对于大多数开发者而言,这么嵌套的情况很少使用。

    <script>"use strict";
    
            function User(username) { 
    
                    this.username = username; 
    
                    this.show = function () {  // 方法
    
                            console.log(this.username);  // 云崖
                            console.log(this);  // {username: "云崖", show: ƒ}  代表指向当前对象
    function inner() {  // 普通函数
                                    console.log(this);  // 严格模式:undefined,非严格模式:window
                            };
    
                            inner();  // 在方法中定义一个函数并调用
    
                    }
    
            }
    
            let user = new User("云崖");
            user.show();
    
    </script>
    <script>"use strict"; 
    
            let obj = {
                    username:"云崖",  // 最终的key都会转为String类型,但是Symbol类型不会转换。
    
                    show:function(){  // 方法
    
                            console.log(this.username); // 云崖
                            console.log(this);  // {username: "云崖", show: ƒ}
    function inner(){  // 普通函数
                                    console.log(this);  // 严格模式:undefined,非严格模式:window
                            };
    
                            inner();  // 在方法中定义一个函数并调用
    
                    },
            }
    
            obj.show();
    
    </script>

    方法中的普通函数改变this指向

      那么,怎么改变方法中普通函数的this指向呢?

      非常简单。使用一个常量将方法的this赋值并传递给其中的普通即可。

    <script>"use strict";
    
            function User(username) { 
    
                    this.username = username; 
    
                    this.show = function () {  // 方法
    
                            let self = this;  // 这个this指向的当前对象,即User
    function inner(self) {  // 普通函数
                                    console.log(self);  // 在方法内的普通函数中使用self即可
                            };
    
                            inner(self);  // 我们将self传递进去
    
                    }
    
            }
    
            let user = new User("云崖");
            user.show();
    
    </script>
    <script>"use strict";
    
            let obj = {
                    username: "云崖",  // 最终的key都会转为String类型,但是Symbol类型不会转换。
    
                    show: function () {  // 方法
    
                            let self = this;  // 这个this指向的当前对象,即obj
    function inner(self) {  // 普通函数
                                    console.log(self);  //  在方法内的普通函数中使用self即可
                            };
    
                            inner(self);  // 在方法中定义一个函数并调用
    
                    },
            }
    
            obj.show();
    
    </script>

    方法中的箭头函数

      箭头函数这玩意儿没有this指向,你可以理解为它始终会与外层定义自己的函数共同使用一个this,在大多数情况下是会如此,但是少部分情况会除外,比如在事件的回调函数中,这个在下面会有举例。

      在方法中的箭头函数this指向始终会与定义自己的函数共同使用一个this

    <script>"use strict";
    
            function User(username) {
    
                    this.username = username;
    
                    this.show = function () {  // 方法
    
                            console.log(this);  // 指向 User
    
                            let inner = () =>   console.log(this);  // 箭头函数,与定义自己的外层指向同一this,即User
    
                            inner();  // 在方法中定义一个函数并调用
    
                    }
    
            }
    
            let user = new User("云崖");
            user.show();
    
    </script>

    事件普通函数

      事件函数是指某一动作方式后所调用的回调函数,如果是普通函数那么this指向即为事件源本身。

    <script>"use strict";
    
            let div = document.querySelector("div");
    
            div.onclick = function (event) { 
    
                    console.log(event);  // 事件
    
                    console.log(this);  // div标签,即为事件源本身,DOM对象
             }
    
    </script>

    事件箭头函数

      事件的回调函数如果箭头函数,那么this指向即为window,一句话,向上找,看在哪个环境下定义了这个箭头函数。

      所以我们尽量不要去用箭头函数作为事件的回调函数。

    <script>"use strict";
    
            let div = document.querySelector("div");
    
            // 由于是在全局定义的,所以此时的this即为window,如果是在方法中定义的事件箭头函数则this指向
            // 就不是window了
    
            div.onclick = event => { 
    
                    console.log(event);  // 事件
    
                    console.log(this);  // Window 
             }
    
    </script>

    事件箭头函数获取事件源

      如果想在事件箭头函数中获取事件源,那就不使用window了。用event参数中的一个叫target的属性即可找到事件源。

    <script>"use strict";
    
            let div = document.querySelector("div");
    
            div.onclick = event => { 
    
                    console.log(event.target);  // 事件源DOM对象
    
                    console.log(this);  // Window 
             }
    
    </script>

    改变this指向

    call方法

      通过call()方法,让原本函数的this发生改变,如下实例我们可以在user函数中使用this去给对象obj进行添加属性。

      参数1:新的this指向对象

      其他参数:函数中本来的传递值

      特点:立即执行

    <script>"use strict";
    
            function user(name,age) {
                    this.name = name; 
                    this.age = age;
                    console.log(this);
            };
    
            let obj = {};
    
            user.call(obj,"云崖",18);
    
            console.log(obj.name); // 云崖
            console.log(obj.age); // 18
    </script>

    apply方法

      call()方法唯一不同的地方在于参数传递,其他都一样。

      参数1:新的this指向对象

      参数2:函数中本来的传递值,请使用数组进行传递。

      特点:立即执行

    <script>"use strict";
    
            function user(name,age) {
                    this.name = name; 
                    this.age = age;
                    console.log(this);
            };
    
            let obj = {};
    
            user.apply(obj,["云崖",18]);
    
            console.log(obj.name); // 云崖
            console.log(obj.age); // 18
    </script>

    bind方法

      该方法最大的特点是具有复制特性而非立即执行,对于函数的参数传递可以有多种。

      但是我这里只介绍一种。

      参数传递:使用bind()方法时传递一个this指向即可

      特性:会返回一个新的函数。

    <script>"use strict";
    
            function user(name,age) {
                    this.name = name; 
                    this.age = age;
                    console.log(this);
            };
    
            let obj = {};
    
            // 可以这么理解,使用bind()方法的第一步,告诉this指向谁,第二步,复制出新函数,第三步,新函数的this已经改变。其他地方与原函数相同。
            let new_func = user.bind(obj); // obj传递进去,返回一个新函数,该新函数与user函数除了this指向不一样其他均相同。
            new_func("云崖",18)
    
            console.log(obj.name); // 云崖
            console.log(obj.age); // 18
    </script>

    总结

     window对象当前对象上级this指向事件源DOM对象undefined
    全局环境(不在函数中)        
    普通函数 非严格模式:√       严格模式:√
    构造函数        
    对象        
    构造函数中的方法        
    对象中的字面量方法        
    构造函数中的方法中的普通函数 非严格模式:√       严格模式:√
    对象中的字面量方法中的普通函数 非严格模式:√       严格模式:√
    构造函数中的方法中的箭头函数      
    对象中的字面量方法中的箭头函数      
    事件普通函数        
    事件箭头函数 大概率是√,主要看上层的this指向      


     

  • 相关阅读:
    什么是电信BOSS系统?
    得到windows系统图标的解决方案
    FusionChart实现金字塔分布图
    OCP-1Z0-051-V9.02-91题
    FusionChart用XML和JSON两种格式提供数据源
    OCP-1Z0-051-V9.02-156题
    OCP-1Z0-051-V9.02-155题
    OCP-1Z0-051-V9.02-154题
    OCP-1Z0-051-V9.02-153题
    OCP-1Z0-051-V9.02-151题
  • 原文地址:https://www.cnblogs.com/Yunya-Cnblogs/p/13409822.html
Copyright © 2020-2023  润新知