调用位置:函数在代码中被调用的
位置(而不是声明的位置)。
调用栈:到达当前执行位置所调用的所有函数。
默认绑定
独立的函数调用
function foo() {console.log( this.a )}var a = 2;foo() // 2
在 strict mode  模式下 this 绑定到 undefiend,在 non-strict mode  下绑定到 全局对象。
隐式绑定
当 foo() 被调用时,它的落脚点指向 obj 对象。当函数有引用上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。
function foo() {console.log(this.a)}var obj = {a: 2,foo: foo}obj.foo() // 2
对象属性引用链中只有最顶层或者说最后一层会影响调用位置。
function foo() {console.log(this.a)}var obj2 = {a: 42,foo: foo}var obj1 = {a: 2,obj2: obj2}obj1.obj2.foo() // 42
显式绑定
使用 call、bind、apply 来绑定 this 称为显式绑定。
apply 和 call 的区别只是传入的参数不同。
function foo() {console.log(this.a, this.b)}let obj = {a: 2,b: 3}foo.call(obj) // 2, 3foo.apply(obj) // 2, 3
bind 返回一个绑定了指定 this 的函数(硬绑定)
var obj = {a: 2}var bar = foo.bind(obj)var b = bar(3) // 2 3console.log(b) // 5
new 绑定
new 自动执行以下操作:
- 创建(或者说构造)一个全新的对象。
 - 这个新对象会被执行 [[ 原型 ]] 连接。
 - 这个新对象会绑定到函数调用的 this。
 - 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
 
function foo(a) {this.a = a}var bar = new foo(2)console.log(bar.a) // 2
优先级
显式绑定高于隐式绑定
function foo() {console.log(this.a)}var obj1 = {a: 2,foo: foo}var obj2 = {a: 3,foo: foo}obj1.foo() // 2obj2.foo() // 3obj1.foo.call(obj2) // 3obj2.foo.call(obj1) // 2
new 绑定高于隐式绑定
function foo(something) {this.a = something}var obj1 = {foo: foo}var obj2 = {}obj1.foo(2)console.log(obj1.a) // 2obj1.foo.call(obj2, 3)console.log(obj2.a) // 3var bar = new obj1.foo(4)console.log(obj1.a) // 2console.log(bar.a) // 4
new 绑定高于显示绑定
function foo(something) {this.a = something}var obj1 = {}var bar = foo.bind(obj1)bar(2)console.log(obj1.a) // 2var baz = new bar(3)console.log(obj1.a) // 2console.log(baz.a) // 3
判断规则
- 函数是否在 new 中调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。
 - 函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是指定的对象。
 - 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上下文对象。
 - 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined,否则绑定到全局对象。
 
题外
硬绑定这种方式可以把 this 强制绑定到指定的对象(除了使用 new 时),防止函数调用应用默认绑定规则。问题在于,硬绑定会大大降低函数的灵活性,使用硬绑定之后就无法使用隐式绑定或者显式绑定来修改 this。
如果可以给默认绑定指定一个全局对象和 undefined 以外的值,那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显式绑定修改 this 的能力。
Function.prototype.softBind = function(obj) {var fn = thisvar curried = [].slice.call(arguments, 1)var bound = function bound() {// !this 考虑在 strict-mode 下运行return fn.apply(!this ||(typeof window !== 'undefined' && this === window) ||(typeof global !== 'undefined' && this === global)? obj: this,curried.concat.apply(curried, arguments))}bound.prototype = Object.create(fn.prototype)return bound}function foo() {console.log('name: ' + this.name)}var obj = { name: 'obj' }var fooOBJ = foo.softBind(obj)fooOBJ() //name:obj
参考
