美团面试关于this指向的问题


var length = 10;
function fn() {
    console.log(this.length);
}
var obj = {
    length: 5,
    method: function(fn) {
        fn();
        arguments[0]();
    }
};

obj.method(fn, 1);

Q:1.以上直接调用obj.method(fn,1)时,method中this的指向为obj,难道不是fn()的指向?

Q:2. arguments[0]不就是fn吗?

阅读 7.6k
15 个回答

第一个问题

var a = 10;
function fn() {
  console.log(a);
}
var obj = {
  method: function(fn) {
    var a = 12;
    fn()
  }
}
obj.method(fn);

这样你就看的明白些了;
第二个,arguments[0]()this指向arguments,返回的就是arguments的长度

var arr = [function(){console.log(this)}];
arr[0]();

问题1:method中的this指向obj,因为obj对象调用了method。
问题2:arguments[0]是fn,但是此时this指向arguments,因为arguments对象调用了fn。

问题一,函数中的this要看在调用哪的,不是看在哪声明的。
如果call改变this指向就输出5

var obj = {
        length: 5,
        method: function(fn) {
            fn.call(this);//输出5
            arguments[0]();
        }
    };

问题二,arguments[0]就是fn没错,是指 obj.method(fn,1);实参中的第0个也就是fn。他的this指向实参,返回的是实参的个数。

 obj.method(fn,1,2,3,4,5);// 输出6

10的话应该很好理解的 和上面大佬说的只是被调用了 并没有更改上下文
至于为什么是2,你可以在fn这个函数里面添加console.log(this),把this指针打印出来看看是什么,另外有一个点事 arguments是函数内部的参数数组,数组其实就是对象,对象内部的函数的this指针指向的是这个对象,那么同理 数组中的某一项如果是函数的话,那么这个函数的上下文this指针就指向的是这个数组

问题 1:

var length = 10;
function fn() {
    console.log(this.length);
}
var obj = {
    length: 5,
    method: function(fn) {
        fn();
    }
};

obj.method(fn, 1);

题主需要理解 this 的指向问题, obj.method 中的 method 的 this 指向确实是 obj, 但是里面的 fn 是传参进去的, 在 javascript 中传参都是值传递,对象是特殊的值传递也就是共享传递,也就是传进去的是对象的引用地址,函数是特殊的对象,所以你在 obj.method 函数体里面执行 fn 和在函数体外面执行 fn 是一样的,此时 this 指向是 window 对象。

fnRefenerce {
    identifier: fn,
    base: window
}

问题 2:
arguments[0] 确实是传进来的 fn 函数这个没错,但是需要注意的是, 现在是通过 arguments 对象去调用 fn 函数, 函数是一个对象, 是一个引用值:

fnRefenerce {
    identifier: fn,
    base: arguments
}

this 的指向是动态的, 需要看调用函数的时候的引用情况, 通过 arguments 对象调用的就像上面的伪代码一样, this 就指向了引用的 base 值,那么 arguments[0] 调用就是打印出了 arguments.length, 这里就是 arguments 的参数长度, 输入了两个参数:fn 和 1, 所以是 2.

长见识了....

var arr=[function(){console.log(this===arr)}]
arr[0]() // true
var q = arr[0];
q() // false

arguments相当于这里的arr,数组内部的元素,如果是一个function,那么直接调用这个function时 内部this指向的是数组。

所以说JS奇葩呢。

怎么这么多this的问题。。?
建议你看一看《你不知道的javascript》中"this & Object Prototypes"这一章节,this的指向讲得清清楚楚,没那么复杂:
1.函数是否由new 调用:如果是,指向新创建的对象;
2.是否显示绑定对象:即apply, call, bind, arrow function,如果是,this指向绑定的对象;
3.是否隐式绑定:即是否存在上下文context(被某一对象调用),如果是,this指向绑定的对象;
4.其余情况:非严格模式下,指向global,严格模式下,不能指向global, this为undefined

用此规则分析你的问题:
1.虽然method是被obj调用,存在上下文,但是fn函数本身并不存在上下文(它不是被某对象调用),因此fn的this指向global;
稍微修改一下:

var length = 10;
function fn() {
    console.log(this.length);
}
var obj = {
    length: 5,
    method: function(fn) {
        console.log(this.length) //添加此行
        fn();
        arguments[0]();
    }
};
obj.method(fn,1)
> 5,10,2

2.arguments[0]确实就是fn,但此时fn由arguments调用,因此this指向arguments,和我上面修改了一点的代码会先输出一个5一样的道理

