JS/JavaScript中 this 关键字对象详解

JS/JavaScript中 this 关键字对象详解 


this是JavaScript中功能最强大的关键字之一。 如果对JavaScript的关键字this理解不够深刻,有时候会掉入意想不到的坑。在这里我花了大量心血,深入浅出总结了几条通用规则来帮助你判断this到底指向什么。虽然没有囊括所有的情况,但日常大部分情况都可以使用这些规则来正确推断。

一、this概述

this是面向对象语言中的一个重要概念,在JAVA,C#等大型语言中,this固定指向运行时的当前对象。但是在JavaScript中,由于JavaScript的动态性(解释执行,当然也有简单的预编译过程),this的指向在运行时才确定。这个特性在给我们带来迷惑的同时,也带来了编程上的自由和灵活,结合apply、call、bind方法,可以使JS变得异常强大。this是Javascript语言的一个关键字,它代表函数运行时自动生成的一个内部对象,一般在函数内部使用

① this的值通常是由所在函数的执行环境决定,也就是说要看函数是如何被调用的;
② 同一个函数每一次调用,this都可能指向不同的对象;

先看下面一段简单的代码:
<script type="text/javascript">

    
    
    
    var name = "javascript";
    var obj = {
            name:'chunlynn',
            foo: function(){
                console.log(this.name); 
            }
    }
    obj.foo(); //output:chunlynn
    //这里obj对象调用它的foo()函数,所以this指的是obj对象 
</script>
问题1:obj是JavaScript中的什么类型?(想一想再往下看)
答案为:obj是js中的对象类型(Object类型)。对象就是一些属性和方法的集合。
问题2:如果执行obj.foo(),会在控制台输出什么呢?
答案为:chunlynn。
通过上面的这个小例子简单的认识下this:this是JS对象中的一个特殊指针,它的指向根据环境不同而会发生改变。

再来看下面一段简单的代码:

      
      
