javascript笔记:深入分析javascript里对象的创建(上)续篇

来源:互联网 发布:java线程间的通信方式 编辑:程序博客网 时间:2024/04/30 04:38
今天回来一看我的博客居然有这么多人推荐真是开心极了,看来大家对我的研究有了认可,写博客的动力越来越大了,而且我发现写javascript在博客园里比较受欢迎,写java的受众似乎少多了,可能博客园里java的程序员要少点吧,也可能是javascript使用的人太多了吧,不过写javascript的文章还是比较大众化,以后多写写javascript了。

  本篇文章不是《深入分析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
functionOuterObj(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);
    }
    varInnerObj = 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);
        }
    }
     
    varinnerVal = newInnerObj('101','InnerObj','Test InnerObj');
    console.log(innerValinstanceof InnerObj);//true
    innerVal.sayHello();//id:101@!@name:InnerObj@!@teststring:Test InnerObj
    InnerObj('102','InnerObj0','Test InnerObj0');
}
 
varoutObj = newOuterObj('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:undefinedwindow.sayHello();//id:undefined@!@name:@!@teststring:undefinedobj.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 Obj4window.sayHello();//id:004@!@name:My Name is obj4@!@teststring:Test Obj4obj.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);//undefinedconsole.log(window.name);//(an empty string)console.log(window.teststring);//undefinedvar obj = new Obj('004','My Name is obj4','Test Obj4');sayHello();//id:undefined@!@name:@!@teststring:undefinedwindow.sayHello();//id:undefined@!@name:@!@teststring:undefinedobj.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:undefinedwindow.sayHello();//id:undefined@!@name:@!@teststring:undefinedobj.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的值为空,而其他都是未定义。

  写完了,总结下吧:我现在定位自己是在做研究,所以我时刻要求自己要严谨,虽然自己本身有粗心毛病。我写博客也是想以文会友,希望大家多交流多提建议,大家一起进步了。