为什么要用this?
以一个优雅的方式隐式传递了一个对象的引用,提高了代码的复用性。
this绑定
- 默认绑定: 函数在哪个词法环境中生效,函数的this就指向哪里(词法环境:函数声明在哪里,它的词法环境就是哪里)。
我们来看段代码:
function foo() {
console.log(this.a);
}
function bar() {
var a = 2
foo()
}
function baz() {
var a = 3
bar()
}
var a = 1
baz()
在这段代码中,函数baz的执行带来的函数bar的调用,函数bar的执行带来了函数foo的调用,然后执行foo。也就是函数foo在bar中生效,bar又在函数baz中生效,而函数baz和函数bar的词法环境在全局,也就是函数的this最终会指向全局,执行结果为a=1。
- 隐式绑定:当函数被某个对象拥有时,触发隐式绑定规则。会把函数中的this绑定到对象当中。 直接来看代码:
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
}
obj.foo()
上面代码就触发了this的隐式绑定;函数foo的引用给到了对象obj,当函数被obj调用的时候,此时this就绑- 多次引用函数,最终函数调用前没有上下文对象,就会隐式丢失,从而走默认绑定定到了对象obj中,打印出来的结果为 2 。
- 隐式丢失:多次引用函数,最终函数调用前没有上下文对象,就会隐式丢失,从而走默认绑定
可能有些抽象,我们直接看代码:
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
}
var bar = obj.foo
bar()
这段代码多次引用了函数,最后函数bar相当于函数foo了,词法环境变为了全局,最终打印出来的结果为 undefined。
- 显示绑定: call 修改函数的this指向,接受零散的参数
function foo(x, y) {
console.log(this.a, x + y);
}
var obj = {
a: 2
}
foo.call(obj,1,2)
像这样,通过call可以显式地让this访问到对象obj里面的值,打印出来的值为 2 , 3;好用且简便。
- apply 修改函数的this指向,参数以数组形式接受
apply的用法和call的用法相似,只不过参数是以数组的形式接受
foo.apply(obj,[1,2])
- bind 修改函数的this指向,会返回一个新的函数,接受零散的参数
function foo(x, y) {
console.log(this.a, x + y);
}
var obj = {
a: 2
}
var bar = foo.bind(obj, 2, 3)
bar()
bind的效果和call,bind一样,都会修改函数的this指向,但是它会返回出一个新的函数,然后接受零散参数。上面代码参数也可以写在返回出的函数bar()中,即bar(2,3)。
注意一下:如果使用这三种方法修改this指向,括号里没有指定。例:
foo.call()或foo.call(null),函数中的this就指向window。
箭头函数
在es6中新的一种函数定义方式,函数function foo(a, b) { return a + b }
用箭头函数可以定义为: var bar = (x, y) => { return x + y; }
在箭头函数中本身没有this这个概念,写在其内部的this,也是其外部的非箭头函数的this。
扩展(call的实现原理)
我们知道通过call能修改函数的this指向,那它到底是怎么实现的呢? 我们来看代码:
function foo(x, y) {
console.log(this.a);
}
var obj = {
a: 2
}
Function.prototype.myCall = function (context) {
//拿到调用myCall的那个函数
//把它引用到context中
//再让context调用该函数
let args = [...arguments].slice(1)//[4,5]
context.fn = this
let res = context.fn(...args)
delete context.fn
return res
}
foo.myCall(obj, 4, 5)
call的实现原理其实就是借助了this的隐式绑定规则;看上面代码,我们在函数原型上定义了一个myCall函数属性,这使得函数都能使用这个方法,然后在myCall中将传入进来的实参进行分解再传入函数foo中;函数foo引用到了对象context中,就触发了隐式绑定,再调用函数foo。这就是call方法修改函数this指向的原理。