this是一个keyword, 它的值老是变换,依赖于调用它场景javascript
有6种状况,this会指向特定的值java
(1) global context (全局)数组
(2) object construction (对象构造函数)浏览器
(3) object method (对象方法)app
(4) simple function (简单函数)函数
(5) arrow function (箭头函数)post
(6) event listener (事件监听)this
当在任何函数以外调用this时,即global context, this指向浏览器的默认全局对象Windowspa
console.log(this) // Window
当使用new建立新实例时,this指向建立的实例instancecode
function Human (age) { this.age = age } let greg = new Human(23) let thomas = new Human(25) console.log(greg) // this.age = 23 console.log(thomas) // this.age = 25
new一个函数对象,返回被调用的函数名和建立的对象
function ConstructorExample() { console.log(this); this.value = 10; console.log(this); } new ConstructorExample(); // -> ConstructorExample {} // -> ConstructorExample { value: 10 }
Methods here are defined with ES6 object literal shorthand, 好比下面
let o = { aMethod () {} }
在method里面的this,都指向该对象自己
let o ={ sayThis () { console.log(this) // o } } o.sayThis() // o
最多见的函数形式,相似下面的
function hello () { // say hello! }
this指向Window, 即便 simple function 在object method中,也是指向Window
function simpleFunction () { console.log(this) } const o = { sayThis () { simpleFunction() } } simpleFunction() // Window o.sayThis() // Window
为何object method中的 simpleFunction() this没有指向o, 这也是让人迷惑的地方啊,或许下面的代码能解释一下,只能说object method里面嵌套的函数的this都从新指向了Window
const o = { doSomethingLater () { setTimeout(function() { this.speakLeet() // Error, this-->Window }, 1000) }, speakLeet() { console.log(`1337 15 4W350M3`) } }
要解决这个问题,须要在嵌套函数外将this赋值给self
const o = { doSomethingLater () { const self = this setTimeout(function () { self.speakLeet() }, 1000) }, speakLeet () { console.log(`1337 15 4W350M3`) } }
在箭头函数中,this老是指向箭头函数所在做用域的对象
好比箭头函数在object method中,那么箭头函数中的this就指向object
const o = { doSomethingLater () { setTimeout(() => this.speakLeet(), 1000) }, speakLeet () { console.log(`1337 15 4W350M3`) } }
在事件监听函数中,this指向触发事件的元素
let button = document.querySelector('button') button.addEventListener('click', function() { console.log(this) // button })
若是要在监听函数中调用其余函数,须要先将this赋值给其余变量,如self
function LeetSpeaker (elem) { return { listenClick () { const self = this elem.addEventListener('click', function () { self.speakLeet() }) }, speakLeet() { console.log(`1337 15 4W350M3`) } } }
固然,使用箭头函数也能够直接使用this, 访问元素可使用e.currentTarget
function LeetSpeaker (elem) { return { listenClick () { elem.addEventListener('click', (e) => { console.log(e.currentTarget) // elem this.speakLeet() }) }, speakLeet () { console.log(`1337 15 4W350M3`) } } } new LeetSpeaker(document.querySelector('button')).listenClick()
若是想要移除监听事件,则须要在绑定事件时,第二个参数--回调函数要使用命名函数,而非匿名函数
function someFunction () { console.log('do something') // Removes the event listener. document.removeEventListener('click', someFunction) } document.addEventListener('click', someFunction)
须要注意的是,有些时候,上述的规则会共同做用,这是会有优先级,好比下面的例子,new和对象方法共同做用的状况,那么,new 规则主导
var obj1 = { value: 'hi', print: function() { console.log(this); }, }; new obj1.print(); // -> print {}
小结:
某些规则共同做用,优先级以下,从前日后,优先级越低:
(1) new 操做符
(2) bind()
(3) call(), apply()
(4) object method
(5) global object, except in strict mode
什么状况下须要改变this的指向?好比想要得到类数组对象如{1:'a', 2: 'b'}的数组值,就可使用Array.slice方法,但OOP中,方法都是和对象绑定的,因此须要手动修改this的指向。
Javascript中共提供了三种方法修改this的指向, call,apply,bind
咱们使用func.call(param), 传递参数给this, 第一个参数绑定给this,后面的做为函数的实参
例子1:不带参数的函数
function logThis() { console.log(this); } var obj = { val: 'Hello!' }; logThis(); // -> Window {frames: Window, postMessage: ƒ, …} logThis.call(obj); // -> { val: 'Hello!' };
例子2:带参数的函数
function logThisAndArguments(arg1, arg2) { console.log(this); console.log(arg1); console.log(arg2); } var obj = { val: 'Hello!' }; logThisAndArguments('First arg', 'Second arg'); // -> Window {frames: Window, postMessage: ƒ, …} // -> First arg // -> Second arg logThisAndArguments.call(obj, 'First arg', 'Second arg'); // -> { val: 'Hello!' } // -> First arg // -> Second arg
aruguments在Javascript中,是传递给函数的参数,一个类数组的对象
function add() { console.log(arguments); } add(4); // -> { '0': 4 } add(4, 5); // -> { '0': 4, '1': 5 } add(4, 5, 6); // -> { '0': 4, '1': 5, '2': 6 }
当有需求要遍历这些参数,使用Array的map, forEach等,咱们可使用call来说arguments转变为数组
Array.slice:经过this引用,返回调用数组的一份拷贝,若是Array.slice传入arguments, 就会返回一份新的数组,从arguments建立而来的
function add() { var args = [].slice.call(arguments); console.log(args); } add(4, 5, 6); // -> [ 4, 5, 6 ]
apply 运行的机制和call相似,不一样的是arguments是以数组的形式传递的
function logThisAndArguments(arg1, arg2) { console.log(this); console.log(arg1); console.log(arg2); } var obj = { val: 'Hello!' }; logThisAndArguments('First arg', 'Second arg'); // -> Window {frames: Window, postMessage: ƒ, …} // -> First arg // -> Second arg logThisAndArguments.apply(obj, ['First arg', 'Second arg']); // -> { val: 'Hello!' } // -> First arg // -> Second arg
bind和call, apply运行不太同样,func.bind调用后函数并不当即执行,而是当函数调用时才会触发, 传参的方式和call同样,一个一个传
function logThisAndArguments(arg1, arg2) { console.log(this); console.log(arg1); console.log(arg2); } var obj = { val: 'Hello!' }; var fnBound = logThisAndArguments.bind(obj, 'First arg', 'Second arg'); console.log(fnBound); // -> [Function: bound logThisAndArguments] fnBound(); // -> { val: 'Hello!' } // -> First arg // -> Second arg
function sayThis () { console.log(this) } const boundFunc = sayThis.bind({hippy: 'hipster'}) boundFunc() // {hippy: "hipster"}
遇到箭头函数,可能状况就不同了
const sayThis = _ => console.log(this) const boundFunc = sayThis.bind({hippy: 'hipster'}) boundFunc() // Window
传给bind的其余参数则做为实参
const sayParams = (...args) => console.log(...args) const boundFunc = sayParams.bind(null, 1, 2, 3, 4, 5) boundFunc() // 1 2 3 4 5
事件监听函数只调用一回的例子
function LeetSpeaker (elem) { return { listenClick () { this.listener = this.speakLeet.bind(this) elem.addEventListener('click', this.listener) }, speakLeet(e) { const elem = e.currentTarget this.addLeetSpeak() elem.removeEventListener('click', this.listener) }, addLeetSpeak () { const p = document.createElement('p') p.innerHTML = '1337 15 4W350M3' document.body.append(p) } } } const button = document.body.querySelector('button') const leetSpeaker = LeetSpeaker(button) leetSpeaker.listenClick()
综合示例:
function logThisAndArguments(arg1, arg2) { console.log(this); console.log(arg1); console.log(arg2); } var obj = { val: 'Hello!' }; // NORMAL FUNCTION CALL logThisAndArguments('First arg', 'Second arg'); // -> Window {frames: Window, postMessage: ƒ, …} // -> First arg // -> Second arg // USING CALL logThisAndArguments.call(obj, 'First arg', 'Second arg'); // -> { val: 'Hello!' } // -> First arg // -> Second arg // USING APPLY logThisAndArguments.apply(obj, ['First arg', 'Second arg']); // -> { val: 'Hello!' } // -> First arg // -> Second arg // USING BIND var fnBound = logThisAndArguments.bind(obj, 'First arg', 'Second arg'); fnBound(); // -> { val: 'Hello!' } // -> First arg // -> Second arg
参考资料:https://zellwk.com/blog/this/