对 js 中的this 的理解
this 指针一直都是一个很玄学的东西,它是函数运行时,函数内部自动生成的对象,只能在函数体内部使用。一下示例都是测试过的,对于答案请放心。如有疑问欢迎评论
纯粹的函数调用
这是函数通常的用法,属于全局性的调用,所以this指向全局。
最简单的示例
var x = 1;
function test() {
console.log(this.x);
}
test(); // 1
作为对象的一个属性
此时,谁调用这个函数,函数内部的this就指向谁
var name = 'window';
var test = {
name: 'test',
getName: function(){
console.log(this.name);
}
}
test.getName(); // test
作为构造函数的一个属性
this指向当前实例
var name = 'window';
function Test(){
this.name = 'test';
this.getName = function(){
console.log(this.name);
}
}
let test = new Test();
test.getName(); // test
call、apply、bind修改this指向
使用call 和 apply 方法可以修改this的指向,两者的区别为:
call this后面的参数为一个参数列表
apply this后面的参数为一个数组
bind bind绑定返回一个新的函数,bind的中this后面的参数是预先添加到绑定函数的参数列表中的参数。
var test = {
name: 'test',
getName: function(extraParam1, extraParam2){
console.log(this.name, extraParam1, extraParam2);
}
}
var testCall = {
name: 'testCall'
}
test.getName.call(testCall,'extraParam1','extraParam2');
// testCall extraParam1 extraParam2
这边可以看到打印出的时testCall对象的name属性,并且call方法后面携带的是一个参数列表
var test = {
name: 'test',
getName: function(extraParam1, extraParam2){
console.log(this.name, extraParam1, extraParam2);
}
}
var testApply = {
name: 'testApply'
}
test.getName.apply(testApply,['extraParam1','extraParam2']);
// testApply extraParam1 extraParam2
apply的参数为一个参数数组
var test = {
name: 'test',
getName: function(extraParam1, extraParam2){
console.log(this.name, extraParam1, extraParam2);
}
}
var testBind = {
name: 'testBind'
}
let getNameBind = test.getName.bind(testBind,'前置参数');
getNameBind('extraParam1'); // testBind 前置参数 extraParam1
test.getName('extraParam1','extraParam2'); // test extraParam1 extraParam2
bind返回一个新的函数
箭头函数
箭头函数本身并不具备this对象,内部的this对象是继承父级作用域中的this,且箭头函数的this不可使用call、apply修改指向,但是可以通过修改父级的this来实现。简单来说要想知道箭头函数的this,需要查看箭头函数的定义位置,而不是调用位置。
this.name = 'window';
var test = {
name: 'getName',
getName: () => {
console.log(this.name);
}
}
var testCall = {
name: 'testCall'
}
var testApply = {
name: 'testApply'
}
var testBind = {
name: 'testBind'
}
test.getName(); // window
test.getName.call(testCall); // window
test.getName.apply(testApply); // window
test.getName.bind(testBind)(); // window
箭头函数定义在 test 对象的内部,test的作用域为全局作用域,所以,getName函数中的this指向的是全局,与调用位置无关。且无法通过call apply bind 直接修改this指向。然后在看下一个示例
this.name = 'window';
var test = {
name: 'test',
getName: function(){
return () => {
console.log(this.name);
}
}
}
var testCall = {
name: 'testCall'
}
var testApply = {
name: 'testApply'
}
var testBind = {
name: 'testBind'
}
test.getName()(); // test
test.getName.call(testCall)(); // testCall
test.getName.apply(testApply)(); // testApply
test.getName.bind(testBind)()(); // testBind
getName函数返回一个箭头函数,所以初始时箭头函数的父级作用域为getName函数的内部作用域,所以此时this指向应该是看谁调用getName就指向谁。既然如此的话,那我们就可以通过call、apply、bind来修改getName的this指向从而改变箭头函数的this指向。
说完以上几种情况,下面看两个综合题
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
person1.show2.call(person2) // window
person1.show3()() // window
person1.show3().call(person2) // person2
person1.show3.call(person2)() // window
person1.show4()() // person1
person1.show4().call(person2) // person1
person1.show4.call(person2)() // person2
第 20 行:show1为普通函数,this指向调用对象,这边是person1调用,所以指向person1
第 21 行:使用call将show1函数的指针指向person2
第 23 行:show2函数为箭头函数,父级作用域为person1作用域即全局作用域,所以this指向window
地 24 行:箭头函数的this指向在定义时就已经确定。不随着call进行修改,所以此处this依旧是指向window
第 26 行:show3 返回一个函数,然后调用这个返回的函数,此行等价于
let show3 = person1.show3();
show3()
属于第一种情况,函数的直接调用,this指向全局
第 27 行:使用call将show3函数返回的函数的this指向了person2,等价与于
let show3 = person1.show3();
show3.call(person2);
第 28 行:和第 26 行道理一样,等价于
let show3 = person1.show3.call(person2);
show3();
函数直接调用,this指向全局
第 30 行:show4返回的是一个箭头函数,箭头函数的this是继承的父级函数show4的this,所以此处箭头函数的this取决于show4的this指向,此处使用person1进行调用,所以shoe4的this指向person1
第 31 行:show4返回的是一个箭头函数,箭头函数的this不会被call改变,依旧时取决于父级的show4函数的this,此处person1进行调用,所以依旧是指向的this
第 32 行:修改了show4函数的this指向,指向了person2,由于上面说的,此处箭头函数的this是取决于show4的,所以箭头函数的this也指向了person2。
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
此题是一个构造函数,并且示例化出两个对象,personA 和 personB。
第 22 行:构造函数实例的对象调用函数,函数this指向调用对象,所以此处是指向了personA
第 23 行:show1函数的this被call修改,指向 personB
第 25 行:show2 为一个箭头函数,this指向父级作用域,此处也就是构造函数内部,取决于调用对象,此处是personA进行调用,所以this指向了personA
第 26 行:箭头函数的this指向不会被call修改,所以依旧是指向personA
第 28 行:show3 返回一个函数,属于全局调用,this指向全局,等价于
let show3 = personA.show3();
show3();
第 29 行:等价于
let show3 = personA.show3();
show3.call(personB);
第 30 行:等价于
let show3 = personA.show3.call(personB);
show3();
第 32 行:show4 返回一个箭头函数,取决于show4的调用者,此处为 personA
第 33 行:箭头函数的this不会被call修改,此处依旧取决于 show4 的调用者,指向 personA
第 34 行:修改 show4 的this指向,而箭头函数的this取决于show4 所以此处this指向了 personB
以上为对js中this指向的阐述。得出结论:代码 === 玄学