举荐浏览地址

掘金

前言:大家好,我是林一一,这是一篇对于 this 指向的文章。this 是 JS 中一个特地重要的一个知识点,this 难吗?如同挺简略的。看完上面这一篇文章,还不会的话,你别来找我。手动狗头。

思维导图

this 的指向

  • 执行函数前有 '.' 点操作符的话,函数体中的 this 就指向后面的对象,没有就指向 window,严格模式下指向 undefined。这句话特地的重要,请记住
  • 函数没有间接调用者 this 指向全局对象(浏览器中是window,node中是 global)。如匿名函数等
  • 构造函数的 this 指向实例自身。
  • 箭头函数自身没有this的,箭头函数的 this 指向最近的非箭头函数 this,找不到就指向 window,严格模式下指向 undefined
再来看一下这句话:执行函数前有 '.' 点操作符的话,函数体中的 this 就指向后面的对象,没有就指向 window

一、一般函数 this 的热身题

热身题 1

var name = '林一一'function fn(){    var name = '林二二'    return this.name}fn()    // 林一一
执行函数 fn(),后面没有 '.' 点操作符吧,那么这里的 this 就指向 window。输入的就是全局下的 name = '林一一'

再来看一下这句话:执行函数前有 '.' 点操作符的话,函数体中的 this 就指向后面的对象,没有就指向 window

热身题 2

var name = '林二二'var obj = {    name: '林一一',    fn: function () {        return this.name    }}console.log(obj.fn())   // '林一一'var fo = obj.fnconsole.log(fo())       // '林二二'     fo() ==> window.fo()
obj.fn() 中函数 fn() 后面有 '.' 点操作符吧,那么这里的 this 就指向 obj 这个对象。再看执行函数 fo(),后面没有 '.' 点操作符吧,那么这里的 this 就指向 window。其实下面的函数 fo() ==> window.fo(),所以执行函数 fo() 后面也是能够看作是有 '.' 操作符的。

再来看一下这句话:执行函数前有 '.' 点操作符的话,函数体中的 this 就指向后面的对象,没有就指向 window

热身题 3,批改一下热身题 2

var name = '林二二'var obj = {    name: '林一一',    fn: function () {        var name = '小三'        return function(){             return this.name        }    }}console.log(obj.fn()())   // 林二二var fo = obj.fn()console.log(fo())    // 林二二
热身3和热身2差不多,obj.fn()()obj.fn()执行完后有一个函数(这里称为函数 A)返回,最初相当于执行函数 A()A() 后面没有 '.' 点操作符吧,那么这里的 this 就指向 window,输入就是 林二二 了。下面的 fo() 函数同理。

二、函数没有间接调用者

函数没有间接调用者 this 指向全局对象(浏览器中是window,node中是 global)。如匿名函数等

热身题 1

