JavaScript之this解析

简介: this的关键字是JavaScript中最复杂的机制之一,它是一个很特别的关键字,被自动定义在所有函数作用域中。但是即使是非常有经验的JavaScript开发者也很难说清它到底指向的是什么。 ——来源《你不知道的JavaScript上卷》

要说 JavaScript 这门语言最容易让人困惑的知识点,this 关键词肯定算一个。学习 this 会拓展你的知识,加深对代码的理解。即使你并不打算在你的代码库中使用它,了解清楚 this 的指向也能让你在接手别人代码、理解代码逻辑的时候事半功倍。

一、在普通函数中的指向

(1)如果一个函数中有this,但是它没有被上一级对象所调用,那么this指向就是window(非严格模式下)/ undefined(严格模式下)。

function a(){     
  var user = "李明"     
  console.log(this); // 非严格模式为window,严格模式为undefined         
  console.log(this.user); //非严格模式为undefined,严格模式报错 
} 
a(); 

(2)如果一个函数中有this,这个函数有被上一级调用,那么this指向的就是上一级的对象。

var o = {     
  user:"李明",     
  fn:function(){         
    console.log(this.user);  //李明     
  } 
};
o.fn(); 

(3)如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它的上一级的对象。

var o = {     
  a:10,     
  b:{         
    a:12,         
    fn:function(){             
      console.log(this.a); // 12         
    }     
  } 
}; 
o.b.fn(); 

总结:this永远指向最后调用它的对象,也就是看他执行时是谁调用的。
注意:将含this的的函数,赋值给变量后,再单独执行该对象,那么this指向就是window(非严格模式下)/ undefined(严格模式下)。

var o = {     
  a:10,     
  b:{         
    a:12,         
    fn:function(){             
      console.log(this); // 非严格模式为window,严格模式为undefined                
      console.log(this.a); //非严格模式为undefined,严格模式报错         
    }     
  } 
}; 
var j = o.b.fn; //将fn赋值给变量j,并没有执行 
j();  //执行,但它没有被上一级调用

二、在构造函数中的指向

(1)构造函数中无return
this指向函数的实例。
因为:new关键字会创建一个空对象,然后会自动调用apply(),将this指向这个空对象。创建实例相当于复制了一份构造函数到实例对象里。

function Fn(){     
  this.user = "李明"; 
}; 
var a = new Fn();  // Fn {user: "李明"} 
console.log(a.user); // 李明 

(2)构造函数中有return
如果返回的是一个引用类型的值,那么使用new创建实例后,this指向的是那个返回的值; 如果返回值不是一个引用类型的值,那么this还是指向函数的实例。

/* * 示例1 */ 
function Fn(){       
  this.user = '李明';       
  return [1,2];  // 或者{},或者function(){} 
}; 
var a = new Fn;   
console.log(a);  // [1,2] 
console.log(a.user); // undefined 
/* * 示例2 */ 
function Fn()   {       
  this.user = '李明';       
  return {user:"lily"};   
}; 
var a = new Fn;  
console.log(a); // {user: "lily"} 
console.log(a.user); //lily 
/* * 示例3 */ 
function Fn()   {       
  this.user = '李明';       
  return 1; // 或者String,或者Boolean,或者Symbol,或者undefined,或者null 
};
var a = new Fn;   
console.log(a); // fn {user: "李明"} 
console.log(a.user); // 李明 

扩展:当使用new运算符时,如果没有参数要传,括号是可以省略来简化写法的。但有一些工具可能会认为这是不规范的写法,进而报警告。
但要注意:new运算符与属性调用一起使用时,有无括号是有区别的。(因为.的优先级高于new)

function Fn()   {       
  this.user = '李明';   
}; 
var a = new Fn.user;   // Fn.user is not a constructor 
var b = new Fn().user;  // '李明' 

三、在事件函数中的指向

this指向触发这个事件的标签。

<button class="btn1">button1</button>

<body>
    <button class="btn1">button1</button>
    <button class="btn2">button2</button>
    <button class="btn3">button3</button>
    <script type="text/javascript">
            
            
        for(var i = 1; i < 4; i++) {
            var btn = document.getElementsByClassName('btn'+i)[0];
            btn.onclick = function () {
                    console.log(this); //分别是btn1、btn2和btn3对应的标签
            }
        } 
    </script>
</body>

注意:

 <button onclick="handleClick()"></button>
 <script>
    function handleClick(){
        console.log(this);  //Window
    };
</script>

四、在定时器中的指向

this指向window。

setInterval( function () {
    console.log(this);  //window
},1000);

setTimeout(function() {
    console.log(this);  //window
}, 1000)


var a = 23;
function Demo() {
      this.a = 12;
      var self = this;
      setInterval(this.show, 1000);
}
Demo.prototype.show = function() {
    alert(this.a);
};
var demo = new Demo();
demo.show();
// 12
// 23.........23

五、在箭头函数中的指向

箭头函数不绑定this,它的this来自于作用域链的上一层。

