this解析(学习总结)全面

349 阅读9分钟

爱这故乡的热土,初心就是踏上家乡黑土的那一刻,回家了--致敬最可爱的人

本次文章借鉴部分引用《你不知道的JavaScript(上卷)》声明本人已够买纸质书籍。(尊重知识)

this真的懂吗??看着this是不是感觉既熟悉又陌生,这篇文章能够在帮助我学习也能带领你学习一些(记得千万不要相信几天速成,一篇文章就直接可以学会,用心去感受,带着学习的炽热之心,勇往直前)

我们为什么会说javaScript中最复杂的机制之一那??我个人认为就是它的指向是多重变换,与孙悟空的72变一样,你不知道哪个是孙悟空变得,所以还的练就一双火眼。

从本质看this就是能够让你的代码变得优雅,能够快速获取你想要的上下文。

this到底是什么??

this的绑定和函数的声明位置没有任何关系,只取决于函数的调用方式。(P80)

在了解this的绑定过程之前,先要了解一下调用位置,我们要多加分析调用栈、调用位置;

        //函数的定义位置
        function wenshu(){
        //调用栈个人理解就是调用函数的顺序链接,就是有个先来后到。
            //调用栈wenshu
            //调用位置
            light()
            console.log('76wenhsu')
        }
        function light(){
            //调用栈 wenshu -> light
            console.log('心里有火眼里有光:‘武')
        }
        //调用位置就是函数的执行位置
        //调用位置
        wenshu()

默认绑定

函数调用类型“独立函数调用。 可以看成是无法应用其他规则的默认规则。

这是在this中最简单的一种绑定方式,上代码:

        //const  a = '76wenshu';
        var  a = '76wenshu';
        function wenshu(){
            debugger
            //调用栈wenshu
            var a = '心里有火,眼里有光'
            //调用位置
            light() // => this.light()
            console.log(this)
            //因为你打断点就会发现const和let声明的变量不在window中(有待进一步了解)
            console.log(this.a)//当a是用const声明是就是undefined
            //var声明时就是‘76wenshu’
            debugger
        }
        function light(){
            //调用栈 wenshu -> light
            console.log(this.a)//与上方相同为什么不会调用wenshu函数的a,请看light函数的引用
        }
        //调用位置
        wenshu()

全局对象window下找不到const、let声明的变量???

隐式绑定

        function light(){
           
           console.log(this.a)
       }
       var obj ={
           a:2,
           light:light
       }
       light();//undefined
       obj.light()//2

个人认为:当你把函数赋给变量时,就相当于吧函数体赋给这个变量,让它能够使用

多层的调用:

        function light(){
          
           console.log(this.a)
       }
       //对象声明的先后顺序是有区别的,防止变量提升的问题出现。
       var obj1 ={
           a:76,
           light:light
       }
       var obj ={
           a:2,
           obj1:obj1,
           light:light
       }
      
       console.log(obj);
       obj.light();//2
       obj.obj1.light();//76

个人认为:近水楼台先得月(就近原则)

  1. 隐式丢失
        function light(){

           console.log(this.a)
       }
       
       var obj ={
           a:2,
           light:light
       }
      var a = '武统';
      var bar = obj.light
      bar();//武统

当你调用bar的时候是不是有点不一样的感觉为什么会出现这样的情况,怎么直接引用全局的变量a了那; 我认为函数是对象,只能进行引用,他们就是一个重新复制的过程

    var bar = obj.light
    =>
    var bar = function(){
        console.log(this.a)
   }

更具有迷惑性的就是函数中用函数作为参数进行使用:

        function light(){
           
           console.log(this.a)
       }
       var obj ={
           a:2,
           // obj1:obj1,
           light:light
       }
       function bar(fn){
           console.log(fn);
           fn()
       }
      var a = '武统';
       bar(obj.light);//武统
       //如果函数是箭头函数,就有区分别之后会有讲解
       setTimeout(obj.light, 100);//武统

总结:隐式丢失的原因,函数调用符合使用规则,相当于用函数,将原函数替代。

显式绑定

