前言
this的概念:
- 在js中,this的意思为“这个;当前”,是一个指针型变量,它动态指向当前函数的运行环境。
- 在不同的场景中调用同一个函数,this的指向也可能会发生变化,但是它永远指向其所在函数的真实调用者;如果没有调用者,就指向全局对象window。
this的绑定规则总共有六种方式:分别为默认绑定、隐式绑定、隐式丢失、显示绑定(bind,apply,call)、new绑定、箭头函数。 我给大家总结了一个口诀,相信我,大家只要记住我这个口诀,就能彻底掌握js中this的指向。
先讲口诀:箭头函数、new、bind、 apply和call、欧比届点(obj.)、直接调用
按照口诀的顺序,只要满足前面某个场景,就可以确定 this 指向了。
一、箭头函数
箭头函数排在第一个是因为它的 this 不会被改变,所以只要当前函数是箭头函数,那么咱们就不用再看其他规则了。箭头函数没有this这个机制,写在箭头函数中的this也是它外层非箭头函数的this。
箭头函数的 this 是在创建它时外层 this 的指向。这里的重点有两个:
- 箭头函数没有自己的this指向,它会捕获自己定义所处的外层执行环境,并且继承这个this值,指向当前定义时所在的对象。箭头函数的this指向在被定义的时候就确定了,之后永远都不会改变。即使使用
call()、apply()、bind()等方法改变this指向也不可以。 - 箭头函数内的 this 指向外层的 this。
所以要知道箭头函数的 this 就得先知道外层 this 的指向,需要继续在外层应用六步口诀。
二、new绑定
当使用 new 关键字调用函数时,函数中的 this 一定是 JS 创建的新的实例对象。
Person.prototype.say = function(){
console.log("hello " + this.name);
}
function Person(name){
this.name = name;
// 相当于下面
// var this = {
// name: name,
// __proto__:Person.prototype
// }
// return this
}
var person1 = new Person("")
person1.say()
new的作用其实就是在构造函数中创建一个this对象,然后构造函数中的内容就相当于往this里面挂属性,另外还会放一个实例对象的隐式原型,其值就是构造函数的显示原型,最终return出这个this对象
三、bind
function foo(n,m){
console.log(this.a,n,m);
}
var obj = {
a: 2
}
var bar = foo.bind(obj,100,200)
bar() // 2 100 200
bind会返回一个新的函数体,这个函数体拥有了obj的词法作用域,并且foo中this会指向obj。
bind传参有下面三种方式
-
就是上面的例子,全部放进bind()中
-
var bar = foo.bind(obj)bar(100,200)foo的参数可以全部写在返回的新函数中 -
var bar = foo.bind(obj,100)bar(200)bind和返回的函数体都可以进行传参如果是下面这种情况
var bar = foo.bind(obj,100,100)bar(200) // 2 100 100这种情况不会覆盖在来看一个案例:
function foo(){ var a = 1 function bar(){ console.log(this.a); } var baz = bar.bind(foo) baz() } foo() // undefined这里使用bind就是让bar中的this指向了foo,但是foo是没有词法作用域,foo指向了全局作用域,因为foo在全局中声明,所以this最终指向了全局,而非foo,因此输出undefined。this不能引用一个词法作用域内部的内容.
多次 bind 时只认第一次 bind 的值
易错点
function func() { console.log(this) } func.bind(1).bind(2)() // 1箭头函数中 this 不会被修改
func = () => { // 这里 this 指向取决于外层 this console.log(this) } func.bind(1)() // Window,口诀 1 优先bind 与 new
易错点
function func() { console.log(this, this.__proto__ === func.prototype) } boundFunc = func.bind(1) new boundFunc() // Object true,口诀 2 优先四、apply和call
使用call、apply或bind方法人为干预他,让他代指谁
call
function foo(){ console.log(this.a); } var obj = { a: 2 } foo.call(obj) // 2这个call的用法就是把foo的this指向了obj
当foo中有参数时
function foo(n){ console.log(this.a,n); } var obj = { a: 2 } foo.call(obj,100) // 2 100call与foo共用这个括号
apply:
function foo(){ console.log(this.a); } var obj = { a: 2 } foo.apply(obj)无参数时与call用法相同
传参时
function foo(n,m){ console.log(this.a,n,m); } var obj = { a: 2 } foo.apply(obj,[100,200]) // 2 100 200apply传参是以数组的形式
箭头函数中 this 不会被修改
func = () => { // 这里 this 指向取决于外层 this,参考口诀 7 「不在函数里」 console.log(this) }
func.apply(1) // Window,口诀 1 优先
bind 函数中 this 不会被修改
function func() { console.log(this) } boundFunc = func.bind(1) boundFunc.apply(2) // 1,口诀 3 优先五、obj
当函数的引用有上下文对象时(当函数被某个对象所拥有时), 函数的this指向引用它的对象,也就是所说的隐式绑定。
直接看下面这个案例:
function foo(){ console.log(this.a); } var obj = { a: 2, foo: foo // 等同于 // foo: function foo(){ // console.log(this.a) // } } obj.foo() // 2obj中有个foo属性,然后这个属性的值又是一个函数体,就相当于foo这个函数体写在了foo这个key里面的value中,最后obj.foo()中obj.foo就是相当于foo这个函数体,然后()就是一个调用的意思
根据隐式绑定的规则,foo这个函数被obj这个函数引用,等你obj再次调用这个函数时,函数中的this最终指向了obj这个对象,而非全局,这就是隐式绑定,因此最终打印2
隐式丢失:隐式丢失是隐式绑定的一种,当一个函数被多个函数链式调用时,函数的this指向就近的那个对象,也就是就近原则。
再来一个
function foo(){ console.log(this.a); } var obj = { a: 3, foo: foo() } obj.foo // undefined这里的对象中并非是引用这个foo函数,因为有个括号,是调用,所以不符合隐式绑定规则,只能是默认绑定,this还是指向了全局作用域,找不到a的值,输出undefined
六、直接调用
在函数不满足前面的场景,被直接调用时,this 将指向全局对象。在浏览器环境中全局对象是 Window,在 Node.js 环境中是 Global。也就是默认绑定。
默认绑定:当一个函数独立调用,不带任何修饰符的时候,函数在哪个词法作用域下生效,函数中的this就指向哪里,(只要是默认绑定,this一定指向window)。
先来个简单的例子
function func() { console.log(this) } func() // Window来一个复杂的例子,外层的 outerFunc 就起个迷惑目的。
function outerFunc() { console.log(this) // { x: 1 } function func() { console.log(this) // Window } func() } outerFunc.bind({ x: 1 })()
最后给大家来两道题目试试:
先背诵口诀在做题,“箭头函数、new、bind、 apply和call、 欧比届点(obj.)、直接调用。”
1、下面代码执行后,输出多少呢?
function func(num) {
this.count++
}
func.count = 0
func(1)
console.log(func.count)
答案: 输出0.
按照口诀,func()调用时属于第六类“直接调用”。this指向全局对象。this跟func一点关系都没有,所以func.count保持不变。很简单。
2.下列箭头函数中的this分别指向谁呢?
obj = {
func() {
const arrowFunc = () => {
console.log(this.name)
}
return arrowFunc
},
name: "obj",
}
1.obj.func()()
2.func = obj.func
func()()
3.obj.func.bind({ name: "newObj" })()()
4.obj.func.bind()()()
5.obj.func.bind({ name: "bindObj" }).apply({ name: "applyObj" })()
答案:
// obj
// undefined
// newObj
// undefined
// bindObj
是不是很简单,你学废了嘛?今天的分享就到这里啦!如果觉得本文对你有帮助的话,可以点个免费的赞赞嘛,感谢感谢!