var name = '林一一';!(function(){   console.log(this.name)   // 林一一})()
自执行函数没有间接的调用者输入的 name = '林一一'

热身题 2

var name = '林一一'var obj = {    name : '二二',    callback: function(){        console.log(this.name)    }}setTimeout(obj.callback,1000)/* 输入*   林一一*/
函数 setTimeoutobj.callback(这只是一个援用地址) 中并没有间接调用者,this 就指向 window。所以输入的 name 就是全局下的 林一一

三、构造函数中的 this

来读一下这句话:构造函数的 this 指向实例自身

对于构造函数的 this 为什么指向实例是浏览器指定的,详情看 new 这个过程产生了什么 面试 | 你不得不懂得 JS 原型和原型链

热身题 1

function Fn(){    var n = 0    this.name = '林一一'    this.age = 18    this.getName = function(){        return this.name    }}Fn.prototype.getAge = function(){    return this.age}Fn.x = '林二二'var f = new Fn()console.log(f.name)     // 林一一console.log(f.getName())     // 林一一console.log(f.getAge())        // 18console.log(f.n)    // undefinedconsole.log(f.x)    // undefined
下面的 Fn 通过 new后就是一个构造函数,this 就指向实例 f。所以下面的1,2输入都是林一一f.getAge() 是实例 f 调用了getAge 输入就是 18,问:实例 f 中并没有属性 getAge 是怎么输入 18的,f.x 输入又为什么是 undefined ?答:这是原型链的查找机制,属性 x 不是在原型 prototype 上的就不是实例的属性,能够读一下这篇文章 面试 | 你不得不懂得 JS 原型和原型链;问:为什么f.n 输入的是 undefined。因为变量 n 是构造函数的公有变量和 new 创立的实例没有关系。

四、箭头函数

  • 箭头函数自身没有 this,箭头函数的this继承上下文的,外面的 this会指向以后最近的非箭头函数的 this,找不到就是 window (严格模式是undefined)
  • 箭头函数的 this 始终指向函数定义时的 this,而非执行时

热身题 1

var name = '林一一'var obj = {    name: '二二',    a: () => {        console.log(this.name)    }}obj.a()/* 输入*   '林一一'*/
箭头函数的 this,找不到非箭头函数的 this 就间接指向 window

热身题 2

var name = '林一一'var obj = {    name: '二二',    fn: function() {        return () => {            console.log(this.name)        }    }}obj.fn()()/* 输入*   '二二'*/
很显著箭头函数的 this 来自函数 fn,对象 obj 调用了函数 fn,所以 fnthis 指向 obj,输入后果就是 二二

五、call,apply,bind 扭转 this 的指向

提醒:所有的函数都是基于 Function 这个基类来创立的,同样领有 Function 原型下面的办法

  • call,承受this的对象,和一组列表。applycall 一样,惟一不同的是 apply 承受的是一个蕴含多个参数的数组。bind 同样也是扭转函数的 this 指向,只不过 bind 执行后会返回一个新的函数,新函数中参数来源于残余的参数

热身题

var name = '林一一'var age = 18function fn(){   return this.name}function p(){    return {        age: this.age,        arg: arguments    }}let obj = {    name: '二二',    age: 18}let o = {    name: '三三'}fn()    // '林一一'fn.call(obj)    // '二二'fn.call(o)  //  '三三'p.call(obj, 12, 23, 45, 67) // {age: 18, arg: Arguments(4)}fn.apply(obj)    // "二二"p.apply(obj, [1, 2, 3, 4, 5])    // {age: 18, arg: Arguments(5)}fn.bind(obj)()  // "二二"p.bind(obj, 12, 23, 34)()   // {age: 18, arg: Arguments(3)}
以上就是 call, apply, bind, 对于 this 的内容,这里不介绍三者的写法,如果介绍能够写另一篇文章了。对这三者不相熟的能够找其余材料看看。

思考题

1. 口试题 this 指向问题

var name = '林一一'var obj = {    name: '林二二',    show: function (){        console.log(this.name)    },    wait: function () {        var fn = this.show        fn()    }}obj.wait()  //  林一一
obj.wait() 中,执行函数 wait() 后面有 '.' 点操作符吧,那么这里的 this 就指向 obj 这个对象,所以this.show ==> obj.show。再看执行函数 fn() 后面没有 '.' 点操作符吧,那么这里的 this 就指向 window,输入就是 林一一

2. 和闭包无关的 this 指向问题

var n = 2 // -> 4 -> 8var obj = {    n: 3,    // 6    fn: (function(n){        n*=2        this.n+=2    // window 下的 n 变成 4        var n = 5    // 这一步不会再从新申明,因为曾经参数赋值,就不会再申明而是间接赋值 n = 5        console.log("window.n", window.n)        return function (m) {            console.log("n:", n, "m", m)    // n:5  m:3  这里的 n 向上查找是 5   //             this.n*=2    // fn(3): 2 * 4 =8  //  obj.fn(3): 2 * 3 = 6             console.log(m + (++n))    // 3 + (++5) ++n 导致下级作用域的n变成了6    // 3 + (++6)        }     })(n)}var fn = obj.fn;fn(3)    // 9obj.fn(3)    // 10console.log(n, obj.n)    // 8 6/* 输入* 9* 10* 8  6/
这道题就留给大家思考了,下面有我的剖析步骤,感觉碍眼的话能够去掉 。你能够在评论区给出你的剖析过程。

完结

感激浏览早这里,我是林一一,如果这文章能对你有一点启发的话,欢送给个 star, 下次见。