THIS
this:
函数的执行主体
this的创建
首先需要明确,
this是执行上下文的一个属性,而不是说是某个变量对象的属性,是一个与执行上下文相关的特殊指针。在实际开发中应该避免eavl函数,所以我们讨论主要的是全局执行上下文和函数私有执行上下文中的this
function add(a,b){
console.log(this);
return a + b;
}
add(1,2); // 生成函数执行上下文,并入栈执行
如何判断执行上下文this的指向,其中最重要的一项指标就是判断
函数执行的服务对象,这个服务对象就是当前函数执行的主体。但像上述 add 函数执行不知道为谁服务的情况下,那么他的this就会指向window。(不清不楚全按window处理)
this指向问题
1. 事件绑定
给当前元素的某一个
事件行为绑定函数,方法执行时的this就是绑定的当前元素本身(IE6~8,基于attachEvent实现的DOM2事件绑定,绑定的方法中的this不是操作的元素,而是window)
document.body.onclick = function(){
// this -> body
};
document.body.addEventListener('click',function(){
// this -> body
});
2. 函数执行
函数执行时,看函数执行之前是不是通过
点的方式调用,有点那点前面是谁,执行主体this就是谁,没有点一律按window算(在JS的严格模式下,没有点,方法中的this是undefined)
function fn(){
console.log(this);
}
let obj = {
name:'xxx',
fn:fn
}
fn(); //this:window
obj.fn(); //this:obj
-
自执行函数中的this一般都是window,严格模式下是undefined
-
回调函数中的this一般都是window,严格模式下是undefined
setTimeout中传入的是回调函数,所以这里的this是window,而不是obj。
let obj = {
name:'xxx',
fn(){
setTimeout(function(){
this.name = "Tom";
},1000);
}
}
obj.fn() //this => window
// 将setTimeout中的回调函数写成箭头函数的形式,就可以解决这个问题了
3. 箭头函数的上下文中没有this
箭头函数在创建执行上下文的时候,在初始化上下文环境时,不会创建this属性,所以在箭头函数中如果使用到
this会根据作用域链向上级上下文寻找
let obj = {add};
function add(a,b){
console.log(this);
let fn = (a,b) => {
console.log(this); // obj
return a + b;
}
return fn(a,b); // obj
}
obj.add(1,1); // 2
4. 构造函数时的this
函数在new执行时,会创建一个实例对象,而this指向的就是这个实例对象
function fn(){
this.a = 1;
this.b = 1;
this.add = function(){
return this.a + this.b;
}
}
let obj = new fn(); // obj = {a:1,b:1,add} 函数new执行创建的实例对象
obj.add(); //2
5. 基于call、apply和bind改变this的指向
Function.prototype上提供了call、apply和bind方法来强制改变this的指向,但是这个操作对于箭头函数是无效的(箭头函数中没有自己的this)
let ArrayLike = {0:'a',1:'b',2:'c',length:3};
[].forEach.call(ArrayLike,(item)=>{ // 基于call方法让类数组能够数组原型上的方法
console.log(item);
})
面试题
例题1
let obj = {
fn: (function () {
return function () {
console.log(this);
}
})()
};
obj.fn(); //obj
let fn = obj.fn;
fn(); //window
例题2
var fullName = 'language';
var obj = {
fullName: 'javascript',
prop: {
getFullName: function () {
return this.fullName;
}
}
};
console.log(obj.prop.getFullName()); //undefined
var test = obj.prop.getFullName;
console.log(test()); //language
例题3
var name = 'window';
var Tom = {
name: "Tom",
show: function () {
console.log(this.name);
},
wait: function () {
var fun = this.show;
fun();
}
};
Tom.wait(); //window
例题4
window.val = 1;
var json = {
val: 10,
dbl: function () {
this.val *= 2;
}
}
json.dbl();
var dbl = json.dbl;
dbl();
json.dbl.call(window);
alert(window.val + json.val); //24
例题5
(function () {
var val = 1;
var json = {
val: 10,
dbl: function () {
val *= 2;
}
};
json.dbl();
alert(json.val + val); //12
})();
例题6
var num = 10; // 60 65
var obj = {
num: 20 // 30
};
obj.fn = (function (num) { // 闭包中的 num 21 22 23
this.num = num * 3;
num++;
return function (n) {
this.num += n;
num++;
console.log(num);
}
})(obj.num);
var fn = obj.fn;
fn(5); //22
obj.fn(10); //23
console.log(num, obj.num); //65 30
手写call
/*
* _call : 用于强制转换this的指向
* @params {object} context 执行主体
* @params {any} params 函数执行时传递的参数列表
* @return {any} result 函数执行的返回结果
*/
Function.prototype._call = function(context,...params){
context == null ? context = window : null;
!(/^(function|object)$/i.test(typeof context)) ? context = Object(context) : null;
let result,
key = Symbol('key');
context[key] = this;
result = context[key](...params);
delete context[key];
return result;
}