前端面试题之this指向

79 阅读6分钟

this指向

this 是 函数中 天生就有的 一个属性 是 js 程序 自动设定生成的

所谓的this指向

    this本质上是 指针操作
    this的指针指向谁 this就是谁
    就可以通过 this 操作谁 

this指向分类

普通函数

除了箭头函数之外 其他所有的函数语法形式 都开始 普通函数语法形式
按照 js程序规定的 设定 函数的this指向

JavaScript中this指向的规定非常复杂

实际项目中 函数的this指向 是一个非常复杂的结果 实在不行 就 console.log( this ) 输出看看

情况1

this指向是 window对象 
实际操作通用一般没啥用

声明式函数 赋值式函数 数组的forEach 定时器 延时器....

情况2

this指向是 事件源标签对象
在 事件处理函数中 this指向 是 事件源标签对象
也就是 绑定事件的标签对象

情况3

this指向是 数组/对象 
存储在数组或者对象中的函数 
this指向 是存储这个函数的 数组/对象
    // 情况1 this指向是 window对象 

    // 声明式函数 
    function fun1 (){ 
        // this是函数天生就有的属性
        // 在函数内部可以直接输出查看函数当前的this指向
        console.log( this );
    }
    fun1();

    // 匿名函数 
    const fun2 = function(){
        console.log( this );
    }
    fun2();

    const arr = [100,200,300,400,500,600];
    // 使用匿名函数的形式 给 forEach的回调函数赋值
    arr.forEach( function( item ){ console.log( item , this ) } );

    // 定时器 延时器
    setInterval( function(){ console.log( this ) } , 1000 );

    setTimeout( function(){ console.log( this ) } , 1000 );

    
    // 存储在 数组中的函数
    const arr = [100,200,300,function(){ console.log( this ) }];
    
    // 调用执行存储在数组中的函数
    arr[3]();

    console.log( arr );

    // 存储在 对象中的函数
    const obj = {
        name:'张三',
        age:18,
        sex:'男',
        ff:function(){ console.log( this ) }
    };

    // 调用存储在对象中的函数
    obj.ff() ;
    console.log( obj );

箭头函数

箭头函数的this指向
箭头函数 本身 没有this指向
箭头函数的this指向 是 父级程序的this指向
如果箭头函数没有父级程序 或者 父级程序没有this指向 
箭头函数的this指向 是 window
//情况1 事件处理函数的this指向

    // 获取div标签对象 绑定点击事件
    const oDiv = document.querySelector('div');

    // 使用 匿名函数 给 div标签绑定点击事件 
    // 事件处理函数 是 匿名函数 this指向是 事件源标签对象 
    // 也就是div标签对象
    oDiv.addEventListener( 'click' , function(){ console.log( this ) });

    // 使用 箭头函数 给 div标签绑定点击事件
    // 事件处理函数 是 箭头函数 this指向是 window
    // 箭头函数的this指向是父级程序
    // 当前 事件绑定语法 没有 父级程序 
    // 箭头函数this指向是 window
    oDiv.addEventListener( 'click' , () => { console.log( this ) });
    
    
    
// 存储在对象中的箭头函数


const obj = {
    name:'张三',
    age:18,
    sex:'男',


// 在对象中存储匿名函数 

    // 匿名函数的this指向是 存储这个函数的对象obj
    f1:function(){ 
        console.log( this ); 

        // 在函数f1中声明一个 赋值式函数
        // 赋值式函数 当前使用的是 匿名函数语法形式
        // this指向是window
        const f3 = function(){ console.log( this ) };
        f3();

// 在函数f1中声明一个 赋值式函数

        // 赋值式函数 当前使用的是 箭头函数语法形式
        // this指向 是 父级程序的this指向
        // 父级程序是 函数f1 
        // 函数f1 有 this指向 指向存储函数f1的对象obj
        // 箭头函数f4 this指向就是 父级程序函数f1的this指向 
        // 就是 对象obj 
        const f4 = ()=>{ console.log( this ) };
        f4();


    },

// 在对象中存储箭头函数


    // 箭头函数的this指向是 父级程序
    // 当前箭头函数的父级程序是 对象obj 
    // 对象obj 没有 this指向 
    // 箭头函数f2 的 this指向 是 window
    f2:()=>{ console.log( this ) },



};

