Node-JS专精02this变态面试题

回顾基础知识,JS如何声明函数

1
2
3
4
5
6
7
const f1 = new Function('x','y','return x + y')

function f2(x,y){return x + y}

const f3 = function(x,y){return x + y}

const f4 = (x, y)=> x + y

其中,f1,f2,f3 是ES6之前的语法,支持 this,arguments,new
f4 是 ES6语法,不支持 this,arguments,new

你可以去 浏览器控制台里试试

1
2
3
4
const f2 = ()=>console.log(this)
f2() // window

f2.call({name:'hjx'}) // 还是 window

箭头函数不支持this

箭头函数如何处理 a ,就如何处理 this

1
2
3
4
5
6
const a = 233
const f2 = ()=>console.log(a)
f2() //

console.log(this)
const f2 = ()=>console.log(this)

箭头函数把 this 当作外部的变量,仅此而已,但是非箭头函数的 this 有很多特殊处理

箭头函数不支持this 指的就是尖头函数对 this 和其他变量一视同仁,不会特殊对待

非箭头函数的this

网传死记方法

  • this是上下文
  • 全局环境执行时候,this是全局对象
  • 调用对象方法时,this是该对象
  • 函数里调用函数时,this是全局对象
  • 箭头函数的this,不看调用,看定义(静态作用域)
  • 还有人说箭头函数里的 this 指向外面的this
  • 用new调用函数时,this是新增对象
  • 可以用call/apply/bind指定this

记忆力好可以这样记

  • 我记不住

那么this是参数还是环境?

  • 答案:this是参数,只有箭头函数 this是环境

如何确定this

显式this

1
2
3
fn.call(asThis,1,2)
fn.bind(asThis,1,2)()
obj.method.call(obj,'hi')

隐式this

1
2
3
4
5
6
7
8
fn(1,2)
// fn.call(undefined,1,2)

obj.method('hi')
// obj.method.call(obj,'hi')

array[0]('hi')
// array[0].call(array,'hi')

为什么你很难确定this,因为你总是让js帮你选this

所以你应该自己指定this

测试一下

1
2
3
4
5
button.onclick = function(e){
console.log(this)
}

// this 是参数, 答 button 是错的

答案是:不知道,要看怎么调用

因为你没看到call,没指定this,所以没法确定

1
2
3
4
5
6
7
8
9
10
11
12
13
button.onclick = function(e){
console.log(this)
}

// 当你手点的时候 是 button

// 如果js调用呢?
button.click()

// 而如果这样,就不是button
var fn = button.onclick
fn() // 打印的是 window
fn.call({name:'hjx'}) // {name:'hjx'}

标准答案

这个this的值无法确定,我们要看this是如何调用的,如果是用户点击按钮的时候浏览器调用的,那么浏览器一定会把button作为this传递进来,
如果是通过其他方式调用的,那么就要看它是如何调用的如以下代码:

1
2
var fn = button.onclick
fn()

由于fn没指定this所以相当于 fn.call(undefined) 那么浏览器就会把这个this变成全局对象 window

再来一个题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const vm = new Vue({
data:{
msg:'hi'
},
methods:{
sayHi(){
console.log(this.msg) // this是什么
}
}
})

答案依然是 不知道
如果你是在 vue正常情况下调用 这个this 就是 vm
但如果你是把 vm.sayHi放在其他地方调用就要看你调用的时候怎么传递参数
根据传参的形式不同,我才能确定this是什么
所以this是一个参数,只有在调用的时候唯一的确定

真实面试题

1
2
3
4
5
6
7
8
9
10
11
12
let length = 10
function fn(){ console.log(this.length) }

let obj = {
length:5,
method(fn){
fn()
arguments[0]()
}
}

obj.method(fn,1) // 输出什么,有陷阱

你可以拷贝代码到浏览器里试一下

分析以下,先把 fn()改写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let length = 10
function fn(){ console.log(this.length) }

let obj = {
length:5,
method(fn){
// fn()
fn.call(undefined) // 所以此时 this 实际是 window
}
}


obj.method(fn,1)
//所以打印的是 window.length
// 然而 let length 是不是 window 的 length?
// 答案是 没关系
// window.length 是什么?

window.length 默认指的是 当前页面有多少个窗口或者说多少个 iframe
// 你说这题是不是非常坑

再来分析arguments[0]()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 改写 arguments[0]()
// 实际就变成了这样 arguments.0.call(arguments)
let length = 10
function fn(){ console.log(this.length) }

let obj = {
length:5,
method(fn){
fn()
arguments[0]()
// arguments.0.call(arguments)
// 所以此时 this是 arguments
// arguments.length 是什么?
// 是形参的长度还是,实参的长度
// 答案是 arguments.length 是 实参的长度
}
}

obj.method(fn,1)

总结

  • 首先碰到这些this问题,一定要改写,你不改写就是坑自己

    1
    2
    3
    4
    5
    6
    7
    8
    fn(1,2)
    // fn.call(undefined,1,2)

    obj.method('hi')
    // obj.method.call(obj,'hi')

    array[0]('hi')
    // array[0].call(array,'hi')
  • this 是 call 的第一个参数

  • new 重新设计了 this/箭头函数不接受 this
  • 函数的返回值由参数和环境确定
  • this是参数,arguments也是参数
  • 全局变量和自由变量是环境