javascript对象详解(JavaScript Objects in Detail)

javascript的核心—最经常用到也是最基础的—数据类型就是对象。javascript有一种复杂的数据类型,即对象类型,和五种简单的数据类型:数字,字符串,布尔,undefined(未定义),和null(空)。记住这些简单(基本)数据类型是不可变的,它们不能被改变,但是对象是可变的。

什么是对象

一个对象是一组没有顺序的存储为名值对形式的基本数据类型(有时也可以是引用数据类型)。在这组数据中的每一项被叫做一个属性(函数被叫做方法),每个属性的名字必须是唯一的,可以为字符串或者数字。

下面这个是一个简单的对象:

var myFirstObject = {firstName: "Richard", favoriteAuthor: "Conrad"};
再重申一下:把对象想象成一个包含多个条目的列表,列表中每个条目(属性)存储为一个名值对。上面例子中属性的名字是firstName 和 favoriteAuthor。它们的值分别为 "Richard" 和 "Conrad"。

属性的名字可以是一个字符串或者一个数字,但是如果属性的名字是数字的话,该属性必须使用中括号的方式访问。后面再详细说明使用中括号访问属性。下面是另一个有数字作为属性名的对象的例子:

 var ageGroup = {30: "Children", 100:"Very Old"};
console.log(ageGroup.30) // 这样会抛出一个错误
// 这是通过访问属性30的值,拿到"Children"这个值的方法
console.log(ageGroup["30"]); // Children

//最好避免使用数字作为属性名
作为一个javascript开发人员,用得最多的数据类型就是对象,主要用来存储数据和创建你自定义的方法和函数。

引用数据类型和基础数据类型

引用数据类型和基础数据类型最主要的区别就是引用数据类型的值是一个引用(reference),而基础数据类型的值是真实的值,比如:

// The primitive data type String is stored as a value
var person = "Kobe";  
var anotherPerson = person; // anotherPerson = the value of person
person = "Bryant"; // value of person changed

console.log(anotherPerson); // Kobe
console.log(person); // Bryant
尽管我们将person的值改成了“Bryant”, anotherPerson 这个变量的值仍然是person原来的那个值。

和上面演示的基本数据类型“存储真实值”比较起来,下面是对象“存储引用”的示例:

var person = {name: "Kobe"};
var anotherPerson = person;
person.name = "Bryant";

console.log(anotherPerson.name); // Bryant
console.log(person.name); // Bryant
在这个例子里,我们把person对象赋值给anotherPerson,但是由于person的值是一个引用,而不是一个真正的值,当我们改变person.name属性为“Bryant”的时候,anotherPerson同样反映出这个改变,因为它并没有复制一个person对象。它只不过是得到了person的引用。

对象数据属性有属性特征(attributes):

每条属性“存储数据的对象属性”不仅仅拥有一个名值对,还有三个属性特征(attributes),这三个attributes默认为true:

—可配置 attribute:指明这个属性是否可以被删除或更改。

—可枚举:指明这个属性在 for/in 循环中是否可以被返回。

—可写:指明这个属性是否可以被更改。

注意在以上attributes之外,ECMAScript 5 还指定了 accessor 属性。accessor属性是函数(getters 和 setters)。在已经计划了的2月15号要发布的文章中我们会学习更多关于ECMAScript 5 的内容。

创建对象

有两种常用方式创建对象:

  1. 对象符号
    最常用,并且事实上也最简单的创建对象的方法,就是像下面这样使用对象符号:
    // This is an empty object initialized using the object literal notation
    var myBooks = {};
    
    // This is an object with 4 items, again using object literal
    var mango = {
    color: "yellow",
    shape: "round",
    sweetness: 8,
    
    howSweetAmI: function () {
    console.log("Hmm Hmm Good");
    }
    }

  2. 使用对象构造函数
    
第二中最常用的的创建对象的方法是使用对象构造函数,一个构造还是是用来实例化一个新对象的函数,使用 new 关键字调用构造函数。
    var mango =  new Object ();
    mango.color = "yellow";
    mango.shape= "round";
    mango.sweetness = 8;
    
    mango.howSweetAmI = function () {
    console.log("Hmm Hmm Good");
    } 
    

虽然你可以在你的对象中使用一些保留字(如“for”)作为属性的名字,但是避免这样使用仍然是一个明智的选择。

