文章目录一、this 是什么二、四条铁律this 绑定规则1. 默认绑定 —— 独立函数调用2. 隐式绑定 —— 作为对象方法调用3. 显式绑定 —— call、apply、bind4. new 绑定 —— 构造函数调用三、箭头函数的 this四、this 绑定优先级五、常见场景实战1. 事件处理函数中的 this2. 类Class中的 this3. 回调函数中的 this六、经典面试题分析七、最佳实践总结一、this 是什么简单说this 是函数执行时的上下文对象。在绝大多数情况下函数的调用方式决定了 this 的值。它不能在函数定义时确定只有在函数被调用时才会绑定。理解这一点至关重要同一个函数不同方式调用this 截然不同。二、四条铁律this 绑定规则1. 默认绑定 —— 独立函数调用当函数直接调用非严格模式时this 指向全局对象浏览器中是 windowNode.js 中是 global。functionshowThis(){console.log(this);}showThis();// window(或 global)如果是严格模式‘use strict’this 为 undefined。functionstrictShow(){use strict;console.log(this);}strictShow();// undefined2. 隐式绑定 —— 作为对象方法调用函数作为对象的属性被调用时this 指向调用该方法的对象即点号前面的对象。const obj{name:Alice,sayName(){console.log(this.name);}};obj.sayName();// Alicethis 指向 obj常见陷阱隐式丢失将方法赋值给变量或作为回调传递时会丢失原对象回退为默认绑定。const fnobj.sayName;fn();// undefined(非严格模式下 window.name 通常为或 undefined)在 setTimeout 中也一样setTimeout(obj.sayName,100);// this 指向 window3. 显式绑定 —— call、apply、bind这三大方法可以强制指定 this。call(thisArg, arg1, arg2, …)立即执行函数参数逐个传递。apply(thisArg, [args])立即执行函数参数以数组形式传递。bind(thisArg, …)返回一个绑定了 this 的新函数不立即执行。functiongreet(greeting){console.log(${greeting}, ${this.name});}const user{name:Bob};greet.call(user,Hi);// Hi, Bob greet.apply(user,[Hello]);// Hello, Bob const boundGreetgreet.bind(user,Hey);boundGreet();// Hey, Bob注意一旦使用 bind 绑定后续再使用 call/apply 也无法改变 this箭头函数同样无法改变见下文。4. new 绑定 —— 构造函数调用使用 new 调用函数时会创建一个全新的对象并将 this 绑定到这个新对象上。functionPerson(name){this.namename;}const pnew Person(Carol);console.log(p.name);// Carolnew 做了四件事1、创建一个空对象。2、该对象的proto指向构造函数的 prototype。3、将 this 绑定到该对象上执行构造函数。4、如果构造函数没有返回对象则返回 this。三、箭头函数的 this箭头函数没有自己的 this。它会捕获定义时外层作用域的 this 作为自己的 this且之后无法被改变call、apply、bind 均无效。const obj{name:Dave,regularFunc(){setTimeout(function(){console.log(regular:, this.name);// undefined(或 window.name)},100);},arrowFunc(){setTimeout((){console.log(arrow:, this.name);// Dave捕获到 arrowFunc 的 this 即 obj},100);}};obj.regularFunc();obj.arrowFunc();这解决了回调函数中 this 丢失的经典难题。但也要注意箭头函数不能用作构造函数new 会报错。四、this 绑定优先级当多个规则同时出现时优先级为new 绑定 显式绑定call/apply/bind 隐式绑定 默认绑定验证一下functionfoo(){console.log(this.a);}const obj1{a:1, foo};const obj2{a:2};obj1.foo.call(obj2);//2(显式 vs 隐式 → 显式赢)const boundfoo.bind(obj1);new bound();// undefinednew 会覆盖 bindthis 指向新对象五、常见场景实战1. 事件处理函数中的 thisDOM 事件中this 默认指向绑定事件的元素除 IE attachEvent 外。button.addEventListener(click,function(){console.log(this);// 指向 button 元素});若在回调中使用箭头函数button.addEventListener(click,(){console.log(this);// 捕获外层 this可能是 window});2. 类Class中的 thisES6 类的方法默认启用严格模式且 this 容易在赋值传递时丢失。class Counter{constructor(){this.count0;}increment(){this.count;}}const cnew Counter();const incc.increment;inc();// TypeError: Cannotreadpropertycountof undefined解决方案在构造函数中使用 bindthis.increment this.increment.bind(this);使用类字段箭头函数语法increment () { this.count; }3. 回调函数中的 this在 map、filter、forEach 等数组方法中可传入 thisArg 作为第二个参数或直接使用箭头函数。const obj{multiplier:2};[1,2,3].map(function(x){returnx * this.multiplier;}, obj);//[2,4,6]// 或者[1,2,3].map(xx * obj.multiplier);六、经典面试题分析var length10;functionfn(){console.log(this.length);}var obj{length:5, method: function(fn){fn();// ① arguments[0]();// ②}};obj.method(fn,1);答案① 10或 undefined严格模式② 2。解析①fn() 独立调用this 指向 windowwindow.length 10或严格模式 undefined。②arguments0 相当于 arguments.0()arguments 对象调用了 fn隐式绑定 this 为 arguments而 arguments.length 是实参个数2 个fn 和 1所以输出 2。这道题极好地体现了隐式绑定与独立调用的差异。七、最佳实践总结1、优先使用箭头函数 来处理回调避免 this 丢失问题但需清楚它没有自己的 this。2、在类中若方法需作为回调传递在构造函数内 bind 或使用箭头函数类字段。3、普通函数用 call/apply/bind 显式绑定尤其在工具函数中。4、避免混用 var 和 this 在对象方法内建议统一用 this 访问实例属性。5、开启严格模式 是个好习惯能让默认绑定的 undefined 尽早暴露错误。6、理解优先级遇到复杂 this 问题时按 new → 显式 → 隐式 → 默认 的顺序推理。
JavaScript 函数 this 全解
文章目录一、this 是什么二、四条铁律this 绑定规则1. 默认绑定 —— 独立函数调用2. 隐式绑定 —— 作为对象方法调用3. 显式绑定 —— call、apply、bind4. new 绑定 —— 构造函数调用三、箭头函数的 this四、this 绑定优先级五、常见场景实战1. 事件处理函数中的 this2. 类Class中的 this3. 回调函数中的 this六、经典面试题分析七、最佳实践总结一、this 是什么简单说this 是函数执行时的上下文对象。在绝大多数情况下函数的调用方式决定了 this 的值。它不能在函数定义时确定只有在函数被调用时才会绑定。理解这一点至关重要同一个函数不同方式调用this 截然不同。二、四条铁律this 绑定规则1. 默认绑定 —— 独立函数调用当函数直接调用非严格模式时this 指向全局对象浏览器中是 windowNode.js 中是 global。functionshowThis(){console.log(this);}showThis();// window(或 global)如果是严格模式‘use strict’this 为 undefined。functionstrictShow(){use strict;console.log(this);}strictShow();// undefined2. 隐式绑定 —— 作为对象方法调用函数作为对象的属性被调用时this 指向调用该方法的对象即点号前面的对象。const obj{name:Alice,sayName(){console.log(this.name);}};obj.sayName();// Alicethis 指向 obj常见陷阱隐式丢失将方法赋值给变量或作为回调传递时会丢失原对象回退为默认绑定。const fnobj.sayName;fn();// undefined(非严格模式下 window.name 通常为或 undefined)在 setTimeout 中也一样setTimeout(obj.sayName,100);// this 指向 window3. 显式绑定 —— call、apply、bind这三大方法可以强制指定 this。call(thisArg, arg1, arg2, …)立即执行函数参数逐个传递。apply(thisArg, [args])立即执行函数参数以数组形式传递。bind(thisArg, …)返回一个绑定了 this 的新函数不立即执行。functiongreet(greeting){console.log(${greeting}, ${this.name});}const user{name:Bob};greet.call(user,Hi);// Hi, Bob greet.apply(user,[Hello]);// Hello, Bob const boundGreetgreet.bind(user,Hey);boundGreet();// Hey, Bob注意一旦使用 bind 绑定后续再使用 call/apply 也无法改变 this箭头函数同样无法改变见下文。4. new 绑定 —— 构造函数调用使用 new 调用函数时会创建一个全新的对象并将 this 绑定到这个新对象上。functionPerson(name){this.namename;}const pnew Person(Carol);console.log(p.name);// Carolnew 做了四件事1、创建一个空对象。2、该对象的proto指向构造函数的 prototype。3、将 this 绑定到该对象上执行构造函数。4、如果构造函数没有返回对象则返回 this。三、箭头函数的 this箭头函数没有自己的 this。它会捕获定义时外层作用域的 this 作为自己的 this且之后无法被改变call、apply、bind 均无效。const obj{name:Dave,regularFunc(){setTimeout(function(){console.log(regular:, this.name);// undefined(或 window.name)},100);},arrowFunc(){setTimeout((){console.log(arrow:, this.name);// Dave捕获到 arrowFunc 的 this 即 obj},100);}};obj.regularFunc();obj.arrowFunc();这解决了回调函数中 this 丢失的经典难题。但也要注意箭头函数不能用作构造函数new 会报错。四、this 绑定优先级当多个规则同时出现时优先级为new 绑定 显式绑定call/apply/bind 隐式绑定 默认绑定验证一下functionfoo(){console.log(this.a);}const obj1{a:1, foo};const obj2{a:2};obj1.foo.call(obj2);//2(显式 vs 隐式 → 显式赢)const boundfoo.bind(obj1);new bound();// undefinednew 会覆盖 bindthis 指向新对象五、常见场景实战1. 事件处理函数中的 thisDOM 事件中this 默认指向绑定事件的元素除 IE attachEvent 外。button.addEventListener(click,function(){console.log(this);// 指向 button 元素});若在回调中使用箭头函数button.addEventListener(click,(){console.log(this);// 捕获外层 this可能是 window});2. 类Class中的 thisES6 类的方法默认启用严格模式且 this 容易在赋值传递时丢失。class Counter{constructor(){this.count0;}increment(){this.count;}}const cnew Counter();const incc.increment;inc();// TypeError: Cannotreadpropertycountof undefined解决方案在构造函数中使用 bindthis.increment this.increment.bind(this);使用类字段箭头函数语法increment () { this.count; }3. 回调函数中的 this在 map、filter、forEach 等数组方法中可传入 thisArg 作为第二个参数或直接使用箭头函数。const obj{multiplier:2};[1,2,3].map(function(x){returnx * this.multiplier;}, obj);//[2,4,6]// 或者[1,2,3].map(xx * obj.multiplier);六、经典面试题分析var length10;functionfn(){console.log(this.length);}var obj{length:5, method: function(fn){fn();// ① arguments[0]();// ②}};obj.method(fn,1);答案① 10或 undefined严格模式② 2。解析①fn() 独立调用this 指向 windowwindow.length 10或严格模式 undefined。②arguments0 相当于 arguments.0()arguments 对象调用了 fn隐式绑定 this 为 arguments而 arguments.length 是实参个数2 个fn 和 1所以输出 2。这道题极好地体现了隐式绑定与独立调用的差异。七、最佳实践总结1、优先使用箭头函数 来处理回调避免 this 丢失问题但需清楚它没有自己的 this。2、在类中若方法需作为回调传递在构造函数内 bind 或使用箭头函数类字段。3、普通函数用 call/apply/bind 显式绑定尤其在工具函数中。4、避免混用 var 和 this 在对象方法内建议统一用 this 访问实例属性。5、开启严格模式 是个好习惯能让默认绑定的 undefined 尽早暴露错误。6、理解优先级遇到复杂 this 问题时按 new → 显式 → 隐式 → 默认 的顺序推理。