01-JavaScript的this指向

199 阅读4分钟

函数的四种调用方式:

  • 独立调用=默认绑定

  • 方法调用=隐式绑定

  • 间接调用=显示绑定

  • 构造函数调用=new绑定

一、默认绑定

  • 1、全局环境下,this默认绑定到window

    console.lg(this === window) // true

  • 函数独立调用时,this默认绑定到window

    function fn() { console.log(this === window) // true}fn()

  • 2、被嵌套的函数独立调用时,this默认绑定到window

    var a = 0;var obj = { a:2, foo:function(){ function test() { console.log(this.a) }; test() }}​obj.foo() // 0// 上面代码虽然test()函数被嵌套在obj.foo()函数中,但test()函数独立调用,而不是方法调用。所以this绑定到window

  • 3、IIFE立即执行函数

    // IIFE立即执行函数实际是函数声明后立即调用执行,内部的this指向了windowvar a = 0;function foo() { (function test() { console.log(this.a) })()};​var obj = { a:2, foo:foo}​obj.foo() // 0

等价于上例:

var a = 0;var obj = {    a:2,    foo:function(){        function test() {            console.log(this.a)        };        test()​    }}​obj.foo() // 0// test()函数是独立调用,而不是方法调用,所以this默认绑定到window
  • 4、闭包

    var a = 0;function foo(){ var that = this; function test() { console.log(this); console.log(that.a); } return test;};​var obj = { a:2, foo:foo};​obj.foo()() // window 2​

二、隐式绑定

一般地,被直接对象所包含的函数调用,也成为方法的调用,this隐式的绑定到该直接对象。

function foo() {    console.log(this.a)};var obj1 = {    a:1,    foo:foo,    obj2:{        a:2,        foo:foo    }}// foo()函数的直接对象是obj1this隐式绑定到obj1obj1.foo();// 1// foo()函数的直接对象是obj2this隐式绑定到obj2obj1.obj2.foo(); // 2

三、隐式丢失

隐式丢失,是指被隐式绑定的函数丢失绑定对象,从而默认绑定到window。这种情况容易出错又常见

  • 1、函数别名

    var a = 0;function foo(){ console.log(this.a)};var obj1 = { a:1, foo:foo};// 把obj.foo赋予别名bar,造成隐式丢失,因为只是把foo()函数赋给了bar,而bar与obj对象毫无关系。var bar = obj1.foobar() // 0

等价于:

var a = 0;var bar = function foo () {    console.log(this.a)};bar() // 0
  • 2、参数传递

    var a = 0;function foo(){ console.log(this.a);};function bar(fn) { fn()};var obj = { a:2, foo:foo};//把obj.foo当做参数传递给bar函数时,有隐式的函数赋值 fn = obj.foo,只是把foo函数赋给了fn,而fn与obj对象毫无关系bar(obj.foo); //0

等价于:

var a = 0;function bar(fn){    fn()}bar(function foo() {    console.log(this.a)})
  • 3、内置函数

    var a = 0;function foo(){ console.log(this.a)}var obj = { a:2, foo:foo}setTimeout(obj.foo,1000); // 0

  • 4、间接调用

    // 函数的“间接引用”一般都在无意间创建,最容易在赋值时发生,会造成隐式丢失function foo() { console.log(this.a)}var a = 2;var o = {a:3,foo:foo}var p = {a:4}o.foo(); // 3//将o.foo函数赋值给p.foo函数,然后立即执行。相当于仅仅是foo()函数的立即调用(p.foo = o.foo)();

    // 另一种情况function foo() { console.log(this.a)}var a =2;var = {a:3,foo:foo}var p = {a:4}o.foo(); // 3p.foo = o.foo;//将o.foo函数赋值给p.foo函数,之后p.foo函数再执行,是属于p对象的foo函数的执行p.foo();// 4

  • 5、其他情况

    // 在JavaScript引擎内部,obj和obj.foo储存在两个内存地址,简称为M1和M2。只有obj.foo()这样调用时,是从M1用M2,因此this指向obj。但是下列三种情况,都是直接取出M2进行运算,然后在全局环境下指向运算结果,因此this指向全局环境。var a = 0;var obj = { a:2, foo:foo}function foo(){ console.log(this.a);}(obj.foo = obj.foo)();//0(false || obj.foo)();//0(1,obj.foo)();//0

四、显示绑定

  • 1、通过call,apply,bind方法把对象绑定this上,叫做显示绑定。对于被调用的函数来说,叫做间接调用。

    var a = 0;function foo(){ console.log(this.a)}var obj = { a:2}foo()// 0foo.call(obj); // 2

  • 2、内置函数

    // JavaScript中新增了许多内置函数,具有显示绑定的功能。如数组的5个迭代方法:map,forEach,filter,some,everyvar id = 'window';function foo(el) { console.log(el,this.id)}var obj = { id:'fn'}let arr =[1,2,3]arr.forEach(foo); // 1 "window" 2 "window" 3 "window"arr.forEach(foo,obj) // 1 "fn" 2 "fn" 3 "fn"

五、new绑定

如果函数或者方法调用之前带有关键字new,它就构成构造函数调用。对于this绑定来说,称为new绑定。

// 1、构造函数通常不适用return关键字,他们通常初始化新对象,他们通常初始化新对象,当构造函数的函数执行完毕时,它会显示返回。在这种情况下,构造函数用表达式的计算结果就是新对象的值。​function fn() {    console.log(this) // fn    this.a = 2    }​var test = new fn()console.log(test) // {a:2}

// 2、如果构造函数使用return语句,但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果function fn(){    console.log(this) // fn    this.a = 2;    return;}var test = new fn();console.log(test);//fn {a:2}

// 3、使用构造函数显示地使用return语句返回一个对象,那么调用表达式的值就是这个对象。​var obj = {a:1};function fn(){    console.log(this) // fn    this.a = 2;    return obj;}var test = new fn();console.log(test);//{a:1}

六、严格模式

  • 1、严格模式下,独立调用的函数的this指向undefined

    function fn(){ 'use strict'; console.log(this);//undefined}fn();function fn(){ console.log(this);//window}fn();

  • 2、 在非严格模式下,使用函数的call()或apply()方法时,null或undefined值会被转换成全局对象。而在严格模式下,函数的this值始终是指定的值

    var color = 'red';function displayColor(){ console.log(this.color);}displayColor.call(null);//redvar color = 'red';function displayColor(){ 'use strict'; console.log(this.color);}displayColor.call(null);//TypeError: Cannot read property 'color' of null