this的指向
默认绑定一般发生在回调函数,函数直接调用;
function test() {
//严格模式下是undefined
//非严格模式下是window
console.log(this);
}
setTimeout(function () {
//setTimeout的比较特殊
//严格模式和非严格模式下都是window
console.log(this);
});
arr.forEach(function () {
//严格模式下是undefined
//非严格模式下是window
console.log(this);
});
隐式绑定:ES5中,this 永远指向最后调用它的那个对象
//例1:
var name = "windowsName";function a() {
var name = "Cherry";
console.log(this.name); // windowsName
console.log("inner:" + this); // inner: Window
}
a();
console.log("outer:" + this) // outer: Window
//调用 a 的地方 a(),前面没有调用的对象,那么就是全局对象 window,这就相当于是 window.a();
//注意,这里没有使用严格模式,如果使用严格模式的话,全局对象就是 undefined,
//那么就会报错 Uncaught TypeError: Cannot read property 'name' of undefined。
//例2:
var name = "windowsName";
var a = {
name: "Cherry",
fn : function () {
console.log(this.name); // Cherry
}
}
a.fn();
//函数 fn 是对象 a 调用的,所以打印的值就是 a 中的 name 的值
//例3:
var name = "windowsName";
var a = {
name: "Cherry",
fn : function () {
console.log(this.name); // Cherry
}
}
window.a.fn();
//调用它的对象仍然是对象 a。
//例4:
var name = "windowsName";
var a = {
//name: "Cherry",
fn : function () {
console.log(this.name); // undefined
}
}
window.a.fn();
//最后调用 fn 的对象是 a,就算 a 中没有 name 这个属性,也不会继续向上一个对象寻找 this.name,而是直接输出 undefined
//例5:
var name = "windowsName";
var a = {
name : null,
// name: "Cherry",
fn : function () {
console.log(this.name); // windowsName
}
}
var f = a.fn;
f();
//虽然将 a 对象的 fn 方法赋值给变量 f 了,但是没有调用,
//由于 “this永远指向最后调用它的那个对象”, f 并没有调用fn(),
// fn()最后仍然是被 window 调用的。所以 this 指向的也就是 window。
// 例6:
var name= "windowsName";
function fn() {
var name = 'Cherry';
innerFunction();
function innerFunction() {
console.log(this.name); // windowsName
}
}
fn()
// fn() 是window调用的,因此this 指向 window
//例7:
var name= "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(function () {
this.func1()
},100);
}
};
a.func2() // this.func1 is not a function
//最后调用 setTimeout 的对象是 window,但是在 window 中并没有 func1 函数,所以会报错
箭头函数
箭头函数的 this 始终指向函数定义时的 this,而非执行时。
箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。
//例8:
var name= "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(()=>{
this.func1()
},100);
}
};
a.func2() //Cherry
new 绑定
function test(){
this.a = 2
console.log(this)
}
var obj = new Test() //this指向new的实例对象
在函数内部使用 _this = this
先将调用这个函数的对象保存在变量 _this 中,然后在函数中都使用这个 _this,这样 _this 就不会改变了
//例9:
var name= "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
var _this = this
setTimeout(function(){
_this.func1()
},100);
}
};
a.func2() //Cherry
//在 func2 中,首先设置 var _this = this,这里的 this 是调用 func2 的对象 a,
//为了防止在 func2 中的 setTimeout 被 window 调用而导致的在 setTimeout 中的 this 为 window。
//我们将 this(指向变量 a) 赋值给一个变量 _this,
//这样,在 func2 中我们使用 _this 就是指向对象 a 了。
使用 apply、call、bind
const obj1 = {
name: 'joy',
getName() {
console.log(this);
console.log(this.name);
}
};
const obj2 = {
name: 'sam'
};
obj1.getName.call(obj2); //obj2 sam
obj1.getName.apply(obj2); //obj2 sam
const fn = obj1.getName.bind(obj2);
fn();//obj2 sam
//例10:使用apply
var name= "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(function(){
this.func1()
}.apply(a),100);
}
};
a.func2() //Cherry
//例11:使用call
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.call(a),100);
}
};
a.func2() // Cherry
//例12:使用bind
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.bind(a)(),100);
}
};
a.func2() // Cherry
apply、call、bind 区别
apply 和 call 只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。
bind() 是创建了一个新的函数,需要手动去调用
fn.apply(thisArg, argsArray)
参数:
thisArg 在 func 函数运行时使用的 this 值。但是,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
argsArray 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。
//例13:
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.apply(a,[1,2]) // 3
fn.call(thisArg, arg1, arg2, …)
参数:arg1, arg2, … 指定的参数列表。
//例14:
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.call(a,1,2) // 3
//例15:
var sData = 'Wisen';
function display() {
console.log('值是:', this.sData);
}
display.call(); // sData value is Wisen
//调用函数方法时,如果没有传递第一个参数,this 的值将会被绑定为全局对象。
// 在严格模式下,this 的值将会是 undefined。
//例16:
function greet(){
var animal = this.name + '-----' + this.hobby
console.log(animal)
}
var obj = {
name: 'cats',
hobby: 'palying'
}
greet.call(obj) //cats-----palying
//调用函数方法时,该方法的this值会绑定到 obj 对象。
//例17:
function Procuct(name, price){
this.name = name
this.price = price
}
function Food(name, price){
Procuct.call(this, name, price)
this.category = 'food'
}
function Toy(name, price){
Procuct.call(this, name, price)
this.category = 'toy'
}
var cheese = new Food('feta', 5)
var fun = new Toy('robot', 10)
//console.log(cheese, fun)
//在一个子构造函数中,通过调用父构造函数的 call 方法可以实现继承。
fn.bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
//例18:
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.bind(a,1,2) //没有输出
b.bind(a,1,2)() //输出 3。 bind 是创建一个新的函数,因此必须要手动去调用
//例19:
var x = 9 // 全局环境中 this指向window
var module = {
x: 81,
getX: function(){ return this.x }
}
module.getX() // 81
var reX = module.getX
reX() //9 函数是在全局作用域中调用的
var boundX = reX.bind(module)
boundX() // 81 创建了新函数,this绑定到了module对象
最后:
这几种this的绑定方式,优先级的先后为:
箭头函数 -> new绑定 -> 显示绑定call/bind/apply -> 隐式绑定 -> 默认绑定
改变this的指向:
- 使用 ES6 的箭头函数;
- 在函数内部使用 _this = this;
- 使用 apply、call、bind;
- new 实例化一个对象;
参考:
https://juejin.cn/post/6844903496253177863