在JavaRanch Journal看到一片不错的JavaScript的入门文章,感觉不错,十分易懂,决定把它翻译下,一来方便不懂e文的同志分享,二来可以巩固下关于JavaScript对于对象的理解。
原文地址: http://www.javaranch.com/journal/2008/10/Journal200810.jsp
作者 Stoyan Staefanov
Java 和JavaScript是两种完全不同的语言,但是名称和都是类C的语法很容易却让人混淆。让我们来看看JavaScript不同于Java中如何创建对象的。在Java里,你得先有类,然后再有对象的,又叫做实例化,创建对象要基于那些类的。但是在JavaScript里,是没有类、对象,而是更像是哈希表中的键值对。但是在JavaScript中是如何实例化对象的呢?好,一步步的来讲。
JavaScript 对象
当你在思考关于一个JavaScript对象时,其实就是在思考一个哈希。这里所有的对象就像是一个键值对的集合,那些值则可以包括是任何的对象和函数。当一个对象的其中一个属性是函数时,你就可以称其为方法。
这个就是一个空方法:
Var myobj = {};
现在你可以添加一些有意义的功能到这个对象了:
myobj,name = “My precious”;
myobj.getName = function() {return this.name};
这里要注意的事项:
正如所预期的那样,内部方法是当前对象的应用
你可以添加、调整、删除属性在任何时候,不只是在创建的时候
另一种创建对象的方式是,在创建对象的同时创建属性和方法,就像这样:
Var another = {
name: ‘My other precious’,
getName:function() {
return this.name;
}
};
这些语法被称之谓文字符号对象,你总之把一切都放在大括号内{},属性则通过逗号隔开。键:值对通过分号隔开。尽管这个不是唯一创建对象的方法。
另一种创建JavaScript对象的方法是使用构造函数的方法。这个是构造函数的例子:
Function ShinyObject(name){
this.name = name;
this.getName = function(){
return this.name;
}
}
现在创建一个对象就很想Java 一样了:
var my = new ShinyObject(’ring’);
Var myname = my .getName();//”ring”
构造函数的语法没有相对于其他任何函数有所不同,而不同是在在于用法不同。如果你以new的方式调用一个函数,那就会创建并返回一个对象,通过这个,你就有权限在它返回之前修改它。通过约定,构造函数不同于普通函数和方法,名称首字母要大写。
对象直接创建和构造函数创建,哪个更好呢?嗯,那就取决于你要做的特定的任务。例如,你要创建很多不同的近似的对象,选择使用类构造的方式是个不错的选择。但是如果你创建不会多于一个的单例的话,则可以使用直接对象创建就显得比较简洁了。
好,那么没有里类,是如何实现继承的呢?在讨论这个之前,这里有些小小的惊喜–在JavaScript里函数是正真的对象。
(实际上在JavaScript里大部分分所有的都是对象,除一些原始数据类型-string,boolean,number 和undefined。函数是对象,数组是对象,甚至null也是对象。此外,这些私有的数据类型也可以转化后使用作对象,就像 “string”.length 是有效的。)
Function 对象和prototype 属性
在JavaScript里,函数就是对象,可以赋给变量,你可以添加属性和方法等到上面。这里是一个例子:
var myfunc = function(param) {
alert(param);
};
就和这一样:
function myfunc(param) {
alertparam);
}
不管如何你创建的这个函数,你最终可以访问 myfunc里的属性和方法。
alert(myfunc.length); // 提示1, 数字型的参数
alert(myfunc.toString()); // 提示函数的源代码
每一个function对象都有一个有趣的特性:都有prototype(原型)这个属性。
当你创建一个function的同时,将自动获取一个指向一个空对象的prototype属性。当然,你能修改这个空对象的属性。
alert(typeof myfunc.prototype); // 提示“Object”
myfunc.prototype.test = 1; // 完全可以这么做
有个问题,prototype有什么用呢?prototype仅可以用在当你引用一个function作为构造函数方式来创建的对象上。当你这么做,这对象就自动获取一个秘密的链接到prototype的属性,然后使之成为自己的属性。真的吗?
让我们来看看这个实例。
一个新的function:
function ShinyObject(name) {
this.name = name;
}
给这个function的prototype属性传参数来添加一些功能:
ShinyObject.prototype.getName = function() {
return this.name;
};
使用function像构造函数的方式来创建的对象:
var iphone = new ShinyObject(’my precious’);
iphone.getName(); // 返回”my precious”
就像你看到的那样,一个新的对象自动获取这个有权限的prototype属性。然后当有些“免费”获取功能,开始闻到有点像继承和reusability 代码的味道了。
以原型的模式继承
现在让我们看看你将如何prototype来实现继承。
这里是一个做为父类的构造函数:
function NormalObject() {
this.name = ‘normal’;
this.getName = function() {
return this.name;
};
}
第二个构造函数:
function PreciousObject(){
this.shiny = true;
this.round = true;
}
现在继承的部分:
PreciousObject.prototype = new NormalObject();
啊哈!你可以创建precious对象了,并且将获取所有Normal对象的功能:
var crystal_ball = new PreciousObject();
crystal_ball.name = ‘Ball, Crystal Ball.’;
alert(crystal_ball.round); // true
alert(crystal_ball.getName()); // “Ball, Crystal Ball.”
注意为什么我们创建一个对象要通过prototype的方式新建或赋予,因为prototype就是一个对象。它不像一个构造函数继承自其他,事实上我们继承是从一个对象的。JavaScript是没有类继承自类的说法,这里只有对象继承自对象。
如果你有一些构造函数继承自NormalObject对象,你或许每次都要new NormalObject()的,但是那是不必要的。甚至整个NormalObject结构体也是不必要的。另一种方式一样做到通过创建一个(单例)的normal object然后使用它就像使用其他的对象。
var normal = {
name: ‘normal’,
getName: function() {
return this.name;
}
};
然后PreciousObject能够像这样来继承:
PreciousObject.prototype =normal;
自从继承者将全部调用代码,已经简单复制属性通过另一种方法来实现。
设想你有以下的对象
var shiny = {
shiny: true,
round: true
};
var normal = {
name: ‘name me’,
getName: function() {
return this.name;
}
};
如何让shiny获取mormal的属性?这里是一个简单的extend()函数,用来循环迭代和复制属性:
function extend(parent, child) {
for (var i in parent) {
child[i] = parent[i];
}
}
extend(normal, shiny); // 继承
shiny.getName(); // “name me”
现在看来这种属性拷贝有一定的开销而感觉不是很好,但是事实上,对于完成大多数任务还不错。你也可以把这个看做实现组合和多重继承的简单途径。
Crockford的由父级对象产生对象的方法
Douglas Crockford,是一位JavaScript的宗师和JSON的创始人,他提出了这个有趣的begetObject()方式来实现继承:
function begetObject(o) {
function F() {}
F.prototype = o;
return new F();
}
在这里你创建一个临时的构造函数,这样以至于可以使用prototype(原型)的功能,原理是创建一个对象同时继承已存在对象的一些功能。
父级对象:
var normal = {
name: ‘name me’,
getName: function() {
return this.name;
}
};
一个新创建继承自父级对象的对象:
var shiny = begetObject(normal);
给这个对象新增更多的功能:
Shiny.round = true;
YUI的extend()
让我们再来看另一个实现继承的方式,这个更接近于Java,因为在这个方式下,看起来构造函数像是继承自另一个构造函数,因此有点像一个类继承自另一个类。
这个方式在非常流行的 YUI JavaScript library(Yahoo! User Interface)这里一个简单版本:
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
}
通过这个方法,你传递两个构造函数,第一个(子类)将会通过原型(prototype)属性得到第二个(父类)的所有属性和方法。
总结
让我们快速总结下关于JavaScript刚才所学习的:
没有类的概念
对象继承自对象
对象枚举的方式:var o ={};
构造函数提供类似Java的语法 var o = new Object();
函数和对象的概念
所有函数对象都有一个原型属性
最后,还有讲了一些实现继承的方式,你可以任意的挑选,可以说是手头的任务,个人癖好,团队偏好,或是当前的月相。
作者和声明
Stoyan Stefanov 是一位资深的Yahoo!开发者, YSlow tool的主导者,开源贡献者,博客作者和技术撰稿人,最近是Packet出版的“Object-Oriented JavaScript”一书 的作者。
shiny对象的例子的灵感来自于 Jim Bumgardner撰写的Theory of the Precious Object一书。
Douglas Crockford的 beget object - 文章点击这里