九、对象
对象是被称为属性的命名值的集合。属性可以是保存对象状态的变量,也可以是定义对象功能的函数。对象的吸引力在于它们在提供功能的同时隐藏了它们的内部工作。你需要知道的只是一个对象能为你做什么,而不是它是如何做的。
对象属性
可以使用 new 指令以下面的显式方式创建一个空对象。
var box = new Object();
使用点符号或数组符号将对象的属性赋给时,会自动创建这些属性。
box.x = 2;
box["y"] = 3;
同样,属性可以用这两种方式之一引用。
console.log(box.x); // "2"
console.log(box["y"]); // "3"
可以使用 delete 指令从对象中删除属性。这不能用常规变量来完成,只能用对象属性来完成。
box.z = 1; // add property
delete box.z; // delete property
要检查对象是否包含属性,可以使用 in 运算符。然后,属性名被指定为字符串。
console.log("x" in box); // "true"
console.log("z" in box); // "false"
对象方法
函数可以以属性的形式添加到对象中。这种功能被称为方法。当引用函数声明时,括号被省略。
box.getArea = myArea;
function myArea() { return this.x * this.y; }
这里使用的this
关键字是对当前拥有该函数的对象的引用。如果在对象上下文之外调用函数,关键字将改为引用window
对象。为了防止这种脱离上下文的调用,最好使用函数表达式内联函数。这也防止了函数名不必要地弄乱了全局名称空间。
box.getArea = function() { return this.x * this.y; };
一旦绑定到对象,就可以以熟悉的方式调用该方法。this
关键字在这里指的是 box 对象,它具有前面定义的属性x
和y
。
console.log( box.getArea() ); // "6"
对象文字
创建对象的一种更简单的方法是使用对象文字,它由一组花括号分隔。创建空对象时,括号是空的。
var box = {};
使用对象文字的优点是,可以在创建对象时设置属性,方法是将它们包含在花括号中。属性的每个名称-值对由冒号分隔,每个属性依次由逗号分隔。
var box = {
x: 2,
y: 3,
getArea: function() { return this.x * this.y; }
};
如果只需要一个对象实例,那么对象文字就很有用。如果需要多个实例,可以使用函数构造函数。
构造函数
可以从构造函数中创建对象。通过允许从一组定义中创建一个对象的多个实例,它们提供了其他语言中的类所提供的功能。按照惯例,打算用作对象构造函数的函数以大写字母开头,以提醒它们的用途。
function Box(x, y) {
this.x = x;
this.y = y;
this.getArea = function() { return this.x * this.y; };
}
为了从这个构造函数中实例化一个或多个对象,用new
指令调用这个函数。就像任何其他函数一样,构造函数可以接受参数,如下例所示。
var b1 = new Box(1, 2);
var b2 = new Box(3, 4);
每个对象实例都包含自己的一组属性,这些属性可以保存与其他实例不同的值。
console.log(b1.x); // "1"
console.log(b2.x); // "3"
先前定义的构造函数包括在另一个函数中声明的函数。这种嵌套函数可以访问其父函数中定义的变量,因为它形成了一个包含外部函数范围的所谓闭包。这种对象创建模式的一个优点是它提供了信息隐藏。例如,下面的示例有一个方法,它使用一个局部变量来记录该方法被调用的次数。这个特性类似于基于类的语言中的私有属性,因为局部变量只在构造函数中可见。
function Counter(x, y) {
var count = 0;
this.printCount = function() { console.log(count++); };
}
var c = new Counter();
c.printCount(); // "0";
c.printCount(); // "1";
这种对象创建模式的一个小缺点是每个实例都有自己的printCount
方法,这会增加每个对象消耗的内存。避免这种情况的另一种模式是利用继承将方法添加到对象的原型中。
遗产
一个对象可以从另一个对象继承属性。这为对象重用其他对象的代码提供了一种方式。专门化的对象通常称为子对象,更一般的对象称为父对象。
在 JavaScript 中,继承是通过原型继承模型实现的。在这个模型中,每个对象都有一个内部属性,作为到另一个对象的链接,称为它的原型。考虑下面的对象。
var myObj = new Object();
这个对象从内置的Object
构造函数继承属性。当请求一个对象不直接包含的属性时,JavaScript 将自动搜索继承链,直到找到请求的属性或到达链的末端。
// Add a property to myObj
myObj.x = 5;
// Call a method from Object
myObj.hasOwnProperty("x"); // true
链中的最后一个链接总是内置的Object
构造函数,它的原型链接又是 null,这标志着链的结束。可以使用__proto__
属性检索对对象原型链接的引用。这不要与构造函数的 prototype 属性混淆,它是函数的文字对象表示,当创建该函数的新实例时,它被分配给__proto__
。
myObj.__proto__ === Object.prototype; // true
myObj.__proto__.__proto__ === null; // true
通过扩展原型链来实现继承。这可以在构造函数中完成,方法是将prototype
属性设置为要继承的对象。
function Parent() { this.a = "Parent"; }
function Child() { }
Child.prototype = new Parent();
var child = new Child();
console.log(child.a); // "Parent"
类型检查
您可以通过比较对象的__proto__
链接和构造函数的prototype
属性来手动确认对象的类型。
child.__proto__ === Child.prototype; //
true
JavaScript 还提供了instanceof
操作符。该操作符在原型链中导航,如果左侧对象指向原型链中右侧的构造函数,则返回 true。
child instanceof Child; // true
child instanceof Parent; // true
child instanceof Object; // true
对象创建
执行继承的另一种方式是通过Object.create
方法。此方法提供了一种更简单的实现继承的方法,它允许一个对象直接从另一个对象继承,而无需使用额外的构造函数。
var p = new Parent();
var c1 = Object.create(p); // inherit from p
方法的第二个可选参数允许您使用特殊的表示法初始化对象的属性。
var c2 = Object.create(p, { name: { value: "Child 2" } } );
console.log(c2.name); // "Child 2"
对象的prototype
属性可以用来动态地向原型添加属性。这将导致链接到该原型的所有对象都继承新的属性。
Parent.prototype.x = "new property";
console.log(c2.x); // "new property"
如本章所示,JavaScript 在创建和使用对象时提供了很大的灵活性。选择使用哪种方法往往归结为个人喜好的问题。
版权属于:月萌API www.moonapi.com,转载请注明出处