This指向


this绑定优先级

显式绑定 > 隐式绑定 > 默认绑定

new绑定 > 隐式绑定 > 默认绑定

this默认绑定

this默认绑定我们可以理解为函数调用时无任何调用前缀的情景,它无法应对我们后面要介绍的另外四种情况,所以称之为默认绑定,

1.默认绑定时this指向全局对象(非严格模式):

function fn1() {
    let fn2 = function () {
        console.log(this); //window
        fn3();
    };
    console.log(this); //window
    fn2();
};

function fn3() {
    console.log(this); //window
};

fn1();

2.在严格模式环境中,默认绑定的this指向undefined

function fn() {
    console.log(this); //window
    console.log(this.name);
};

function fn1() {
    "use strict";
    console.log(this); //undefined
    console.log(this.name);
};

var name = '听风是风';

fn(); 
fn1() //TypeError: Cannot read property 'a' of undefined

函数以及调用都暴露在严格模式中的例子:

"use strict";
var name = '听风是风';
function fn() {
    console.log(this); //undefined
    console.log(this.name);//报错
};
fn();

3.在严格模式下调用不在严格模式中的函数,并不会影响不在严格模式中的函数的this指向

var name = '听风是风';
function fn() {
    console.log(this); //window
    console.log(this.name); //听风是风
};

(function () {
    "use strict";
    fn();
}());

this隐式绑定

1.如果函数调用时,前面存在调用它的对象,那么this就会隐式绑定到这个对象上

function fn() {
    console.log(this.name);
};
let obj = {
    name: '听风是风',
    function: fn
};
obj.function() //听风是风

2.如果函数调用前存在多个对象,this指向距离调用自己最近的对象

function fn() {
    console.log(this.name);
};
let obj = {
    name: '行星飞行',
    function: fn,
};
let obj1 = {
    name: '听风是风',
    o: obj
};
obj1.o.function() //行星飞行

那如果我们将obj对象的name属性注释掉,现在输出什么呢?

function fn() {
    console.log(this.name);
};
let obj = {
    function: fn,
};
let obj1 = {
    name: '听风是风',
    o: obj
};
obj1.o.function() //??

这里输出undefined,大家千万不要将作用域链和原型链弄混淆了,obj对象虽然obj1的属性,但它两原型链并不相同,并不是父子关系,由于obj未提供name属性,所以是undefined。

既然说到原型链,那我们再来点花哨的,我们再改写例子,看看下面输出多少:

function Fn() {};
Fn.prototype.name = '时间跳跃';

function fn() {
    console.log(this.name);
};

let obj = new Fn();
obj.function = fn;

let obj1 = {
    name: '听风是风',
    o: obj
};
obj1.o.function() //?

这里输出时间跳跃,虽然obj对象并没有name属性,但顺着原型链,找到了产生自己的构造函数Fn,由于Fn原型链存在name属性,所以输出时间跳跃了。

隐式丢失

在特定情况下会存在隐式绑定丢失的问题,最常见的就是作为参数传递以及变量赋值

参数传递时隐式丢失

这个例子中我们将 obj.fn 也就是一个函数传递进 fn1 中执行,这里只是单纯传递了一个函数而已,this并没有跟函数绑在一起,所以this丢失这里指向了window。

var name = '行星飞行';
let obj = {
    name: '听风是风',
    fn: function () {
        console.log(this.name);
    }
};

function fn1(param) {
    param();
};
fn1(obj.fn);//行星飞行

变量赋值时的隐式丢失:

var name = '行星飞行';
let obj = {
    name: '听风是风',
    fn: function () {
        console.log(this.name);
    }
};
let fn1 = obj.fn;
fn1(); //行星飞行

隐式绑定丢失并不是都会指向全局对象

var name = '行星飞行';
let obj = {
    name: '听风是风',
    fn: function () {
        console.log(this.name);
    }
};
let obj1 = {
    name: '时间跳跃'
}
obj1.fn = obj.fn;
obj1.fn(); //时间跳跃

this显式绑定

显式绑定是指我们通过call、apply以及bind方法改变this的行为

方法 参数 返回值 是否调用
function.call(thisArg, arg1, arg2, ...) thisArg(必选)
指定 function 函数的 this 非严格模式下指定为 nullundefined 时会自动替换为指向全局对象
arg1, arg2, ...(可选)
传给function的参数,如果该参数的值为 nullundefined,则表示不需要传入任何参数
谁调用call返回谁的返回值 会立即调用function
function.apply(thisArg, [argsArray]) 和call类似,唯一区别就是[argsArray]参数是一个数组或者类数组,其中的数组元素将作为单独的参数传给 function函数 谁调用apply返回谁的返回值 会立即调用function
function.bind(thisArg,[arg1, arg2, ...]) 参数和apply一样 返回改变了this指向后的function的拷贝 不会调用fuction

new绑定

this指向通过new调用产生的构造函数的实例

function fn(){
    console.log(this)
}
let person=new fn();	//this==>person

构造函数内部有return时,如果return的是基本数据类型,则仍然返回实例对象,如果return的是引用数据类型,则返回return的引用数据类型

箭头函数的this指向

箭头函数不会创建自己的this,它只会从自己作用域的上一层继承this

总结

1.this默认指向全局对象,严格模式下指向undefined

2.在严格模式中调用非严格模式函数不会影响非严格模式函数的this指向;反之亦然

3.在函数调用中,this指向距离自己最近的对象,即点前面那个对象(o.fu()指向o,obj.o.fun()指向o)

4.把函数作为值时,会发生隐式绑定丢失,比如将函数赋值给一个变量,谁调用这个变量函数的this就指向谁(同第三点)

5.自调用函数this指向全局

6.new关键字创建的函数,this指向新创建的构造函数的实例

7.显示绑定的this指向call,apply,bind第一个参数指向的对象

8.箭头函数不会创建自己的this,它只会从自己的作用域的上一层继承this

9.事件绑定方法中this指向绑定事件的对象

番外:作用域链与原型链的区别

当访问一个变量时,解释器会先在当前作用域查找标识符,如果没有找到就去父作用域找,作用域链顶端是全局对象window,如果window都没有这个变量则报错。

当在对象上访问某属性时,首选i会查找当前对象,如果没有就顺着原型链往上找,原型链顶端是null,如果全程都没找到则返一个undefined,而不是报错。

相关