<script type="text/javascript"> function doSomething(){ 
 var name = "chunlynn";  
      
      
    function doSomething(){ 
        var name = "linda";
        console.log(this.name); 
    } 
    console.log(window.doSomething);//打印doSomething函数代码,证明了doSomething是属于window的 
    window.doSomething(); //输出:chunlynn。证明了doSomething是属于window的,this指向全局对象window 
    doSomething();//输出:chunlynn 。因为的owner是window
 this.doSomething();//输出:chunlynn 。this引用全局对象window,这个this一般是省略的. 
 
</script> 

查看chrome浏览器控制台的输出结果:


对于doSomething()这个函数: 
这个函数是全局函数,这种全局函数实际上是属于window的(可以通过window.doSomething来访问),如果直接调用,那么根据this always refers to the “owner” of the function we're executing”,那么函数中的this就是window对象,window对象为全局对象,全局对象的属性name,就是全局变量,结果为:”chunlynn"; 

简而言之,就是function的this永远指向调用它的对象。当我们在页面中定义了函数doSomething()的时候,它的owner是页面(注:用owner代表this所指向的内容),或者是JavaScript中的window对象(或 global对象,即全局对象, 浏览器环境下,全局对象指的就是window对象)。对于一个onclick属性,它为它所属的HTML元素所拥有,this应该指向该HTML元素。

---------------------------------全局对象 start-------------------------------
关于全局对象(window)的概念与说明:
① JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。在浏览器 JavaScript 中,通常 window 是全局对象。
② 全局对象是预定义的对象,作为 JavaScript 的全局函数和全局属性的占位符。通过使用全局对象,可以访问所有其他所有预定义的对象、函数和属性。全局对象不是任何对象的属性,所以它没有名称。
在顶层 JavaScript 代码中,可以用关键字 this 引用全局对象。但通常不必用这种方式引用全局对象,因为全局对象是作用域链的头,这意味着所有非限定性的变量和函数名都会作为该对象的属性来查询。例如,当JavaScript 代码引用 parseInt() 函数时,它引用的是全局对象的 parseInt 属性。全局对象是作用域链的头,还意味着在顶层 JavaScript 代码中声明的所有变量都将成为全局对象的属性。
④全局对象只是一个对象,而不是类。既没有构造函数,也无法实例化一个新的全局对象。
在客户端浏览器 JavaScript 中,全局对象就是 window 对象,表示允许 JavaScript 代码的 Web 浏览窗口。window对象可以在浏览器的JS中作为一个具体存在的对象进行访问。

全局对象通俗化理解:
⑥ 《JavaScript高级程序设计》中谈到,global对象(Global Object)可以说是ECMAScript中最特别的一个对象了,因为不管你从什么角度上看,这个对象都是不存在的。从某种意义上讲,它是一个终极的“兜底儿对象”,换句话说呢,就是不属于任何其他对象的属性和方法,最终都是它的属性和方法。我理解为,这个global对象呢,就是整个JS的“老祖宗”,找不到归属的那些“子子孙孙”都可以到它这里来认祖归宗。所有在全局作用域中定义的属性和函数,都是global对象的属性和方法,比如isNaN()、parseInt()以及parseFloat()等,实际都是它的方法;还有就是常见的一些特殊值,如:NaN、undefined等都是它的属性,以及一些构造函数Object、Array等也都是它的方法。总之,记住一点:global对象就是“老祖宗”,所有找不到归属的就都是它的。

:“global”这个单词本身是不可以直接访问的,本身就是一个概念,就是中文“全局”的英文原义,"global object"翻译就是“全局对象”或“global对象”。比如global.Math.abs(1) 就是错误的。对全局对象的属性访问往往省略前缀,如Math.abs(1)就行了。而window对象是浏览器的JavaScript中具体存在的一个全局对象,是可以直接访问的。

来个栗子:

      
      
<script type="text/javascript">
    console.log(this); 
</script>
打开chrome浏览器的控制台,看到输出什么了吗?

window对象! 因为在全局作用域下,this指向全局对象。在浏览器中全局对象就是window对象。
来个第2个栗子:

      
      
<script type="text/javascript">
    console.log(window); 
</script>
打开chrome浏览器的控制台:

输出window对象,里面是各种属性的,如speechSynthesis等等....。说明 "window"这个单词在浏览器JS中代表的就是一个真实存在的全局的对象,可以直接访问window这对象的。
那么“global”这个单词呢?能直接访问吗?
再来第3个栗子:

      
      
<script type="text/javascript">
    console.log(global);
</script>
运行结果:

显示 "Uncaught ReferenceError:global is not defined"。即global未定义,说明"global"这个单词不是一个默认的全局对象,不能在JS中直接访问的。正如前面所说,“global”只是中文”全局“的英语原义,不是一个对象,而是一个概念。
---------------------------------全局对象 end------------------------------

为了让你更加清楚理解为什么this会指向window对象,我们来看另外一个例子:

       
       
<script type="text/javascript">
    var name = "chunlynn";
    this.sex = "man"; // 这里的this就是隐式的window对象,相当于 sex = "man";
age = 27;
</script>
我们可以通过在控制台输入name,window.name ,window.name === name等来测试:

===为严格运算符,即数据类型和值都要相等结果才为"true"。
以上说明,所有在全局作用域定义的变量都绑定到window对象。

两点说明:
① 在全局作用域中(所有函数外)出现的this,指全局对象。在浏览器中就是window对象。
② 在函数内部出现的this,指什么要看这个this所在的函数的被调用方式。    
不论这个this出现在什么样的函数中,层次有多深,结构多复杂,只要看直接包含它的函数即可。

二、this指向用法分类详解

this指向小结预览
① 谁调用或者哪个对象调用this所在的函数,this就指向谁。
② 如果有嵌套调用,则其值会被绑定到调用this所在函数的最近的父对象。不论这个this出现在什么样的函数中,层次有多深,结构多复杂,只要看直接包含它的函数即可。
简而言之,就是function的this永远指向直接调用它的对象。

1、对象构造函数中的this (new关键字构造对象)
当使用new关键字构建一个新的对象,this会绑定到这个新对象。

     
     
<script type="text/javascript">
    var name = "chunlynn"
    //构造函数 
    function Foo (){
        this.name = 'linda';
        this.foo = function(){
            console.log(this.name); 
        }
    }
    // 实例化对象
    var obj = new Foo();
    obj.foo(); //output:linda
    // this指向:谁调用或者哪个对象调用this所在的函数,this就指向谁。
    //这里是Foo的实例化对象obj调用的foo()函数,所以this指向是Foo对象。
</script>
注意构造函数和普通函数的区别,构造的属性用this.name="chunlynn",普通函数用var name = 'linda'。构造函数函数名称大写。

2、 普通函数中的this
 作为普通函数直接调用时,this指向window对象. 

     
     
<script type="text/javascript">
    var name = "chunlynn"
    //普通函数  
    function foo(){
        var name = 'linda';
        console.log(this.name); 
    }
    foo(); //output:chunlynn
    // this指向:谁调用或者哪个对象调用this所在的函数,this就指向谁。
    //这里是window调用的foo()函数,所以this指的是实例window对象,this.name指的就是chunlynn
</script>

下面再来看一个有些难度的:

      
      
<script type="text/javascript">
    var name = "chunlynn"
    //构造函数 
    function Foo (){
        this.name = 'linda';
        this.foo = function(){
            var name = 'doudou';
            return function(){
                console.log(this.name); 
            };
        }
    }
    // 实例化对象
    var obj = new Foo();
    obj.foo()(); //output:chunlynn
    // this指向:谁调用或者哪个对象调用this所在的函数,this就指向谁。
    // obj.foo()();拆开分步执行如下:
    // var re = obj.foo();
    // re(); ---> window.re();
    // 这里是window调用的foo()函数,所以this指向window对象, this.name指的就是chunlynn 
</script>

上面例子中的obj.foo()(); 也可以拆开了写成这样:
var re = obj.foo();
re(); ====> window.re();
写成这样是不是更利于理解了呢。

3、onclick(this)中的this指向
对于一个onclick属性,它为它所属的HTML元素所拥有,this指向该HTML元素。
例子:

      
      
<a href="javascript:void(0);" title="这个是一个标题!!" onclick="test(this);">A标签测试</a> 
<script type="text/javascript">
    function test(obj){ 
        var value = obj.title;
        console.log(value);  //这是一个标题!!
    } 
</script> 
onclick为它所属的<a>元素所有,this指向<a>元素。



4、定时器setTimeout中的this指向
定时器中的this指向window对象。因为定时器就是全局函数,由window调用。

先来看一个定时器的例子:

        
        
<script type="text/javascript">
    var name = "chunlynn"
    //普通函数  
    function foo(){
        var name = 'linda';
        console.log(this.name); 
    }
    setTimeout(foo,5000); //output:chunlynn
    // this指向:谁调用或者哪个对象调用this所在的函数,this就指向谁。
    //定时器为全局函数,由window调用,因此定时器中的this指向window对象。
</script> 

面试题:下题输出结果是多少,说明为什么。

        
        
<script type="text/javascript">
    var name = "chunlynn";
    var obj = {
        name:'linda',
        fn: function(){
            console.log(this.name); 
        },
        foo:function(){
            setTimeout(this.fn,5000); 
        }
    }
    obj.foo(); //output:chunlynn
    // this指向:谁调用或者哪个对象调用this所在的函数,this就指向谁。
    // 嵌套带入进去最终执行的就是
// window.setTimeout(function(){console.log(this.name)},5000); this指向的就是window对象
</script>
换个思路,上面最终执行的代码为:

            
            
<script type="text/javascript">
    window.setTimeout(function(){
        console.log(this.name); //this指向的就是window对象,直接包含this.name的为setTimeout函数
    }, 5000);
</script>
这样答案一目了然了吧,this绑定到了window对象。

5、使用apply、call 或 bind 方法时this的指向
使用apply 、call 或 bind方法时,this指向所传递的对象。 

      
      
<script type="text/javascript">
    
var name = "chunlynn";
    var obj = {
        name:'linda',
        foo:function(){
            console.log(this.name);
        }
    }
    var f = {};
    f.name = "doudou";
    f.fun = obj.foo; //将obj对象的foo函数赋值给f对象的fun属性。本质为复制。
    f.fun(); //output:doudou
    f.fun.apply(obj); //output:linda
    f.fun.apply(window); //output:doudou
f.fun.apply(); //output:chunlynn
    // this指向:谁调用或者哪个对象调用this所在的函数,this就指向谁。
</script> 
apply()的参数为空时,默认调用全局对象window。
查看chrome浏览器控制台:


6、ES6 箭头函数和 this
箭头函数的特征就是:定义在哪,this 就指向那。

     
     
//es6中的双箭头操作符,类似于java中的lamada表达式 
<script type="text/javascript">
    var author = "chunlynn";
    var book = {
        name: 'linda',
        init: function(){
            setTimeout(ev=>{
                console.log(this.name);
            },3000); 
        }
    }
    book.init(); //output:linda
    // this指向:谁调用或者哪个对象调用this所在的函数,this就指向谁。
</script>

箭头函数的特征就是:定义在哪,this 就指向那。即箭头函数定义在一个对象里,那箭头函数里的 this 就指向该对象。

三、总结
谁调用或者哪个对象调用 this所在的函数,this就指向谁 如果有嵌套调用,则其值会被绑定到调用this所在函数的最近的父对象 不论这个this出现在什么样的函数中,层次有多深,结构多复杂,只要看直接包含它的函数即可。
this” always refers to the “owner” of the function we're executing
③ this通常指向的是我们正在执行的函数本身,或者是,指向该函数所属的对象。
④ this是Javascript语言的一个关键字,它代表函数运行时自动生成的一个内部对象,只能在函数内部使用。

相信能看到最后的各位,对于JavaScript中的有关this的坑都能跳出来的~~~。

相关博文:


  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 原型 在 JavaScript ,每个对象都有一个原型对象,原型对象是一个普通的对象,其包含了一些属性和方法。当我们访问对象的属性或方法时,如果该对象本身没有该属性或方法,JavaScript 引擎会自动去该对象的原型对象查找,如果原型对象仍然没有,则会在原型对象的原型对象查找,一直查找到 Object.prototype,最后如果还没有找到,则会返回 undefined 。 2. 继承 在 JavaScript ,我们可以通过继承来实现代码复用和对象之间的关系。JavaScript 的继承有两种方式:通过原型链继承和通过构造函数继承。 3. 原型继承 原型继承是指在 JavaScript 通过设置对象的原型来实现继承的方式。具体的实现方式有很多种,其一种比较常见的方式是使用 Object.create() 方法来创建一个新对象,并将原型指定为另一个对象。示例如下: ``` let animal = { eat() { console.log("eating"); } }; let rabbit = Object.create(animal); rabbit.eat(); // 输出 "eating" ``` 在上面的示例,我们首先定义了一个 animal 对象,它包含一个 eat() 方法。然后我们通过 Object.create() 方法创建了一个新对象 rabbit,并将其原型设置为 animal,这样就实现了 rabbit 对象从 animal 对象继承 eat() 方法的功能。我们可以通过调用 rabbit.eat() 来验证该方法确实存在于 rabbit 对象。 4. 关键字JavaScript ,有一些关键字和函数可以用来实现原型继承。其最常用的关键字是 extends 和 super。 extends 关键字可以用来继承一个类,示例如下: ``` class Animal { eat() { console.log("eating"); } } class Rabbit extends Animal { run() { console.log("running"); } } let rabbit = new Rabbit(); rabbit.eat(); // 输出 "eating" rabbit.run(); // 输出 "running" ``` 在这个示例,我们首先定义了一个 Animal 类,并为其定义了一个 eat() 方法。然后我们定义了一个 Rabbit 类,该类使用 extends 关键字继承了 Animal 类,并为其定义了一个 run() 方法。我们通过实例化 Rabbit 类来创建一个 rabbit 对象,并通过调用 rabbit.eat() 和 rabbit.run() 来验证其继承效果。 super 函数用于在子类调用父类的同名方法。示例如下: ``` class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + " makes a noise."); } } class Dog extends Animal { constructor(name) { super(name); } speak() { console.log(this.name + " barks."); } } let dog = new Dog("Rex"); dog.speak(); // 输出 "Rex barks." ``` 在这个示例,我们首先定义了一个 Animal 类,并为其定义了一个构造函数和一个 speak() 方法。然后我们定义了一个 Dog 类,该类继承了 Animal 类,并为其定义了一个构造函数和一个 speak() 方法。 在 Dog 类的构造函数,我们调用了父类的构造函数,并传入了 name 参数。在 Dog 类的 speak() 方法,我们使用 super 函数调用了父类的同名方法,并将其重写为输出“barks.”。我们通过实例化 Dog 类来创建一个 dog 对象,并通过调用 dog.speak() 来验证其继承效果。 总结 在 JavaScript ,原型继承是一种非常重要的对象复用和代码组织方式。我们可以使用原型对象来实现原型继承,也可以使用关键字和函数来简化继承的过程。在实际开发,我们需要根据具体的业务场景和需求来选择最合适的继承方式和实现方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值