对象可以包含其他的任何数据类型,包括数字、数组、甚至对象。

创建对象实用的模式

对于在你的应用里面甚至可以只会被使用一次的用于存储数据的简单的对象,使用上面两种方法创建对象已经足够了。

想象一下你有一个应用,需要显示所有水果的详细信息,你的应用中的所有水果都有这些属性:颜色,形状,甜度,价格和一个showName函数。当每次你想创建一个新的水果对象时,都需要打上下面这段代码,那会非常烦人并且效率低下。

var mangoFruit = {
color: "yellow",
sweetness: 8,
fruitName: "Mango",
nativeToLand: ["South America", "Central America"],

showName: function () {
console.log("This is " + this.fruitName);
},
nativeTo: function () {
 this.nativeToLand.forEach(function (eachCountry)  {
            console.log("Grown in:" + eachCountry);
        });
}
}

如果你有10种水果,你需要写10段相同的代码。如果你想要改变nativeTo函数?你就需要改10个不同的地方。再这样往远一点想,如果你在为一个网站的会员创建对象,突然你发现你之前创建的对象的方法不是理想的符合实体的对象?尤其是在开发大型项目的时候。

为了解决这些重复性问题,软件工程师发明了模式(重复和通用问题的解决方法)来使开发更高效更合理。

下面试两种创建对象的常用模式。如果你已经学习了《如何正确学习javascript》课程,你应该已经在Code Academy 里面经常用到第一种模式:

  1. 构造函数模式
    function Fruit (theColor, theSweetness, theFruitName, theNativeToLand) {
    
        this.color = theColor;
        this.sweetness = theSweetness;
        this.fruitName = theFruitName;
        this.nativeToLand = theNativeToLand;
    
        this.showName = function () {
            console.log("This is a " + this.fruitName);
        }
    
        this.nativeTo = function () {
        this.nativeToLand.forEach(function (eachCountry)  {
           console.log("Grown in:" + eachCountry);
            });
        }
    
    
    }

    有了这个模式,创建各种各样的水果对象就非常容易了。像这样:

    var mangoFruit = new Fruit ("Yellow", 8, "Mango", ["South America", "Central America", "West Africa"]);
    mangoFruit.showName(); // This is a Mango.
    mangoFruit.nativeTo();
    //Grown in:South America
    // Grown in:Central America
    // Grown in:West Africa
    
    var pineappleFruit = new Fruit ("Brown", 5, "Pineapple", ["United States"]);
    pineappleFruit.showName(); // This is a Pineapple.
    如果你要改变fruitName函数,你只需要改一个地方。这个模式用一个Fruit函数通过继承,把所有水果和所有功能性和特点性的东西封装起来了。

    注意:
    — 一个继承的属性被定义在对象的原型(prototype)属性里,举个例子:someObject.prototype.firstName = “rich”;

    — 一个自有的属性直接定义在对象自己的属性里,例如:

    // Let’s create an object first:
    var aMango = new Fruit ();
    // Now we define the mangoSpice property directly on the aMango object
    // Because we define the mangoSpice property directly on the aMango object, it is an own property of aMango, not an inherited property.
    aMango.mangoSpice = “some value”;
    
    — To access a property of an object, we use object.property, for example:
    console.log(aMango.mangoSpice); // “some value”
    
    
— To invoke a method of an object, we use object.method(), for example
:
    // First, lets add a method
    aMango.printStuff = function () {return “Printing”;}
    
    // Now we can invoke the printStuff method:
    aMango.printStuff ();

  2. 原型(prototype)模式
    在我们开始讨论原型模式之前,你需要理解javascript的原型(prototype)。如果你不了解,请阅读我的文章JavaScript Prototype in Plain, Detailed Language
    function Fruit () {
    
    }
    
    Fruit.prototype.color = "Yellow";
    Fruit.prototype.sweetness = 7;
    Fruit.prototype.fruitName = "Generic Fruit";
    Fruit.prototype.nativeToLand = "USA";
    
    Fruit.prototype.showName = function () {
    console.log("This is a " + this.fruitName);
    }
    
    Fruit.prototype.nativeTo = function () {
                console.log("Grown in:" + this.nativeToLand);
    }

    下面是我们如何调用这个原型模式的Fruit()构造函数:

    var mangoFruit = new Fruit ();
    mangoFruit.showName(); //
    mangoFruit.nativeTo();
    // This is a Generic Fruit
    // Grown in:USA

