this到底是什么?

308 阅读3分钟

关于this,MDN是这样描述的

ES2015 引入了箭头函数,箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)。

无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。

在函数内部,this的值取决于函数被调用的方式。

可能直接看mdn文档有点抽象,下面看一下,另一种说法,看完之后,再去看mdn文档,突然就豁然开朗了, 我是这样的感觉的。

开始前先看一下Javascript中的函数调用

JS里面有三种函数调用形式

func(p1,p2)
obj.child.method(p1,p2)
func.call(context,p1,p2)

一般初学者都是用前两种,但是第三种才是正常的调用形式: 其他两种都是语法糖,可以等价地变为call形式

func(p1,p2)等价于
func.call(undefined,p1,p2)

obj.child.method(p1,p2)等价于
obj.child.method.call(obj.child,p1,p2)

记住了,this就是函数调用中的转成call的第一个参数

func.call(undefined,p1,p2)  //  this是undefined,浏览器会把undefined转换成window
obj.child.method.call(obj.child,p1,p2)   //  this是obj.child


日常开发中,涉及到的this的两种使用方法,其实就是上面说的:

隐式传递:

fn(1,2) // 等价于fn.call(undefined,1,2)
obj.child.fn(1) //等价于 obj.child.fn.call(obj.child,1)

显示传递

fn.call(undefined,1,2)
fn.apply(undefined,[1,2])

看一下几个常见的关于this的考题

例题一:

下面的函数调用中this分别是什么?

var a  = {
    name:'里面的name',
    sayName:function(){
        console.log("this.name = " + this.name)
    }
}
var name = "外面的name"
function sayName(){
        var sss = a.sayName
        sss();  //this.name=?
        a.sayName();  //this.name=?
        (a.sayName)();  //this.name=?
        (b = a.sayName)();  //this.name=?
}
sayName();

方法:把所有的函数调用改为call的写法

  • sayName() 就等价于sayName.call(undefined),这个调用的this就是window

  • sss() 等价于sss.call(),this就是window,即外面的name

  • a.sayName() 等价于a.sayName.call(a),this就是a,即里面的name

  • (a.sayName)() 等价于 a.sayName(),【解释:因为在js里面,加不加括号都一样】,即等价于a.sayName.call(a), this就是a,就是里面的name

  • (b = a.sayName)() 等价于第一种情况【解释:sss = a.sayName,sss()】这种,于是等价于b(),等价于b.call(),这里的this指向window,即外面的name,现在控制台看一下输出结果:

image.png 而下面这段代码是把上面那段代码用call改写后的样子,可以对比看看,以后尽量写调用都用call,这样显得逼格高一点:

var a  = {
    name:'里面的name',
    sayName:function(){
        console.log("this.name = " + this.name)
    }
}
var name = "外面的name"
function sayName(){
        var sss = a.sayName
        sss.call(undefined);  //this是undefined
        a.sayName.call(a);  //this是a
        (a.sayName).call(a);  //this是a
        (b = a.sayName).call(undefined);  //this是undefined
}
sayName();

例题二:

代码如下:

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

解题方法:把所有的函数调用改成call的写法

obj.method(fn,1) 等价于 obj.method.call(obj,fn,1),等价于obj.method.call(obj,arguments) //其中arguments=[fn,1]

fn() 等价于 fn.call(undefined) // this是指的是length=10 arguments[0] 等价于 arguments.0.call(arguments)// this指的是arguments,即this就是[fn,1],this.length = 2,[fn,1].length = 2

控制台输出结果如下所示:

image.png

总结:

this就是一个函数调用,call改写后的第一个参数

比如func() 改成后func.call(undefined)//undefined是占位符,占第一个位置,浏览器会自动转成window 这里是this就是window

另外,箭头函数没有this

因为this对于js而言确实不是太好的东西,箭头函数应运而生

console.log(this)  //打印出window
let fn = () => console.log(this) 
fn() //调用fn,打印出window

上面这段代码表示,箭头函数里面的this就是外面的this,就是外面是什么里面是什么 加上call也没有用 fn.call({name:'liulei'}) //打印出window,而不是 {name:'liulei'}

image.png

而如果fn是普通函数,则this会改变

image.png

总结

看了上面的所说的,再去看MDN文档里面关于this的描述,感觉清晰明朗了许多。

本文资料参考

mdn文档

方应杭:this 的值到底是什么?一次说清楚