ps:很多时候会有这些说法:谁调用指向谁,从结果来看正确但对新手来说不够清楚,因为新手不一定能分析清楚到底是谁“调用”的,比如

window.age = 18
let obj = {
    age: 88,
    showAge() {
        console.log(this.age)
    }
}
let showAge = obj.showAge
showAge() // 18

不先回答问题的都是耍流氓!!先回答提主问题,Q1:不是!;Q2:是!稍安勿躁,下面解释为什么。

 var length = 10;
    function fn() {
        console.log(this.length);
    }
    var obj = {
        length: 5,
        method: function(fn) {
            fn();
            arguments[0]();
        }
    };
    
    obj.method(fn, 1);



首先,this的指向问题是分情况的!!!
首先,this的指向问题是分情况的!!!
首先,this的指向问题是分情况的!!!

重要的事说三遍~~~

JavaScript中函数调用分四种情况:

  1. 作为函数调用;
  2. 作为方法调用;
  3. 作为构造函数调用;
  4. 通过函数的apply和call方法间接调用;

具体例子不展开了,请看犀牛书P168~谢谢!

分析一下代码,首先在全局环境即window环境中定义了变量length,函数fn,对象obj;在obj对象中定义了length属性和method方法;

在执行 obj.method(fn, 1)后(这是方法调用),该操作下首先执行了 fn(),这属于函数调用,函数调用中的this值指向全局对象window,此时输出this.length相当于window.length,最后结果是10。

紧接着执行arguments[0]()的操作。这里首先明白一个概念arguments是一个对象!arguments是一个对象!arguments是一个对象!只不过这个实参对象碰巧具有以数字为索引的属性。这里是最容易让人产生误区的地方,也是面试题中经常晃点你的地方!你可以把它理解成这样一个对象arguments = {1:fn,2:1,length:2},既然arguments是一个对象,那么arguments[0]()可以理解为这样arguments.0()arguments.fn()。因此,这里是一个赤果果的方法调用啊!同学们!而函数方法调用中的this值指向调用该方法的对象!输出的this.length就是arguments.length,最后调用传了两个实参fn和1,因此输出是2。

回答的有点啰嗦了,希望大家能看懂,也欢迎大家及时指正!怀念在美团做数据运营的日子~~~

谁调用这个函数,谁就是this~

第一次10 是因为window调用了
第二个2 是arguments对象调用了

function中的this通常只与其声明所在的作用域有关,改变function的this指向的方式有apply,call,bind,new XXX()或指定函数的上下文对象,所以
1、method中直接执行fn()并不影响this的指向,this仍然指向window
2、函数中的arguments指向当前所在函数,所以arguments[0]中的this指向method方法而不是obj

楼上回答的都挺好,我就不献丑了。楼主今天面的吗?是一面还是?我昨天面了,问了同样的问题+_+

第一个没加.或者[],且不是严格模式。this指向window.
第2个arguments[0](),有[],this指向[]前面的arguments对象,而arguments.length返回的是实参的长度。返回2。如果method方法里添加arguments.callee.length则返回1,函数的length属性返回形参的长度。
简单理解非严格模式下的this:只要不是通过.或者[]调用函数,this就指向window。反之this就指向.或者[]前面的对象(不严谨的说法,解决一般的问题够用了,注意"链式调用",call、apply、bind)

var length = 10;
function fn() {
    console.log(this.length);
}
var obj = {
    length: 5,
    method: function(fn) {
        fn();
        arguments[0]();
    }
};

obj.method(fn, 1);

this绑定一般有4种方式,即

  1. 默认绑定(window或global)
  2. 显式绑定(call,apply)
  3. new(绑定到实例)
  4. 隐式绑定(obj.foo)。
    第一个属于默认绑定,第二个属于隐式绑定。

关于arguments那行的解释

var a = [function(){console.log(this === a)}, 2];
a[0](); // true

总结:一,如果函数独立运行,this指向 undefined;
二,如果函数被某个对象拥有,this指向那个对象。

拿例子来说:
var obj = {

length: 5,
method: function(fn) {
    fn();
    arguments[0]();
}

};
fn定义的时候是独立函数,运行也是独立函数;所以 this是undefine。运行的时候发现this并不是undefine,是存在的。那是因为浏览器有点特别,函数虽然独立运行,但是他在 window对象下面。正好符合规则2,函数被对象所拥有,this应该指向对象(window);当然 如果在node里面或者严格模式下,this就是undefine了

arguments[0]() 同理可知,函数被arguments 拥有,所以this指向arguments

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题