JavaScript对象类型详解

Posted Qianliwind

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript对象类型详解相关的知识,希望对你有一定的参考价值。



javascript对象类型详解

JavaScrtip有六种数据类型,一种复杂的数据类型(引用类型),即Object对象类型,还有五种简单的数据类型(原始类型):NumberStringBooleanUndefinedNull。其中,最核心的类型就是对象类型了。同时要注意,简单类型都是不可变的,而对象类型是可变的。

什么是对象

一个对象是一组简单数据类型(有时是引用数据类型)的无序列表,被存储为一系列的名-值对(name-value pairs)。这个列表中的每一项被称为 属性(如果是函数则被称为 方法)。

下面是一个简单的对象:

    var myFirstObject = { 
        firstName: "Richard",
        favoriteAuthor: "Conrad"
    };

可以把对象考虑成一个列表,列表中的每一项(属性或方法)都以名-值对的方式存储。上面例子中,对象的属性名就是firstNamefavortieAuthor,相应的,对象的属性值为RichardConrad

属性名可以是字符串或者数字,但是如果以数字作为属性名,则必须以方括号(方括号记法)来获得这个数字属性名对应的属性值。稍后有方括号记法的更详细解释。下面是一个方括号记法的例子:

    var ageGroup = {30: "Children", 100:"Very Old"};
    console.log(ageGroup.30) // 报错
    ?// 访问30这个属性的正确方法
    console.log(ageGroup["30"]); // Children?
    ?
    ?//最好避免使用数字作为属性名

作为一个JavaScript程序员,你会经常使用到对象数据类型。一般用它来储存数据,或者创建自定义的方法或函数。

引用数据类型和原始数据类型

引用类型与原始类型最主要的一个不同点就是引用类型是按引用存储的,它不会像原始类型一样,将值直接存储在变量中。比如:

    // 原始类型数据是按值存储的 
    ?var person = "Kobe";  
    ?var anotherPerson = person; // anotherPerson = the value of person?
    person = "Bryant"; // person的值改变了
    ?
    console.log(anotherPerson); // Kobe?
    console.log(person); // Bryan

可以注意到,即使我们将person的值改为"Bryant",对anthoerPerson也会不有丝毫影响,它仍然保存了原本person赋给它的值。

将原始类型的按值存储跟引用类型的按引用存储进行一下比较:

    var person = {name: "Kobe"};
    ?var anotherPerson = person;
    person.name = "Bryant";
    ?
    console.log(anotherPerson.name); // Bryant?
    console.log(person.name); // Bryant

在这个例子中,我们将person对象复制给了anthoerPerson,但是由于person对象中存储的是引用而不是真正的值。所以当我们将person.name改变为"Bryant"的时候,anotherPerson变量也反应出了这个变化,因为它并没有将person中的所有属性都复制一份保存起来,而是直接保存了对象的引用。

对象属性的特性(Attributes)

注:Attribute一般也是翻译为属性,但是为了跟Propertie(也翻译为属性)进行区分,这里将其翻译为特性,这也是咨询过别人的,应该无伤大雅

每个对象属性不止保存了自身的名-值对,它同时还包含了三个特性,这三个特性默认被设置为true。

  • Configurable Attribute: 指定这个对象属性是否可以被删除或修改。
  • Enumerable:指定这个对象属性在for-in循环中是否可以被取得。
  • Writable:指定这个对象属性是否可以被修改。

在EMACScript 5中有一些新的特性,这里不做详细讲解。

创建对象

创建对象有两种比较常用的方法:

  1. 对象字面量
    这是创建对象最常用,也是最简单的方式,直接使用字面量进行创建:

    // 空对象
    ?var myBooks = {};
    ?
    ?// 使用字面量创建的包含4个属性的对象
    ?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作为对象属性的名称,不过这可不是一个明智的选择。

对象的属性可以包含任何数据类型,包括NumberArrays,甚至是其它的Object

对象创建的实践模式

对于创建只使用一次的用于存储数据的简单对象,上面的两种方法就可以满足需求。

但是,假设有一个程序用于展示水果和它的详细信息。程序中的每个水果类型都有如下对象属性:colorshapesweetnesscost 和一个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个不同的地方进行修改。再进一步推想,如果你在开发一个大型网站,你为上面的对象都一个一个添加了属性。但是,你突然发现你创建对象的方式不是很理想,你想要进行修改,这时又该怎么办。

