废话不多说,直接上干货!
var test = (function(){
var that;
var obj = function(){
this.name = "name";
that = this;
}
obj.prototype = {
aaa: function(){
console.log("this is aaa!");
console.log(this,that,test);
},
bbb: function(){
console.log("this is bbb!");
that.aaa.call(this);
}
}
return new obj;
})()
分析一下这个函数包含的知识点:立即执行函数、闭包、原型链、继承、封装、call / apply 的应用等等。接下来我将一点点说一下我的理解,如果说的有不对之处,还请大神指教。
先说好处:
1、只定义了一个全局变量,能减少全局污染。
2、把子函数写进了原型(prototype),方便继承调用。
3、函数看起来干净整洁,可扩展,可相互调用;
4、想不出来了……
从立即执行函数说起:
立即执行函数都干了什么事?实例化 obj 函数对象,并赋值给了 test 变量,所以实际上是 test 变量实例化了 obj 对象。
那 new 都干了什么事?简单的说,初始化一个对象、实现继承;
初始化一个对象我就不说了,就是给一个对象赋予一个预定义好的值;
实现继承:
test 变量的值是 new obj 后赋予的,test 继承了 obj 的属性(如:this.name)及 obj.prototype
怎么验证 test.__proto__ === obj.prototype?改版一下上面的代码
var obj = function(){
this.name = "name";
}
obj.prototype = {
aaa: function(){
console.log("this is aaa!");
},
bbb: function(){
console.log("this is bbb!");
}
}
var test = new obj();
运行代码,在控制台测试:
再说的深一点:obj 是 Function 对象得来的,证明一下:
再深一点:
再深一点:
OK,到头了,原型链的尽头,这也就是我们在 js 中常说的 ”一切皆对象“! 【斜眼笑】
回归正题:再说下那个 that :
这个 that 首先实现了闭包(背一下面试题答案:内层函数用到外层函数的变量就是闭包!)
再加一行代码:(看注释部分)
var test = (function(){
var that;
var obj = function(){
this.name = "name";
that = this;
}
console.log('我是that:' + that); //这里是注释
console.log('我是this:' + this); //这里是注释
obj.prototype = {
aaa: function(){
console.log("this is aaa!");
},
bbb: function(){
console.log("this is bbb!");
}
}
return new obj;
})()
呵呵!
但是没错,js 是顺序执行的,到输出 that 的时候,that 只是定义的,并没有赋值,所以是 undefined;当然,this 的指向取决于调用函数的对象,目前谁调用了 this ?没有!那就是 window。
然后,想看 that 是什么鬼,再动动代码:
var test = (function(){
var that;
var obj = function(){
this.name = "name";
that = this;
console.log( that ); //这里是注释
console.log( this ); //这里是注释
}
obj.prototype = {
aaa: function(){
console.log("this is aaa!");
},
bbb: function(){
console.log("this is bbb!");
}
}
return new obj;
})()
看下结果:
哈哈,有什么发现吗?是不是跟上面输出的 test 一模一样?
好吧,重新捋一下思路:test 立即执行函数返回了实例化的 obj ,执行了定义的 obj 函数,实现了继承。(说到这里等于没说)那要那个闭包的 that 有何用?别急,看一下 obj.prototype 这个对象里面的函数,再动动代码
var test = (function(){
var that;
var obj = function(){
this.name = "name";
that = this;
}
obj.prototype = {
aaa: function(){
console.log("this is aaa!");
console.log(this,that,test);
},
bbb: function(){
console.log("this is bbb!");
that.aaa.call(this);
}
}
return new obj;
})()
test.aaa(); //这里是注释,一口气输出了 this、that、test 三个东西!
看下结果:(先提前呵呵!)
OK,先说 this ,谁调用 aaa 函数?test!那 test 就是它的指向。
再说 that
that 在实例化的时候被赋予了 this ,这个this return 之前,它自己也不知道指向谁,后来(return 之后),它指向了 test ,也就是说 这时候的 that 实际上就是 test。(其实这样说不严谨,在实例化 new 的时候,this 就注定指向了被实例化后的对象,不论这个对象是谁,只是这里多了一步赋值给 that )
test 本身就不多说了,这个像是递归的调用。
利用上面的代码再执行:
test.bbb();
看下结果:
这似乎看不出来什么,好吧,那就动动代码
var test = (function(){
var that;
var obj = function(){
this.name = "name";
that = this;
}
obj.prototype = {
aaa: function(){
console.log("this is aaa!");
this.xixi = "xixixi";
console.log(this.haha + this.xixi);
},
bbb: function(){
console.log("this is bbb!");
this.haha = "hahaha"; //我动了
that.aaa.call(this);
}
}
return new obj;
})()
test.bbb(); //这里是注释,一口气输出了 this、that、test 三个东西!
看下结果:
想必结果大家都知道,但是我想说的是,这里的 call 是怎么运行的,看代码
var test = (function(){
var that;
var obj = function(){
this.name = "name";
that = this;
}
obj.prototype = {
aaa: function(){
// console.log("this is aaa!");
// this.xixi = "xixixi";
// console.log(this.haha + this.xixi);
},
bbb: function(){
console.log("this is bbb!");
this.haha = "hahaha"; //我动了
// that.aaa.call(this); //我被注释了
console.log("this is aaa!"); //下面是挪过来的
this.xixi = "xixixi";
console.log(this.haha + this.xixi);
}
}
return new obj;
})()
test.bbb(); //这里是注释,一口气输出了 this、that、test 三个东西!
看看是不是一样的:
这里 call 的作用就是把 aaa 函数的内容拿过来在 bbb 函数里执行,然后还要用 bbb 函数的 this (毫不留情的那种,霸占了地盘还霸占了夫人【斜眼笑】)哈哈。
apply 的用法也是一样的,只是两者参数格式不一样而已,call 可以传多个参数,apply 只有两个参数,第二参数是个数组。
OK , 最后照抄大神的代码,用 call 实现一下 jQuery 中的 each 函数
var each = function(array,fn){
for(var index = 0; index < array.length; index++ ){
fn.call(array[index], index, array[index]);
}
}
each([2,3,4],function(i,item){
console.log(i, item);
});
好了,今天我就学到这么点,都给大家分享了,有什么不对的地方,还请多多指正。