一、this
JavaScript 中的 this 总是指向一个对象,而具体指向那个对象是在运行时基于函数的执行环境动态绑定的,而非函数声明时的环境
实际应用中 this 的指向大致可以分为以下 4 种:
作为对象的方法调用
作为普通函数调用
构造器调用
class中的this调用
箭头函数中的this调用
call、apply和bind中的this调用
1. 作为对象的方法调用时, this 指向该对象(javascript:void(0)
1 var obj = {
2 a: 1,
3 getA: function(){
4 console.log( this == obj ); // true
5 console.log( this.a ); // 1
6 }
7 };
8 obj.getA();
2. 作为普通函数调用,this 总是指向全局对象 window
1 console.log(this); // Windows
2
3 window.name = "globalName";
4 var getName = function() {
5 return this.name;
6 }
7
8 console.log( getName() ); // globalName
3. 构造器调用, 当用 new 运算符调用函数时,该函数总是会返回一个对象,通常情况下,构造函数里的 this 就指向返回的这个对象
1 var MyClass = function(){
2 this.name = "class";
3 }
4 var obj = new MyClass();
5 console.log( obj.name ); // class
如果使用 new 调用构造器时,构造器显式地返回了一个 object 类型的对象,那么此次运算结果最终会返回这个对象,而不是我么之前期待的 this
1 var MyClass = function(){
2 this.name = "class";
3 return {
4 name: "other"
5 }
6 }
7 var obj = new MyClass();
8 console.log(obj.name); // other
4.class中的this
在es6中,类,是 JavaScript 应用程序中非常重要的一个部分。类通常包含一个 constructor , this可以指向任何新创建的对象。
不过在作为方法时,如果该方法作为普通函数被调用, this也可以指向任何其他值。与方法一样,类也可能失去对接收器的跟踪。
class Hero {
constructor(heroName) {
this.heroName = heroName;
}
dialogue() {
console.log(`I am ${this.heroName}`)
}
}
const batman = new Hero("Batman");
batman.dialogue();
5.箭头函数中的this
es5中的this要看函数在什么地方调用(即要看运行时),通过谁是最后调用它该函数的对象来判断this指向。但es6的箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined。箭头函数的 this 始终指向函数定义时的 this,而非执行时。
let name = "zjk";
let o = {
name : "Jake",
sayName: function () {
console.log(this.name)
},
func: function () {
setTimeout( () => {
this.sayName()
},100);
}
};
o.func() // Jake
使用 call 、 apply或 bind等方法给 this传值,箭头函数会忽略。箭头函数引用的是箭头函数在创建时设置的 this值。
let obj = {
name: "Jake",
func: (a,b) => {
console.log(this.name,a,b);
}
};
func.call(obj,1,2);// 1 2
func.apply(obj,[1,2]);// 1 2
5.call、apply和bind中的this
call、apply、bind 被称之为 this 的强绑定,用来改变函数执行时的this指向,目前所有关于它们的运用,都是基于这一点来进行的。
var name = 'zjk';
function fun() {
console.log (this.name);
}
var obj= {
name: 'jake'
};
fun(); // zjk
fun.call(obj); //Jake
上面的fun.ccall(obj)等价于fun.capply(obj)和fun.cbind(obj)()
二、怎样改变this的指向
改变this的指向,我总结以下的方法:
(1)使用ES6中箭头函数
(2)函数内部使用_this = this
(3)使用apply,call,bind方法
(4)new实例化一个对象
三、apply,call,bind使用
每个函数都包含两个非继承而来的方法:apply()和 call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。
apply()
apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是 Array 的实例,也可以是arguments 对象。
function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1, num2){
return sum.apply(this, arguments); // 传入 arguments 对象
}
function callSum2(num1, num2){
return sum.apply(this, [num1, num2]); // 传入数组
}
console.log(callSum1(10,10)); //20
console.log(callSum2(10,10)); //20
在严格模式下,未指定环境对象而调用函数,则 this 值不会转型为 window。除非明确把函数添加到某个对象或者调用 apply()或 call(),否则 this 值将是undefined。
call()
call()方法与 apply()方法的作用相同,它们的唯一区别在于接收参数的方式不同。在使用call()方法时,传递给函数的参数必须逐个列举出来。
function sum(num1, num2){
return num1 + num2;
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
console.log(callSum(10,10)); //20
call()方法与 apply()方法返回的结果是完全相同的,至于是使用 apply()还是 call(),完全取决于你采取哪种给函数传递参数的方式最方便。
参数数量/顺序确定就用call,参数数量/顺序不确定的话就用apply。
考虑可读性:参数数量不多就用call,参数数量比较多的话,把参数整合成数组,使用apply。
bind()
bind()方法会创建一个函数的实例,其 this 值会被绑定到传给 bind()函数的值。意思就是 bind() 会返回一个新函数。例如:
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue
四.call/apply与bind的区别
执行:
- call/apply改变了函数的this上下文后马上执行该函数
- bind则是返回改变了上下文后的函数,不执行该函数
function add (a, b) {
return a + b;
}
function sub (a, b) {
return a - b;
}
add.bind(sub, 5, 3); // 这时,并不会返回 8
add.bind(sub, 5, 3)(); // 调用后,返回 8
返回值:
- call/apply 返回fun的执行结果
- bind返回fun的拷贝,并指定了fun的this指向,保存了fun的参数。
参数:
apply和call基本类似,他们的区别只是传入的参数不同。apply传入的参数是包含多个参数的数组,call传入的参数是若干个参数列表。
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b);
console.log( this.name );
}
}
var b = a.fn;
b.apply(a,[1,2]) // 3 Cherry
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b);
console.log( this.name );
}
}
var b = a.fn;
b.call(a,1,2) // 3 Cherry
bind方法会创建一个新的函数,当被调用的时候,将其this关键字设置为提供的值,我们必须手动去调用。
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b);
console.log( this.name );
}
}
var b = a.fn;
b.bind(a,1,2)() //3 //Cherry
五.this总结
在浏览器里,在全局范围内this 指向window对象;
在函数中,this永远指向最后调用他的那个对象;
构造函数中,this指向new出来的那个新的对象;
call、apply、bind中的this被强绑定在指定的那个对象上;
箭头函数中this比较特殊,箭头函数this为父作用域的this,不是调用时的this.要知道前四种方式,都是调用时确定,也就是动态的,而箭头函数的this指向是静态的,声明的时候就确定了下来;
apply、call、bind都是js给函数内置的一些API,调用他们可以为函数指定this的执行,同时也可以传参。