this一直都是一个谈论的话题,其实我感觉this不是仅仅看一篇this的总结就能理解的东西;this是一个综合的知识体现:它需要你对原型、作用域和引用的综合理解,才能透彻理解这个多变的this;小菜我在这里献丑一下了;
为什么要用this
首先看两端代码:
(1)about this
function sayName(){
return this.name.toUpperCase();
}
function intro(){
var myself = "Hello,I am "+sayName.call(this);
console.log(myself);
}
var me = {
name : "jack"
}
var you ={
name : "rose"
}
sayName.call(me);
sayName.call(you);
intro.call(me); //Hello,I am JACK
intro.call(you);//Hello,I am ROSE
(2)显示传递上下文
function sayName(context){
return context.name.toUpperCase();
}
function intro(context){
var myself = "Hello,I am "+sayName(context);
console.log(myself);
}
var me = {
name : "jack"
}
var you ={
name : "rose"
}
sayName(me);
sayName(you);
intro(me); //Hello,I am JACK
intro(you); //Hello,I am ROSE
上面两段代码,可以看到一样的效果;
- 通过this隐式”传递”一个对象的应用,看起来简洁、容易复用;
- 显式传递上下文,结构复杂的话,会使代码变得越来越复杂,或许参数context就看着越来越混乱
this的误解
误解:this理解成指向函数自身
上代码:
function foo(num){
console.log("foo: "+num);
this.count++;
}
foo.count = 0;
var i;
for (var i = 0; i < 10; i++) {
if (i>5) {
foo(i);
}
}
console.log(foo.count);
console.log("window count:"+this.count);
结果
foo: 6
foo: 7
foo: 8
foo: 9
0
window count:NaN
结果可以看出:
- foo.count依然是0,如果this指向自身的话,怎么不是4呢!!
- 如果是this指向自身,this.count又怎么会是NaN呢
原因:这里的this.count其实是在window(也就是global)里面创建了一个count,并不是foo.count,然而初始值是Undefined,所以,在this.count++的过程返回了NaN;
this的绑定规则
通过上面我们可以看出,函数中的this到底指的谁,其实完全取决于函数调用的位置;
默认绑定
function foo(){
console.log(this.a);
}
var a=2;
foo();//2
这里foo()是window在调用(window.foo());所以这里的this也就是window,this.a自然是全局的2了;当然,严格模式下会报错的;
隐式绑定
var a = 3;
function test(){
console.log(this.a);
}
var obj = {
a:2,
foo:test
}
obj.foo();//2
结果:obj.foo()是2,分析可以看出,test()函数是不属于obj对象,跟obj没毛线关系,就像声明了一个函数一样;在obj对象里面,有一个属性foo引用,指向test这个函数;所以obj调用foo()的时候,this指向了obj对象,this.a就是2喽;
再看一个隐式的例子
var a = "Hello World";
function test(){
console.log(this.a);
}
var obj = {
a:2,
foo:test
}
var bar = obj.foo;
bar(); //Hello World
这里输出的是全局的Hello World;这也更说明obj和test没毛线关系,obj.foo是函数名,一个引用类型,赋值给bar,这时候bar也指向test;所以相当于全局调用test()喽;
在看一个,传入回调函数会怎样
var a = "Hello World";
function test(){
console.log(this.a);
}
function doFoo(fn){
fn(); //调用位置
}
var obj = {
a:2,
foo:test
}
doFoo(obj.foo); //Hello World
- 这里把obj.foo,(函数的引用/名字)作为doFoo函数的参数,在里面才调用;这时候同上一个例子一样,也相当于执行test();
再再看一个
var name = "Hello";
var obj = {
name: 'World',
foo: function() {
console.log(this.name);
},
foo2: function() {
console.log(this.name);
setTimeout(this.foo, 1000);
}
}
obj.foo2();
- 结果先输出World,1S后输出Hello;
- 第一次是 foo2 中直接输出”World”,指向 obj 这个对象
setTimeout 也只是一个函数而已,把 this.foo 当作一个参数传给 setTimeout 这个函数,就像上面的fn,和obj无关了,所以输出Hello
显示绑定
function foo(){
console.log(this.a);
}
var obj ={
a:2
}
foo.call(obj);//2
- 强制把它的this绑定到obj上;
new绑定
使用new初始化构造函数,在这个过程一定要透彻的知道做了那些事!!!
使用new操作符经历4个步骤:
- 创建一个新对象;
- 将构造函数的作用域给新对象(因此this就指向了这个新对象);
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
看个面试题或许就明白了
function MyObj(){
this.p.pid++;
}
MyObj.prototype.p = {"pid":0};
MyObj.prototype.getNum = function(num){
return this.p.pid + num;
}
var _obj1 = new MyObj();
var _obj2 = new MyObj();
console.log(_obj1.getNum(1)+_obj2.getNum(2)); //7
- 结果是7,分析:在new MyObj()过程都会执行this.p.pid++,根据原型链,自己找不到去原型里面找,这里的pid是引用类型所以每次都会++,this.p.pid++最终得到2,同时this的作用域分别是_obj1和_obj2;
当你好好去理解面向对象里面的东西,这些就迎刃而解了,当然还有箭头函数指定this,这个我还没有深入研究;以后会补上;