对 js 中 this 指向的理解


对 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指向的阐述。得出结论:代码 === 玄学

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值