this 其实是在函数被调用时发生的绑定,它指向什么彻底取决于函数在哪里被调用(也就是函数的调用方法)。数组
独立函数调用时,应用 this
的默认绑定, this
指向全局对象
若是使用严格模式(strict mode),那么全局对象将没法使用默认绑定,所以 this
会绑定到 undefined
观察下面这段代码浏览器
function aba () { console.log(this) } function bab () { "use strict" console.log(this) } var obj = { aba: aba, bab: bab } aba() //Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} bab() //undefined obj.aba() //{aba: ƒ, bab: ƒ} obj.bab() //{aba: ƒ, bab: ƒ}
在一个对象内部包含一个指向函数的属性,并经过这个属性间接引用函数,从而把 this 间接(隐式)绑定到这个对象上。
当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this
绑定到这个上下文对象。
因此上个示例代码中, 调用 obj.aba()
时 this
被绑定到 obj
,所以 this.aba
和 obj.aba
是同样的。app
对象属性引用链中只有
最顶层或者说最后一层会影响调用位置。函数
function foo() { console.log( this.a ); } var obj2 = { a: 42, foo: foo }; var obj1 = { a: 2, obj2: obj2 }; obj1.obj2.foo(); // 42
一个最多见的 this 绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把 this 绑定到全局对象或者 undefined 上,取决因而否是严格模式。post
function fun1 () { console.log(this) } var obj = { fun1: fun1, fun2: function () { console.log(this) } } var getfun1 = obj.fun1 var getfun2 = obj.fun2 function doCallBack (cb) { //参数传递其实就是一种隐式赋值,所以咱们传入函数时也会被隐式赋值 cb() } function testSetTimeout (fun) { setTimeout(fun, 0) } fun1() // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} obj.fun1() // {fun1: ƒ, fun2: ƒ} obj.fun2() // {fun1: ƒ, fun2: ƒ} getfun1() // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} getfun2() // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} doCallBack(obj.fun1) // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} doCallBack(obj.fun2) // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} testSetTimeout(obj.fun1) // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} testSetTimeout(obj.fun2) // Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
使用 call
、 apply
、 bind
或者箭头函数显示绑定函数调用的 this
若是把 null
或者 undefined
做为 this
的绑定对象传入 call
、apply
或者 bind
,这些值在调用时会被忽略,实际应用的是默认绑定规则this
var obj = { c: 33, fun: function (a,b) { console.log( "a:" + a + ", b:" + b + ", c:" + this.c ) } } // 把数组“展开”成参数 obj.fun.apply( null, [2, 3] ); a:2, b:3, c:undefined // 使用 bind(..) 进行柯里化 var bar = obj.fun.bind( null, 2 ); bar( 3 ); // a:2, b:3, c:undefined
上面代码中 apply
、 bind
这两种方法都须要传入一个参数看成 this 的绑定对象。若是函数并不关心 this
的话,仍然须要传入一个占位值,这时 null 多是一个不错的选择。
可是若是某个函数确实使用了 this
,那默认绑定规则会把 this
绑定到全局对象(在浏览器中这个对象是 window
),这将致使不可预计的后果(好比修改全局对象)。code
在 JavaScript 中,构造函数只是一些被 new 操做符调用的普通函数而已。
实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。对象使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操做。
1 建立(或者说构造)一个全新的对象。
2 这个新对象会被执行 [[ 原型 ]] 链接。
3 这个新对象会绑定到函数调用的 this。
4 若是函数没有返回其余对象,那么 new 表达式中的函数调用会自动返回这个新对象。ip
注意: 对 bind
返回的函数进行构造调用时,实际调用的是原函数,调用 bind
时传入的第一个参数将被忽略,剩余参数做为构造调用的参数传入原函数get
function test (a, b) { this.a = a this.b = b this.say = function () { console.log(`a: ${this.a}, b: ${this.b}, c: ${this.c}`) } } var bindObj = { c: 12 } var bindReturn = test.bind(bindObj, 1, 2) var newBindReturn = new bindReturn() newBindReturn.say() // a: 1, b: 2, c: undefined console.log(bindObj) // {c: 12} bindReturn() console.log(bindObj) // {c: 12, a: 1, b: 2, say: ƒ}