了解函数中 this 在不同场景下的默认值,知道动态指定函数 this 值的方法。
- 事件处理函数中,this表示事件源(事件给谁添加的,this就表示谁)
- 构造函数中或者类中this、对象的方法中,this表示实例对象(静态方法中的this表示类或者构造函数)
- 普通函数中、全局中、回调函数中,this表示window对象
- 箭头函数中没有 this(如果出现this,则按照作用域链去查找)
```javascript
<a name="U8idG"></a> ## 默认值 `this` 是 JavaScript 最具“魅惑”的知识点,不同的应用场合 `this` 的取值可能会有意想不到的结果,在此我们对以往学习过的关于【 `this` 默认的取值】情况进行归纳和总结。 <a name="ll061"></a> ### 普通函数 **普通函数**的调用方式决定了 `this` 的值,即【谁调用 `this` 的值指向谁】,如下代码所示: ```javascript <script> // 普通函数 function sayHi() { console.log(this); } // 函数表达式 let sayHello = function () { console.log(this); } // 函数的调用方式决定了 this 的值 sayHi(); // window window.sayHi(); // 普通对象 let user = { name: '小明', walk: function () { console.log(this); } }; // 动态为 user 添加方法 user.sayHi = sayHi; uesr.sayHello = sayHello; // 函数调用方式,决定了 this 的值 user.sayHi(); user.sayHello(); </script>
注: 普通函数没有明确调用者时
this
值为window
,严格模式下没有调用者时this
的值为undefined
。箭头函数
箭头函数中的
this
与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在this
!箭头函数中访问的this
不过是箭头函数所在作用域的this
变量。<script> console.log(this); // 此处为 window // 箭头函数 let sayHi = function() { console.log(this); // 该箭头函数中的 this 为函数声明环境中 this 一致 } // 普通对象 let user = { name: '小明', // 该箭头函数中的 this 为函数声明环境中 this 一致 walk: () => { console.log(this); }, sleep: function () { let str = 'hello'; console.log(this); let fn = () => { console.log(str); console.log(this); // 该箭头函数中的 this 与 sleep 中的 this 一致 } // 调用箭头函数 fn(); } } // 动态添加方法 user.sayHi = sayHi; // 函数调用 user.sayHi(); user.sleep(); user.walk(); </script>
在开发中【使用箭头函数前需要考虑函数中
this
的值】,事件回调函数使用箭头函数时,this
为全局的window
,因此DOM事件回调函数不推荐使用箭头函数,如下代码所示:<script> // DOM 节点 let btn = document.querySelector('.btn'); // 箭头函数 此时 this 指向了 window btn.addEventListener('click', () => { console.log(this); }) // 普通函数 此时 this 指向了 DOM 对象 btn.addEventListener('click', function () { console.log(this); }) </script>
同样由于箭头函数
this
的原因,基于原型的面向对象也不推荐采用箭头函数,如下代码所示:<script> function Person() { } // 原型对像上添加了箭头函数 Person.prototype.walk = () => { console.log('人都要走路...'); console.log(this); // widow } let p1 = new Person(); p1.walk(); </script>
定义值
call、apply、bind只能由函数调用。
以上归纳了普通函数和箭头函数中关于this
默认值的情形,不仅如此 JavaScript 中还允许指定函数中this
的指向,有 3 个方法可以动态指定普通函数中this
的指向:call
使用
call
方法调用函数,同时指定函数中this
的值,使用方法如下代码所示:// 修改 this 的指向,可以使用 函数的call、apply、bind三方方法 // 语法: // 'adfasdfadsf'.toUpperCase(); // [].push(); // /sss/g.test() // 函数.call() 函数.apply() 函数.bind() // 1. -------------------------- call -------------------------- // function sayHi(x, y) { // console.log(x + y + this.age) // console.log(this) // } // // sayHi(3, 5) // let user = { name: 'user', age: 20 } // let student = { name: 'zhangsan', age: 30 } // // call方法的作用是:1. 调用函数(函数会执行) 2. 修改函数中this的指向 // sayHi.call(user, 3, 5) // sayHi.call(student, 1, 2) let obj = { say: function () { console.log(this) } } let student = { name: 'zhangsan', age: 30 } // obj.say.call(student) obj.say.call(Math)
总结:
call
方法能够在调用函数的同时指定this
的值- 使用
call
方法调用函数时,第1个参数为this
指定的值 call
方法的其余参数会依次自动传入函数做为函数的参数
基于call的使用:
<input type="checkbox"> <input type="checkbox"> <input type="checkbox"> <button>判断</button> <script> // 扩展一个小知识(记一下规律) let btn = document.querySelector('button') let ck = document.querySelectorAll('input') btn.addEventListener('click', function () { // console.log(ck) // 伪数组 // 复选框.checked // console.log(ck[0].checked) // 如果复选框选中,则返回true;否则返回false // console.log(ck[1].checked) // console.log(ck[2].checked) // 判断三个复选框是否都选中(checked属性值都是true)了? // let result = [6, 9, 7, 8].every(item => item > 5) // console.log(result) let result = [].every.call(ck, item => item.checked === true) // 规律是:如果一个伪数组,希望使用数组方法的话,可以使用下面的语法 // [].方法.call(伪数组, 方法的参数) // let result = ck.every(item => { // return item.checked === true // }) console.log(result) }) </script>
apply
使用 apply方法调用函数,同时指定函数中
this
的值,使用方法如下代码所示:function sayHi(x, y) { console.log(x + y + this.age) console.log(this) } // sayHi(3, 5) let user = { name: 'user', age: 20 } let student = { name: 'zhangsan', age: 30 } // 语法: // 函数.apply(对象, [3, 5]) // sayHi.apply(user, [3, 5]) // 一个牵强的案例 // 计算数组的最大值 // 关于展开语法(...) https://www.yuque.com/lao-tang/gaoji/oa7f6u#irzql // console.log(Math.max(...[4, 3, 9, 5, 0])) // let arr = [4, 3, 9, 5, 0] console.log(Math.max.apply(null, [4, 10, 3, 9, 5, 0]))
总结:
apply
方法能够在调用函数的同时指定this
的值- 使用
apply
方法调用函数时,第1个参数为this
指定的值 apply
方法第2个参数为数组,数组的单元值依次自动传入函数做为函数的参数bind
bind
方法并不会调用函数,而是创建一个指定了this
值的新函数,使用方法如下代码所示: ```javascript function sayHi(x, y) { console.log(x + y + this.age) console.log(this) } // sayHi(3, 5)
let user = { name: ‘user’, age: 20 } let student = { name: ‘zhangsan’, age: 30 }
// sayHi.call(user, 3, 5) —— 1. 调用函数; 2. 修改this指向
// bind语法和意义 // sayHi.bind(user, 3, 5) ——- 1. 修改this的指向; 2. 返回一个新函数 // let fn = sayHi.bind(user, 3, 5) // fn()
// sayHi.bind(user, 3, 6)()
// let fn = sayHi.bind(user) // fn(3, 9)
sayHi.bind(user)(4, 10)
注:`bind` 方法创建新的函数,与原函数的唯一的变化是改变了 `this` 的值。<br />**改变this三个方法总结:**<br />面试会问<br />底层代码会用,正常开发基本不会用上 ```javascript call: fun.call(this,arg1, arg2,......) apply: fun.apply(this, [arg1, arg2,......]) bind: fun.bind(this, arg1, arg2,......) 相同点: 都可以用来改变this指向,第一个参数都是this指向的对象 区别: call和apply:都会使函数执行,但是参数不同 bind:不会使函数执行,参数同call