this 它总是返回一个对象
- this就是属性或方法“当前”所在的对象
let obj = {
name: "中小余",
age: 18,
gender: "女",
say: function() {
return "hello " + this.name; // this指的name属性所在的对象
}
};
obj.say(); // 'hello 中小余'
- this的指向是可变的
let obj = {
name: "中小余",
age: 18,
gender: "女",
say: function() {
return "hello " + this.name;
}
};
obj.say(); //'hello 中小余'
let obj2 = {
name: "中小鱼",
age: 17
};
obj2.say = obj.say; // 对象的属性可以赋值给另一个对象
obj2.say();//'hello 中小鱼'
使用场合
- 全局环境
在全局环境中使用`this`它指的就是顶层对象`window`
this === window; // true
- 构造函数
构造函数中的this,指的是实例对象。
let Obj = function(val) {
this.val = val;
};
let o = new Obj("hello world");
console.log(o); // { val: 'hello world' }
- 对象的方法
如果对象的方法里面包含this,this的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this的指向。
let obj = {
name: "中小余",
say: function() {
console.log(this.name + "说:我是一只酸菜鱼,又酸又菜又多余");
}
};
obj.say(); // '中小余说:我是一只酸菜鱼,又酸又菜又多余'
* 下面这几种用法,都会改变this的指向:
/*
// 情况一
(obj.foo = obj.foo)() // window
// 情况二
(false || obj.foo)() // window
// 情况三
(1, obj.foo)() // window
*/
* 如果this所在的方法不在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。
let obj = {
name: "中小余",
method: {
say: function() {
console.log(this.name + "说:我是一只酸菜鱼,又酸又菜又多余");
}
}
};
obj.method.say(); // 'undefined说:我是一只酸菜鱼,又酸又菜又多余'
使用this的注意事项
- 避免使用多层this
由于this的指向是不确定的,所以切勿在函数中包含多层的this
解决办法:将this的值赋值给一个固定的变量。
let obj = {
name: "中小余",
method: function() {
let _this = this;// 将外层this赋值给变量_this ,然后在内层使用_this,就不会发生this指向的改变
let say = (function() {
console.log(_this.name + "说:我是一只酸菜鱼,又酸又菜又多余");
})();
}
};
obj.method();//'中小余说:我是一只酸菜鱼,又酸又菜又多余'
- 避免数组处理方法(map forEach)中的this
数组处理方法中使用this有两种处理方式:
* 将this的值赋值给一个固定的变量(同上)
* 将this当作forEach方法的第二个参数固定它的运行环境
let obj = {
name: "中小余",
faves: ["漫画", "动漫", "鲁迅的文字"],
method: function fun() {
this.faves.forEach(function(item) {
console.log(this.name + "喜欢" + item);
}, this);
}
};
obj.method();
//'中小余喜欢漫画'
//'中小余喜欢动漫'
//'中小余喜欢鲁迅的文字'
- 避免回调函数中的this
回调函数中的this往往会改变指向,最好避免使用
// 为了解决这个问题,绑定this方法,也就是使得this固定指向某个对象,减少不确定性。
绑定this的方法
JavaScript提共了三个方法用来绑定this的指向:call
、apply
、 bind
Function.prototype.call()
- Function.prototype.call()
函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。
let obj = {};
let fun = function() {
console.log(this);
};
fun(); // window
fun.call(obj); // {}
// call方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象。
let str = "字符串";
let obj = { str: "对象属性" };
let fun = function() {
console.log(this);
};
fun.call(); // window
fun.call(null);// window
fun.call(undefined);// window
fun.call(window);// window
fun.call(obj);{str:'对象属性'}
//如果call方法的参数是一个原始值,那么这个原始值会自动转成对应的包装对象,然后传入call方法
let fun = function() {
return this;
};
fun.call(1); //[Number: 1]
// call方法还可以接受多个参数
fun.call(thisValue, arg1, arg2, ...)
// call的第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数
let fun = function(val1, val2) {
return val1 + val2;
};
fun.call(this, 1, 2); // 3 call方法指定函数fun内部的this绑定当前环境(对象)
- call方法的一个应用: 调用对象的原生方法
let obj = {};
obj.hasOwnProperty("toString");//false
// 覆盖掉继承的 hasOwnProperty 方法
obj.hasOwnProperty = function() {
return true;
};
obj.hasOwnProperty("toString");// true
Object.prototype.hasOwnProperty.call(obj, "toString");//false
Function.prototype.apply()
- apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。
func.apply(thisValue, [arg1, arg2, ...])
// apply方法的第一个参数也是this所要指向的那个对象,如果设为null或undefined,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在call方法中必须一个个添加,但是在apply方法中,必须以数组形式添加
let fun = function(val1, val2) {
return val1 + val2;
};
fun.call(this, 1, 2); // 3
fun.apply(this, [1, 2]); // 3
apply可以做的事
- 结合使用apply方法和Math.max方法,找出数组最大元素
let arr = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 51, 53, 59];
Math.max.apply(this, arr); //59
- 将数组的元素变为undefined
//空元素与undefined的差别在于,数组的forEach方法会跳过空元素,但是不会跳过undefined。因此,遍历内部元素的时候,会得到不同的结果
let arr = [1, 2, 3, , 5];
function p(i) {
console.log(i);
}
arr.forEach(p); // 1 2 3 5
Array.apply(this, arr).forEach(p);// 1 2 3 undefined 5
- 转换类似数组的对象
// 利用数组对象的slice方法,可以将一个类似数组的对象(比如arguments对象)转为真正的数组
Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0: 1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]
// 参数都是对象 且对象有length属性才行
- 绑定回调函数的对象
let obj = new Object();
obj.f = function() {
console.log(this === obj);
};
let fun = function() {
obj.f.apply(obj);
};
fun(); // true
Function.prototype.bind()
bind()
方法用于将函数体内的this
绑定到某个对象,然后返回一个新函数
let date = new Date();
let p = date.getTime.bind(date);
p;// [Function: bound getTime] 返回的是一个新函数
p(); // 1618481231883
- bind方法的参数就是所要绑定this的对象
let obj = {
name: "中小余",
age: 18,
method: function() {
this.age++;
}
};
let fun = obj.method.bind(obj);
fun();
obj; // { name: '中小余', age: 19, method: [Function: method] } age已经加了
- 也可以绑定到其它对象上
let obj = {
name: "中小余",
age: 18,
method: function() {
this.age++;
}
};
let obj2 = {
age: 16
};
let fun = obj.method.bind(obj2);
fun();
obj;// { name: '中小余', age: 18, method: [Function: method] }
obj2; //{ age: 17 }
- bind()还可以接受更多的参数,将这些参数绑定原函数的参数。
let add = function(x, y) {
return x * this.m + y * this.n;
};
let obj = {
m: 2,
n: 2
};
let newAdd = add.bind(obj, 5);
newAdd(5); // 20
- 如果bind()方法的第一个参数是null或undefined,等于将this绑定到全局对象,函数运行时this指向顶层对象(浏览器为window)。
function add(x, y) {
return x + y;
}
let plus5 = add.bind(null, 5);
plus5(10) // 15
注意事项
- bind()方法每运行一次,就返回一个新函数,这会产生一些问题。比如,监听事件的时候,不能写成下面这样。
element.addEventListener('click', o.m.bind(o));
element.removeEventListener('click', o.m.bind(o));
//click事件绑定bind()方法生成的一个匿名函数。这样会导致无法取消绑定,所以下面的代码是无效的。
* 正确的写法:
var listener = o.m.bind(o);
element.addEventListener('click', listener);
// ...
element.removeEventListener('click', listener);
- 结合回调函数使用
回调函数是 JavaScript 最常用的模式之一,但是一个常见的错误是,将包含this的方法直接当作回调函数。
let obj = {
name: "中小余",
faves: ["漫画", "动漫", "鲁迅的文字"],
method: function() {
this.faves.forEach(function(n) {
console.log(this.name + "喜欢" + n);
});
}
};
obj.method();
// 喜欢漫画'
//'喜欢动漫'
//'喜欢鲁迅的文字'
* 改成:
let obj = {
name: "中小余",
faves: ["漫画", "动漫", "鲁迅的文字"],
method: function() {
this.faves.forEach(
function(n) {
console.log(this.name + "喜欢" + n);
}.bind(this)
);
}
};
obj.method();
// '中小余喜欢漫画'
//'中小余喜欢动漫'
//'中小余喜欢鲁迅的文字'
- 结合 call()方法使用
利用bind()方法,可以改写一些 JavaScript 原生方法的使用形式,以数组的slice()方法为例。
[1, 2, 3].slice(0, 1) // [1]
// 等同于
Array.prototype.slice.call([1, 2, 3], 0, 1) // [1]
// 改成写bind()
var slice = Function.prototype.call.bind(Array.prototype.slice);
slice([1, 2, 3], 0, 1) // [1]