为了解决这些重复性的问题,软件工程师们发明了各种模式(对于重复问题和常见任务的解决方案),使用开发程序更有效率和合理化。

下面是两种创建对象的常用模式:

  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.

    如果你要改变属性或方法,你只需要在一个地方进行修改就可以了。这个模式通过一个Fruit函数的继承,封装了所有水果的功能和特性。

    注意:

    • 可继承的属性需要定义在对象的prototype对象属性上。比如

      someObject.prototype.firstName = "rich";
    • 属于自身的属性要直接定义在对象的上。比如:

      // 首先,创建一个对象
      var aMango = new Fruit ();
      // 接着,直接在对象上定义mongoSpice方法
      // 因为我们直接在对象身上定义了mangoSpice属性,所以它是aMango自身的属性,不是一个可继承的属性
      aMango.mangoSpice = “some value”;
    • 要访问一个对象的属性,使用object.property,如:

      console.log(aMango.mangoSpice); // "some value"
    • 要调用一个对象的方法,使用object.method(),如:

      // 首先,增加一个方法
      aMango.printStuff = function() { return "Printing"; }
      
      // 现在,可以调用printStuff方法
      aMango.printStuff(); 
  2. 原型模式

    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

扩展阅读

如果需要了解这两种模式的更详细的解释,可以阅读《JavaScript高级程序设计》的第六章,其中详细讨论了这两种方法的优缺点。书中还讨论了除这两个外的其它模式。

如何访问对象中的属性

访问对象属性的两种主要方法是点记法(dot notation)和中括号记法(bracket notation)。

  1. 点记法

    // 这是我们前面例子中一直使用的访问属性的方法
    ?var book = {title: "Ways to Go", pages: 280, bookMark1:"Page 20"};
    ?
    ?// 使用点记法访问book对象的title和pages属性:?
    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

自身属性和继承属性

对象拥有自身属性和继承属性。自身属性是直接定义在对象上的属性,而继承属性是从ObjectPrototype继承的属性。

为了确写一个对象是否拥有某个属性(不管是自身属性还是继承属性),可以使用in操作符:

    // 创建一个有schoolName属性的对象
    var school = {schoolName:"MIT"};

    ?// 打印出true,因为对象拥有schoolName这个属性
    console.log("schoolName" in school);  // true?
    ?
    ?// 打印出false,因为我们既没有定义schoolType属性,也没有从Object的Prototype中继承schoolType属性
    console.log("schoolType" in school);  // false?
     
    ?// 打印出true, 因为从Object的Prototype中继承了toString方法
    console.log("toString" in school);  // true

hasOwnProperty

为了确定一个对象是否拥有一个特定的自身属性,可以使用hasOwnPrototype方法。这个方法十分有用,因为我们经常需要枚举一个对象的所有自身属性,而不是继承属性。

    // 创建一个拥有schoolName属性的对象
    ?var school = {schoolName:"MIT"};
    ?
    ?// 打印出true,因为schooName是school的自身属性
    console.log(school.hasOwnProperty ("schoolName"));  // true?
     
    ?// 打印出false,因为toString是从Object的Prototype中继承下来的,并且school的自身属性
    console.log(school.hasOwnProperty ("toString"));  // false 

访问和枚举对象中的属性

为了访问对象中可以枚举的属性(自身或者继承的),可以使用for-in循环或普通的循环方式。

    // 创建拥有3个属性的school对象: schoolName, schoolAccredited, and schoolLocation.?
    ?var school = {schoolName:"MIT", schoolAccredited: true, schoolLocation:"Massachusetts"};
    ?
    ?//使用for-in循环获取对象中的属性
    ?for (var eachItem in school) {
        console.log(eachItem); // Prints schoolName, schoolAccredited, schoolLocation?
    }

访问继承的属性

ObjectPrototype中继承的属性不可枚举的,所以在for-in循环中不会访问到这些属性。然而,如果是可枚举的继承属性,它们也是能够从for-in循环中访问到的。

比如:

    //使用for-in循环访问school对象中的属性
    for (var eachItem in school) {
        console.log(eachItem); // Prints schoolName, schoolAccredited, schoolLocation?
    }



    // 注:以下这段说明是原文的说明
    /* 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

以上是关于JavaScript对象类型详解的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段12——JavaScript的Promise对象

JavaScript对象类型详解

(转) Java中的负数及基本类型的转型详解

JavaScript--对象类型详解

JavaScript对象内置对象值类型和引用类型详解

JavaScript的对象详解