this指向
this 永远指向最后调用它的那个对象。虽然 JS 是静态作用域,但是 this 的执行是函数执行时候确认的,它是需要根据上下文的进行判断的。同一个函数在不同地方被调用,结果可能会不一样,因为函数也是一个对象,可以赋值给另外一个变量进行调用,它本质上可以当作一个对象,只不过是在执行过程值的指向会不同。
var name = "windowsName";
function a() {
var name = "Cherry";
console.log(this.name); // windowsName
console.log("inner:" + this); // inner: Window
}
a();
console.log("outer:" + this); // outer: Window
2
3
4
5
6
7
8
9
10
a 调用的地方是 a(),前面没有调用的对象那么就是全局 window 对象,故 this 指向 window。
var name = "windowsName";
var a = {
name: "Cherry",
fn: function () {
console.log(this.name); // Cherry
},
};
a.fn();
2
3
4
5
6
7
8
此时是 a 调用的 fn(),故 fn 中 this 指向 a。但是如果把 fn 改为匿名函数,则打印的是 windowsName,因为匿名函数执行的是最近一层非箭头函数的 this,即 window:
var name = "windowsName";
var a = {
name: "Cherry",
fn: () => {
console.log(this.name); // windowsName
},
};
a.fn();
2
3
4
5
6
7
8
如果改成这样子,则会输出 undefined,因为此时 fn 中 this 仍然是 a,但是 a 中没有 name 属性,故报错。这说明了 this 永远指向最后调用它的那个对象。
var name = "windowsName";
var a = {
// name: "Cherry",
fn: function () {
console.log(this.name); // undefined
},
};
window.a.fn();
2
3
4
5
6
7
8
在通过将函数赋值的方式调用呢?可以看出首先将 a 中的 fn 赋值给了 f,但是并没有调用,而通过 f()调用时,此时 this 指向 window。
var name = "windowsName";
var a = {
name: null,
// name: "Cherry",
fn: function () {
console.log(this.name); // windowsName
},
};
var f = a.fn;
f();
2
3
4
5
6
7
8
9
10
11
提示
this 的指向并不是在创建的时候可以确定的,在 es5 中,this 永远是指向最后调用的它的那个对象。
var name = "windowsName";
function fn() {
var name = "Cherry";
innerFunction();
function innerFunction() {
console.log(this.name); // windowsName
}
}
fn();
2
3
4
5
6
7
8
9
10
11
# 改变 this 的指向
- 使用 ES6 的箭头函数
- 在函数内部使用_this = this;
- 使用 apply、call、bind;
- new 实例化一个对象。
var name = "windowsName";
var a = {
name: "Cherry",
func1: function () {
console.log(this.name);
},
func2: function () {
setTimeout(function () {
this.func1();
}, 100);
},
};
a.func2(); // this.func1 is not a function
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
上述代码中执行过程中,找不到 func1 是啥子情况呢?因为最后调用 setTimeout 的对象是 window,在 window 中并没有 func1 函数,如何可以改变 this 指向呢?
# 箭头函数
var name = "windowsName";
var a = {
name: "Cherry",
func1: function () {
console.log(this.name);
},
func2: function () {
setTimeout(() => {
this.func1();
}, 100);
},
};
a.func2(); // Cherry
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
箭头函数的this始终指向函数定义时候的this,而非执行时的。因为箭头函数中没有this的绑定,必须通过查找作用域链来解决其值,如果箭头函数被非箭头函数包含,则this绑定的是最近一层非箭头函数的this,否则this是undefined。
var name = "windowsName";
function func1(){
console.log(this.name);
}
var a = {
name: "Cherry",
func1: function () {
console.log(this.name);
},
func2: function () {
setTimeout(() => {
this.func1();
}, 100);
},
};
let b = a.func2;
b(); // windowsName
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
上述代码输出结果是windowsName,此时将a中的func2函数赋值给了b,执行b()时,此时this.func1()的this指向window。这也说明了,箭头函数的this并没有在定义的时候确认,而是在执行的时候才确认,通过向上查找作用域链来确定。
# 函数内部使用_this = this
原理:先将调用这个函数的对象保存在变量_this中,然后在函数中都使用_this,这样的话this的指向仍然是调用这个函数时候的this了。
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
var _this = this;
setTimeout( function() {
_this.func1()
},100);
}
};
a.func2() // Cherry
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
在这个例子中,在func2中首先设置var _this = this
,这里的this指向的是调用func2的对象a,为了防止在func2中的setTimeout被window调用而导致在setTimeout中的this变为window。所以提前将this(指向变量a)赋值给一个变量_this,这样在func2中使用的_this就是指向对象a了。
如果是这样呢?这样时候func2中的this由于是在window中调用的,故this指向的是window,所以这种方法只能保证在对象中调用方法时候,this指向能保证是在对象内部。
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
var _this = this;
setTimeout( function() {
_this.func1()
},100);
}
};
let c = a.func2;
c(); // Uncaught TypeError: _this.func1 is not a function
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# apply、call、bind
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
},100);
}
};
a.func2() // this.func1 is not a function
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
三者都可以改变this的指向,但有些区别。
apply和call的区别是call方法接收的是若干个参数列表,而apply接收的是一个包含多个参数的数组。
bind与apply和call的区别是:bind会创建一个新的函数,需要手动去调用。
# JS中函数调用
上面说到this指向是最后调用它的那个对象,当然函数也是对象,如果是函数调用的话,就要考虑函数调用的方法。
- 作为一个函数调用;
- 函数作为方法调用;
- 使用构造函数调用函数;
- 作为函数方法调用函数(call、apply)。
# 作为一个函数调用
var name = "windowsName";
function a() {
var name = "Cherry";
console.log(this.name); // windowsName
console.log("inner:" + this); // inner: Window
}
a();
console.log("outer:" + this) // outer: Window
2
3
4
5
6
7
8
9
10
作为简单函数,不属于任何一个对象,就是一个函数,在浏览器的非严格模式下默认是属于全局对象window的。但是全局的函数,很容易产生命名冲突,不建议使用。
# 函数作为方法调用
var name = "windowsName";
var a = {
name: "Cherry",
fn : function () {
console.log(this.name); // Cherry
}
}
a.fn();
2
3
4
5
6
7
8
常见的情况是将函数作为对象的方法使用。这里定义了一个对象a,对象a有一个属性name和方法fn;然后对象a通过.方法调用了fn方法。this永远指向最后调用它的那个对象,所以在fn中的this就是指向a的。
# 使用构造函数调用函数
如果函数调用前使用了new关键字,则是调用了构造函数。这看起来像是创建了新的函数,但是实际上是JavaScript函数是重新创建的对象。
// 构造函数:
function myFunction(arg1, arg2) {
this.firstName = arg1;
this.lastName = arg2;
}
// This creates a new object
var a = new myFunction("Li","Cherry");
a.lastName; // 返回 "Cherry"
2
3
4
5
6
7
8
9
这就涉及到new的过程了:
- 创建一个空对象,作为将要返回的实例对象;
- 将这个空对象的原型,指向构造函数的prototype属性;
- 将这个空对象赋值给函数内部的this关键字;
- 开始执行构造函数内部的代码;
- 如果构造函数内有返回值且为对象类型,则返回该对象;否则返回上面创建的实例对象。
function Person(name,age) {
this.name = name;
this.age = age;
}
function _new() {
const args = [].slice.call(arguments);
const constructor = args.shift();
const context = Object.create(constructor.prototype);
const result = constructor.apply(context, args);
return (typeof result === 'object' && result != null) ? result : context;
}
// 通过自定义_new 返回实例
var actor = _new(Person, '张三', 28);
console.log(actor.name) // 张三
// 通过自定义_new2 返回实例
var actor2 = _new2(Person, ['李四', 29]);
console.log(actor2.name) // 李四
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 作为函数方法调用
在JavaScript中,函数也是对象,所以函数也有自己的属性和方法。比如说call()、apply()是预定义的函数方法,这两个方法可以用于调用函数,第一个参数必须是对象本身。
除了预定义的方法,也可以自定义一些方法,在非严格模式下,调用函数方法第一个参数的值是null或undefined,它将使用全局对象替代。
var name = "windowsName";
function fn() {
var name = 'Cherry';
innerFunction();
function innerFunction() {
console.log(this.name); // windowsName
}
}
fn()
2
3
4
5
6
7
8
9
10
11
上述代码作为函数方法调用,没有挂载在任何对象上,在非严格模式下this指向window。
参考文章: