首先想一下this出现的目的是什么?
this 关键字允许在调用函数或方法时决定哪个对象应该是焦点。 之后讨论的所有东西都是基于这个理念。我们希望能够在不同的上下文或在不同的对象中复用函数或方法。
目的是为了 复用
隐式绑定
在 ES5 中, this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象。
let height= '170cm'
const person = {
name: 'xiaoying',
age: 27,
sayName: function() {
console.log (`my nama is ${this.name}`)
console.log (`my height is ${this.height}`)
}
}
person.sayName() //my nama is xiaoying
// my height is undefined
person为调用sayName函数的对象,所有this就指向了person,那么它将会在person内部寻找到name和hieght,找到了name但是没有找到height。始终坚持一个原理:this 永远指向最后调用它的那个对象。再读一遍看看下面的这段代码。
let height= '170cm'
const person = {
name: 'xiaoying',
age: 27,
sayName: function() {
console.log (`my nama is ${this.name}`)
console.log (`my height is ${this.height}`)
},
mother: {
name: 'mother',
age: '46',
sayName: function() {
console.log (`my nama is ${this.name}`)
console.log (`my height is ${this.height}`)
}
}
}
person.mother.sayName() //my nama is mother
//my height is undefined
所有在隐式绑定中仅仅只需要看最后调用它的对象。接下来看看显式绑定
显式绑定
很明显person和mother都用到了sayName的函数,那将上面的函数分离开,提高它的复用。
function sayName() {
console.log (`this is my ${this.name}`)
}
const person = {
name: 'xiaoxian',
age: 27
}
现在分离开了,那么怎么才可以让sayName()函数中的this指向person呢?
在 JavaScript 中,每个函数都包含了一个能让你恰好解决这个问题的方法,这个方法的名字叫做 call。
“call” 是每个函数都有的一个方法,它允许你在调用函数时为函数指定上下文。
function sayName() {
console.log (`this is my name ${this.name}`)
}
const person = {
name: 'xiaoxian',
age: 27
}
sayName.call(person) //this is my name xiaoxian
那当sayName()函数自身需要接收参数时呢?
直接在person的后面添加参数 sayName.call(person,args[0],args[1],args[2])
function sayName(arg1,arg2,arg3) {
console.log (`this is my ${this.name}`)
console.log (`there are arguments ${arg1} , ${arg2}, ${arg3} `)
}
const person = {
name: 'xiaoxian',
age: 27
}
args = ['java', 'ruby', 'python']
sayName.call(person,args[0],args[1],args[2]) //this is my xiaoxian
//there are arguments java , ruby, python
那在数组很长的情况下,怎么办呢?难道手动一个一个的传递参数?
使用 func.apply(thisArg, [argsArray]) 也就是将代码修改为
sayName.apply(person,args) //输出与上面相同
上面两个函数都有一个特点,那就是将对象和参数传递进去以后就立刻执行了,那有没有不立刻执行的呢? 它来了!
function.bind(thisArg[, arg1[, arg2[, ...]]])
定义:bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
跟call的使用方法一模一样,只是需要你自己调用,因为它只是创建一个新的函数
const mybind = sayName.bind(person,args[0],args[1],args[2])
mybind() //this is my xiaoxian
//there are arguments java , ruby, python
new绑定
function User (name, age) {
/*
JavaScript 会在底层创建一个新对象 `this`,它会代理不在 User 原型链上的属性。
如果一个函数用 new 关键字调用,this 就会指向解释器创建的新对象。
*/
this.name = name
this.age = age
}
const me = new User('Tyler', 27)
console.log(me.name,me.age) //Tyler 27
new做了什么呢?
- 创建类的实例。这步是把一个空的对象的 proto 属性设置为 Bar.prototype。
- 初始化实例。函数 Bar 被传入参数并调用,关键字 this 被设定为该实例。
- 返回实例。
window绑定
我们直接调用下面的函数,会输出什么呢?
function sayName(arg1,arg2,arg3) {
console.log (`this is my ${this.name}`)
}
sayName()
//this is my undefined
答案是
undefined,因为sayName没有通过其他对象调用,没有通过call,apply,bind改变this的指向,也没有通过new。JavaScript 会默认 this 指向 window 对象。这意味着如果我们向 window 对象添加 name 属性并再次调用 sayName 方法,this.name 将不再是 undefined 并且变成 window 对象的 name 属性值。
window.name = 'window'
function sayName(arg1,arg2,arg3) {
console.log (`this is my ${this.name}`,'this:',this)
}
sayName() //this is my window this: Window
在 ES5 添加的 严格模式 中,JavaScript 不会默认 this 指向 window 对象,而会正确地把 this 保持为 undefined。
'use strict'
window.age = 27
function sayName () {
console.log(`My age is ${this.name}`)
}
sayAge() // TypeError: Cannot read property 'name' of undefined
最后
在函数内部看到 this 关键字时,为了判断它的引用而采取的步骤。
- 查看函数在哪被调用。
- 点左侧有没有对象?如果有,它就是 “this” 的引用。如果没有,继续第 3 步。
- 该函数是不是用 “call”、“apply” 或者 “bind” 调用的?如果是,它会显式地指明 “this” 的引用。如果不是,继续第 4 步。
- 该函数是不是用 “new” 调用的?如果是,“this” 指向的就是 JavaScript 解释器新创建的对象。如果不是,继续第 5 步。
- 是否在“严格模式”下?如果是,“this” 就是 undefined,如果不是,继续第 6 步。
- JavaScript 很奇怪,“this” 会指向 “window” 对象。