obj.f1();
obj.f2();

改变 this 指向

箭头函数

箭头函数 不能改变this指向 
箭头函数本身没有this指向 箭头函数的this指向是父级程序的this指向

普通函数

函数执行时

    在 执行函数时 改变this指向

    函数.call( 参数1 , 其他参数 );
        参数1   新的this指向
        其他参数 原始函数执行时需要输入的实参
            如果原始函数需要输入多个实参 
            按照顺序一个一个的设定实参

    函数.apply( 参数1 , 其他参数 );
        参数1   新的this指向
        其他参数 原始函数执行时需要输入的实参
            以 数组 形式 设定原始函数需要的实参
            如果原始函数需要输入多个实参 
            按照顺序一个一个的设定数组中存储的数据 作为 原始函数的实参

    call()语法 和 apply()语法
        只是 原始函数实参设定方式不同 
        执行效果 执行原理 都是完全相同
    const obj1 = {
        name:'张三' ,
        age:18,
        sex:'男',
        addr:'北京',
        // 匿名函数 this指向是 存储这个函数的对象obj
        ff:function( a , b , c ){
            // 匿名函数中 this指向 是 存储这个函数的对象obj
            // this.name 也就是 obj.name 也就是 数据张三
            console.log( this.name ); 
            console.log( this.age ); 
            console.log( this.sex ); 
            console.log( this.addr ); 

            console.log( a ); 
            console.log( b ); 
            console.log( c ); 


        }
    }


    // 正常调用 对象obj1 中 存储的函数ff
    // 因为 函数ff 的 this 指向是 存储函数ff的对象obj1
    // 调用的 this.name this.age 等数据 都是 obj1 中 对应的数据
    obj1.ff( 100 , 200 , 300 );

    // 设定的 对象obj2
    const obj2 = { 
        name:'李四' ,
        age:20,
        sex:'女',
        addr:'广州',
    };


    // 通过 call 语法 在调用对象obj1中存储的函数ff时
    // 强行 改变 对象obj1中存储的函数ff的this指向
    // 让 函数ff 的this指向变成 对象obj2
    // 函数ff中的程序 this.name this.age 等 调用的就是 obj2中 对应的数据
    obj1.ff.call( obj2 , 'aaa' , 'bbb' , 'ccc' );


     // 设定的 对象obj3
     const obj3 = { 
        name:'王五' ,
        age:22,
        sex:'保密',
        addr:'上海',
    };

    obj1.ff.apply( obj3 , [ 'ddd' , 'eee' , 'fff'] );

函数封装时

函数封装时 改变this指向
  • 使用bind语法声明封装一个新的函数
  • 新函数和原始函数完全相同 只是 this指向 不同
        function fun( a,b,c ){
            console.log( this );
            console.log( a,b,c );
        }
        fun( 100,200,300 );

        const obj = { name:'张三' , age:18 , sex:'男' };

        // 声明一个新函数
        // 使用bind语法 声明一个新的函数
        // 新的函数 代码程序内容 和 原始函数fun完全相同
        // 只是 this指向 不同
        // 声明一个新函数f1 新函数f1的程序代码和原始函数fun完全相同
        // 只是this指向变成了 设定的对象obj

        // 在声明封装新函数时 没有绑定原始函数的实参
        const f1 = fun.bind( obj );
        // 调用函数时 可以输入实参
        f1( 'aaa' , 'bbb' , 'ccc' );


        // 在声明封装函数时 绑定了原始函数的实参
        const f2 = fun.bind( obj , '北京' , '上海' , '广州' );
        // 直接调用函数 不用在输入实参了
        f2();
        // 绑定了实参 没办法再改变实参了
        f2(300,400,500);