扩展阅读

这两种模式完整的探讨,每种模式的原理的彻底解释以及它们各自的缺点,请阅读Professional JavaScript for Web Developers的第六章。你也将学到Zakas认为最好使用的模式(提示:上面的两种都不是)。

如何访问对象的属性

两种访问属性的基本方法是:点或者方括号。

  1. 使用点访问属性
    // 我们在上面的例子中一直使用的都是点来访问属性的,下面是另一个例子:
    var book = {title: "Ways to Go", pages: 280, bookMark1:"Page 20"};
    
    // 使用点访问book对象的属性,你可以这样:
    console.log ( book.title); // Ways to Go
    console.log ( book.pages); // 280
  2. 使用方括号访问属性
    //你这样使用方括号访问book对象的属性:
    console.log ( book["title"]); //Ways to Go
    console.log ( book["pages"]); // 280
    
    //或者,为了避免你的变量和属性名重名,你可以这样:
    var bookTitle = "title";
    console.log ( book[bookTitle]); // Ways to Go
    console.log (book["bookMark" + 1]); // Page 20



访问一个对象不存在的属性得到的结果是“undefined”。

自有和继承的属性

对象有继承的属性和自有的属性。自有的属性就是在这个对象上定义的属性,而继承的属性就是从对象的prototype对象继承而来的属性。

要判断一个对象是否拥有一个属性(包括继承的和自有的属性),你可以使用 in 操作符。

// 创建一个school对象,给它一个schoolName属性
var school = {schoolName:"MIT"};

// Prints true because schoolName is an own property on the school object
console.log("schoolName" in school);  // true

// Prints false because we did not define a schoolType property on the school object, and neither did the object inherit a schoolType property from its prototype object Oject.prototype.
console.log("schoolType" in school);  // false
 
// Prints true because the school object inherited the toString method from Object.prototype. 
console.log("toString" in school);  // true

hasOwnProperty

要判断一个对象是否拥有一个特定的自有属性,你可以使用 hasOwnProperty 函数。因为你有时候总会需要遍历一个对象的自有属性,所以这个函数式非常有用的。

// Create a new school object with a property name schoolName
var school = {schoolName:"MIT"};

// Prints true because schoolName is an own property on the school object
console.log(school.hasOwnProperty ("schoolName"));  // true
 
// Prints false because the school object inherited the toString method from Object.prototype, therefore toString is not an own property of the school object.
console.log(school.hasOwnProperty ("toString"));  // false 

访问和枚举对象的属性

你可以使用for/in 循环或者是普通的循环来访问对象的可枚举的属性(包括自有的和继承来的)

// Create a new school object with 3 own properties: schoolName, schoolAccredited, and schoolLocation.
var school = {schoolName:"MIT", schoolAccredited: true, schoolLocation:"Massachusetts"};

//Use of the for/in loop to access the properties in the school object
for (var eachItem in school) {
console.log(eachItem); // Prints schoolName, schoolAccredited, schoolLocation

}

访问继承的属性

从Object.prototype 继承来的属性是不可枚举的。所以使用 for/in 循环访问不了它们。但是如果继承来的属性石可枚举的, for/in 循环就可以访问到它。

譬如:

//Use of the for/in loop to access the properties in the school object
for (var eachItem in school) {
console.log(eachItem); // Prints schoolName, schoolAccredited, schoolLocation

}

// Create a new HigherLearning function that the school object will inherit from.
/* SIDE NOTE: As Wilson (an astute reader) correctly pointed out in the comments below, the educationLevel property is not actually inherited by objects that use the HigherLearning constructor; instead, the educationLevel property is created as a new property on each object that uses the HigherLearning constructor. The reason the property is not inherited is because we use of the "this" keyword to define the property.
*/


function HigherLearning () {
this.educationLevel = "University";
}

// Implement inheritance with the HigherLearning constructor
var school = new HigherLearning ();
school.schoolName = "MIT";
school.schoolAccredited = true;
school.schoolLocation = "Massachusetts";


//Use of the for/in loop to access the properties in the school object
for (var eachItem in school) {
console.log(eachItem); // Prints educationLevel, schoolName, schoolAccredited, and schoolLocation
}

