目录
一、普通函数和箭头函数this 指向的区别
二、改变this指向的方法
1、call()方法
2、apply()方法
3、bind()方法
4、示例
5、特殊情况
三、总结
箭头函数的 this 与普通函数的区别,this 的指向问题_普通函数this_小草莓蹦蹦跳的博客-CSDN博客
箭头函数不能改变this指向,只有普通function函数能改变this指向
① 语法:函数.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)
① 语法:函数.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方法,作用和效果都是完全一致的;
不同在于对原始函数的参数赋值方式不同;
① 语法: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指向
其他参数:一般不定义,就使用原始函数的形参
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: "未知"}
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)
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
的指向(编译时绑定)。唯一需要注意的就是要避开箭头函数带来的坑。