this的指向

这篇文章酝酿了许久,this的指向一直是让初学者痛苦的事情,但也是有迹可循的在学习中绝对不能秉承猜测的想法来进行,这里参考了阮一峰的博客与《你所不知道的JavaScript》一书,以及自己的理解。
这篇文章分为三大部分,第一部分是ES5,第二部分是ES6,因为ES6新增了箭头函数,它有些特殊,第三这里暂时指其他。
如果不特殊说明,全局环境默认为浏览器(window)。


首先要明确一点,this总是返回一个对象,他的返回值与运行环境有关,其次我们思考一下为什么要使用this?
这里我参考了阮一峰的this原理,首先JavaScript中的类型分为基本类型和引用类型,对于引用类型存储在内存中,当变量引用时,赋值给变量他的内存地址,
这也是为什么修改一个变量,另外指向相同内存地址变量也会随之更改的原因。 首先看一个例子
var x = 1;
function foo() {
    console.log(x);
}
var a = {
    x : 5,
    foo: foo
};
a.foo(); //1

这里返回的是1,因为函数的定义时作用域指向全局,但是函数体内允许引用当前环境的变量,函数也可以在不同环境下运行,这就是引用this的原因。

一:ES5
1.全局环境
全局环境下会返回window对象,怎么样调用才算是全局呢?在全局作用域下调用就是全局环境。
function foo() {
    console.log(this.x === x);
}
var x = 5;
foo(); //true
 
  
这条规则比较简单,不过需要注意的是,在严格模式下(‘use strict’)this会返回undefined。
 function foo() { 
    'use strict' console.log(this === undefined);
 } 
var x = 5; foo(); //true 2.
 
  

2.对象方法使用
作为对象方法this返回调用方法的对象,下面是一个例子
var a = {
    x : 5,
    foo: function() {
        console.log(this === a);
    }
};
a.foo(); //true
 
  
这里是由foo方法的对象是a,所以this是a;
在看一个例子,
var a = {
x: 5,
    fn: {
    text: 'es5',
        foo: function () {
            console.log(this === a.fn);
        }
    }
};
a.fn.foo(); //true
 
  
 这里foo方法是由a.fn来调用的,所以this指向a.fn,不过需要注意,在使用的时候需要注意避免嵌套,比如下面这个例子。 
var a = {
    x: 5,
    fn: {
        text: [1, 2, 3],
        foo: function () {
            this.text.forEach(function() {
            console.log(this === window);
        });
        }
    }
};
a.fn.foo(); //true
 
  
 这里foo内部定义了一个函数,这个函数指向的是全局作用域,可以这样来理解,ES5只有全局作用域和函数作用域,因为变量提升的原因,这个函数提升到了头部。

function obj() {
    console.log(this === window);
}
var a = {
    x: 5,
    fn: {
        text: [1, 2, 3],
        foo: function () {
            this.text.forEach(obj);
        }
    }
};
a.fn.foo(); //true
 
  

3.构造函数使用

作为构造函数使用时,默认指向构造函数的实例。

new命令会执行下面四个步骤:

  • 创建一个对象,作为要返回的实例对象;
  • 将新建对象的原型对象指向构造函数的prototype属性;
  • 将新建对象作为this;
  • 开始执行代码;

下面是一个例子

 

function Foo() {
    this.a = 10;
}
var a = new Foo();
console.log(a.a === 10); //true

 

有一种情况需要注意,就是在构造函数内部使用了return命令,并且返回的是对象

 

function Foo() {
    this.a = 10;
    return {obj: 123};
}
var a = new Foo();
console.log(a.a); //undefined

这里new Foo()返回的就是return的对象,内部的this会被忽略掉。

 

4.绑定对象

可以分为三种情况 call,apply,bind,

他们三个第一个参数都是要绑定的对象,如果忽略或者传入undefined和null会默认为全局对象,不过严格模式下全局对象默认为undefined。

call和apply只是第二个参数接收不同,call“”,“”的形式传递参数,比如

a.call(null, 1, 2, 3);

而apply是以数组的形式传递参数

a.call(null, [1, 2, 3]);

bind与call方法很像,不过他返回的是一个函数,函数的this指向就是传递bind的第一个参数,多余参数会作为调用对象的初始值储存。

 

var x = 10;
function foo(x, y) {
    console.log(x + y);
}
var fn = foo.bind(null, 10);
fn(x); //20

 

可以看到,bind不会像call和apply方法调用就立刻执行函数。

 

二:ES6

ES6新增了箭头函数,简单来说,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象

我们把之前在上面举的几个例子重试一下。

全局环境:

 

var a = () => this;
console.log(a() === window); //true

 

 没有区别,定义时所在的对象是window。

对象方法

 

var i = 10
var a = {
    i : 5,
    fn: () => this.i
};

 这里返回有所不同,之所以这样是因为在a.fn这个函数内的this根本没有,所以指向window上。

这样说可能有所难理解,实际上箭头函数内部没有this。

在看一个例子,

 

function foo() {
    return () => {
        return () => {
            return () => {
                console.log('id:', this.id);
            };
        };
    };
}
var i = foo.call({id: 'box'});
i.call({ id: '1'})()(); //id: box
i().call({id: '2'})(); //id: box 
i()().call({id: '3'}); //id: box 

 

 这里可以看到返回结果全部是id: box也就是foo函数内部的this,

箭头函数没有自己的this,那么绑定方法自然也是无效的了。

构造函数:

箭头函数内部不能使用new命令,否则会报错

 

var Foo = () => this = 10;
new Foo(); //Invalid left-hand side in assignment

 

 

作为绑定方法:

因为箭头函数没有自己的this,因为作为绑定方法this会失效,下面是一个例子,

 

var foo = () => this.id;
var a = {
    id: 'hello ES6'
};
var id = 'box';
console.log(foo.call(a)); //box

 

 这里会报错。 不过需要注意,虽然箭头函数没有自己的this,但是可以给箭头函数外部的函数绑定this指向。

 

var id = 'box';
function foo() {
    let f = () => {
        console.log(this.id);
    };
    f();
}
foo.call({id: 'es6'}); //es6

 

 

 

 三:其他

1.作为数组方法调用

 

function f1() {
    console.log(this === arr);
}
var arr = [f1, 1, 2, 3];
arr[0](); //true

 

这里可以简单理解为arr数组方法“0”调用了函数,this返回arr数组。

2.作为定时器使用

 

var id = 'es6';
var a = {
    id: 'box',
    foo: function() {
        setTimeout(function() {
        console.log(this.id === 'es6');
        });
    }
};
a.foo(); //true

这是因为定时器是异步操作,当foo运行后,他将指向全局环境,这个例子如果用箭头函数写就会绑定在a上,因为箭头函数的this是定义时所在的对象。

 

var id = 'es6';
var a = {
    id: 'box',
    foo: function() {
        setTimeout(() => {console.log(this.id === 'box')});
    }
};
a.foo(); //true

 

可以看到。

3.作为DOM节点

作为节点时,this指向定义节点事件所在的节点。

 

    <div>hello word</div>
<body>
    <script>
        var div = document.querySelector('div');
        div.addEventListener('click', function() {
            console.log(this === div);
    });
</script>

这里会返回true,不过注意,如果用箭头函数写会返回全局,因为这里没有this本身没有this会返回全局环境。

 

转载于:https://www.cnblogs.com/boses/p/9585818.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值