接着上一篇文章说,上一篇创建的对象没有向外部提供直接设置属性值的入口。都是在new 创建对象时,给定默认值。
本文在创建新的实例时指定属性值。
上面JavaScript 定义过程中使用了一种设置默认值的特殊惯用法:
this.name = name || "";
JavaScript 的逻辑或操作符(||)会对第一个参数进行判断。如果该参数值运算后结果为真,则操作符返回该值。否则,操作符返回第二个参数的值。因此,这行代码首先检查name是否 是对 name 属性有效的值。 如果是,则设置其为this.name 的值。否则设置this.name 的值为空的字符串。
由上面的定义,当创建对象的实例时,您可以为本地定义的属性指定值。
var jane = new Engineer("belau");
此时,Jane的属性如下所示:
jane.name == "";
jane.dept == "engineering";
jane.projects == [];
jane.machine == "belau"
注意,由上面对类的定义,您无法为诸如name 这样的继承属性指定初始值。如果想在JavaScript 中为继承的属性指定初始值,需要在构造器函数中添加更多的代码。
下面是Engineer 构造器的重新定义:
function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";}
假如您创建了一个新的Engineer 对象, 如下所示:
var jane = new Engineer(
"Doe, Jane",
["navigator", "javascript"],
"belau");
执行时,JavaScript 会有以下步骤:
可以认为,在 Engineer 的构造器中调用了 WorkerBee 的构造器,也就为 Engineer 对象设置好了继承关系。事实并非如此。调用WorkerBee 狗仔器确保了 Engineer 对象以所有在构造器中所有的属性被调用。但是,如果后续在 Employee 或者 WorkerBee 原型中添加了属性,那些属性不会被 Engineer 对象继承。例如,假设如下语句:
function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
var jane = new Engineer(
"Doe, Jane",
["navigator", "javascript"],
"belau");
Employee.prototype.specialty = "none";
对象 jane 不会继承 specialty 属性。您必须显式地设置原型才能确保动态的继承。如果修改成如下的语句:
function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
var jane = new Engineer(
"Doe, Jane",
["navigator", "javascript"],
"belau");
Employee.prototype.specialty = "none";
现在 jane 对象的specialty 属性为 “none” 了。
继承的另一种途径是使用call() / apply() 方法。
function Engineer (name, projs, mach) {
WorkerBee.call(this, name, "engineering", projs);
this.machine = mach || "";}
二、本地值和继承值
在访问一个对象的属性时, JavaScript 将执行下面的步骤:
以上步骤的结果依赖于是如何定义的。最早的例子中具有如下定义:
function Employee () {
this.name = "";
this.dept = "general";
}
function WorkerBee () {
this.projects = [];
}
WorkerBee.prototype = new Employee;
基于这些定义,假定通过如下的语句创建 WorkerBee 的实例 amy:
var amy = new WorkerBee;
则 amy 对象将具有一个本地属性,projects。name 和 dept 属性则不是 amy 对象本地的,而是从 amy 对象的 __proto__属性获得的。因此,amy 将具有如下的属性值:
amy.name == "";
amy.dept == "general";
amy.projects == [];
1. 优先本地值
现在,假设修改了与 Employee 的相关原型中的 name 属性的值:
Employee.prototype.name = "Unknown"
乍一看,可能觉得新的值传递给所有 Employee 的实例。然而,并非如此。
在创建 Employee 对象的任意实例时,该实例的 name属性将获得一个本地值(空的字符串)。这就意味着在创建一个新的 Employee 对象作为 WorkerBee 的原型时,WorkerBee.prototype 的 name 属性将具有一个本地值。因此,当 JavaScript 查找 amy 对象(WorkerBee 的实例)的 name 属性时,JavaScript 将找到 WorkerBee.prototype 中的本地值。因此,也就不会继续在原型链中向上找到 Employee.prototype 了。
2. 修改所有后代的某属性值
如果想在运行时修改一个对象的属性值并且希望该值被所有该对象的后代所继承,您就不能在该对象的构造器函数中定义该属性。而应该将该属性添加到该对象所关联的原型中。例如,假设将前面的代码作如下修改:
function Employee () {
this.dept = "general";
}
Employee.prototype.name = "";
function WorkerBee () {
this.projects = [];
}
WorkerBee.prototype = new Employee;
var amy = new WorkerBee;
Employee.prototype.name = "Unknown";
3. 判断实例的关系
JavaScript 的属性查找机制首先在自身对象的属性中查找,如果指定的属性名称没有找到,将在对象的特殊属性__proto__中查找。这个过程是递归的;被称为“在原型链中查找”。
特殊的__proto__属性是在构建对象时设置的;设置为构造器的prototype 属性的值。所有表达式 new Foo() 将创建一个对象,其 __proto__ == Foo.prototype。因而,修改Foo.prototype 的属性,将改变所有通过 new Foo() 创建的对象的属性的查找。
每个对象都有一个__proto__对象属性(除了Object);每个函数都有一个Prototype 对象属性。因此,通过“原型继承(prototype inheritance)”,对象与其他对象之间形成关系。通过比较对象的 __proto__ 属性和函数的prototype 属性可以检测对象的继承关系。JavaScript 提供了便捷方法:instanceof 操作符可以用来将一个对象和一个函数做检测,如果对象继承子函数的原型,则该操作符返回真。例如:
var f = new Foo();
var isTrue = (f instanceof Foo);
作为详细一点的例子,假定我们使用和在 继承属性 中相同的一组定义。创建 Engineer 对象如下:
var chris = new Engineer("Pigman, Chris", ["jsd"], "fiji");
对于该对象,以下所有语句均为真:
chris.__proto__ == Engineer.prototype;
chris.__proto__.__proto__ == WorkerBee.prototype;
chris.__proto__.__proto__.__proto__ == Employee.prototype;
chris.__proto__.__proto__.__proto__.__proto__ == Object.prototype;
chris.__proto__.__proto__.__proto__.__proto__.__proto__ == null;
基于此,可以写出一个如下所示的 instanceOf 函数:
function instanceOf(object, constructor) {
while (object != null) {
if (object == constructor.prototype)
return true;
if (typeof object == 'xml') {
return constructor.prototype == XML.prototype;
}
object = object.__proto__;
}
return false;
}
instanceOf (chris, Engineer)
instanceOf (chris, WorkerBee)
instanceOf (chris, Employee)
instanceOf (chris, Object)
但如下表达式为假:
instanceOf (chris, SalesPerson)