javascript 编程思想
为什么80%的码农都做不了架构师?>>>
注意:typeof(undefined) 返回也是 undefined。
可以将undefined赋值给任何变量或属性,但并不意味了清除了该变量,反而会因此多了一个属性。
注意:typeof(null)返回object,但null并非object,具有null值的变量也并非object。
注意:typeof(NaN)和typeof(Infinity)都返回number 。
NaN参与任何数值计算的结构都是NaN,而且 NaN != NaN 。
Infinity / Infinity = NaN 。
没有类
object就是对象的类型。在JavaScript中不管多么复杂的数据和代码,都可以组织成object形式的对象。
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();
};
这段JavaScript程序一开始产生了一个生命对象life,life诞生时只是一个光溜溜的对象,没有任何属性和方法。在第一次生命过程中,它有了 一个身体属性body,并有了一个say方法,看起来是一个“卵细胞”。在第二次生命过程中,它又长出了“尾巴”和“腮”,有了tail和gill属性, 显然它是一个“蝌蚪”。在第三次生命过程中,它的tail和gill属性消失了,但又长出了“四条腿”和“肺”,有了legs和lung属性,从而最终变 成了“青蛙”。不过,在看完这段程序之后,请你思考一个问题:
{
alert( " hello " );
};
alert( typeof (myfunc));
这个代码运行之后可以看到typeof(myfunc)返回的是function。以上的函数写法我们称之为“定义式”的,如果我们将其改写成下面的“变量式”的,就更容易理解了:
{
alert( " hello " );
};
alert( typeof (myfunc));
这里明确定义了一个变量myfunc,它的初始值被赋予了一个function的实体。因此,typeof(myfunc)返回的也是function。 其实,这两种函数的写法是等价的,除了一点细微差别,其内部实现完全相同。也就是说,我们写的这些JavaScript函数只是一个命了名的变量而已,其 变量类型即为function,变量的值就是我们编写的函数代码体。
{
alert( " hello " );
};
myfunc(); // 第一次调用myfunc,输出hello
myfunc = function ()
{
alert( " yeah " );
};
myfunc(); // 第二次调用myfunc,将输出yeah
{
alert( " hello " );
};
myfunc(); // 这里调用myfunc,输出yeah而不是hello
function myfunc ()
{
alert( " yeah " );
};
myfunc(); // 这里调用myfunc,当然输出yeah
这又是为什么呢?
function myfunc ()
{
alert( " hello " );
};
myfunc(); // 这里调用myfunc,输出hello
</ script >
< script >
function myfunc ()
{
alert( " yeah " );
};
myfunc(); // 这里调用myfunc,输出yeah
</ script >
这时,输出才是各自按顺序来的,也证明了JavaScript的确是一段段地执行的。
奇妙的对象
先来说说函数的对象化能力。
{
with (arguments.callee)
alert(author + " : " + poem);
};
Sing.author = " 李白 " ;
Sing.poem = " 汉家秦地月,流影照明妃。一上玉关道,天涯去不归 " ;
Sing();
Sing.author = " 李战 " ;
Sing.poem = " 日出汉家天,月落阴山前。女儿琵琶怨,已唱三千年 " ;
Sing();
在这段代码中,Sing函数被定义后,又给Sing函数动态地增加了author和poem属性。将author和poem属性设为不同的作者和诗句,在 调用Sing()时就能显示出不同的结果。这个示例用一种诗情画意的方式,让我们理解了JavaScript函数就是对象的本质,也感受到了 JavaScript语言的优美。
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]));
同样对于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与object的超然结合吧。
{
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
从上面的代码可以看出,同一个函数可以从不同的角度来调用,this并不一定是函数本身所属的对象。this只是在任意对象和function元素结合时的一个概念,是种结合比起一般对象语言的默认结合更加灵活,显得更加超然和洒脱。
对象素描
怎样建立对象?
创建一个对象并设置属性及初始值:
创建一个对象并设置属性和方法:
创建一个更复杂的对象,嵌套其他对象和对象数组等:
{
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);}
};
JSON的形式就是用大括“{}”号包括起来的项目列表,每一个项目间并用逗号“,”分隔,而项目就是用冒号“:”分隔的属性名和属性值。这是典型的字典 表示形式,也再次表明了 JavaScript里的对象就是字典结构。不管多么复杂的对象,都可以被一句JSON代码来创建并赋值。
var anObj = new MyFunc(); // 使用new操作符,借助MyFun函数,就创建了一个对象
JavaScript的这种创建对象的方式可真有意思,如何去理解这种写法呢?
var anObj = {}; // 创建一个对象
MyFunc.call(anObj); // 将anObj对象作为this指针调用MyFunc函数
我们就可以这样理解,JavaScript先用new操作符创建了一个对象,紧接着就将这个对象作为this参数调用了后面的函数。其实, JavaScript内部就是这么做的,而且任何函数都可以被这样调用!但从 “anObj = new MyFunc()” 这种形式,我们又看到一个熟悉的身影,C++和C#不就是这样创建对象的吗?原来,条条大路通灵山,殊途同归啊!
君看到此处也许会想,我们为什么不可以把这个MyFunc当作构造函数呢?恭喜你,答对了!JavaScript也是这么想的!请看下面的代码:
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
这段代码表明,函数不但可以当作构造函数,而且还可以带参数,还可以为对象添加成员和方法。其中的第9行,Employee构造函数又将自己接收的 this作为参数调用Person构造函数,这就是相当于调用基类的构造函数。第21、22行还表明这样一个意思:BillGates是由Person构 造的,而SteveJobs是由Employee构造的。对象内置的constructor属性还指明了构造对象所用的具体函数!
其实,如果你愿意把函数当作“类”的话,她就是“类”,因为她本来就有“类”的那些特征。难道不是吗?她生出的儿子各个都有相同的特征,而且构造函数也与类同名嘛!
{
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
其中,最后一行的输出结果表明两个对象确实共享了一个函数对象。虽然,这段程序达到了共享了一份方法代码的目的,但却不怎么优雅。因为,定义 SayHello方法时反映不出其与Person类的关系。“优雅”这个词用来形容代码,也不知道是谁先提出来的。不过,这个词反映了程序员已经从追求代 码的正确、高效、可靠和易读等基础上,向着追求代码的美观感觉和艺术境界的层次发展,程序人生又多了些浪漫色彩。
prototype源自法语,软件界的标准翻译为“原型”,代表事物的初始形态,也含有模型和样板的意义。JavaScript中的prototype概念恰如其分地反映了这个词的内含,我们不能将其理解为C++的prototype那种预先声明的概念。
{
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
程序运行的结果表明,构造函数的prototype上定义的方法确实可以通过对象直接调用到,而且代码是共享的。显然,把方法设置到prototype的 写法显得优雅多了,尽管调用形式没有变,但逻辑上却体现了方法与类的关系,相对前面的写法,更容易理解和组织代码。
那么,对于多层次类型的构造函数情况又如何呢?
我们再来看下面的代码:
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的方法是共享的
这段代码的第17行,构造了一个基类的对象,并将其设为子类构造函数的prototype,这是很有意思的。这样做的目的就是为了第28行,通过子类对象也可以直接调用基类prototype的方法。为什么可以这样呢?
原来,在JavaScript中,prototype不但能让对象共享自己财富,而且prototype还有寻根问祖的天性,从而使得先辈们的遗产可以代 代相传。当从一个对象那里读取属性或调用方法时,如果该对象自身不存在这样的属性或方法,就会去自己关联的prototype对象那里寻找;如果 prototype没有,又会去prototype自己关联的前辈prototype那里寻找,直到找到或追溯过程结束为止。
{
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还是按老样子输出
对象可以掩盖原型对象的那些属性和方法,一个构造函数原型对象也可以掩盖上层构造函数原型对象既有的属性和方法。这种掩盖其实只是在对象自己身上创建了新的属性和方法,只不过这些属性和方法与原型对象的那些同名而已。JavaScript就是用这简单的掩盖机制实现了对象的“多态”性,与静态对象语言的虚函数和重载(override)概念不谋而合。
{
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(); // 动态扩展的方法即可被先前建立的对象立即调用
原型扩展
如果在JavaScript内置的那些如Object和Function等函数的prototype上添加些新的方法和属性,是不是就能扩展JavaScript的功能呢?
if (arguments.length !== 0 ) throw Error.parameterCount();
return this .replace( / ^\s+|\s+$ / g, '' );
}
这段代码就是给内置String函数的prototype扩展了一个trim方法,于是所有的String类对象都有了trim方法了。有了这个 扩展,今后要去除字符串两端的空白,就不用再分别处理了,因为任何字符串都有了这个扩展功能,只要调用即可,真的很方便。
当然,几乎很少有人去给Object的prototype添加方法,因为那会影响到所有的对象,除非在你的架构中这种方法的确是所有对象都需要的。
{
// 私有变量:
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); // 这里不能访问到私有变量
很显然,这种模型的类描述特别象C#语言的描述形式,在一个构造函数里依次定义了私有成员、公共属性和可用的方法,显得非常优雅嘛。特别是“闭包”机制可以模拟对私有成员的保护机制,做得非常漂亮。
原型模型需要一个构造函数来定义对象的成员,而方法却依附在该构造函数的原型上。大致写法如下:
function Person(name)
{
this .name = name; // 在构造函数中定义成员
};
// 方法定义到构造函数的prototype上
Person.prototype.SayHello = function ()
{
alert( " Hello, I'm " + this .name);
};
// 子类构造函数
function Employee(name, salary)
{
Person.call( this , name); // 调用上层构造函数
this .salary = salary; // 扩展的成员
};
// 子类构造函数首先需要用上层构造函数来建立prototype对象,实现继承的概念
Employee.prototype = new Person() // 只需要其prototype的方法,此对象的成员没有任何意义!
// 子类方法也定义到构造函数之上
Employee.prototype.ShowMeTheMoney = function ()
{
alert( this .name + " $ " + this .salary);
};
var BillGates = new Person( " Bill Gates " );
BillGates.SayHello();
var SteveJobs = new Employee( " Steve Jobs " , 1234 );
SteveJobs.SayHello();
SteveJobs.ShowMeTheMoney();
原型类模型虽然不能模拟真正的私有变量,而且也要分两部分来定义类,显得不怎么“优雅”。不过,对象间的方法是共享的,不会遇到垃圾回收问题,而且性能优于“闭包”模型。正所谓“有失必有得”嘛。
在原型模型中,为了实现类继承,必须首先将子类构造函数的prototype设置为一个父类的对象实例。创建这个父类对象实例的目的就是为 了构成原型链,以起到共享上层原型方法作用。但创建这个实例对象时,上层构造函数也会给它设置对象成员,这些对象成员对于继承来说是没有意义的。虽然,我 们也没有给构造函数传递参数,但确实创建了若干没有用的成员,尽管其值是undefined,这也是一种浪费啊。
唉!世界上没有完美的事情啊!
原型真谛
我们已经知道,用 var anObject = new aFunction() 形式创建对象的过程实际上可以分为三步:第一步是建立一个新对象;第二步将该对象内置的原型对象设置为构造函数prototype引用的那个原型对象;第 三步就是将该对象作为this参数调用构造函数,完成成员设置等初始化工作。对象建立之后,对象上的任何访问和操作都只与对象自身及其原型链上的那串对象有关,与构造函数再扯不上关系了。换句话说,构造函数只是在创建对象时起到介绍原型对象和初始化对象两个作用。
那么,我们能否自己定义一个对象来当作原型,并在这个原型上描述类,然后将这个原型设置给新创建的对象,将其当作对象的类呢?我们又能否将这个原型中的一个方法当作构造函数,去初始化新建的对象呢?例如,我们定义这样一个原型对象:
{
Create: function (name, age) // 这个当构造函数
{
this .name = name;
this .age = age;
},
SayHello: function () // 定义方法
{
alert( " Hello, I'm " + this .name);
},
HowOld: function () // 定义方法
{
alert( this .name + " is " + this .age + " years old. " );
}
};
这个JSON形式的写法多么象一个C#的类啊!既有构造函数,又有各种方法。如果可以用某种形式来创建对象,并将对象的内置的原型设置为上面这个“类”对象,不就相当于创建该类的对象了吗?
但遗憾的是,我们几乎不能访问到对象内置的原型属性!尽管有些浏览器可以访问到对象的内置原型,但这样做的话就只能限定了用户必须使用那种浏览器。这也几乎不可行。
那么,我们可不可以通过一个函数对象来做媒介,利用该函数对象的prototype属性来中转这个原型,并用new操作符传递给新建的对象呢?
其实,象这样的代码就可以实现这一目标:
anyfunc.prototype = Person; // 将原型对象放到中转站prototype
var BillGates = new anyfunc(); // 新建对象的内置原型将是我们期望的原型对象
不过,这个anyfunc函数只是一个躯壳,在使用过这个躯壳之后它就成了多余的东西了,而且这和直接使用构造函数来创建对象也没啥不同,有点不爽。
可是,如果我们将这些代码写成一个通用函数,而那个函数躯壳也就成了函数内的函数,这个内部函数不就可以在外层函数退出作用域后自动消亡吗?而且,我们可以将原型对象作为通用函数的参数,让通用函数返回创建的对象。我们需要的就是下面这个形式:
{
function new_() // 定义临时的中转函数壳
{
aClass.Create.apply( this , aParams); // 调用原型中定义的的构造函数,中转构造逻辑及构造参数
};
new_.prototype = aClass; // 准备中转原型对象
return new new_(); // 返回建立最终建立的对象
};
var Person = // 定义的类
{
Create: function (name, age)
{
this .name = name;
this .age = age;
},
SayHello: function ()
{
alert( " Hello, I'm " + this .name);
},
HowOld: function ()
{
alert( this .name + " is " + this .age + " years old. " );
}
};
var BillGates = New(Person, [ " Bill Gates " , 53 ]); // 调用通用函数创建对象,并以数组形式传递构造参数
BillGates.SayHello();
BillGates.HowOld();
alert(BillGates.constructor == Object); // 输出:true
这里的通用函数New()就是一个“语法甘露”!这个语法甘露不但中转了原型对象,还中转了构造函数逻辑及构造参数。
有趣的是,每次创建完对象退出New函数作用域时,临时的new_函数对象会被自动释放。由于new_的prototype属性被设置为新的原型 对象,其原来的原型对象和new_之间就已解开了引用链,临时函数及其原来的原型对象都会被正确回收了。上面代码的最后一句证明,新创建的对象的 constructor属性返回的是Object函数。其实新建的对象自己及其原型里没有constructor属性,那返回的只是最顶层原型对象的构造 函数,即Object。
有了New这个语法甘露,类的定义就很像C#那些静态对象语言的形式了,这样的代码显得多么文静而优雅啊!
当然,这个代码仅仅展示了“语法甘露”的概念。我们还需要多一些的语法甘露,才能实现用简洁而优雅的代码书写类层次及其继承关系。好了,我们再来看一个更丰富的示例吧:
var object = // 定义小写的object基本类,用于实现最基础的方法等
{
isA: function (aType) // 一个判断类与类之间以及对象与类之间关系的基础方法
{
var self = this ;
while (self)
{
if (self == aType)
return true ;
self = self.Type;
};
return false ;
}
};
function Class(aBaseClass, aClassDefine) // 创建类的函数,用于声明类及继承关系
{
function class_() // 创建类的临时函数壳
{
this .Type = aBaseClass; // 我们给每一个类约定一个Type属性,引用其继承的类
for ( var member in aClassDefine)
this [member] = aClassDefine[member]; // 复制类的全部定义到当前创建的类
};
class_.prototype = aBaseClass;
return new class_();
};
function New(aClass, aParams) // 创建对象的函数,用于任意类的对象创建
{
function new_() // 创建对象的临时函数壳
{
this .Type = aClass; // 我们也给每一个对象约定一个Type属性,据此可以访问到对象所属的类
if (aClass.Create)
aClass.Create.apply( this , aParams); // 我们约定所有类的构造函数都叫Create,这和DELPHI比较相似
};
new_.prototype = aClass;
return new new_();
};
// 语法甘露的应用效果:
var Person = Class(object, // 派生至object基本类
{
Create: function (name, age)
{
this .name = name;
this .age = age;
},
SayHello: function ()
{
alert( " Hello, I'm " + this .name + " , " + this .age + " years old. " );
}
});
var Employee = Class(Person, // 派生至Person类,是不是和一般对象语言很相似?
{
Create: function (name, age, salary)
{
Person.Create.call( this , name, age); // 调用基类的构造函数
this .salary = salary;
},
ShowMeTheMoney: function ()
{
alert( this .name + " $ " + this .salary);
}
});
var BillGates = New(Person, [ " Bill Gates " , 53 ]);
var SteveJobs = New(Employee, [ " Steve Jobs " , 53 , 1234 ]);
BillGates.SayHello();
SteveJobs.SayHello();
SteveJobs.ShowMeTheMoney();
var LittleBill = New(BillGates.Type, [ " Little Bill " , 6 ]); // 根据BillGate的类型创建LittleBill
LittleBill.SayHello();
alert(BillGates.isA(Person)); // true
alert(BillGates.isA(Employee)); // false
alert(SteveJobs.isA(Person)); // true
alert(Person.isA(Employee)); // false
alert(Employee.isA(Person)); // true
“语法甘露”不用太多,只要那么一点点,就能改观整个代码的易读性和流畅性,从而让代码显得更优雅。有了这些语法甘露,JavaScript就很像一般对象语言了,写起代码了感觉也就爽多了!
令人高兴的是,受这些甘露滋养的JavaScript程序效率会更高。因为其原型对象里既没有了毫无用处的那些对象级的成员,而且还不存在 constructor属性体,少了与构造函数间的牵连,但依旧保持了方法的共享性。这让JavaScript在追溯原型链和搜索属性及方法时,少费许多工夫啊。
我们就把这种形式称为“甘露模型”吧!其实,这种“甘露模型”的原型用法才是符合prototype概念的本意,才是的JavaScript原型的真谛!
原文地址:http://7685941014.blog.163.com/blog/static/124615480201061782347164/
转载于:https://my.oschina.net/megan/blog/128353
javascript 编程思想相关推荐
- 《JavaScript函数式编程思想》——递归
第7章 递归 王二.张三和赵四一日无聊,决定玩击鼓传花讲冷笑话的游戏.王二和张三围成一圈传花,赵四负责击鼓.张三接连讲了几个诸如小菜.狐狸狡猾的笑话.花停在了王二的手中. 王二:这个笑话很短.你要保 ...
- 《JavaScript函数式编程思想》
自序 伴随着Web技术的普及,JavaScript已成为应用最广泛的编程语言之一.由于其在Web前端编程中的统治地位.语言本身的表现力.灵活性.开源的本质和ECMAScript标准近年来的快速发展,J ...
- 《JavaScript函数式编程思想》——名称
第1章 名称 一般对函数式编程的介绍都会从一等值和纯函数等概念开始,本书却准备在那之前先花些篇章讨论两个通常未得到足够重视的主题:名称和类型系统.前者包括名称绑定.作用域和闭包等内容,后者包括类型的 ...
- JavaScript 面向对象编程思想简介
JavaScript 面向对象编程 JavaScript 执行过程 JavaScript 面向对象编程 面向对象介绍 什么是对象 什么是面向对象 程序中面向对象的基本体现 创建对象 简单方式 简单方式 ...
- JavaScript 面向对象编程思想
JavaScript 面向对象编程思想 什么是面向对象 面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性. 面向对象编程 -- Object Oriented ...
- JavaScript进阶-编程思想、构造函数的原型对象、对象原型、原型继承以及原型链
编程思想 面向过程 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次 调用就可以了. 优点: 性能比面向对象高,适合跟硬件联系很紧密 的东西,例如单 ...
- JavaScript 编程精解 中文第三版 零、前言
零.前言 原文:Introduction 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 部分参考了<JavaScript 编程精解(第 2 版)> We think ...
- 几种常用编程语言的编程思想和方法 转
搞软件的人,编程语言的掌握是基本功,如果单单是学习语法,最慢的一周之内也应该可以搞定(个人认为 语法层面c++是最复杂的).不幸的是,软件的本质是逻辑,解决方案的设计是要借助某种解决问题或编程的思想的 ...
- java编程思想 入门_java编程思想学习(基础)
第一章 java介绍 1.编程的本质: 机器空间:解空间 问题空间:实际需要解决的业务问题,将该问题抽象化,在解空间中对问题建模. 编程就是建立问题空间和机器空间中的关联 面向对象编程思想: 1.万物 ...
最新文章
- csdn第4名靠转载上位
- 反射 reflect
- 信息系统项目管理师历年论文题目
- feedback from waic
- 用户id生成规则_阿里/网易/美团/58用户画像中的ID体系建设
- MSP430F5529 DriverLib 库函数学习笔记(四)UART通信
- Codeforces340B Maximal Area Quadrilateral
- SpringBoot创建第一个Web项目——Hello SpringBoot
- HTML - 文本及其格式化
- 等高线生成地形_等高线一键变地形模型
- HTTP协议【详解】——经典面试题
- 2021公路水运工程试验检测考试大纲
- 盘点!AGV的12种导引导航方式
- Polkit授权管理
- 使用video speed controller给视频加速
- ios 设置属性的center_iOS游戏平台Game Center成就显示设置指南
- java url链接超时_Java编程中HttpURLConnection的连接超时中的异常
- java 计时 纳秒_Java精确测量代码运行时间 代码执行时间 纳秒 nanoTime
- TypeScript Essential Notes 2 - ES6 Language Features
- HTML+CSS修改li前小圆点的样式or颜色
热门文章
- ArcGIS Server发布WFS中文图层名称乱码问题解决方案
- 北斗导航 | C语言实现PPP(精密单点定位)
- tiny cc在云服务centos上运行
- 无空头链表详解(增删改查)
- 阿里云 nginx php mysql_阿里云 Ubuntu + Nginx + PHP + MySQL
- C++之全局对象、局部对象、静态对象详解
- c++计算-eigen(1)
- access开发精要(14)-货币与数字类型格式(2)
- 【Python】推荐6个好用到爆的Pycharm插件
- 【Python】Python时间序列之calendar模块