作为前端开发者,
this关键字绝对是让人又爱又恨的存在.今天我们就来彻底搞懂它.一、this到底是什么简单来说, this是JavaScript中的一个特殊关键字, 它指向当前执行代码的上下文对象. 但具体指向谁, 得看它是怎么被调用的, 来看个示例:
console.log(this); //在浏览器中输出window对象
这里有个重要概念: this的指向不是在函数定义时确定的, 而是在函数被调用时确定的. 这点和很多其他语言不同, 也是容易混淆的地方.二、this的四大绑定规则1) 默认绑定当函数独立调用时(不作为对象方法, 不通过call/apply/bind,也不是构造函数),this在非严格模式下指向全局对象(浏览器中是window), 严格模式下是undefined.
2) 隐式绑定(方法调用)当函数作为对象的方法被调用时,function showThis() {console.log(this);}showThis(); //浏览器中输出 window(非严格模式)// 严格模式function strictShowThis() {'use strict';console.log(this);}strictShowThis(); //undefined
this指向调用该方法的对象.
隐式丢失问题: 当方法被赋值给变量后调用, 会丢失原来的const person = {name: '小明',sayHi: function() {console.log(`你好,我是${this.name}`);}};person.sayHi(); //输出 "你好,我是小明" - this指向person对象
this绑定.
3) 显示绑定我们可以强制指定函数调用时的const sayHi = person.sayHi;sayHi(); //输出 "你好,我是undefined"-this指向全局对象或undefined
this值, 这就是显式绑定. 使用call, apply, bind函数, 我们来看个示例:
function introduce(lang, hobby) {console.log(`我是${this.name},我会${lang},喜欢${hobby}`);}const person1 = { name: '张三' };const person2 = { name: '李四' };//call接受参数列表introduce.call(person1, 'JavaScript', '打篮球');//输出 "我是张三,我会JavaScript,喜欢打篮球"//apply接受参数数组introduce.apply(person2, ['Python', '游泳']);//输出 "我是李四,我会Python,喜欢游泳"
bind会创建一个新函数, 永久绑定this值:
4) new绑定使用const person3 = { name: '王五' };const boundIntroduce = introduce.bind(person3, 'Java', '跑步');boundIntroduce(); // 输出 "我是王五,我会Java,喜欢跑步"
new调用函数(构造函数)时, this指向新创建的对象.
三、箭头函数的this特性箭头函数没有自己的function Person(name) {this.name = name;this.sayName = function() {console.log(this.name);};}const jack = new Person('Jack');jack.sayName(); // 输出 "Jack"
this, 它会捕获所在上下文的this值, 作为自己的this值, 且一旦确定就无法改变(即使使用call/apply/bind).
再看一个示例:const obj = {name: '箭头函数示例',regularFunc: function() {console.log(this.name); //this指向obj},arrowFunc: () => {console.log(this.name); //this指向外层作用域(这里是window或undefined)}};obj.regularFunc(); //输出 "箭头函数示例"obj.arrowFunc(); //输出 undefined(严格模式)或空字符串(非严格模式)
四、特殊场景与实战应用1) DOM事件处理函数中的this:在DOM事件处理函数中,function Timer() {this.seconds = 0;// 传统函数写法会有this问题setInterval(function() {this.seconds++; // 这里的this指向window/undefinedconsole.log(this.seconds);}, 1000);// 箭头函数写法正确setInterval(() => {this.seconds++; // 这里的this指向Timer实例console.log(this.seconds);}, 1000);}const timer = new Timer();
this指向触发事件的元素.
2) 定时器回调中的this:document.getElementById('myBtn').addEventListener('click', function() {console.log(this); // 指向按钮元素});
3) 立即执行函数中的this:setTimeout(function() {console.log(this); //浏览器中指向window}, 1000);
4) 如何避免this的隐式丢失?(function() {console.log(this); //非严格模式指向window,严格模式undefined})();
5) 多层对象调用时this的指向://错误写法const obj = {name: '小明',sayHi: function() {setTimeout(function() {console.log(`你好,我是${this.name}`);}, 100);}};obj.sayHi(); //输出 "你好,我是undefined"// 解决方案1:使用箭头函数const obj1 = {name: '小明',sayHi: function() {setTimeout(() => {console.log(`你好,我是${this.name}`);}, 100);}};// 解决方案2:保存this引用const obj2 = {name: '小明',sayHi: function() {const self = this;setTimeout(function() {console.log(`你好,我是${self.name}`);}, 100);}};
6) 类中的this:ES6类中的方法默认使用严格模式,const obj = {name: '外层',inner: {name: '内层',sayName: function() {console.log(this.name);}}};obj.inner.sayName(); // 输出 "内层" - this指向直接调用者inner
this行为更符合预期:
class Person {constructor(name) {this.name = name;}sayName() {console.log(this.name);}}const p = new Person('类示例');p.sayName(); // 输出 "类示例"const say = p.sayName;say(); // 报错,因为严格模式下this是undefined
| Austin Liu 刘恒辉 Project Manager and Software Designer E-Mail:lzhdim@163.com Blog:https://lzhdim.cnblogs.com 欢迎收藏和转载此博客中的博文,但是请注明出处,给笔者一个与大家交流的空间。谢谢大家。 |