你可能遇到过这样的 JS 面试题:
{
var obj = {
foo: function () {
console.log(this)
}
}
var bar = obj.foo
obj.foo(); // 打印出的 this 是 obj
bar(); // 打印出的 this 是 window
}
这个原因是因为“Javascript中this永远是指向调用它的对象”
函数的三种调用方式
func(p1, p2)
obj.child.method(p1, p2)
func.call(context, p1, p2) // 先不讲 apply
前面两种是常见的调用方式,但是第三种调用形式,才是正常调用形式:
{
func(p1, p2)
// 等价于
func.call(undefined, p1, p2)
obj.child.method(p1, p2)
// 等价于
obj.child.method.call(obj.child, p1, p2)
}
其他两种都是语法糖,可以等价地变为 call 形式:
至此我们的函数调用只有一种形式:
func.call(context, p1, p2)
这样,this 就好解释了
this,就是上面代码中的 context。就这么简单。
this 是你 call 一个函数时传的 context,由于你从来不用 call 形式的函数调用,所以你一直不知道。
先看 func(p1, p2) 中的 this 如何确定:
当你写下面代码时
{
function func() {
console.log(this)
}
func();
等价于
func.call(undefined) // 可以简写为 func.call()
}
按理说打印出来的 this 应该就是 undefined 了吧,但是浏览器里有一条规则:
如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)
因此上面的打印结果是 window。
如果你希望这里的 this 不是 window,很简单:
{
func.call(obj) // 那么里面的 this 就是 obj 对象了
}
再看 obj.child.method(p1, p2) 的 this 如何确定
{
var obj = {
foo: function () {
console.log(this)
}
}
obj.foo()
}
按照「转换代码」,我们将 obj.foo() 转换为
{
obj.foo.call(obj)
}
好了,this 就是 obj。搞定。
回到题目:
{
var obj = {
foo: function () {
console.log(this)
}
}
var bar = obj.foo
obj.foo() // 转换为 obj.foo.call(obj),this 就是 obj
bar()
}
/转换为 bar.call(); 由于没有传 context;所以 this 就是 undefined;最后浏览器给你一个默认的 this —— window 对象
语法
{
function fn() { console.log(this) }
var arr = [fn, fn2]
arr[0]() // 这里面的 this 又是什么呢?
}
我们可以把 arr[0]() 想象为arr.0() ,虽然后者的语法错了,但是形式与转换代码里的 obj.child.method(p1, p2) 对应上了,于是就可以愉快的转换了: arr[0](),假想为arr.0(),然后转换为 arr.0.call(arr),那么里面的 this 就是 arr 了:)
总结:
this 就是你 call 一个函数时,传入的 context。
如果你的函数调用形式不是 call 形式,请按照「转换代码」将其转换为 call 形式。
在Javascript中this总是指向调用它所在方法的对象。因为this是在函数运行时,自动生成的一个内部对象,只能在函数内部使用。
1.1在一般函数方法中使用 this 指代全局对象
{
function test() {
this.x = 1;
console.log(this.x);
console.log(this);
};
test();
console.log(window.x)
}
1.2作为对象方法调用,this 指代上级对象
{
function test() {
console.log(this.x);
console.log(this)
}
var o = {};
o.x = 1;
o.m = test;
o.m(); // 1
}
1.3作为构造函数调用,this 指代new 出的对象
{
function test() {
this.x = 1;
console.log(this);
}
var o = new test();
console.log(o.x); // 1
运行结果为1。为了表明这时this不是全局对象,我对代码做一些改变:
var x = 2;
function test2() {
this.x = 1;
console.log(this);
}
var o = new test2();
console.log(x); //2
}
1.4 apply 调用 ,apply方法作用是改变函数的调用对象,此方法的第一个参数为改变后调用这个函数的对象,this指代第一个参数
{
var x = 0;
function test() {
console.log(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m.apply(); //0
//apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。如果把最后一行代码修改为
o.m.apply(o); //1
}
{
function fn(num) {
console.log("fn: " + num);
// count用于记录fn的被调用次数
this.count++;
console.log(this)
}
fn.count = 0;
var i;
for (i = 0; i < 10; i++) {
if (i > 5) {
fn(i);
}
}
console.log(fn.count); //0
console.log(this.count); //NaN
}
2.箭头函数的简单讲解
资料上说的箭头函数内部是没有this的,也就是说,箭头函数里面的this会继承自外部的this,下面有个例子,用来详细说明下:
{
var x = 11;
var obj = {
x: 22,
y: this,//window
say: () => {
console.log(this.x);
}
}
obj.say();
//输出的值为11
console.log(obj.y);
//输出的值为window对象
}
obj对象中的this指代的就是window,也就是全局环境,因为箭头函数中的this就会就近找到上一个对象中this所指代的对象,
从以上例子可以看出来,obj内部属性y就为obj内部this指代的对象,输出是window。
看看下面这个例子
{
var a = 11
function test1() {
this.a = 22;
let b = function () {
console.log(this.a);
};
b();
}
var x = new test1();
//输出11
}