《JavaScript》彻底理解this指向

《JavaScript》彻底理解this指向

简介

在学习编程的过程中,this这个关键字是无论如何都避免不了的一个坎,如果还没有使用到this,那么可以暂时忽略这篇文章中的内容,标个记号,等以后回过头来再来了解;

关键字this

(本文仅在非严格模式下进行讨论)

this是一个JS的保留关键字,这个单词在JS中有特殊的含义,其意义代表:当前执行代码的环境对象,并且有一个默认值,默认值指向的是window,如果有新指向那么,新指向的优先级将高于默认指向

既然是代表当前执行代码的环境,那么换个说法是不是可以理解成很多网上以及书籍上的说法,this的指向在定义的时候确定不了,只有在使用的时候才能确定;因为定义且尚未使用的时候,你无法确定当前执行代码是处于什么环境中;

数据结构

在了解this之前,再明确一点,对象,函数这些的数据类型都是****引用类型,新建的时候会在内存中开辟一个位置存储这些引用类型的值,之后,赋值给a,赋值给b这些操作,都只是将内存中的地址给赋值过去,因此,修改一个的时候才会出现所有引用这个地址的值同步发生了改变;

let obj = {a:1}	//在内存中生成了一个对象{a:1},并且将这个对象的地址赋值给了obj

let obj2 = obj;	//相当于obj做了个中介,让obj2这个变量指向的地址是内存中的对象{a:1}

理解及示例

普通函数中的this

function demo(){
    let name = "你好";
    console.log(this.name); //undefined
    console.log(this); //Window
}
demo();

小结:函数普通调用中的this,在没有外界干扰的情况下(也就是call,apply等),默认指向全局对象window

对象方法中的this

var obj = {
    name: '你好',
    func(){
        console.log(this.name)  //你好
        console.log(this)   //{name:'你好',func:f}
    }
}
obj.func()

小结:当函数作为对象里的方法被调用时,this默认指向的是调用该函数的对象,示例中,是obj这个对象调用了函数func(),因此指向了obj;

为了进一步确认,对象obj再多层嵌套对象

var obj = {
    name: '你好',
    func(){
        console.log(this.name)  //你好
        console.log(this)   //{name:'你好',func:f}
    },
    b:{
        name:'大家好',
        func(){
            console.log(this.name)  //大家好
            console.log(this)   //{name:'大家好',func:f}
        }
    }
}
obj.func()
obj.b.func()

按照上例的结论,this指向的是调用该函数的对象,因此,第一个是obj调用的func()函数,因此this指向的是obj,第二个是obj.b调用的func()函数,因此this指向的是obj.b,实验符合结论,因此小结成立;

小结

  • 函数普通调用中的this,在没有外界干扰的情况下(也就是call,apply等),默认指向全局对象window
  • 当函数作为对象里的方法被调用时,this默认指向的是调用该函数的对象

为了验证这个结论,在做一些更复杂的实验:

其他示例

经典案例-对象上的方法赋值给了变量

var obj = {
    name: '你好',
    func(){
        console.log(this.name)  //undefined
        console.log(this)   //window
    }
}
var obj2 = obj.func;
obj2()

将对象中的函数赋值给一个变量,然后运行,发现其this的值是window,而不是obj?

个人理解:其实是相同的,前文“数据结构”这一篇幅内说了,对象和函数是存在内存中的,变量只是引用了他们的地址,函数名obj.func引用的是存在内存中的函数地址,之后通过赋值操作,将obj.func引用的地址复制给了obj2,结果就是变成变量obj2直接和函数对应了起来,最终执行的时候其实和obj.func没关系,就是普通调用了函数obj2

这个和上面得到的结论不冲突,最终调用的时候就是一个普通调用的函数,这对象方法没有关系,那么this指向的就是window

经典案例-对象上的方法内包含普通函数

var obj = {
    name: '你好',
    func(){
        function demo(){
            console.log(this.name)  //你好
            console.log(this)   //{name:'你好',func:f}
        }
        demo()
    }
}
obj.func()

这里面的函数执行的时候也没有指向对象obj,这中该怎么理解?

个人理解是:还是看函数最终的调用方式,在函数demo调用的时候,demo是作为对象上的方法被调用的吗,显然不是,只是普通调用,那么其函数体内的作用域的this就是指向的全局对象window;

同理,如果函数内包含了一个自执行的函数,那么其内的this也会因为没有被谁调用,而采取默认指向window;

经典案例-匿名函数中的this

var p3 = {
    sayName(){
        return function() {
            console.log(this)	//window对象
        }
    }
}
p3.sayName()()

在《高程-第三版》中写道“匿名函数的执行环境具有全局性,因此其this对象通常指向window”,那么在正常情况下(没有什么骚操作改变指向),可以认为匿名函数中的this指向的就是window;

同理,如果是setTimeout()()之类的使用的时候,其匿名函数内this指向的也就是window

构造函数

根据上面的理解函数内部的this,普通调用的时候指向的是window,但是通过构造函数创建的普通调用的却指向的是当前函数,这是为什么?

先不急解答,分析一下

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        console.log(this)
    }
}
var p1 = new Person('oliver',18);
var p2 = Person('oliver',18);
console.log(p1)
console.log(p1.sayName())
console.log(p2)

p1和p2的区别在于p1有用到关键字new,按理来说函数Person并没有return一个值,那么打印的p1和p2都应该是undefined,但结果是

p1是一个对象,p2则是undefined,由此可见,关键字new在其中产生了某些操作,使得其有了一个返回值,并且返回值的类型是一个对象

//通过new关键字,创建了一个新对象,
Person:{
	name:'oliver',
  age:18,
  sayName(){
  	console.log(this)
  }
}

实际上,new执行的过程中,它会主动将其作用域内的this指向新创建的实例对象,因此,当执行到this.name,this.age等代码时相当于在执行Person.name,Person.age;

console.log(p1.sayName())

之后,新创建的实例对象上的方法sayName自然指向的就是调用sayName的Person实例对象

说到构造函数,不得不说一种特殊情况,就是上例中Person中并没有return,其return是由new提供的,那么,假如定义了一个return,会怎么样?

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        console.log(this)
    }
    return{ }
}
var p1 = new Person('oliver',18);
console.log(p1)	//{}

如果手动设置了返回对象,那么由new创建的默认对象就会被丢弃;

DOM中的事件处理函数

当函数被作为事件处理时,this指向的时触发事件的元素

// 被调用时,将关联的元素变成蓝色
function bluify(e){
  console.log(this === e.currentTarget); // 总是 true

  // 当 currentTarget 和 target 是同一个对象时为 true
  console.log(this === e.target);        
  this.style.backgroundColor = '#A5D9F3';
}

// 获取文档中的所有元素的列表
var elements = document.getElementsByTagName('*');

// 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
for(var i=0 ; i<elements.length ; i++){
  elements[i].addEventListener('click', bluify, false);
}

猜你喜欢

转载自blog.csdn.net/zy21131437/article/details/106241641
今日推荐