winowsformshost 的构造函数执行符合指定的绑定约束的_JavaScript执行机制 - this

3ed4a13dd085fdfaa8cf778324a67efb.png

背景

完成了对作用域,作用域链的学习,今天让我们看this在JavaScript中的表现

先看下面的代码

var bar = {
    myName:"bar",
    printName: function () {
        console.log(myName)
    }    
}
let myName = "global"
let _printName = bar.printName
_printName() // global
bar.printName() // global

虽然bar.printName所指向的函数被定义在bar对象内部,但其作用域链依然指向global中的myName变量。

往往在实际开发中,我们需要在对象内部的方法使用对象内部的属性,但JavaScript的作用域机制并不支持这一功能,所以基于这种需求,this被提出。

我们将代码稍作修改

var bar = {
    myName:"bar",
    printName: function () {
        console.log(this.myName)
    }    
}
let myName = "global"
let _printName = bar.printName
_printName() // undefined
bar.printName() // bar

通过测试代码,我们发现同样的函数在不同的调用方式下this发生了变化,这显然与符合词法作用域规则的作用域不相同,所以this机制与作用域机制是两套不同的系统

JavaScript中的this

在之前的文章中,我们了解过执行上下文的结构:变量环境、词法环境和this绑定

6b4fa1da51c0aedec51153f389b46514.png

this的指向与执行上下文绑定,我们知道执行上下文有三种:

  • 全局执行上下文
  • 函数执行上下文
  • eval执行上下文

所以this也有同样的三种,但这里我们只讨论全局中的this函数中的this

全局中的this

直接在浏览器控制台中输入console.log(this)得到是window对象,而在nodejs中打印this得到的是global对象。我们发现,全局中的this会指向全局对象,当然随着运行环境的不同全局对象在浏览器中是window,在nodejs中就是global。

函数中的this

相比全局中的this,函数中的this会复杂的多。

首先观察如下代码:

function foo(){
  console.log(this)
}
foo() // window

运行函数foo后,打印出的依然是window对象。我们可以得出结论,默认情况下使用函数名直接调用函数,函数中的this会指向全局对象。 ** 这也解释了为什么在开篇的代码中,用一个新的变量指向bar.printName并调用,其中的log(this.myName)其实是访问了window.myName,则得到了undefined的结果。

1. 调用对象的方法

还是开篇的代码,当我们调用bar.printName方法时,可以打印出bar对象中的myName变量。所以使用对象.方法名的方式调用函数时,函数中的this会指向对象

2. apply、call和bind

JavaScript为我们提供了动态改变函数中this指向的api: apply、call和bind

apply和call

function foo() {
    console.log(this)
}

var obj1 = {
    myName: 'obj1'
}

var obj2 = {
    myName: 'obj2'
}

foo.apply(obj1) // { myName: "obj1" }
foo.apply(obj2) // { myName: "obj2" }

foo.call(obj1) // { myName: "obj1" }
foo.call(obj2) // { myName: "obj2" }

同一个函数foo,调用其apply和call方法并传入一个对象做为其this的指向可以动态的改变this。

apply和call的区别在于参数不同,并不是这里的重点,感兴趣的同学可自行查阅:

  • MDN Function.prototype.apply()
  • MDN Function.prototype.call()

bind

bind与call、apply略有不同

function foo() {
    console.log(this)
}

var obj = {
    myName: 'obj'
}

var bar = foo.bind(obj)

foo() // window
bar() // { myName: "obj" }

我们发现调用bind方法并传入一个对象后,会返回一个新的函数。调用这个新的函数时,其中的this就指向了调用bind方法时传入的对象了。

由于bind的存在,我们可以将一些函数通过bind生成新的函数,并将这些新生成的函数传递给其他函数调用,无论如何传递,当这些函数被调用时,其中的this都是之前的对象。这一功能在没有ES6 箭头函数之前尤为重要。

3. 构造函数中的this

由于JavaScript并没有原生class的存在,class关键字只是一种语法糖,所以当我们想动态的创建一个对象时,可以定一个一个函数做作创建对象的构造函数,并使用new关键字创建对象。

function Person(name, age) {
    this.name = name
  this.age = age
}

var person1 = new Person('小明', 18)
var person2 = new Person('小刚', 19)

console.log(person1) // { name: '小明', age: 18 } 
console.log(person2) // { name: '小刚', age: 19 }

new关键字主要完了以下的操作:

  1. 创建一个空的简单JavaScript对象(即 {})
  2. 将构造函数挂载在创建的对象上
  3. 以空对象做为构造函数的this调用函数
  4. 如果构造函数没有返回值,则以this做为返回

感兴趣的同学可以参考 MDN new 运算法

虽然this的存在,提高了JavaScript的动态性,为开发者提供了便利,但还是有缺陷需要注意。

this的缺陷

1. 在嵌套的函数中,this不会继承

让我们分析如下代码

var myObj = {
  name : "myObj", 
  showThis: function(){
    console.log(this)
    function bar(){console.log(this)}
    bar()
  }
}
myObj.showThis()

第4行的log打印了myObj对象,而第5行的log打印了window。虽然bar函数被定义在showThis所指向的函数内,但两个this不没有什么关系。这也充分印证了this与作用域的区别。

我们修改一些代码来解决这个问题

a. 定义变量self

var myObj = {
  name : "myObj", 
  showThis: function(){
    console.log(this)
    const self = this
    function bar(){
      console.log(self.name)
    }
    bar()
  }
}
myObj.showThis()

我们在能正确访问myObj.name的函数中定一个变量self,使其指向this,并在内部函数中用self变量代替this。

这种方式其实就是将this的机制转换成作用域的机制.

b. 使用call/apply

var myObj = {
  name : "myObj", 
  showThis: function(){
    console.log(this)
    function bar(){
      console.log(this.name)
    }
    bar.call(this) // 同bar.apply(this)
  }
}
myObj.showThis()

我们将内部函数bar的调用方式由原来的常规函数名调用方式改成使用call/apply的方式,将showThis的this传递给bar函数,依然可以实现同样的效果。

c. ES6 箭头函数

var myObj = {
  name : "myObj", 
  showThis: function(){
    console.log(this)
    const bar = () => {
      console.log(this.name)
    }
    bar()
  }
}
myObj.showThis()

由于箭头函数并不会创建自己的上下文,所以其this其实是外部函数的this名,这样同样可以达到效果。

2. 常规调用

通过前面章节的介绍,我们知道函数在常规调用下,其中的this会指向全局对象window。

往往不经意间可能改变了window中变量的值,而导致一些意想不到的异常,而这类全局变量污染的异常比常规的异常更难重现,所以在实际开发中一定要注意this的指向。

当然JavaScript为我们提供了“严格模式”。在严格模式下,函数被常规调用时,this 的值为undeifnd。

总结

至此,我们探讨了this的部分知识,在使用this时需要注意

  • 常规调用函数,this会指向全局对象
  • 通过对象.方法名的方式,this会指向对象
  • 可以通过apply、call和bind改变函数的this指向
  • 在多层嵌套中的函数中,this是不相同的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值