如何改变this指向?this指向总结

目录

一、普通函数和箭头函数this 指向的区别

二、改变this指向的方法

1、call()方法

2、apply()方法

3、bind()方法

4、示例

5、特殊情况

三、总结


一、普通函数和箭头函数this 指向的区别

箭头函数的 this 与普通函数的区别,this 的指向问题_普通函数this_小草莓蹦蹦跳的博客-CSDN博客

二、改变this指向的方法

箭头函数不能改变this指向,只有普通function函数能改变this指向

1、call()方法

① 语法:函数.call(参数1,其他参数....可以是多个或者没有 )

参数1:改变的this的指向内容;
其他参数:原始函数的实参。

(原始函数有几个形参,此时就要对应的输入几个实参。没有形参, 就没有实参。

        function foo(a, b) {
            console.log(this)
            console.log(a, b) // 1,2
        }
        function bar(fn) {
            // fn() // 指向Window

            // 父函数是有能力决定子函数的this指向
            fn.call(obj, 1, 2) // 指向obj对象
        }
        var obj = {
            foo: foo
        }
        bar(obj.foo)

 ② 作用 :调用并且执行函数,同时将函数的this指向,定义为指定的内容(参数1)

2、apply()方法

① 语法:函数.apply(参数1,参数2)

参数1:改变的this的指向内容;
参数2:原始函数的实参,
必须是一个数组的形式,将实参定义成数组的单元。

        function foo(a, b) {
            console.log(this)
            console.log(a, b) // 1, 2
        }
        function bar(fn) {
            // fn() // 指向Window

            // 父函数是有能力决定子函数的this指向
            fn.apply(obj, [1, 2]) // 指向obj对象
        }
        var obj = {
            foo: foo
        }
        bar(obj.foo)

② 作用: 调用并且执行函数,同时将函数的this指向,定义为指定的内容(参数1)

③ 总结:call方法与apply方法,作用和效果都是完全一致的;

                不同在于对原始函数的参数赋值方式不同;

3、bind()方法

① 语法:const 变量 = 函数.bind(参数1)

参数1:改变的this的指向内容 ;

(其他参数一般不定义,是使用函数原有的形参)

        function foo(a, b) {
            console.log(this)
            console.log(a, b) // 1, 2
        }
        function bar(fn) {
            // fn() // 指向Window

            // 父函数是有能力决定子函数的this指向
            fn.bind(obj)(1, 2) // 指向obj对象
        }
        var obj = {
            foo: foo
        }
        bar(obj.foo)

② 作用: 不是立即执行函数。生成一个新的函数,这个新的函数是改变this指向之后的新的函数

③ 总结: call 和 apply 都是立即执行函数

参数1:都是改变的this指向
其他参数:是原始函数的形参(可以有,也可以没有)

                bind 不是立即执行函数,是生成一个新的函数

参数1:是改变的this指向
其他参数:一般不定义,就使用原始函数的形参    

4、示例

function fn(name, age, sex) {
    console.log(name, age, sex, this)
}

// this指向是window
fn('小草莓', 18, '女') // 小草莓 18 女 Window 


// 改变this指向。call方法:this指向的obj1
let obj1 = {
    name: '未知',
    age: 16,
    sex: '未知',
}
fn.call(obj1, '张三', 20, '男') // 张三 20 男 {name: "未知", age: 16, sex: "未知"}


// 改变this指向。apply方法:this指向的obj1
fn.apply(obj1, ['李四', 40, '不知道']) // 李四 40 不知道 {name: "未知", age: 16, sex: "未知"}


// bind方法,不是立即执行函数,而是定义生成一个新的函数
// 新生成的函数,this指向是参数1 是obj1
// 新生成的函数,形参是原始函数fn的形参
const fn2 = fn.bind(obj1)
fn2('王五', 100, '不详') // 王五 100 不详 {name: "未知", age: 16, sex: "未知"}

5、特殊情况

        function foo(a, b) {
            console.log(this)
            console.log(a, b) // 2, undefined
        }
        function bar(fn) {
            // fn() // 指向Window

            // 第一个参数默认是对象
            fn.call(1, 2) // 指向Number类
            fn.call(false, 2) // 指向Boolean类
            fn.call([1, 2], 2) // 指向[1, 2]这个数组
            fn.call(undefined, 2) // undefined不是对象,也没有无包装类,所以采用默认的方式(指向Window)
            fn.call(null, 2) // null不是对象,也没有无包装类,所以采用默认的方式(指向Window)
            fn.call('1', 2) // 指向String类
            fn.call(Symbol(1), 2) // 指向Symbol类
        }
        var obj = {
            foo: foo
        }
        bar(obj.foo)

 

6、bind() 连续调用多次,this的绑定值是什么呢?

var bar = function(){
    console.log(this.x);
}
var foo = {
    x:3
}
var sed = {
    x:4
}
var func = bar.bind(foo).bind(sed);
func(); // ?
  
var fiv = {
    x:5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); // ?

在Javascript中,多次 bind() 是无效的。 更深层次的原因:

bind() 的实现相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。

具体解释:

var func = bar.bind(foo).bind(sed);  func()

这一句相当于:

var func1 = function() {
  return bar.apply(foo) // 2、将bar的this改为foo
}
var func2 = function() {
  return func1.apply(sed) // 1、将func1的this改为sed对象
}
func2()

后面的bind只能改变上一个bind的this指向,例如foo.bind(obj).bind(obj2)改变的是foo.bind(obj)的指向。最终foo执行绑定的this是由第一次bind决定

因此不管foo执行多少bind,都是第一次bind绑定的对象

所以两次输出都是 3

三、总结

1、普通函数的this存在四种绑定规则:

① 默认绑定:

也就是在全局声明的变量和函数,默认指向Window;

以及函数独立调用时,比如声明式函数、匿名函数、立即执行函数和闭包等,都是指向Window;

② 隐式绑定:

也就是通过对象调用的形式,这时候this指向了调用这个函数的对象;在函数赋值和参数赋值的情况下,存在隐式丢失的问题;

③ 显示绑定:

也就是通过 call / apply / bind 这三种方法绑定,this指向的是第一个参数;

④ new绑定:

也就是构造函数中的this指向的是实例化出来的对象;

2、而在箭头函数中,以上4种绑定规则都不适用:

箭头函数本身不存在this指向,this指向取决于父环境中的this指向;

如果没有,则this指向Window;

3、箭头函数中的 this 是在声明的时候就定义好了的,而普通函数中的 this 则是在函数执行时确定的。

4、通过apply/call/bind 三种方法来改变 普通函数的this 的指向

5、下面是最常见的例子:

const obj = {
  sayThis: function() {
    console.log(this);
  }
};

obj.sayThis(); // obj
const globalSay = obj.sayThis;
globalSay(); // window 浏览器中的 global 对象

解析:谁调用了某个方法,那么这个方法中的 this 指向谁。这个“谁”我们可以通过 . 操作符来判断。

比如 obj.sayThis(),这里就是 obj 调用了 sayThis 方法,所以 this 指向 obj

而后一个 globalSay() 是直接调用的,在 JavaScript 中会把这个方法绑在全局(window)上。所以本质就是 window.globalSay(),自然这里的 this 就指向了 window

 把上面的例子改为箭头函数,结果完全不同:

const obj = {
  sayThis: () => {
    console.log(this);
  }
};

obj.sayThis(); // window 浏览器中的 global 对象
const globalSay = obj.sayThis;
globalSay(); // window 浏览器中的 global 对象

解析: window因为 JavaScript 没有块作用域,所以在定义 sayThis 的时候,里面的 this 就绑到 window 上去了

6、JavaScript 给我们提供了 apply/call/bind 三种方法来改变 this 的指向

在 ES6 的语法中还提供了箭头函语法,让我们在代码书写时就能确定 this 的指向(编译时绑定)。唯一需要注意的就是要避开箭头函数带来的坑。

你可能感兴趣的:(每日专栏,算法,JavaScript,前端,javascript)