显示绑定不得不说call/apply/bind三个函数

        //call如何实现的
       function call(content=window,...arguments){
           //const content = content || window;
           arguments=arguments?arguments:[];
           //个人觉的没有必要像下边注释这样写
           //const fn = Symbol()
           content.fn=this;
           const result =content.fn(...arguments)
           delete content.fn
           return result
       }

在这里我说一下:

有很多人都是告诉你写call的源码,但是你真的记得住吗??

你理解了吗??多写写记得不要复制也不抄写代码,切记切记,不要死记硬背了解他,理解他,才能够看出为什么要写call函数。(切记不要假学习,自己感动自己,那样没有任何意义)

每个人都有一套自己的学习方法,及早的找到他领悟它,才能少走学习的弯路。我就是自学的前端走了很多弯路。所以我要极力构建前端的自学网络。

废话不多说,下面说一下它思路:

问:先抛出问题call 要解决的问题是什么??它是干什么的??

答:个人理解call就是用来改变函数中this指向,能够让你获取this指向你想要的对象。

记住这句话之后,就是call返回的 return obj(你想指向的那个对象).fn(你想使用的那个函数)(arguments)(你想传进来的参数)。

知道你想要的结果了那就看怎么去实现它那,那就是call中传进来两参数(content(你想指向的那个对象),...arguments(你想传进来的参数)),一点都不绕你的去了解它,

传进来两个参数之后,那你符合把你想使用的函数fn绑定到你想用的对象上那,obj.fn的方式就很好,那么我们就在函数的

const content = content 
//func.call()
//这里的this指向的就是func
content.fn =this;
return content.fn();

函数体就写完了那么我们还的去完善它,因为他太简陋了,你考虑到现有的绑定情况; 突然感觉忘了传什么哈哈,忘了传参数了。。。

const content = content
//传参不能忘
cosnt argus = arguments
//func.call()
//这里的this指向的就是func
content.fn =this;
return content.fn(argus);

整体的思路如上 apply和这样一样就是,传参不一样而已。

call,apply的我觉得完全一样,如果非想说出他们的区别的话那就是参数的不同.参数数量少,参数的顺序是固定的就用call, 与其相反的就用apply。 如果参数是数组就直接只用apply

讲完了call和apply那就说一下bind,他们之间有什么区别吗? 总结一句就是call和apply返回调用函数的结果而bind是返回的函数,如果你需要,获取结果还还需要重新调用,

var a= fn.call(obj,...args)//直接获取到你想要的值

var b = fn.bind(obj,..args);//返回的不是结果是函数
b()//返回结果;

//根据这个区别我们就可已根据原有call和apply函数进行改造了

        function bind(content=window,...arges){
            const fn = this
            arges=arges?arges:[];
            return function newFn(...newArges){
            //为什么会有这样的判断后边会有讲,mark一下
                if(fn instanceof newFn){
                    return new fn(...arges,...newArges)
                }
                return fn.apply(content,[...arges,...newArges])

            }
        }

先给大家讲解一下call/apply/bind,下面在讲解显示绑定的例子:

        function light(){
            console.log(this.a)
        }
      
        var obj ={
            a:2,
        }
        //强制吧light函数绑定到obj对象上
        light.call(obj)//2

//显式绑定call/apply

        function light(){
            console.log(this.a)
        }
       
        var obj ={
            a:2,
            light:light
        }
        var bar =obj.ligh.call(obj)//直接返回值所以说显示绑定解决不了隐式丢失的问题
        bar()//报错

//硬绑定bind(显式的强制绑定)柯里化

        function light(){
            //调用栈 wenshu -> light
            console.log(this.a)
        }
        var obj ={
            a:2,
            light:light
        }
        //var bar =function(){
            //light.call(obj)
        //}
        //硬绑定之后不能在修改他的this
        var bind = obj.light.bind(obj);
        bar()

API调用上下文例如forEach等,第二个参数数就是函数内的this指向。

new绑定

function light(a){
    this.a = a
}
var bar = new light(2)
console.log(bae.a)//2

//会构造一个新对象并把它绑定到light();调用的this上,成为new 绑定

优先级

在调用位置可以使用多条规则的时候,总会有先后,权重高和低之分。

