【面试】this代码输出易错题

208 阅读3分钟

题目一 箭头函数

var a = 10
var obj = {
    a: 20,
    say: () => {
        console.log(this.a)
    }
}
obj.say()

var anotherObj = { a : 30 }
obj.say.apply(anotherObj)

10、10

  1. 箭头函数是没有this的,是指向定义时外层的this
  2. 因为没有this,所以即使使用apply也没什么作用

这道题一般会再问一句,如果say变成普通函数会是什么样的结果,那就完全不同了。依稀记得几年前的面试好像就遇到过。😄

变为普通函数后就是:20、30

题目二

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

a.call(null)

解析:call如果传入的参数是null或者undefined,则把window作为this的值。例如也可以看我们手写的call,也是会做个判断。

Function.prototype.call2 = function(context) {
    if (typeof this !== 'function') {
        throw new Error('type error');
    }
    context = context || window; // context false则取windows
    const args = [...arguments].slice(1);
    context.fn = this;
    const res = context.fn(...args);
    delete context.fn;
    return res;
}

题目三

var obj = {
    name: 'cuggz',
    fun: function() {
        console.log(this.name)
    }
}
obj.fun()
new obj.fun()

解析:想想new的操作,会构造一个新对象,然后函数将this改变为内部新创建的对象,但现在并没有什么name属性,所以是undefined

cuggz、undefined

function myNew() {
    // 取出第一个参数,就是构造函数
    const constructor = Array.prototype.shift.call(arguments);
    if (typeof constructor !== 'function') {
        throw new Error('not function');
    }
    const o = new Object;
    // 原型指向构造函数原型
    o.__proto__ = constructor.prototype;
    // 改变构造函数的this,使它指向新创建的对象,o可以访问到构造函数的属性。apply参数是数组,call是一个一个的
    const res = constructor.apply(o, arguments);
    return typeof res === 'object' ? res : o;
}

题目四

var obj = {
    say: function() {
        var f1 = () => {
            console.log("1111", this);
        }
        f1();
    },
    pro: {
        getPro: () => {
            console.log(this);
        }
    }
}
var o = obj.say;
o();
obj.say();
obj.pro.getPro();

解析:这个题目易错的是最后一行,还是考察的箭头函数的this,箭头函数是没有this的,取的是外层作用域的this,而对象是无法构成作用域的,所以this只能是最外层的,而最外层就是windows,再用babel编译看看最后结果就知道了

image.png

windows obj windows

题目五

var obj = {
    number: 3,
    db1: (function() {
        console.log(this);
        this.number *= 4;
        return function() {
            console.log(this);
            this.number *= 5;
        }
    })()
}

var db1 = obj.db1
db1();
obj.db1();
console.log(obj.number);
console.log(window.number);

解析:

  1. db1是立即执行函数,立即执行this是windows,这时候执行this.number *= 5; 实际上没有number,所以是NAN。
  2. obj.db1实际上是里面返回的函数
  3. 其次就是this指向, obj.db1的this是obj

image.png

题目六

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

解析:这个题有坑,arguments0 竟然是2!!!相当于argumens.fn()。。。

10、2

题目七

var a = 10;
var obt = {
    a: 20,
    fn: function() {
        var a = 30;
        console.log(this.a)
    }
}
obt.fn();
obt.fn.call();
(obt.fn)()

解析:最后一个加括号和不加效果一样,所以是20

20、10、20

题目8

function a(xx) {
    console.log(this);
    this.x = xx;
    return this;
}

var x = a(5);
var y = a(6);

console.log(x.x);
console.log(y.x);

解析:undefined 6

这个题很具有迷惑性啊,不理解的地方估计就是x.x, 这里原因是y = a(6)后,window.x被更新为6,而x.x = window.x.x, 所以是undefined

题目9

function foo(something) {
    this.a = something
}
var obj1 = {}
var bar = foo.bind(obj1)
bar(2)
console.log(obj1.a)

var baz = new bar(3);
console.log(obj1.a) 
console.log(baz.a);

2 2 3

解析:这里可能疑惑的就是最后new bar,考察的知识点就是bind函数,如果是用new调用,则忽略传入的obj,还是原来初始函数的this

/**
 * 返回一个新的函数
 * 函数参数接收bind的函数和调用的函数,bind函数拼接调用函数构成最后参数
 * 如果bind后的函数通过new来调用,则忽略bind绑定的context,使用原来构造函数上下文
 */
Function.prototype.bind2 = function(context) {
    const outArgs = [...arguments].slice(1);
    const func = this;
    function fBound() {
        const inArgs = [...arguments];
        // 这里判断如果调用时this还是指向了F,则说明是new调用,上下文还是传入当前this
        return func.apply(this instanceof F ? this : context, outArgs.concat(inArgs));
    }
    function F() {}
    F.prototype = this.prototype;
    fBound.prototype = new F();
    return fBound;
}

题目10

var foo = 1;
function fn() {
    foo = 3;
    return;
    function foo() {
        // todo
    }
}
fn();
console.log(foo);

里面foo的更新只会影响到内部foo的定义,不会影响到外面。

1 以上题目来自前端面试题之代码输出篇,记录做的时候可能有疑惑的题。