This的常见场景

78 阅读4分钟

1.this永远指向一个对象。

2.正常函数中的this取决于在调用路径。

对第二点举例来说:

// window
var a = 0
var obj = {
    a : 10,
    test:{
        a : 20,
        con : function(){
            console.log(this.a)
            }
    }
}
obj.test.con() //20

obj.addtest = obj.test.con
obj.addtest() //10

var wintest = obj.test.con
wintest()//0

this指向完全区别于函数的静态作用域特性,跟函数定义位置完全无关,调用函数的this指向调用路径的最后一个对象。

obj.test.con() 调用路径obj->test->con() 所以this指向test;

obj.addtest() 指向obj;

wintest() 指向window;

this常见的五种情况——对象中的方法,事件绑定 ,构造函数 ,定时器,函数对象的call()、apply() 方法;

事件绑定:

  1. 直接通过 html 事件属性来绑定 js 执行代码

<button onclick="console.log(this)">click</button>
// 这里的 this 是button元素

--------

<button onclick="say()">click</button>

function say() {
    console.log(this)
}
// 这里的 this 是window
// html 事件绑定的函数在执行时,有权访问全局作用域中的任何代码。也即我们可以在 html 中可以直接调用 script 标签中定义的函数。

--------

<button onclick="obj.say()">click</button>

let obj = {
        say: function() {
                console.log(this)
        }
}
// 这里的 this 是obj
  1. DOM元素事件绑定

给 DOM 元素绑定事件相当于是从 attribute 改成了 property

  • property是DOM中的属性,是JavaScript里的对象;
  • attribute是HTML标签上的特性,它的值只能够是字符串;
<button id="btn">click</button>

let oBtn = document.getElementById('btn');
oBtn.onclick = say; // 这里的 this 是button元素
function say() {
    console.log(this)
}

--------

let obj = {
        say: function() {
                console.log(this)
        }
}
oBtn.onclick = obj.say;
// 这里的 this 也是button元素

oBtn.onclick = null
// 删除事件处理程序
// html 上绑定的事件也可以通过这种方式来解绑
  1. addEventListener 绑定函数

addEventListener 参数中回调函数的this指向目标元素。

解决方法为:

oBtn.addEventListener('click', obj.say.bind(obj), false);

<button id="btn">click</button>

let oBtn = document.getElementById('btn');
oBtn.addEventListener('click', function(){
        console.log(this) // 这里的 this 是button元素
}, false)

--------

oBtn.addEventListener('click', say, false)
function say() {
    console.log(this) // 这里的 this 也是button元素
}

--------

let obj = {
        say: function() {
                console.log(this)
        }
}
oBtn.addEventListener('click', obj.say, false)
// 这里的 this 也是button元素

总结:

直接通过 html 事件属性来绑定 js 执行代码,取决于函数位置。

通过DOM对象绑定的this指向DOM对象

构造函数

构造函数其实和普通函数本质上并无区别,唯一的区别有两个:

  1. 函数首字母大写,这个区别只是约定俗成的,便于区分。你实在要小写定义构造函数也完全没问题,所以这个区别可以忽略。
  2. 构造函数的调用需要用new操作符,而普通函数的调用又分很多种,但是都不会用到new操作符。所以,构造函数和普通函数的区别就在这个new操作符里,现在让我们来好好研究一下这个new操作符。

用new操作符创建对象时发生的事情:

  • 第一步: 创建一个Object对象实例。
  • 第二步: 将构造函数的执行对象赋给新生成的这个实例。
  • 第三步: 执行构造函数中的代码.。
  • 第四步: 返回新生成的对象实例。
function Pro(){
    this.str = '1';
    this.fun = function(){
        console.log(this.str)
    };
}
var p = new Pro();
p.fun() //'1'

定时器中的this

var obj = {
    fun:function(){this ;}
}

setInterval(obj.fun,1000);      // this指向window对象 setInterval('obj.fun()',1000);    // this指向obj对象

setInterval(obj.fun,1000) 的第一个参数是obj对象的fun ,因为 JS 中函数可以被当做值来做引用传递,实际就是将这个函数的地址当做参数传递给了 setInterval 方法,换句话说就是 setInterval 的第一参数接受了一个函数,那么此时1000毫秒后,函数的运行就已经是在window对象下了,也就是函数的调用者已经变成了window对象,所以其中的this则指向的全局window对象;

而在 setInterval('obj.fun()',1000) 中的第一个参数,实际则是传入的一段可执行的 JS 代码;1000毫秒后当 JS 引擎来执行这段代码时,则是通过 obj 对象来找到 fun 函数并调用执行,那么函数的运行环境依然在 对象 obj 内,所以函数内部的this也就指向了 obj 对象;

函数对象的call()、apply() 方法

call和apply的作用一致,区别仅仅在函数实参参数传递的方式上;

手写部分call()

      let Person = {
            name: 'Tom',
            say() {
                console.log(this)
                console.log(`我叫${this.name}`)
            }
        }

        // 先看代码执行效果
        Person.say() //我叫Tom 
        Person1 = {
            name: 'Tom1'
        }

        // 我们尝试用原生方法call来实现this指向Person1
        Person.say.call(Person1) //我叫Tom1
        
        
        
        Function.prototype.MyCall = function(context) {
            //context就是demo中的Person1
            // 必须此时调用MyCall的函数是say方法,那么我们只需要在context上扩展一个say方法指向调用MyCall的say方法这样this
            console.log(this)
            context.say = this //Mycall里边的this就是我们虚拟的say方法
            context.say()
        }
        // 测试
        
        Person.say.MyCall(Person1)//我叫Tom1

prototype添加方法中的this指向该函数实例(因为函数也是一个对象,但需要注意函数的this并不指向它自己)

context.say = this将调用Mycall的函数加入到传入的参数对象中;

context.say()等于从Person1这个对象中调用say()方法;

所以需要添加delete context.say将方法从Person1中删除;