web前端之悟透JavaScript二:JavaScript真经(对象)
任何一个程序都会在一个原始的环境中开始运行,这个原始的环境就被称为全局环境。全局环境中包含了一些预定义元素,这些元素对于我们的程序来说是自然存在的,他们本来就存在着,我们拿来就可以使用了。
在JavaScript里的全局环境就是一个对象,这个对象是JavaScript运行环境的根。对于浏览器中的JavaScript来说,这个根对象就是我们熟知的window对象。对于全局的JavaScript语句来说,window对象就相当于当前的作用域。
当我们写下:
var myName=”leadzen”;
就是定义了window作用域的一个变量myName,而当我们写下:
myName=”leadzen”;
就是定义了window对象的一个属性myName
这里,“window作用域的一个变量myName”和“window对象的一个属性myName”几乎等价。对于全局的JavaScript语句来说,加不加var都无所谓,但是对于一个函数体内的语句,有没有“var”就要小心了。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>有无var的区别</title>
<script type="text/javascript">
var yourName="王菲";
myName="fzw";
alert(myName+" like "+yourName);//fzw like 王菲
ChangeNames();
function ChangeNames(){
alert('Your old name is '+yourName);//undefined
alert('My old name is '+myName);//fzw
var yourName="张三";
myName="李四";
alert(myName+" like "+yourName);//李四 like 张三
};
alert(myName+" like "+yourName);//李四 like 王菲
</script>
</head>
<body>
</body>
</html>
显然使用var修饰的yourName标识符在函数内外是两个东西,外面的“王菲”不会因为使用ChangeNames函数内改成“张三”而改变,回到外面还是“王菲”。而myName没有使用var修饰,就是一个东西了,函数内的修改也能在函数外发生改变。
值得注意的是,changeNames函数中的第一句,我们本来希望最外面的那个yourName的值,但此时的输出却表现为undefined,而第二句myName又是我们希望的值。
解释: var定义的是作用域上的一个变量,而没有var的标识符却可能是全局根对象的一个属性。当代码运行在全局作用域时,作用域就是根对象window,所以,有没有“var”都无所谓。当然,不同的JavaScript执行引擎对此可能有不同的实现方式,但都可以大致这么来理解。
但是当代码运行进入一个函数时,JavaScript会创造一个新的作用域,来作为当前作用域的子作用域。然后,当全局变量作用域切换为一个新建的子作用域,开始执行函数逻辑。JavaScript执行引擎会把此函数内的逻辑代码,当一个代码段单元来分析与执行。
在第一步的预编译分析中,JavaScript执行引擎将所有定义式函数直接创建为作用域上的函数变量,并将其值初始化为定义的函数代码逻辑,也就是为其建立了可调可用的函数变量。而对于所有“var”定义的变量,也会在第一步的预编译中创建起来,并将初始值设为undefined。
随后,JavaScript开始解释执行代码。当遇到函数名或变量名的使用时,JavaScript执行引擎会首先在当前作用域查找函数或变量,如果没有就到上层作用域查找,以此类推。因此,前面的语句引用后面的语句定义的“var”变量时,该变量其实已经存在,只是初始值是undefined。
因此,用var定义的变量只对本作用域有效,尽管此时上层作用域有同名的东西,都与本作用域的var变量无关。退出本作用域之后,此var消失,回到原来的作用域该有啥还是有啥。
王菲就是王菲,她更喜欢父亲取得名字,当回到自己可以控制的作用域时,当然就立马改回来。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>caller属性</title>
<script type="text/javascript">
function WhoCallMe(){
alert("My caller is "+WhoCallMe.caller);//输出自己的caller
};
function CallerA(){
WhoCallMe();
};
function CallerB(){
WhoCallMe();
};
alert(WhoCallMe.caller);//输出null
WhoCallMe();//输出null
CallerA();
CallerB();
</script>
</head>
<body>
</body>
</html>
如果函数的caller属性是Null,表示函数没有被调用或者是被全局代码调用。函数的caller属性值实际上是动态变化的。函数的初始caller值都是null,当调用一个函数时,如果代码已经运行在某个函数体内,JavaScript执行引擎会将被函数的caller属性设置为当前函数。在退出被调函数作用域时,被调函数的caller属性又会被恢复为null值。
this关键字表示函数正在服务的”这个”对象,后续我们会进行讲述。
arguments对象,从字面上看表示调用当前函数的参数们。除了我们可以直接使用函数参数定义列表中的那些标识符来访问之外,还可以用arguments对象按数组方式来访问参数,尽管arguments对象并非一个真正的数组。
值得注意的是:用eval()函数动态执行的代码并不创建新的作用域,期待吗就是在当前作用域中执行的。eval()中的动态代码可以访问到当前作用域的this,arguments等对象,因此可以使用evel()实现一些高级的多态和动态扩展方面的应用。
现在开始我们的对象之旅:
前面我们说,在JavaScript中只有object和function两种东西才有对象化的能力。任何人一个函数都可以为其动态地添加或取出属性,这些属性可以是简单的类型,可以是对象,可以是其他的函数。也就是说,函数具有对象的全部特征,你完全可以把函数作对象来使用。其实,函数就是对象,只不过比一般对象多了一个“()”操作符,这个操作符用来执行函数的逻辑,即函数本身还可以被调用,一般对象却不可以被调用,除此之外完全相同。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>对象</title>
<script type="text/javascript">
function Sing(){
alert(Sing.author+": "+Sing.poem);
};
Sing.author="李白";
Sing.poem="床前明月光,疑是地上霜。举头望明月,低头思故乡。";
Sing();
Sing.author="李时珍";
Sing.poem="12345,上山打老虎,老虎打不到,打到小松鼠。";
Sing();
</script>
</head>
<body>
</body>
</html>
在这段代码中,Sing函数定义之后,又给Sing函数动态添加了author和poem属性。将author和poem属性设为不同的作者和诗句,在调用Sing()时就能显示出不同的结果。通过这个我们理解到了function类型都是和object类型一样的东西,我们称之为对象。
在面向对象的编程里,数据和代码的有机结合就构成了对象的概念。其主要分为两部分:一个是对象内的世界,一个是对象外的世界。对象天生具有自私的一面,外面的世界未经允许是不可访问对象内部的。对象也有大方的一面,他对外提供属性和方法,也为他人服务。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>this</title>
<script type="text/javascript">
function WhoAml(){
alert("I'm "+this.name+" of "+typeof(this));
};
WhoAml();//此时this是根对象window,其name属性为空字符串,输出I'm of object
var BillGates={name:"Bill Gates"};
BillGates.WhoAml=WhoAml;//将函数WhoAml作为BillGates的方法
BillGates.WhoAml();//I'm Bill Gates of object.
var SteveJobs={name:"Steve Jobs"};
SteveJobs.WhoAml=WhoAml;
SteveJobs.WhoAml();//I'm Steve Jobs of object.
WhoAml.call(BillGates);//直接将BillGates作为this,调用WhoAml I'm Bill Gates of object.
WhoAml.call(SteveJobs);//I'm Steve Jobs of object.
BillGates.WhoAml.call(SteveJobs);//将SteveJobs作为this,却调用BillGates的方法 I'm Steve Jobs of object.
SteveJobs.WhoAml.call(BillGates);// I'm Bill Gates of object.
WhoAml.WhoAml=WhoAml;//将WhoAml函数设置为自身的方法。
WhoAml.name="WhoAml";
WhoAml.WhoAml();//此时的this是WhoAml自身 I'm WhoAml of object.
({name:"nobody",WhoAml:WhoAml}).WhoAml(); //创建一个匿名对象,并调用这个方法I'm nobody of object.
</script>
</head>
<body>
</body>
</html>
从上面代码中可以看出,同一个函数可以从不同的角度来调用,this并不一定是函数本身所属的对象。this只是在任意对象和function元素结合的一个概念,这个结合比起一般对象语言的默认结合更加灵活,显得更加超然和洒脱。
我们使用json形式来创建对象:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>如何建立一个对象</title>
<script type="text/javascript">
var o={};//创建一个没有任何属性的对象
var parent={name:"angle",age:18,married:false};//创建一个对象并设置属性及初始值
var speaker={text:"Hello World",say:function(){alert(this.text)}};//创建一个对象并设置属性和方法。
var company={
name:"Microsoft"
,product:"soft"
,chairman:{name:"Bill Gates",age:53}
,employees[{name:"Angel",age:25},{name:"Hanson",age:23}]
,readme:function(){
document.write(this.name+" product"+this.product);
}
};
</script>
</head>
<body>
</body>
</html>
为什么逗号写在最前面,查看详情
json的形式就是用“{}”包括起来的项目列表,每一个项目之间用“,”分隔,而项目就是使用“:”分隔属性名和属性值。对象可以作为一个json形式的字符串,在网络间自由传递和交换信息。当需要使用json字符变成JavaScript对象时,只需要使用eval函数这个强大的数码转化引擎,就立即能得到一个JavaScript内存对象
构造对象:
function MyFunc(){};//定义一个空函数
var anObj1=new MyFunc();//使用new操作符,借助MyFunc函数创建一个对象
var anObj2=new MyFunc;//函数也可以没有括号,但仍然调用该函数
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Document</title>
<script type="text/javascript">
function Person(name){
this.name=name;
this.SayHello=function(){
alert("Hello,I'm"+this.name);
};
};
function Employee(name,salay){
Person.call(this,name);//调用父构造函数
this.salay=salay;
this.ShowMeTheMoney=function(){
alert(this.name+" $ "+this.salay);
};
};
var BillGates=new Person("Bill Gates");
var SteveJobs=new Employee("Steve Jobs",1234);
BillGates.SayHello();
SteveJobs.SayHello();
SteveJobs.ShowMeTheMoney();
alert(BillGates.constructor == Person);
alert(SteveJobs.constructor == Employee);
alert(BillGates.SayHello== SteveJobs.SayHello);
</script>
</head>
<body>
</body>
</html>