在上一个例子中,注意 educationLevel 属性是定义在HigherLearning 函数里面的,尽管educationLevel 不是school对象的自有属性—它是继承来的,但是也被作为school对象的一个属性列出来了。

对象的原型特征和原型属性

对于理解javascript,对象的原型特征和原型属性是一个特别重要的概念。阅读我的文章 JavaScript Prototype in Plain, Detailed Language 了解更多。

删除一个对象的属性

删除对象的属性,使用delete操作符。你不能删除一个继承来的属性,也不能删除属性特征(attribute)为可配置(configurable)的属性。要删除继承的属性,你必须在原型对象(定义这个属性的对象)上删。同样,你也不能删除使用var关键字声明的全局对象的属性。(Also, you cannot delete properties of the global object, which were declared withe var keyword.)

如果删除成功,delete操作符会返回true。让人吃惊的事,如果要删除的属性不存在,或者这个属性不能被删除(比如不可配置的属性或者不是对象自有的属性),它仍然返回true。

看下面的例子:

var christmasList = {mike:"Book", jason:"sweater" }
delete christmasList.mike; // deletes the mike property

for (var people in christmasList) {
	console.log(people);
}
// Prints only jason
// The mike property was deleted

delete christmasList.toString; // returns true, but toString not deleted because it is an inherited method

// Here we call the toString method and it works just fine—wasn’t deleted 
christmasList.toString(); //"[object Object]"

// You can delete a property of an instance if the property is an own property of that instance. For example, we can delete the educationLevel property from the school's object we created above because the educationLevel property is defined on the instance: we used the "this" keyword to define the property when we declare the HigherLearning function. We did not define the educationLevel property on the HigherLearning function's prototype.

console.log(school.hasOwnProperty("educationLevel")); true
// educationLevel is an own property on school, so we can delete it
delete school.educationLevel; true 

// The educationLevel property was deleted from the school instance
console.log(school.educationLevel); undefined

// But the educationLevel property is still on the HigherLearning function
var newSchool = new HigherLearning ();
console.log(newSchool.educationLevel); // University

// If we had defined a property on the HigherLearning function's prototype, such as this educationLevel2 property:
HigherLearning.prototype.educationLevel2 = "University 2";

// Then the educationLevel2 property on the instances of HigherLearning would not be own property. 

// The educationLevel2 property is not an own property on the school instance
console.log(school.hasOwnProperty("educationLevel2")); false
console.log(school.educationLevel2); // University 2

// Let's try to delete the inherited educationLevel2 property
delete school.educationLevel2; true (always returns true, as noted earlier)

// The inherited educationLevel2 property was not deleted
console.log(school.educationLevel2); University 2

序列化和反序列化对象

要通过HTTP传输你的对象,或者把你的对象转换成字符串你需要序列化它(将它转换成为字符串);你可以使用JSON.stringify函数来序列化你的对象。注意,ECMAScript 5 之前,你需要使用流行的json2库(作者Douglas Crockford)得到JSON.stringify函数。现在ECMAScript 5当中,已经把这个函数标准化了。

反序列化对象(把字符串转换成对象),可以用同样的json2库中的JSON.parse函数。这个函数也被ECMAScript5定为标准了。

JSON.stringify 例子:

var christmasList = {mike:"Book", jason:"sweater", chelsea:"iPad" }
JSON.stringify (christmasList);
// Prints this string:
// "{"mike":"Book","jason":"sweater","chels":"iPad"}"



// To print a stringified object with formatting, add "null" and "4" as parameters:
JSON.stringify (christmasList, null, 4);
// "{
    "mike": "Book",
    "jason": "sweater",
    "chels": "iPad"
}"

// JSON.parse Examples \\

// The following is a JSON string, so we cannot access the properties with dot notation (like christmasListStr.mike)
var christmasListStr = '{"mike":"Book","jason":"sweater","chels":"iPad"}';

// Let’s convert it to an object
var christmasListObj = JSON.parse (christmasListStr); 

// Now that it is an object, we use dot notation
console.log(christmasListObj.mike); // Book

更多详细的javascript对象内容,包括ECMAScript5 增加的操作对象的内容,请阅读avaScript: The Definitive Guide 6th Edition的第六章。.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值