this
关于this的指向问题,一直都很困惑,趁此机会就好好的研究一下,this到底指向了什么。
首先要明确的是,this并不是指的函数自身
function foo(){
console.log(this)
}
foo() //window,并不是foo
this是运行时绑定的,而不是编写绑定的,因此this取决于执行的上下文,也就是函数的调用条件。
在《你不知道的javascript(上卷)》中提到了this的四种绑定规则,如果不知道的可以看一下书,也可以参考别人总结好的this的四种绑定规则
其实就可以总结为:
this执行主体:谁把他执行的
1、函数执行,看前面是否有.点
,如果有.点
,.点
前面是谁this就是谁,如果没有.点
,this是window(严格模式是undefined),自执行函数中的this一般都是window
fn() this->window
obj.fn() this->obj
obj.__proto__.fn() this->obj.__proto__
2、给元素的实践行为绑定方法(DOM0/DOM2),事件触发,方法会执行,此时方法中的this一般都是当前元素本身
box.onclick = function(){//=>this->box}
box.addEventListener('onclick',function(){
//this->box
})
3、 ES6箭头函数:this指的是内部的this就是外层代码块的this
// 普通函数
function foo() {
setTimeout(function() {
console.log('id:', this.id);
});
}
var id = 21;
foo.call({ id: 42 }); //21
// 箭头函数
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 }); //42
// 上面的匿名函数定义时所在的执行环境就是foo函数,所以匿名函数内部的this执向始终会和foo函数的this执向保持一致,不会更改,如同下面的这个案例
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo(); //21(没有用call)
4、特殊情况:IE8及以下,基于attachEvent完成DOM2事件绑定,this是不准确的
box.attachEvent('onclick',function(){
//this->window
})
面试题
整理出一批面试题
第1题
var a = 10;
var foo = {
a: 20,
bar: function() {
var a = 30;
console.log(this);
return this.a;
}
};
foo.bar();
(foo.bar)()
(foo.bar = foo.bar)();
(foo.bar, foo.bar)();
// 第2题
function t() {
this.x = 2;
}
t();
console.log(window.x);
// 第3题
var obj = {
x: 1,
y: 2,
t: function() {
console.log(this.x);
}
};
obj.t(); //1
var dog = { x: 11 };
dog.t = obj.t;
dog.t(); //11
show = function() {
console.log("show" + this.x);
};
dog.t = show;
dog.t(); //show11
// 第4题
name = "this is window";
var obj1 = {
name: "php",
t: function() {
console.log(this.name);
}
};
var dog1 = {
name: "huzi"
};
obj1.t(); //php
dog1.t = obj1.t;
var tmp = dog1.t;
tmp(); //this is window
(dog1.t = obj1.t)(); //this is window
dog1.t.call(obj1); // php
// 第5题
var number = 2;
var obj = {
number: 4,
fn1: (function() {
var number;
this.number *= 2; //window.number*=2=4
number = number * 2; //NaN
number = 3; //number=3
return function() {
var num = this.number;
this.number *= 2;
console.log(num);
number *= 3; //number引用的是匿名函数中的number,不是全局number
alert(number);
};
})(),
db2: function() {
this.number *= 2;
}
};
var fn1 = obj.fn1;
alert(number); // 4
fn1(); //4,9,window.number=8
obj.fn1(); //4,27,obj.num=4*2=8
alert(window.number); //8
alert(obj.number); //8
// 第6题
var x = 3;
var y = 4;
var obj = {
x: 1,
y: 6,
getX: function() {
var x = 5;
return (function() {
return this.x;
})();
},
getY: function() {
var y = 7;
return this.y;
}
};
console.log(obj.getX()); //3
console.log(obj.getY()); //6
//第7题
var name = "the window";
var object = {
name: "My Object",
getName: function() {
return this.name;
}
};
object.getName();
object.getName();
(object.getName = object.getName)();
(object.getName, object.getName())();
// 第8题
var a = 10;
var obt = {
a: 20,
fn: function() {
var a = 30;
console.log(this.a);
}
};
obt.fn(); //20
obt.fn.call(); //10
obt.fn(); // 20
// 左边括号内是一个逗号表达式,逗号表达式的值是逗号分隔的最后一个表达式的值,在这里就是obt.fn。
// 注意在js代码中书写函数名的时候,除了函数定义,函数名后面带有括号的情况,是对函数的调用(执行);
// 如果不带括号,则是获得了一个指向函数的指针,但并没有实际执行它。这里的逗号表达式返回了obt.fn的指针,它正确的指向了这个函数,
// 但是与成员运算符相比,有一个重大的区别:丢失了上下文环境,即函数内部的this不再绑定为obt,而是指向全局对象window。
(obt.fn, obt.fn)(); //10
new obt.fn(); //undefined
// 第9题
function a(xx) {
this.x = xx;
return this;
}
var x = a(5); //x=window
var y = a(6); //y=window
console.log(x.x); // 6
console.log(y.x); // 6
// 第10题
this.x = 9;
var module = {
x: 81,
getX: function() {
return this.x;
}
};
module.getX(); // 81
var retrieveX = module.getX;
retrieveX(); //9
var boundGetX = retrieveX.bind(module);
boundGetX(); //81
// 第11题
var A = function(name) {
this.name = name;
};
var B = function() {
A.apply(this, arguments);
};
B.prototype.getName = function() {
return this.name;
};
var b = new B("sven");
console.log(b.getName()); //seven
// 第12题
function foo(el) {
console.log(el, this.id);
}
var obj = {
id: "awesome"
};
[1, 2, 3].forEach(foo, obj);
// 第13题
function foo() {
console.log(this.a);
}
var obj1 = {
a: 2,
foo: foo
};
var obj2 = {
a: 3,
foo: foo
};
obj1.foo(); //2
obj2.foo(); // 3
obj1.foo.call(obj2); //3
obj2.foo.call(obj1); //2
第14题
var a=10;
var foo={
a:20,
bar:function(){
var a=30;
console.log(this)
console.log(this.a)
return this.a;
}
};
foo.bar() //foo,this.a==20;
(foo.bar)() // window,this.a==10
(foo.bar=foo.bar)() // window,this.a==10
(foo.bar,foo.bar)() // window,this.a==10
第15题 //解析
function Foo() {
getName = function () { alert (1); };
return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
//请写出以下输出结果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
//参考:4