function foo() {
  setTimeout(() => {
    console.log(id); // {id: 42}
    console.log('id:', this.id);  // id: 42
    //setTimeout里的函数使用了箭头函数,所以它会和外层的this保持一致,也就是{ id: 42 }
    //如果setTimeout里的是普通函数,执行时this应该指向全局对象window,这时应该输出21。
  }, 100);
}
var id = 21;
foo.call({ id: 42 });

使用call/apply/bind无法修改箭头函数的this指向,它是固定的。

function foo() {
    //返回箭头函数
    return(a) => {
        //this 继承自foo()
        console.log(this.a);
    };
}
var obj1 = {
    a:2
};
var obj2 ={
    a:3
};
var bar = foo.call(obj1);
bar.call(obj2); //是2, 不是3!!!
//箭头函数this对象的指向是固定的,所以后面的call修改不了绑定。

六、可以使用call/apply/bind可以修改this指向(除箭头函数外)

注意:对箭头函数使用call/apply/bind时,只能传递参数,不能绑定this。(他们的第一个参数会被忽略)

const obj = {
    a: () => {
        console.log(this)
    }
}
obj.a.call('123')  //打出来的结果依然是window对象
//箭头函数本身与a平级以key:value的形式,也就是箭头函数本身所在的对象为obj,而obj的this就是window,因此这里的this实际上是window

七、全局作用域时,严格模式与非严格模式下,this的指向

非严格模式下,默认的this是window;
严格模式("use strict;")下,默认的this是undefined。
例外:
1、定时器的回调函数为普通函数时,无论严格模式还是非严格模式,函数内this都指向window。
2、箭头函数中,箭头函数的作用域的上一层的this,如果是全局作用域,无论严格模式还是非严格模式,函数内this都指向window(因为ES6中,全局作用域下,无论是否为严格模式,this都指向window)

相关文章
|
JavaScript 前端开发 IDE
TypeScript vs. JavaScript:技术对比与核心差异解析
TypeScript 作为 JavaScript 的超集,通过静态类型系统、编译时错误检测和强大的工具链支持,显著提升代码质量与可维护性,尤其适用于中大型项目和团队协作。相较之下,JavaScript 更灵活,适合快速原型开发。本文从类型系统、错误检测、工具支持等多维度对比两者差异,并提供技术选型建议,助力开发者合理选择。
1913 1
|
JavaScript 前端开发 开发者
Nest.js控制器深度解析:路由与请求处理的高级特性
以上就是对 NestJS 控制层高级特性深度解析:从基本概念到异步支持再到更复杂场景下拦截其与管道等功能性组件运用都有所涉及,希望能够帮助开发者更好地理解和运用 NestJS 进行高效开发工作。
629 15
|
存储 JavaScript 前端开发
JavaScript 语法全面解析
JavaScript 语法体系丰富且不断更新,从基础的变量声明、数据类型,到复杂的函数、对象、异步语法,每个知识点都需要开发者深入理解并灵活运用。本文梳理的 JS 语法核心内容,可为开发者提供系统的学习框架,后续还需通过大量实践(如编写交互组件、实现业务逻辑)巩固知识,逐步提升 JS 编程能力,应对前端开发中的各类挑战。
|
机器学习/深度学习 JavaScript 前端开发
JS进阶教程:递归函数原理与篇例解析
通过对这些代码示例的学习,我们已经了解了递归的原理以及递归在JS中的应用方法。递归虽然有着理论升华,但弄清它的核心思想并不难。举个随手可见的例子,火影鸣人做的影分身,你看到的都是同一个鸣人,但他们的行为却能在全局产生影响,这不就是递归吗?雾里看花,透过其间你或许已经深入了递归的魅力之中。
563 19
|
JSON 前端开发 Serverless
Mock.js 语法结构全解析
Mock.js 的语法规范介绍,从数据模板定义规范和数据占位符定义规范俩部分介绍, 让你更好的使用 Mock.js 来模拟数据并提高开发效率。
|
数据采集 前端开发 JavaScript
金融数据分析:解析JavaScript渲染的隐藏表格
本文详解了如何使用Python与Selenium结合代理IP技术,从金融网站(如东方财富网)抓取由JavaScript渲染的隐藏表格数据。内容涵盖环境搭建、代理配置、模拟用户行为、数据解析与分析等关键步骤。通过设置Cookie和User-Agent,突破反爬机制;借助Selenium等待页面渲染,精准定位动态数据。同时,提供了常见错误解决方案及延伸练习,帮助读者掌握金融数据采集的核心技能,为投资决策提供支持。注意规避动态加载、代理验证及元素定位等潜在陷阱,确保数据抓取高效稳定。
756 17
|
存储 前端开发 JavaScript
JavaScript垃圾回收机制深度解析
【10月更文挑战第21】JavaScript垃圾回收机制深度解析
456 59
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
存储 JavaScript 前端开发
全网最全情景,深入浅出解析JavaScript数组去重:数值与引用类型的全面攻略
如果是基础类型数组,优先选择 Set。 对于引用类型数组,根据需求选择 Map 或 JSON.stringify()。 其余情况根据实际需求进行混合调用,就能更好的实现数组去重。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
710 17

推荐镜像

更多
  • DNS