this的作用
function identify() {
return this.name.toUpperCase()
}
function speak() {
var greeting = "Hello" + identify.call(this)
console.log(greeting)
}
var me = {
name: 'aa'
}
var you = {
name: 'bb'
}
identify.call(me) // AA
identify.call(you) // BB
speak.call(me) // HelloAA
speak.call(you) // HelloBB
这段代码可以在不同的上下文对象中重复使用函数identify()和speak()、不用针对每个对象编写不同版本的函数。
如果不使用this,那就需要给identify()和speak()显示传入一个上下文对象
function identify(context) {
return context.name.toUpperCase()
}
function speak(context) {
var greeting = "Hello" + identify(context)
console.log(greeting)
}
identify(you) // BB
speak(me) //HelloAA
this提供了一种更优雅的方式来隐式”传递“一个对象引用,因此可以将API设计得更加简洁并且易于复用,随着使用的模式越来越复杂,显示传递上下文对象会让代码变得越来越混乱,使用this则不会这样
this是什么
人们很容易把this理解成指向函数自身,从英文翻译上理解似乎是对的,下面看个例子:
function foo(num) {
console.log("foo" + num)
this.count++
console.log(this === window) // true
}
foo.count = 0
var i;
for (i = 0; i < 10; i++) {
if (i > 5) {
foo(i)
}
}
console.log(foo.count) // 0
console.log语句产生了4条输出,证明foo(...)确实被调用了4次,但是foo.count任然是0。显然从字面意思来理解this行不通
执行foo.count = 0时的确向函数对象foo添加了一个属性count。但是函数内部代码this.count的this并不是指向那个函数对象,指向的是全局对象window
那么this到底是什么呢?
this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数调用的方式(调用位置)。
当一个函数被调用时,会创建一个活动记录(执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。this就是这个记录的一个属性,会在函数执行的过程中用到
绑定规则
默认绑定
首先介绍的是最常用的函数调用类型:独立函数调用。可以把这条规则看作是无法应用其他规则时的默认规则,举个例子:
function foo() {
console.log(this.a) //2
}
var a = 2
foo()
当调用foo()时,this.a被解析成了全局变量a,因为此时foo()是直接使用不带任何修饰的函数引用进行调用的,只能用默认绑定,无法应用其他规则
但是当严格模式,不能将全局对象应用于默认绑定,this会绑定到undefined
隐式绑定
另一条需要考虑的规则是调用位置是否有上下文对象。举个例子
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
}
obj.foo() // 2
当foo()被调用时,它的前面加上了对obj的引用,当函数引用有上下文对象是,隐式绑定规则会把函数调用中this绑定到这个上下文对象。
对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。举个例子
function foo() {
console.log(this.a)
}
var obj2 = {
a: 42,
foo: foo
}
var obj1 = {
a: 2,
obj2: obj2
}
obj1.obj2.foo() // 42
隐式丢失
隐式绑定最常见的一个问题就是被隐式绑定的函数会丢失绑定对象,也就是会所它会应用默认绑定,从而把this绑定到全局对象或者undefine上,取决于是否严格模式,举个例子:
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
}
var bar = obj.foo;
var a = "global"
bar() //相当于执行 foo() "global"
虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定
显示绑定
javascript中提供的显示绑定的方法就是大家都非常熟悉的call(...)、apply(...)和bind(...)方法,下面就简单介绍一下它们的用法:
call的语法为:
fun.call(thisArg[, arg1[, arg2[, ...]]])
apply的语法为:
fun.apply(thisArg, [argsArray])
它们的第一个参数对象是一个对象,是个this准备的,接着在调用函数将其绑定到this。第二个参数 call 方法接受的是若干个参数列表,而apply接收的是一个包含多个参数的数组。
bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。因此:bind 是创建一个新的函数,我们必须要手动去调用,举个例子:
var a ={
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.bind(a,1,2)() // 3
new绑定
使用new来调用函数,会自动执行下面的操作
- 创建(或者说构造)一个全新的对象
- 这个新对象会被执行[[Prototype]]连接
- 这个对象会绑定到函数调用的this
- 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
举个例子:
function foo(a) {
this.a = a
}
var bar = new foo(2)
console.log(bar.a) // 2
使用new来调用foo(...)时,会构造一个新对象并把它绑定到foo(...)调用中的this上
绑定优先级
- 函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象
- 函数是否通过call、apply、bind(显示绑定)?如果是的话,this绑定是指定的对象
- 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象
- 如果都不是,使用默认绑定,如果在严格模式下就绑定到undefined,否则绑定到全局对象
ES6中this指向
ES6中的箭头函数并不会使用上述的绑定规则,而是根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this绑定(无论this绑定到什么)举个例子:
function Timer() {
this.s1 = 0;
this.s2 = 0;
setInterval(() => this.s1++, 1000);
setInterval(function () {
this.s2++;
}, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 1100); // 1
setTimeout(() => console.log('s2: ', timer.s2), 1100); // 0
上例中s1的值是在箭头函数中,会继承Timer函数作用域中的this,而s2在普通函数中,根据之前的绑定绑定规则可以看出此时的this会执行默认的绑定规则绑定到全局对象上
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。