本篇文章不是《深入分析javascript里对象的创建》的下篇,而是对我上篇写错的地方以及有童鞋不明白的地方做做解答。
首先是下面一位童鞋的留言:
写的精彩啊.关于// 深入分析函数式 2有点困惑. window.sayHello();//为什么输出的会是//id:102@!@name:InnerObj0@!@teststring:Test InnerObj0而不//id:007@!@name:My Name is obj7@!@teststring:Test Obj7..求解
上篇里我写这个例子目的是要加深对“不论哪里直接调用函数,里面的this都是指向全局的”运用,因此写了一个函数内部的方法来验证,希望大家能熟记这个原理,其实这个是可以用javascript原理来进行解释的。
我开始思考这个问题时候,想这个产生的原因是不是因为function定义的方式不同所产生的(这个想法很让我兴奋,我前面有篇博文叫《javascript笔记:javascript里面不同function定义的区别》,其实这篇文章里面讨论的问题我到现在还有点模拟两可,究其原因还是没有找到好的实例代码说明这个问题),如是我改了下代码:
// 深入分析函数式 2
function
OuterObj(id1,name1,teststring1)
{
this
.id = id1;
this
.name = name1;
this
.teststring = teststring1;
this
.sayHello =
function
()
{
console.log(
'id:'
+
this
.id +
'@!@name:'
+
this
.name +
'@!@teststring:'
+
this
.teststring);
}
var
InnerObj =
function
(id2,name2,teststring2)
{
this
.id = id2;
this
.name = name2;
this
.teststring = teststring2;
this
.sayHello =
function
()
{
console.log(
'id:'
+
this
.id +
'@!@name:'
+
this
.name +
'@!@teststring:'
+
this
.teststring);
}
}
var
innerVal =
new
InnerObj(
'101'
,
'InnerObj'
,
'Test InnerObj'
);
console.log(innerVal
instanceof
InnerObj);
//true
innerVal.sayHello();
//id:101@!@name:InnerObj@!@teststring:Test InnerObj
InnerObj(
'102'
,
'InnerObj0'
,
'Test InnerObj0'
);
}
var
outObj =
new
OuterObj(
'007'
,
'My Name is obj7'
,
'Test Obj7'
);
outObj.sayHello();
//id:007@!@name:My Name is obj7@!@teststring:Test Obj7
sayHello();
//id:102@!@name:InnerObj0@!@teststring:Test InnerObj0
window.sayHello();
//id:102@!@name:InnerObj0@!@teststring:Test InnerObj0
console.log(id);
//102
console.log(name);
//InnerObj0
console.log(teststring);
//Test InnerObj0
|
结果一样,哎,实在有点沮丧。不过回过头来思考“不论哪里直接调用函数,里面的this都是指向全局的”这句话,似乎又更加理解了这句话的含义。我记得前篇博文里说道“javascript是可以做面向对象编程的,我们不能把它当做面向过程的语言”,我想javascript设计人员不可能在设计这套语言时候凭空做了两套函数执行方式(就是我自己擅自定义的构造函数式和函数式),理由很简单,因为没有任何书籍这么写道,从我多年开发的经验我感觉一群优秀的程序员,不可能会傻到平白无故的做两套解析器,二者一定有关系。关系在那里呢?答案就是window。
下面是我的分析:大家都知道页面嵌入了javascript(浏览器支持javascript的意思),javascript解析器会自动构建window对象,这个道理可以这么理解,浏览器的javascript解析器里早就写好了window类,页面一加载window对象就被new了一下,为了简便程序员的开发,这个window对象一般可以省略,但是它确实肯定以及一定存在我们的页面里。此外javascript有个我们常常会忽视的特点:极晚绑定,也就是说在javascript对象实例化后我们任然可以为该对象定义方法和属性。
那么我可以这么猜想了,其实javascript里面都是通过new来产生实例对象,window也不例外,而且方法的调用都是[某某对象].[方法]的格式,我们平时直接在script标签里面写function方法,然后直接调用其实就是通过极晚绑定来为window对象定义新方法,在javascript没有不属于任何对象的function,当javascript发现某个方法被调用时候找不到该方法的对象,javascript自动会把这个弃儿丢给window这个福利院。
哈哈,这个理解似乎完美,不知道对不对,请大虾人多多指教啊。
此外还有位童鞋指出了我前一篇博文里面的错误,哎,大庭广众下丢人,所以今天熬夜赶写博文纠正啊。这位童鞋的留言如下:
@深蓝色梦想 楼主的// 深入分析构造函数1 下面的sayHello()和window.sayHello()的结果错了,结果应该都是 id:004@!@name:My Name is obj4@!@teststring:Test Obj4 因为实例化的时候传参都传给了windows对象(id,name,teststring),而调用sayHello方法的时候里面的this也指向了windows对象的属性,所以结果不会是undefined... 回楼上的,因为InnerObj('102','InnerObj0','Test InnerObj0'); 楼主已经说了:直接调用function,this指针都是指向window,也就是全局对象. 这里是直接调用,所以指向了windows,所以调用window.sayHello()就会得到它自己(window)的属性值... 而007那个值是属于outObj对象的.
上篇博文跨天写的,第二天对我前一天写的实例没有认真回忆体会,其实深入分析构造函数1的源代码是:
function Obj(id,name,teststring) { id = id; name = name; teststring = teststring; sayHello = function(){ console.log('id:' + this.id + '@!@name:' + this.name + '@!@teststring:' + this.teststring); } } var obj = new Obj('004','My Name is obj4','Test Obj4'); sayHello();//id:undefined@!@name:@!@teststring:undefined window.sayHello();//id:undefined@!@name:@!@teststring:undefined obj.sayHello();//obj.sayHello is not a function [在此错误处中断] obj.sayHello();
结果是id和teststring为undefined,而name为空值。当时写时候这个方法结果没有反应出我的初衷,但是当时没有深入分析就休息了,第二天赶写时候根据模糊的记忆,觉得变量命名要明确不要重复,这样能更好的讲出自己的主题,没想确犯了低级错误。下面我就这个原始写法做深入分析。
上面代码问题就是id、name和teststring到底是在那个作用域下定义,javascript有个定义变量的关键字var,但是javascript同时也可以对没有做var定义的变量进行正确的解析,如果变量本身作用域里找到该变量的var定义,javascript解析器会一直向上一层作用域查找知道找到该变量的var定义为止,但是如果到了window作用域下还没有var定义,那么javascript解析器会自动把这个变量授予window对象。看下面代码,我把程序改为和我上篇博客一样,并且打印window.id等属性:
function Obj(id1,name1,teststring1) { id = id1; name = name1; teststring = teststring1; sayHello = function(){ console.log('id:' + this.id + '@!@name:' + this.name + '@!@teststring:' + this.teststring); } } var obj = new Obj('004','My Name is obj4','Test Obj4'); console.log(window.id); console.log(window.name); console.log(window.teststring); sayHello();//id:004@!@name:My Name is obj4@!@teststring:Test Obj4 window.sayHello();//id:004@!@name:My Name is obj4@!@teststring:Test Obj4 obj.sayHello();//obj.sayHello is not a function [在此错误处中断] obj.sayHello();
结果就是那位童鞋指出的那样,而且我们发现没有this的属性和方法都是属于window的。
疑问来了,为什么function Obj(id,name,teststring)定义的结果就会产生id和teststring为undefined,而name为空值呢?如果按上面分析直观理解的话,就是id、name和teststring都是属于Obj对象,而不是window的,那么id、name和teststring本身都是有值的,但结果为什么又各不相同了?
function Obj(id,name,teststring) { console.log(window.id);//undefined console.log(window.name);//(an empty string) console.log(window.teststring);//undefined id = id; name = name; teststring = teststring; sayHello = function(){ console.log('id:' + this.id + '@!@name:' + this.name + '@!@teststring:' + this.teststring); } } console.log(window.id);//undefined console.log(window.name);//(an empty string) console.log(window.teststring);//undefined var obj = new Obj('004','My Name is obj4','Test Obj4'); sayHello();//id:undefined@!@name:@!@teststring:undefined window.sayHello();//id:undefined@!@name:@!@teststring:undefined obj.sayHello();//obj.sayHello is not a function [在此错误处中断] obj.sayHello();
结果在window下,id、name和teststring的确没有被定义,我测试的页面只有这一个function,那么id、name和teststring只可能被定义在Obj内部了,如是我做了下面测试:
function Obj(id,name,teststring) { id = id; name = name; teststring = teststring; console.log(window.id);//undefined console.log(window.name);//(an empty string) console.log(window.teststring);//undefined console.log(id);//004 console.log(name);//My Name is obj4 console.log(teststring);//Test Obj4 sayHello = function(){ console.log('id:' + this.id + '@!@name:' + this.name + '@!@teststring:' + this.teststring); } } var obj = new Obj('004','My Name is obj4','Test Obj4'); sayHello();//id:undefined@!@name:@!@teststring:undefined window.sayHello();//id:undefined@!@name:@!@teststring:undefined obj.sayHello();//obj.sayHello is not a function [在此错误处中断] obj.sayHello(
看来id、name和teststring的确定义在Obj内部了,但是为什么this.id、this.name和this.teststring打印出了window下的id、name和teststring的结果了?
这个解释很简单,因为this指针在javascript里面都是指向调用该属性和方法的对象,sayHello属于window的,自然this指向的是window下的id、name和teststring了。
这里面还有一个疑难杂症:为什么id和teststring是undefined而那么是空对象了?
如是我做了如下测试:
function TestObj() { console.log(teststring);//teststring is not defined } var testobj = new TestObj();
接下来是id的:
function TestObj() { console.log(id);//id is not defined } var testobj = new TestObj();
最后来是name的:
function TestObj() { console.log(name);//(an empty string) } var testobj = new TestObj();
大家不把id、name和teststring放到自定义function里,而是直接写到window下也是一样结果,这就表明javascript自身对象里就定义好了name属性,因此name的值为空,而其他都是未定义。
写完了,总结下吧:我现在定位自己是在做研究,所以我时刻要求自己要严谨,虽然自己本身有粗心毛病。我写博客也是想以文会友,希望大家多交流多提建议,大家一起进步了。