默认绑定绝对是这里权重最低的我们先看,隐式绑定和显式绑定, 上代码:

        //隐式绑定和显示绑定的优先级
        function dq(){
            console.log(this.a)
        }
        var obj={
            a:'76文书',
            dq:dq
        }
        var obj1={
            a:'心里有火,眼里有光',
            dq:dq
        }
        obj.dq();
        obj1.dq();
        
        obj.dq.call(obj1);
        obj1.dq.call(obj);

从上述代码来判断 显式绑定>隐式绑定

隐式绑定和new绑定谁的会更高一点那??

        function dq(params){
            this.a = params;
            // console.log(this.a)
        }
        var obj={
            a:'76文书',
            dq:dq
        }
        obj.dq(3);
        console.log(obj.a);

        var bar = new obj.dq('心里有火,眼里有光')
        console.log(obj.a)
        //new 绑定 和隐式绑定的对象不产生任何关系
        console.log(bar.a)

从上述代码来判断

new绑定>隐式绑定

记得new绑定>显示绑定 具体后期会讲

判断this

就可以根据new>显式>隐式>默认的优先级去可以判断this

bug无穷无尽的bug this被忽略。

        function dq(){
            console.log(this.a)
        }
        var obj={
            a:'76文书',
            dq:dq
        }
        var a = 2;
        var ø = Object.create(null);
        //使用null可能带来更多无穷无尽的bug,程序的稳定运行才是硬道理
        dq.call(null);//默认将会忽略this
        dq.call(obj);
        //ø比null更空
        dq.call(ø);//不会忽略this,只不过是指向的更空了

间接引用

我觉的这种方式使用的函数我还没有见到过,我觉得只是存在理论上;

        function dq(){
            console.log(this.a)
        }
        var a = 2;
        var o = {a:3, foo:foo};
        var p = {a:4};
        o.dq();//3
        //我觉的一个正常人谁会这么写。。。。,考验自己还是考验后来接手的同学,易读,易用才是好的,不是别人技术菜不菜的问题,是你写的代码干净利落,能读懂,写的代码除了自己别人看不懂那有什么实际的意义那。
        (p.dq = o.dq)()//2

软绑定

就是为了能后让绑定的更加灵活,有够指向其他对象: 书上的软绑定的代码

if(!Function.prototype.softBind){
    Function.prototype.softBind = function(obj){
        const fn = this;
        var curried = [].slice.call(arguments,1);
        var bound = function(){
            return fn.apply({
                (!this || this ===(window || global))?obj:this,
                curried.concat.apply(curried,arguments)
            })
        };
        bound.prototype = Object.create(fn.prototype);
        return bound;
        
    }
}
```javaScript
function foo(){
    console.log(`name:${this.name}`)
}
var obj = {name:'obj'},obj2={name:'obj2'},obj3={name:'obj3'};

var fooOBJ = foo.softBind(obj);
fooOBJ(); //name:obj
obj2.foo = foo.softBind(obj);
obj2.foo() //name:obj2

fooOBJ.call(obj3); //name:obj3

//有疑问可以看看隐式绑定
setTimeOut(obj2.foo, 10) //name:obj

***箭头函数 ***

先给出我的总结:

不管在什么情况下,箭头函数的this跟外层的function的this是一致的,外城function的this指向谁,如果外层的不是function则窒息那个window。

        function foo(){
            setTimeout(()=>{
                //这里的this在词法上继承自foo()
                console.log(this.a)
            },100);
        }
        var obj = {
            a:2
        }
        foo.call(obj); //2

self = this;

        function foo(){
            var self = this;
            setTimeout(function(){
                console.log(self.a);
            },100)
        }
        var obj = {
            a:2
        }
        foo.call(obj); //2

惊讶的问题:

箭头函数中的this是undefined,debugger看到的,不影响正常使用打印consoel.log(this);

出现原因就是chrome调试器优化造成的,如果未在函数内部引用局部变量(就是相当于块级作用域)这里是this,这个变量就不会存储在此函数上下文对象中。

总结的结果就是箭头函数内部的this,存在的事件与函数的封闭局部变量相同,都是未显示引用输出就是undefined

{
console.log(a)
var a=2
}

this的解析基本上写完了,在这里致谢:

《你不知道的JavaScript(上卷)【美】KYLE SIMPOSON 著 赵望野/梁杰 译》