JavaScript系列之this指向

本文详细探讨JavaScript中的this指向问题,包括普通函数中的new绑定、显式绑定、隐式绑定和默认绑定的优先级及规则。特别指出new绑定具有最高优先级,因为它在函数调用之前改变this。此外,还讨论了箭头函数的不同之处,箭头函数的this来自作用域链,并不随函数调用方式变化。最后,文章提到了在DOM事件处理函数中,this始终指向触发事件的元素。
摘要由CSDN通过智能技术生成

this指向

对于普通函数

  1. new绑定:this指向构造函数调用中的this上,即this指向新创建的对象。
  2. 显式绑定:使用bindcallapply,this指向函数中的第一个参数。如果第一个参数不是对象,会先用ToObject操作将其转换为对象(装箱)。当第一个参数为null时,使用默认绑定。
  3. 隐式绑定:谁调用指向谁。
  4. 默认绑定:函数体处于严格模式this被绑定到undefined,否则,this被绑定到window。

以上规则,从上往下优先级依次降低。

new绑定的优先级为什么那么高?

运算符的优先级中可以看到:

在这里插入图片描述

new的优先级小于成员访问,函数先调用call或者其他成员函数,然后再被new。而new时又改变了函数的this

使用new调用构造函数会经历如下过程:

  1. 创建一个对象
  2. 将构造函数的作用域赋给新对象
  3. 执行构造函数中的代码
  4. 返回新对象

关于new的实现机制,可以参考下面的代码:

function myNew() {
    let obj = new Object(); // 寄生对象
    let Constructor = Array.prototype.shift.call(arguments);    // 第一项参数,即构造函数
    obj.__proto__ = Constructor.prototype;  // 核心代码,确定实例与构造函数的关系,这样obj就可以访问到构造函数原型中的属性
    let res = Constructor.apply(obj, arguments);    // 执行构造函数中的内容,返回res。注意在这个地方将this绑定到了obj上
    return typeof res === 'object' ? res : obj; // 如果返回结果不是对象,就返回一个空对象
}

对于箭头函数

  • this来自作用域链,由于作用域链在函数进入上下文时就已经确定,因此this与函数执行时无关。
  • 在箭头函数中,通过 call()apply() 方法调用一个函数时,只能传递参数,他们的第一个参数会被忽略。

另外要注意的是:箭头函数不能用作构造器,和 new 一起用会抛出错误。

作为一个DOM事件处理函数

  • this指向触发事件的元素,也就是始事件处理程序所绑定到的DOM节点。

题目分析

结合几个题目具体分析一下:

  1. 含new的情况
function Foo() {
    getName = function () { console.log(1); };
    return this;
}
Foo.getName = function () { console.log(2);};
Foo.prototype.getName = function () { console.log(3);};
var getName = function () { console.log(4);};
function getName() { console.log(5);}

//请写出以下输出结果:
Foo.getName();  // 2
getName();  // 4 函数声明提升和变量声明提升,函数声明提升优先,之后变量赋值覆盖函数声明
Foo().getName();    // 1 相当于window.getName() Foo()执行时改变了getName
getName();  // 1 还是window.getName()
new Foo.getName();  // 2 .运算符优先级大于new,实际上是 new (Foo.getName)()
new Foo().getName();    // 3  相当于(new Foo()).getName()
new new Foo().getName(); // 3 
  1. 含箭头函数和闭包的情况
var name = 'window'

var person1 = {
  name: 'person1',
  show1: function () {
    console.log(this.name)
  },
  show2: () => console.log(this.name),
  show3: function () {
    return function () {
      console.log(this.name)
    }
  },
  show4: function () {
    return () => console.log(this.name)
  }
}
var person2 = { name: 'person2' }

person1.show1() // person1
person1.show1.call(person2) // person2

person1.show2() // window 箭头函数的this绑定在父级作用域
person1.show2.call(person2) // window

person1.show3()()   // window 可以理解为let foo1 = person1.show3(); foo1();
person1.show3().call(person2)   // person2 继续引用上面的foo1,这个可以理解为foo1.call(person2)
person1.show3.call(person2)()   // window let foo2 = person1.show3.call(person2); foo2();

person1.show4()()   // person1 let foo3 = person1.show4(); foo3(); 这时foo3是个箭头函数,this指向父级作用域,而父级函数被person1调用,因此this指向person1
person1.show4().call(person2) // person1 foo3箭头函数会忽略call的第一个参数
person1.show4.call(person2)() // person2 父级函数被绑定到了person2
  1. 综合分析,这个就不给解释了
var name = 'window'

function Person (name) {
  this.name = name;
  this.show1 = function () {
    console.log(this.name)
  }
  this.show2 = () => console.log(this.name)
  this.show3 = function () {
    return function () {
      console.log(this.name)
    }
  }
  this.show4 = function () {
    return () => console.log(this.name)
  }
}

var personA = new Person('personA')
var personB = new Person('personB')

personA.show1() // personA
personA.show1.call(personB) // personB

personA.show2() // personA
personA.show2.call(personB) // personA

personA.show3()() // window
personA.show3().call(personB) // personB
personA.show3.call(personB)() // window

personA.show4()()   // personA
personA.show4().call(personB) // personA
personA.show4.call(personB)() // personB
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值