因此可以看出,apply, call 的作用就是给函数绑定一个执行上下文,且是显式绑定的。因此,函数内的this自然而然的绑定在了 call 或者 apply 所调用的对象上面。
// 带参数 function count(num1, num2) { console.log(this.a * num1 + num2) } var obj1 = { a: 2 } var obj2 = { a: 3 } count.call(obj1, 1, 2) // 4 count.apply(obj1, [1, 2]) // 4 count.call(obj2, 1, 2) // 5 count.apply(obj2, [1, 2]) // 5
上面这个例子则说明了 apply 和 call 用法上的差异。
而 bind 函数,则返回一个绑定了指定的执行上下文的新函数。还是以上面这段代码为例
// 带参数 function count(num1, num2) { console.log(this.a * num1 + num2) } var obj1 = { a: 2 } var bound1 = count.bind(obj1) // 未指定参数 bound1(1, 2) // 4 var bound2 = count.bind(obj1, 1) // 指定了一个参数 bound2(2) // 4 var bound3 = count.bind(obj1, 1, 2) // 指定了两个参数 bound3() //4 var bound4 = count.bind(obj1, 1, 2, 3) // 指定了多余的参数,多余的参数会被忽略 bound4() // 4
所以,bind 方法只是返回了一个新的函数,这个函数内的this指定了执行上下文,而返回这个新函数可以接受参数。
new 绑定
最后要讲的一种 this 绑定规则,是指通过 new 操作符调用构造函数时发生的 this 绑定。首先要明确一点的是,在 javascript 中并没有其他语言那样的类的概念。构造函数也仅仅是普通的函数而已,只不过构造函数的函数名以大写字母开头,也只不过它可以通过 new 操作符调用而已.
function Person(name,age) { this.name = name this.age = age console.log("我也只不过是个普通函数") } Person("zxt",22) // "我也只不过是个普通函数" console.log(name) // "zxt" console.log(age) // 22 var zxt = new Person("zxt",22) // "我也只不过是个普通函数" console.log(zxt.name) // "zxt" console.log(zxt.age) // 22
上面这个例子中,首先定义了一个 Person 函数,既可以普通调用,也可以以构造函数的形式的调用。当普通调用时,则按照正常的函数执行,输出一个字符串。 如果是通过一个new操作符,则构造了一个新的对象。那么,接下来我们再看看两种调用方式, this 分别绑定在了何处首先普通调用时,前面已经介绍过,此时应用默认绑定规则,this绑定在了全局对象上,此时全局对象上会分别增加 name 和 age 两个属性。当通过new操作符调用时,函数会返回一个对象,从输出结果上来看 this 对象绑定在了这个返回的对象上。
因此,所谓的new绑定是指通过new操作符来调用函数时,会产生一个新对象,并且会把构造函数内的this绑定到这个对象上。
事实上,在javascript中,使用new来调用函数,会自动执行下面的操作。
创建一个全新的对象
这个新对象会被执行原型连接
这个新对象会绑定到函数调用的this
如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
四种绑定的优先级
上面讲述了javascript中四种this绑定规则,这四种绑定规则基本上涵盖了所有函数调用情况。但是如果同时应用了这四种规则中的两种甚至更多,又该是怎么样的一个情况,或者说这四种绑定的优先级顺序又是怎么样的。
首先,很容易理解,默认绑定的优先级是最低的。这是因为只有在无法应用其他this绑定规则的情况下,才会调用默认绑定。那隐式绑定和显式绑定呢?还是上代码吧,代码可从来不会说谎。
function speak() { console.log(this.name) } var obj1 = { name: 'obj1', speak: speak } var obj2 = { name: 'obj2' } obj1.speak() // obj1 (1) obj1.speak.call(obj2) // obj2 (2)
所以在上面代码中,执行了obj1.speak(),speak函数内部的this指向了obj1,因此(1)处代码输出的当然就是obj1,但是当显式绑定了speak函数内的this到obj2上,输出结果就变成了obj2,所有从这个结果可以看出显式绑定的优先级是要高于隐式绑定的。事实上我们可以这么理解obj1.speak.call(obj2)这行代码,obj1.speak只是间接获得了speak函数的引用,这就有点像前面所说的隐式绑定丢失了上下文。好,既然显式绑定的优先级要高于隐式绑定,那么接下来再来比较一下new 绑定和显式绑定。
function foo(something) { this.a = something } var obj1 = {} var bar = foo.bind(obj1) // 返回一个新函数bar,这个新函数内的this指向了obj1 (1) bar(2) // this绑定在了Obj1上,所以obj1.a === 2 console.log(obj1.a) var baz = new bar(3) // 调用new 操作符后,bar函数的this指向了返回的新实例baz (2) console.log(obj1.a) console.log(baz.a)