| 【收藏】---悟透JavaScript(李战) | |
引子
编程世界里只存在两种基本元素,一个是数据,一个是代码。编程世界就是在数据和代码千丝万缕的纠缠中呈现出无限的生机和活力。 数据天生就是文静的,总想保持自己固有的本色;而代码却天生活泼,总想改变这个世界。 你看,数据代码间的关系与物质能量间的关系有着惊人的相似。数据也是有惯性的,如果没有代码来施加外力,她总保持自己原来的状态。而代码就象能量,他存在的唯一目的,就是要努力改变数据原来的状态。在代码改变数据的同时,也会因为数据的抗拒而反过来影响或改变代码原有的趋势。甚至在某些情况下,数据可以转变为代码,而代码却又有可能被转变为数据,或许还存在一个类似E=MC2形式的数码转换方程呢。然而,就是在数据和代码间这种即矛盾又统一的运转中,总能体现出计算机世界的规律,这些规律正是我们编写的程序逻辑。 不过,由于不同程序员有着不同的世界观,这些数据和代码看起来也就不尽相同。于是,不同世界观的程序员们运用各自的方法论,推动着编程世界的进化和发展。 众所周知,当今最流行的编程思想莫过于面向对象编程的思想。为什么面向对象的思想能迅速风靡编程世界呢?因为面向对象的思想首次把数据和代码结合成统一体,并以一个简单的对象概念呈现给编程者。这一下子就将原来那些杂乱的算法与子程序,以及纠缠不清的复杂数据结构,划分成清晰而有序的对象结构,从而理清了数据与代码在我们心中那团乱麻般的结。我们又可以有一个更清晰的思维,在另一个思想高度上去探索更加浩瀚的编程世界了。 在五祖弘忍讲授完《对象真经》之后的一天,他对众弟子们说:“经已讲完,想必尔等应该有所感悟,请各自写个偈子来看”。大弟子神秀是被大家公认为悟性最高的师兄,他的偈子写道:“身是对象树,心如类般明。朝朝勤拂拭,莫让惹尘埃!”。此偈一出,立即引起师兄弟们的轰动,大家都说写得太好了。只有火头僧慧能看后,轻轻地叹了口气,又随手在墙上写道:“对象本无根,类型亦无形。本来无一物,何处惹尘埃?”。然后摇了摇头,扬长而去。大家看了慧能的偈子都说:“写的什么乱七八糟的啊,看不懂”。师父弘忍看了神秀的诗偈也点头称赞,再看慧能的诗偈之后默然摇头。就在当天夜里,弘忍却悄悄把慧能叫到自己的禅房,将珍藏多年的软件真经传授于他,然后让他趁着月色连夜逃走... 后来,慧能果然不负师父厚望,在南方开创了禅宗另一个广阔的天空。而慧能当年带走的软件真经中就有一本是《JavaScript真经》! 回归简单 要理解JavaScript,你得首先放下对象和类的概念,回到数据和代码的本原。前面说过,编程世界只有数据和代码两种基本元素,而这两种元素又有着纠缠不清的关系。JavaScript就是把数据和代码都简化到最原始的程度。 JavaScript中的数据很简洁的。简单数据只有 undefined, null, boolean, number和string这五种,而复杂数据只有一种,即object。这就好比中国古典的朴素唯物思想,把世界最基本的元素归为金木水火土,其他复杂的物质都是由这五种基本元素组成。 JavaScript中的代码只体现为一种形式,就是function。 注意:以上单词都是小写的,不要和Number, String, Object, Function等JavaScript内置函数混淆了。要知道,JavaScript语言是区分大小写的呀! 任何一个JavaScript的标识、常量、变量和参数都只是unfined, null, bool, number, string, object 和 function类型中的一种,也就typeof返回值表明的类型。除此之外没有其他类型了。 先说说简单数据类型吧。 undefined: 代表一切未知的事物,啥都没有,无法想象,代码也就更无法去处理了。 注意:typeof(undefined) 返回也是 undefined。 可以将undefined赋值给任何变量或属性,但并不意味了清除了该变量,反而会因此多了一个属性。 null: 有那么一个概念,但没有东西。无中似有,有中还无。虽难以想象,但已经可以用代码来处理了。 注意:typeof(null)返回object,但null并非object,具有null值的变量也并非object。 boolean: 是就是,非就非,没有疑义。对就对,错就错,绝对明确。既能被代码处理,也可以控制代码的流程。 number: 线性的事物,大小和次序分明,多而不乱。便于代码进行批量处理,也控制代码的迭代和循环等。 注意:typeof(NaN)和typeof(Infinity)都返回number 。 NaN参与任何数值计算的结构都是NaN,而且 NaN != NaN 。 Infinity / Infinity = NaN 。 string: 面向人类的理性事物,而不是机器信号。人机信息沟通,代码据此理解人的意图等等,都靠它了。 简单类型都不是对象,JavaScript没有将对象化的能力赋予这些简单类型。直接被赋予简单类型常量值的标识符、变量和参数都不是一个对象。 所谓“对象化”,就是可以将数据和代码组织成复杂结构的能力。JavaScript中只有object类型和function类型提供了对象化的能力。 没有类 object就是对象的类型。在JavaScript中不管多么复杂的数据和代码,都可以组织成object形式的对象。 但JavaScript却没有 “类”的概念! 对于许多面向对象的程序员来说,这恐怕是JavaScript中最难以理解的地方。是啊,几乎任何讲面向对象的书中,第一个要讲的就是“类”的概念,这可是面向对象的支柱。这突然没有了“类”,我们就象一下子没了精神支柱,感到六神无主。看来,要放下对象和类,达到“对象本无根,类型亦无形”的境界确实是件不容易的事情啊。 这样,我们先来看一段JavaScript程序: var life = {};
for(life.age = 1; life.age <= 3; life.age++) { switch(life.age) { case 1: life.body = "卵细胞"; life.say = function(){alert(this.age+this.body)}; break; case 2: life.tail = "尾巴"; life.gill = "腮"; life.body = "蝌蚪"; life.say = function(){alert(this.age+this.body+"-"+this.tail+","+this.gill)}; break; case 3: delete life.tail; delete life.gill; life.legs = "四条腿"; life.lung = "肺"; life.body = "青蛙"; life.say = function(){alert(this.age+this.body+"-"+this.legs+","+this.lung)}; break; }; life.say(); };
function myfunc()
{ alert("hello"); }; alert(typeof(myfunc));
var myfunc = function ()
{ alert("hello"); }; alert(typeof(myfunc));
var myfunc = function ()
{ alert("hello"); }; myfunc(); //第一次调用myfunc,输出hello myfunc = function () { alert("yeah"); }; myfunc(); //第二次调用myfunc,将输出yeah
function myfunc ()
{ alert("hello"); }; myfunc(); //这里调用myfunc,输出yeah而不是hello function myfunc () { alert("yeah"); }; myfunc(); //这里调用myfunc,当然输出yeah
<script>
function myfunc () { alert("hello"); }; myfunc(); //这里调用myfunc,输出hello </script> <script> function myfunc () { alert("yeah"); }; myfunc(); //这里调用myfunc,输出yeah </script>
function Sing()
{ with(arguments.callee) alert(author + ":" + poem); }; Sing.author = "李白"; Sing.poem = "汉家秦地月,流影照明妃。一上玉关道,天涯去不归 ";Sing(); Sing.author = "李战"; Sing.poem = "日出汉家天,月落阴山前。女儿琵琶怨,已唱三千年 ";Sing();
var anObject = {}; //一个对象
anObject.aProperty = "Property of object"; //对象的一个属性 anObject.aMethod = function(){alert("Method of object")}; //对象的一个方法 //主要看下面: alert(anObject["aProperty"]); //可以将对象当数组以属性名作为下标来访问属性 anObject["aMethod"](); //可以将对象当数组以方法名作为下标来调用方法 for( var s in anObject) //遍历对象的所有属性和方法进行迭代化处理 alert(s + " is a " + typeof(anObject[s]));
var aFunction = function() {}; //一个函数
aFunction.aProperty = "Property of function"; //函数的一个属性 aFunction.aMethod = function(){alert("Method of function")}; //函数的一个方法 //主要看下面: alert(aFunction["aProperty"]); //可以将函数当数组以属性名作为下标来访问属性 aFunction["aMethod"](); //可以将函数当数组以方法名作为下标来调用方法 for( var s in aFunction) //遍历函数的所有属性和方法进行迭代化处理 alert(s + " is a " + typeof(aFunction[s]));
function WhoAmI() //定义一个函数WhoAmI
{ alert("I'm " + this.name + " of " + typeof(this)); }; WhoAmI(); //此时是this当前这段代码的全局对象,在浏览器中就是window对象,其name属性为空字符串。输出:I'm of object var BillGates = {name: "Bill Gates"}; BillGates.WhoAmI = WhoAmI; //将函数WhoAmI作为BillGates的方法。 BillGates.WhoAmI(); //此时的this是BillGates。输出:I'm Bill Gates of object var SteveJobs = {name: "Steve Jobs"}; SteveJobs.WhoAmI = WhoAmI; //将函数WhoAmI作为SteveJobs的方法。 SteveJobs.WhoAmI(); //此时的this是SteveJobs。输出:I'm Steve Jobs of object WhoAmI.call(BillGates); //直接将BillGates作为this,调用WhoAmI。输出:I'm Bill Gates of object WhoAmI.call(SteveJobs); //直接将SteveJobs作为this,调用WhoAmI。输出:I'm Steve Jobs of object BillGates.WhoAmI.call(SteveJobs); //将SteveJobs作为this,却调用BillGates的WhoAmI方法。输出:I'm Steve Jobs of object SteveJobs.WhoAmI.call(BillGates); //将BillGates作为this,却调用SteveJobs的WhoAmI方法。输出:I'm Bill Gates of object WhoAmI.WhoAmI = WhoAmI; //将WhoAmI函数设置为自身的方法。 WhoAmI.name = "WhoAmI"; WhoAmI.WhoAmI(); //此时的this是WhoAmI函数自己。输出:I'm WhoAmI of function ({name: "nobody", WhoAmI: WhoAmI}).WhoAmI(); //临时创建一个匿名对象并设置属性后调用WhoAmI方法。输出:I'm nobody of object
var o = {};
var person = {name: "Angel", age: 18, married: false};
var speaker = {text: "Hello World", say: function(){alert(this.text)}};
var company =
{ name: "Microsoft", product: "softwares", chairman: {name: "Bill Gates", age: 53, Married: true}, employees: [{name: "Angel", age: 26, Married: false}, {name: "Hanson", age: 32, Marred: true}], readme: function() {document.write(this.name + " product " + this.product);} };
function MyFunc() {}; //定义一个空函数
var anObj = new MyFunc(); //使用new操作符,借助MyFun函数,就创建了一个对象
function MyFunc(){};
var anObj = {}; //创建一个对象 MyFunc.call(anObj); //将anObj对象作为this指针调用MyFunc函数
1 function Person(name) //带参数的构造函数
2 { 3 this.name = name; //将参数值赋给给this对象的属性 4 this.SayHello = function() {alert("Hello, I'm " + this.name);}; //给this对象定义一个SayHello方法。 5 }; 6 7 function Employee(name, salary) //子构造函数 8 { 9 Person.call(this, name); //将this传给父构造函数 10 this.salary = salary; //设置一个this的salary属性 11 this.ShowMeTheMoney = function() {alert(this.name + " $" + this.salary);}; //添加ShowMeTheMoney方法。 12 }; 13 14 var BillGates = new Person("Bill Gates"); //用Person构造函数创建BillGates对象 15 var SteveJobs = new Employee("Steve Jobs", 1234); //用Empolyee构造函数创建SteveJobs对象 16 17 BillGates.SayHello(); //显示:I'm Bill Gates 18 SteveJobs.SayHello(); //显示:I'm Steve Jobs 19 SteveJobs.ShowMeTheMoney(); //显示:Steve Jobs $1234 20 21 alert(BillGates.constructor == Person); //显示:true 22 alert(SteveJobs.constructor == Employee); //显示:true 23 24 alert(BillGates.SayHello == SteveJobs.SayHello); //显示:false
function SayHello() //先定义一份SayHello函数代码
{ alert("Hello, I'm " + this.name); }; function Person(name) //带参数的构造函数 { this.name = name; //将参数值赋给给this对象的属性 this.SayHello = SayHello; //给this对象SayHello方法赋值为前面那份SayHello代码。 }; var BillGates = new Person("Bill Gates"); //创建BillGates对象 var SteveJobs = new Person("Steve Jobs"); //创建SteveJobs对象 alert(BillGates.SayHello == SteveJobs.SayHello); //显示:true
function Person(name)
{ this.name = name; //设置对象属性,每个对象各自一份属性数据 }; Person.prototype.SayHello = function() //给Person函数的prototype添加SayHello方法。 { alert("Hello, I'm " + this.name); } var BillGates = new Person("Bill Gates"); //创建BillGates对象 var SteveJobs = new Person("Steve Jobs"); //创建SteveJobs对象 BillGates.SayHello(); //通过BillGates对象直接调用到SayHello方法 SteveJobs.SayHello(); //通过SteveJobs对象直接调用到SayHello方法 alert(BillGates.SayHello == SteveJobs.SayHello); //因为两个对象是共享prototype的SayHello,所以显示:true
1 function Person(name) //基类构造函数
2 { 3 this.name = name; 4 }; 5 6 Person.prototype.SayHello = function() //给基类构造函数的prototype添加方法 7 { 8 alert("Hello, I'm " + this.name); 9 }; 10 11 function Employee(name, salary) //子类构造函数 12 { 13 Person.call(this, name); //调用基类构造函数 14 this.salary = salary; 15 }; 16 17 Employee.prototype = new Person(); //建一个基类的对象作为子类原型的原型,这里很有意思 18 19 Employee.prototype.ShowMeTheMoney = function() //给子类添构造函数的prototype添加方法 20 { 21 alert(this.name + " $" + this.salary); 22 }; 23 24 var BillGates = new Person("Bill Gates"); //创建基类Person的BillGates对象 25 var SteveJobs = new Employee("Steve Jobs", 1234); //创建子类Employee的SteveJobs对象 26 27 BillGates.SayHello(); //通过对象直接调用到prototype的方法 28 SteveJobs.SayHello(); //通过子类对象直接调用基类prototype的方法,关注! 29 SteveJobs.ShowMeTheMoney(); //通过子类对象直接调用子类prototype的方法 30 31 alert(BillGates.SayHello == SteveJobs.SayHello); //显示:true,表明prototype的方法是共享的
function Person(name)
{ this.name = name; }; Person.prototype.company = "Microsoft"; //原型的属性 Person.prototype.SayHello = function() //原型的方法 { alert("Hello, I'm " + this.name + " of " + this.company); }; var BillGates = new Person("Bill Gates"); BillGates.SayHello(); //由于继承了原型的东西,规规矩矩输出:Hello, I'm Bill Gates var SteveJobs = new Person("Steve Jobs"); SteveJobs.company = "Apple"; //设置自己的company属性,掩盖了原型的company属性 SteveJobs.SayHello = function() //实现了自己的SayHello方法,掩盖了原型的SayHello方法 { alert("Hi, " + this.name + " like " + this.company + ", ha ha ha "); }; SteveJobs.SayHello(); //都是自己覆盖的属性和方法,输出:Hi, Steve Jobs like Apple, ha ha ha BillGates.SayHello(); //SteveJobs的覆盖没有影响原型对象,BillGates还是按老样子输出
function Person(name)
{ this.name = name; }; Person.prototype.SayHello = function() //建立对象前定义的方法 { alert("Hello, I'm " + this.name); }; var BillGates = new Person("Bill Gates"); //建立对象 BillGates.SayHello(); Person.prototype.Retire = function() //建立对象后再动态扩展原型的方法 { alert("Poor " + this.name + ", bye bye!"); }; BillGates.Retire(); //动态扩展的方法即可被先前建立的对象立即调用
String.prototype.trim = function String$trim() {
if (arguments.length !== 0) throw Error.parameterCount(); return this.replace(/^\s+|\s+$/g, ''); }
function Person(firstName, lastName, age)
{ //私有变量: var _firstName = firstName; var _lastName = lastName; //公共变量: this.age = age; //方法: this.getName = function() { return(firstName + " " + lastName); }; this.SayHello = function() { alert("Hello, I'm " + firstName + " " + lastName); }; }; var BillGates = new Person("Bill", "Gates", 53); var SteveJobs = new Person("Steve", "Jobs", 53); BillGates.SayHello(); SteveJobs.SayHello(); alert(BillGates.getName() + " " + BillGates.age); alert(BillGates.firstName); //这里不能访问到私有变量
| |