this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象
1.举个简单的例子
const a = {
str: '我爱工作',
fn: function () {
console.log(this.str);
}
}
console.log(this);//window
a.fn();//我爱工作
2.再来个例子
const name = '我爱工作';
function fn() {
const name = 'doctor';
function a() {
console.log(this.name);
}
a();
}
fn();//我爱工作 = window.fn()
总结:this 永远指向最后调用它的那个对象
那要怎么改变this指向呢?
1.使用 ES6 的箭头函数
2.在函数内部使用 _this = this
3.new 实例化一个对象
4.使用 apply、call、bind
举例
1.使用 ES6 的箭头函数
箭头函数的 this 始终指向函数定义时的 this,而非执行时。,箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。
1.来个例子
let obj={
id:123,
testFun:function(){
let a= ()=>console.log(this.id);
a();
}
};
//testFun的this指的是obj,则箭头函数的this指向obj。
obj.testFun();//123
//testFun的this指向window,箭头函数的this指向window。
obj.testFun.apply(null)// undefined 使用apply指向window
2.拿文章最开头的举例子
const a = {
str: '我爱工作',
fn: ()=> {
console.log(this.str);
}
}
a.fn();//undefined
当改成箭头函数时,函数定义时在全局定义,this也指向全局window,但通常来说,这不是我们想要的结果,我们是希望得到str的
所以:有些时候,不适合用箭头函数
番外:不适合用箭头函数的例子
①在对象上定义方法
const a = {
str: '我爱工作',
fn: ()=> {
console.log(this.str);
}
}
a.fn();//undefined
②创建原型对象中
正常情况下
function MyCat(name) {//类似的构造函数也不可以用箭头函数
this.person = name;
}
MyCat.prototype.sayCatName = function(){
console.log(this === window); // false
console.log(this.person); //TOM
};
let cat = new MyCat('TOM');
cat.sayCatName();
改为箭头函数
function MyCat(name) {
this.person = name;
}
MyCat.prototype.sayCatName = () => {
console.log(this === window); // true
console.log(this.person); // undefined
};
let cat = new MyCat('TOM');
cat.sayCatName();
③类似addEventListener 结合动态上下文的回调函数
正常情况下
var button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log(this === button); // => true
this.innerHTML = 'Clicked button';
})
箭头函数下
var button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
2.在函数内部使用 _this = this
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 了
3.new实例化一个对象
function person(name) {
this.name = name;
this.say = function () {
console.log("我是" + this.name);
}
}
var baby = new person("王麻子");
baby.say();//我是王麻子
4.使用 apply、call、bind
(1).call
定义:调用一个对象的一个方法,以另一个对象替换当前对象。
说明: call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
thisObj的取值有以下4种情况:
(1) 不传,或者传null,undefined, 函数中的this指向window对象
(2) 传递另一个函数的函数名,函数中的this指向这个函数的引用
(3) 传递字符串、数值或布尔类型等基础类型,函数中的this指向其对应的包装对象,如 String、Number、Boolean
(4) 传递一个对象,函数中的this指向这个对象
举个例子
function a(){
console.log(this); //输出函数a中的this对象
}
function b(){}
var c={name:"call"}; //定义对象c
a.call(); //window
a.call(null); //window
a.call(undefined); //window
a.call(1); //Number
a.call(''); //String
a.call(true); //Boolean
a.call(b); //function b(){}
a.call(c); //Object
再来个
function Animal(){
this.name="animal";
this.showName=function(){
console.log(this.name);
}
}
function Dog(){
this.name="dog";
}
var animal=new Animal();
var dog=new Dog();
animal.showName.call(dog);
输出:dog
所以 可以用call实现继承
function Animal(name){
this.name=name;
this.showName=function(){
console.log(this.name);
}
}
function Dog(name){
Animal.call(this,name);
}
var dog=new Dog("Crazy dog");
dog.showName();
输出:Crazy dog
(2).apply
apply和call基本一致,不同的是apply传递的参数必须为数组,他会遍历数组依次传入参数
所以当你的参数是明确知道数量时用 call ;而不确定的时候用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个类数组对象来遍历所有的参数。
扩展:
所以apply有一个用处就是打散数组,类似…(扩展运算符)
var arr = new Array(1,3,4,6,1,3,5);
var max = Math.max.apply(null,arr);//6
(3)bind
bind()方法会创建一个新函数,称为绑定函数。
bind是ES5新增的一个方法,不会执行对应的函数(call或apply会自动执行对应的函数),而是返回对绑定函数的引用。
当调用这个绑定函数时,thisArg参数作为 this,第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
简单地说,bind会产生一个新的函数,这个函数可以有预设的参数。
举例1
this.value = 2
var foo = {
value: 1
}
var bar = function() {
console.log(this.value)
}
var result = bar.bind(foo)
bar() // 2
result() // 1,即this === foo
举例2
this.value = 2
var foo = {
value: 1
};
var bar = function(name, age, school) {
console.log(name) // 'An'
console.log(age) // 22
console.log(school) // '家里蹲大学'
}
var result = bar.bind(foo, 'An') //预置了部分参数'An'
result(22, '家里蹲大学') //这个参数会和预置的参数合并到一起放入bar中
我们可以看出在最后调用result(22, ‘家里蹲大学’)的时候,其内部已经包含了在调用bind的时候传入的 ‘An’。
一句话总结 apply、call、bind
cat.call(dog, a, b) = cat.apply(dog, [a, b]) = (cat.bind(dog, a, b))() = dog.cat(a, b)