this指向及绑定方法

this关键字

this指向

简单来说,this就是属性或方法当前所指的对象,并且它也总是返回一个对象

1
2
3
4
5
6
7
var person = {
name: "joyce",
describe: function() {
return '姓名:' + this.name;
}
};
person.describe() // "姓名:joyce"

this.name 表示name属性所在对象,name是在describe方法中调用,而describe方法所在的当前对象是person,故this指向person。this.name就是person.name

this指向的可变性

由于对象属性可以赋给另一个对象,属性所在对象的可变性,所以this指向也是可变的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function callName() {
return '姓名:' + this.name
}

var A = {
name: 'joyce',
describe: callName
};

var B = {
name: 'tony',
describe: callName
}

A.describe() // "姓名:joyce"
B.describe() // "姓名:tony"

callName函数内部使用了this关键字,随着callName所在对象不同,this指向也发生改变

绑定this的方法

Function.prototype.call()

该方法可以指定函数内部this的指向(即函数执行时的作用域),然后在指定的作用域中调用该函数

1
2
3
4
5
6
7
var obj = {};
var f = function () {
return this;
};

f() === window // true
f.call(obj) === obj // true

指定call方法的第一个参数,改变this指向。若参数为空、null、undefined则默认传入全局对象

1
2
3
4
5
6
7
8
9
10
11
12
var n = 123;
var obj = { n: 456 };

function a() {
console.log(this.n);
}

a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456

call的第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数

1
2
3
4
5
function add(a,b) {
return a+b
}

add.call(this, 1,2) // 3

Function.prototype.apply()

apply与call方法类似,同样是改变this指向,然后再调用该函数。唯一区别是apply将参数放在数组里而call需要将参数按顺序传递进去

1
2
3
4
5
6
function f(x,y) {
console.log(x+y);
}

f.call(null, 1, 3) // 4
f.apply(null, [1,3]) // 4

apply方法的应用

  1. 找出数组最大元素
    使用apply和Math.max方法,可以返回数组最大元素

    1
    2
    var arr = [1,4,7,10,26,9]
    Math.max.apply(null, a) // 26
  2. 将数组中的空元素变为undefined
    通过apply,利用Array构造函数将数组元素变成undefined

    1
    Array.apply(null, ['a', ,'b'])  // ['a',undefined,'b']

空元素与undefined差别在于,数组的forEach方法会跳过空元素,但不会跳过undefined。因此遍历内部元素时,会得到不同结果

1
2
3
4
5
6
7
8
var arr = ['a', ,'b']

function print(i) {
console.log(i)
}

a.forEach(print) // a b
Array.apply(null,a).forEach(print) // a undefined b

  1. 转换类似数组的对象
    利用数组对象的slice方法,可将一个伪数组(如arguments对象)转为真正的数组
    1
    2
    3
    4
    Array.prototype.slice.apply({0: 1, length: 1})  // [1]
    Array.prototype.slice.apply({0: 1}) // []
    Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
    Array.prototype.slice.apply({length: 1}) // [undefined]

上述apply方法的参数都是对象,返回结果都是数组,这就达到将对象转为数组的目的,但被处理的对象需要有length属性以及相对应的数字键

Function.prototype.bind()

bind()方法会创建一个新函数,称为绑定函数。当调用这个绑定函数时,绑定函数会以创建它时传入 bind() 方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

常见的用法,我们通常使用_this、that、self等保存this,方便在改变了上下文后继续引用到它

1
2
3
4
5
6
7
8
9
var foo = {
bar: 1,
eventBind: function(){
var _this = this;
$('.oneClass').on('click', function(event){
console.log(_this.bar) // 1
})
}
}

由于Javascript特有机制,上下文环境在 eventBind:function(){} 过渡到 $(‘.oneClass’).on(‘click’, function(event){}) 发生了改变。使用新的变量保存this这样的方法是有效的,使用bind()可以更优雅的解决

1
2
3
4
5
6
7
8
var foo = {
bar: 1,
eventBind: function(){
$('.oneClass').on('click', function(event) {
console.log(this.bar) // 1
}.bind(this));
}
}

再举一个简单的栗子🌰

1
2
3
4
5
6
7
8
9
10
11
var bar = function(){
console.log(this.x)
}

var foo = {
x: 3
}

bar() // undefined
var func = bar.bind(foo)
func() // 3

在创建了一个新函数func,使用bind()创建一个绑定函数,在执行的时候this被设置成foo,而不是直接调用bar()时的全局作用域

三者比较

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {
x: 886
}

var foo = {
getX: function(){
return this.x
}
}

console.log(foo.getX.bind(obj)()) // 886
console.log(foo.getX.call(obj)) // 886
console.log(foo.getX.apply(obj)) // 886

三个输出都是886,不同的是bind方法后多了一个括号,当你希望改变上下文环境后并非立即执行,而是回调时执行,使用bind()方法,而call、apply会立即执行函数

总结:

  • call、apply、bind三者都是用来改变函数的this对象的指向,并且第一个参数都是要指向的对象(也即函数执行的上下文)
  • 三者都可以利用后续参数传参
  • bind是返回对应的函数,便于稍后调用,call和apply则是立即调用
0%