切记:this的指向是在函数被调用的时候确定的1
2
3
4
5
6
7
8
9var a = 10;
var obj = {
a: 20
}
function fn () {
console.log(this.a);
}
fn(); // 10
fn.call(obj); // 20
函数执行过程中,一旦this被确定,就不可更改了
一.全局对象中的this
- 通过this绑定到全局对象 this.a=20;
- 通过声明绑定到变量对象 var a=20;
- 只有赋值,隐式绑定全局对象 a=20。
二.函数中的this
在函数上下文中,this由调用者提供,由调用函数的方式决定;
如果调用者函数被某一个对象所拥有,那么该函数调用时,内部的this指向该对象;
如果函数独立调用,该函数的内部this,指向undefined,非严格模式下,this指向undefined时,自动指向全局对象。1
2
3
4
5
6
7
8
9
10var a = 20;
var obj = {
a: 10,
c: this.a + 20,
fn: function () {
return this.a;
}
}
console.log(obj.c); // 40
console.log(obj.fn()); // 10
1 | 'use strict'; |
对调用者和独立运行的理解
1 | var a = 20; |
- foo.getA()中,getA是调用者,他不是独立调用,被foo对象所拥有,this指向foo
- test()作为调用者,尽管引用与foo.getA相同,他是独立调用的,this指向undefined,非严格模式下,指向window
1 | function foo() { |
上例函数独立调用,this指向window
三.使用call,apply显式指定this
JavaScript中使用call,apply自行设置this的指向,1
2
3
4
5
6
7
8function fn(num1, num2) {
console.log(this.a + num1 + num2);
}
var obj = {
a: 20
}
fn.call(obj, 100, 10); // 130
fn.apply(obj, [20, 10]); // 50
call和apply的应用场景
将类数组对象转化为数组
1
2
3
4
5
6
7
8function exam(a, b, c, d, e) {
// 先看看函数的自带属性 arguments 什么是样子的
console.log(arguments);
// 使用call/apply将arguments转换为数组, 返回结果为数组,arguments自身不会改变
var arg = [].slice.call(arguments);
console.log(arg);
}
exam(2, 8, 9, 10, 3);灵活修改this指向
1
2
3
4
5
6
7
8
9
10var foo = {
name: 'joker',
showName: function() {
console.log(this.name);
}
}
var bar = {
name: 'rose'
}
foo.showName.call(bar);实现继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 定义父级的构造函数
var Person = function(name, age) {
this.name = name;
this.age = age;
this.gender = ['man', 'woman'];
}
// 定义子类的构造函数
var Student = function(name, age, high) {
// use call
Person.call(this, name, age);
this.high = high;
}
Student.prototype.message = function() {
console.log('name:'+this.name+', age:'+this.age+', high:'+this.high+', gender:'+this.gender[0]+';');
}
new Student('xiaom', 12, '150cm').message();
在Student的构造函数中,借助call方法,将父级的构造函数执行了一次,相当于将Person中的代码,在Sudent中复制了一份,其中的this指向为从Student中new出来的实例对象。call方法保证了this的指向正确,因此就相当于实现了继承。
- 在向其他上下文传递过程中,确保this指向不变
如下例,匿名函数的存在导致this指向的丢失。1
2
3
4
5
6
7
8
9var obj = {
a: 20,
getA: function() {
setTimeout(function() {
console.log(this.a)
}, 1000)
}
}
obj.getA();
解决方案一:使用变量保存this1
2
3
4
5
6
7
8
9
10var obj = {
a: 20,
getA: function() {
var self=this;
setTimeout(function() {
console.log(self.a)
}, 1000)
}
}
obj.getA();
解决方案二:使用ES5自带bind方法1
2
3
4
5
6
7
8
9var obj = {
a: 20,
getA: function() {
setTimeout(function() {
console.log(this.a)
}.bind(this), 1000)
}
}
obj.getA();
解决方案三:借助apply和闭包封装一个bind方法1
2
3
4
5
6
7
8
9
10
11
12
13
14function bind(fn,obj){
return function(){
return fn.apply(obj,arguments)
}
}
var obj = {
a: 20,
getA: function() {
setTimeout(bind(function(){
console.log(this.a)
},this),1000)
}
}
obj.getA();
四.构造函数和原型对象上的this
1 | // 构造函数 |
构造函数中,this指向了新创建的对象p1,原型中,this指向调用的对象p1,因此getName的this也指向p1
new操作符调用构造函数四个阶段:
- 创建一个新的对象
- 将构造函数的this指向新对象
- 指向构造函数的代码,为这个对象添加属性和方法等
- 返回新对象
参考文章:
this——这波能反杀
如有错误,烦请指正,谢谢!