你的浏览器不支持canvas

Love You Ten Thousand Years

JavaScript的Object

Date: Author: M/J

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自小可嗒嗒的博客

JavaScript is designed on a simple object-based paradigm(简单的基于对象的范式)。

An object is a collection of properties, and a property is an association between a name (or key) and a value.

A property’s value can be a function, 这种情况下,属性也被称为方法。

Objects and properties

一个javascript对象有很多属性。一个对象的属性可以被解释成一个附加到对象上的变量。可以通过点符号来访问一个对象的属性。

objectName.propertyName

定义属性

可以在定义一个属性的时候就给它赋值。

var myCar = new Object();
myCar.make = "Ford";
myCar.model = "Mustang";
myCar.year = 1969;

//未赋值的属性值为undefined
myCar.noProperty; // undefined

当一个属性第一次被添加给对象时,JavaScript在对象上调用了一个名为[[Put]]的内部方法。[[Put]]方法会在对象上创建一个新的节点来保存属性。所以当属性makemodelyear在对象上被第一次定义时,[[Put]]方法都在该对象上被调用了。

调用[[Put]]的结果是在对象上创建了一个自有属性(own property),一个自有属性表明仅仅该指定的对象实例拥有的属性。

当一个已有的属性被赋予一个新的值时,调用的是一个名为[[Set]]的方法。

mycar.year = 1988;

JavaScript对象的属性也可以通过方括号访问或者设置。对象有时也被叫作关联数组, 因为每个属性都有一个用于访问它的字符串值。

myCar["make"] = "Ford";
myCar["model"] = "Mustang";
myCar["year"] = 1969;

一个对象的属性名可以是任何有效的JavaScript字符串,或者可以被转换为字符串的任何类型,包括空字符串。然而,一个属性的名称如果不是一个有效的JavaScript标识符(例如,一个由空格或连字符,或者以数字开头的属性名),就只能通过方括号标记访问。

// 同时创建四个变量,用逗号分隔
var myObj = new Object(),
    str = "myString",
    rand = Math.random(),
    obj = new Object();

myObj.type              = "Dot syntax";
myObj["date created"]   = "String with space";
myObj[str]              = "String value";
myObj[rand]             = "Random Number";
myObj[obj]              = "Object";
myObj[""]               = "Even an empty string";

console.log(myObj);

方括号中的所有键都将转换为字符串类型,因为JavaScript中的对象只能使用String类型作为键类型。

也可以通过存储在变量中的字符串来访问属性:

var propertyName = "make";
myCar[propertyName] = "Ford";

propertyName = "model";
myCar[propertyName] = "Mustang";

可以在for...in语句中使用方括号标记以枚举一个对象的所有属性。下面的函数当你将对象及其名称作为参数传入时,显示对象的属性:

function showProps(obj, objName) {
  var result = "";
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
        result += objName + "." + i + " = " + obj[i] + "\n";
    }
  }
  return result;
}


//
showProps(myCar, "myCar")

for...infor...of的区别


属性检测 – Detecting Properties

初学者通常使用以下代码去检测一个属性是否存在:

//unreliable
if (person1.age){
  //do something
}

因为对象是可以包含一些假值的,所以上述的判断是不准确的。更加可靠的判断属性是否存在的方法是使用in操作符。

var person1 = {
  name: "Timi",
  sayName: function(){
    console.log(this.name);
  }
};

console.log("name" in person1);
console.log("sayName" in person1);

in操作符会检查自有属性原型属性(prototype properties),所以如果仅希望检测一个自有属性,可以选择另外一条途径:所有的对象都拥有hasOwnProperty()方法,该方法在给定的属性存在且为自有属性时返回true

console.log("name" in person1);         //true
console.log(person1.hasOwnProperty("name"));  //true

console.log("toString" in person1);   //true
console.log(person1.hasOwnProperty("toString"));  //false

删除属性

将一个属性的值设置为null并不是删除那个属性,而是调用[[Set]]null替换了属性原来的值而已。你需要使用delete操作符来彻底移除对象的一个属性。

delete操作符针对单个对象的属性调用名为[[Delete]]的内部方法,你可以认为该操作在哈希表中移除了一个键值对。


枚举一个对象的所有属性

ECMAScript 5开始,有三种原生的方法用于列出或枚举对象的属性:

  • for...in循环 : 该方法依次访问一个对象及其原型链中所有可枚举的属性
  • Object.keys(o) : 该方法返回一个对象o自身包含(不包括原型中)的所有属性的名称的数组
  • Object.getOwnPropertyNames(o) : 该方法返回一个数组,它包含了对象o所有拥有的属性(无论是否可枚举)的名称

属性类型

属性有两种类型:数据属性(data properties)访问器属性(accessor properties)。数据属性包含一个值,[[Put]]方法的默认行为是创建数据属性。访问器属性不包含值而是定义了一个当属性被读取时调用的函数(称为getter)和一个当属性被写入时调用的函数(称为setter)。

var person1 = {
  _name: "Nicholas",

  get name(){
    return this._name;
  }
  set name(value){
    this._value = value;
  }
}

console.log(person1.name);

persole1.name = "Greg";
cosole.log(person1.name);

本例定义了一个访问器属性name。一个数据属性_name保存了访问器属性的实际值(前置下划线是一个约定俗成的命名规范,表示该属性被认为是私有的,但实际它还是公开的。),


Property Attributes – 属性特征(暂译)

ECMAScript 5引入了多种方法来与属性特征直接互动。

通用特征 – Common Attributes

有两个属性特征是数据和访问器属性都具有的。一个是[[Enumerable]],决定了你是否可以遍历该属性;另一个是[[Configurable]],决定了该属性是否可配置。你可以使用delete删除一个可配置的属性,但无法删除一个不可配置属性。

可以使用Object.defineProperty()方法来改变属性特征,该方法接受3个参数:拥有该属性的对象、属性名和包含需要设置的特征的属性描述对象。所以可以用enumerable属性来设置[[Enumerable]]特征,用configurable来设置[[Configurable]]特征。

var person1 = {
  name: "Nicholas";
};

Object.defineProperty(person1,"name",{
  enumerable: false
});

console.log("name" in person1); //true
console.log(person1.propertyIsEnumerable("name"));  //false

var properties = Object.keys(person1);
console.log(properties.length);     //0

Object.defineProperty(person1,"name",{
  configurable: false
});

//试图删除不可配置属性,删除失败,但并没有错误
delete person1.name;
console.log("name" in person1); //true

Object.defineProperty(person1,"name",{  //试图回复可配置属性,失败,并抛出错误
  configurable:true
});

配置属性的概念就是可以对属性进行操作,一旦修改配置属性为不可配置,则很多操作都失效了。

数据属性特征

数据属性有两个独有的特征,一个是[[Value]],包含属性的值。当你在对象上创建属性时该特征被自动赋值。所有的属性的值都保存在[[Value]]中,哪怕该值是一个函数。

第二个特征是[[Writable]],该特征是一个布尔值,指示该属性是否可以写入。所有的属性都是默认可写入的,除非你额外指定。

同样,你可以使用Object.defineProperty()方法来设置这些属性。特别注意的是,当你用Object.defineProperty()定义一个新的属性时一定记得为所有的特征指定一个值,否则布尔型的特征会被默认为false

var person1 = {};

Object.defineProperty(person1, "name", {
  value: "Nicholas"
});

console.log("name" in person1);   //true
console.log(person1.propertyIsEnumerable("name"));  //false

访问器属性特征

访问器属性也有两个额外特征,[[Get]][[Set]],内含gettersetter函数。

var person1 = {
  _name: "Nicholas"
};

Object.defineProperty(person1, "name",{
  get: function(){
    return this._name;
  },
  set: function(value){
    this._name = value;
  },
  enumerable: true,
  configurable: true
});

//这段代码与下面是一致的
var person1 = {
  _name: "Nicholas",

  get name(){
    return this._name;
  },
  set name(value){
    this._name = value;
  }
};

使用访问器属性特征比使用对象字面形式定义访问器属性的优势在于:你可以为已有的对象定义这些属性。如果你想要用对象字面值形式,你只能在创建对象时定义访问器属性。

定义多重属性

使用Object.defineProperties()方法可以为一个对象同时定义多个属性。这个方法接受两个参数:需要改变的对象和一个包含所有属性信息的对象。

获取属性特征

使用Object.getOwnPropertyDescriptor()方法,该方法只可用于自有属性。接受两个参数:对象名和属性名。如果属性存在,返回一个属性描述对象。

var person1 = {
  name: "Nicholas"
};
var descriptor = Object.getOwnPropertyDescriptor(person1, "name");

consol.log(descriptor.enumerable);  //true
consol.log(descriptor.configurable);  //true
consol.log(descriptor.writable);  //true
consol.log(descriptor.value);  //"Nicholas"


对象的内部特征

对象也具有指导其行为的内部特征。其中,[[Extensible]]是一个布尔值,指明该对象本身是否可以被修改。默认都是可扩展的,意味着新的属性可以随时被添加。你有三种方法可以帮助你锁定对象属性。

Preventing Extensions – 禁止扩展

第一种方法是Object.preventExtensions()创建一个不可扩展的对象,该方法接受一个参数:对象。一旦对象使用了该方法,就永远不能再给它添加新的属性了。

var person1 = {
  name: "Nicholas"
};
console.log(Object.isExtensible(person1));  //true

Object.preventExtensions(person1);
console.log(Object.isExtensible(person1));  //false

person1.sayName = function(){
  console.log(this.name);
};

console.log("sayName" in person1);  //false

Sealing Objects – 对象封印

一个被封印的对象是不可扩展且其所有的属性都不可配置。如果一个对象被封印,只能读写它的属性。

方法是Object.seal(),该方法被调用时,[[Extensible]]特征被置为false,其所有属性的[[Configurable]]特征被置为false

var person1 = {
  name: "Nicholas"
};
console.log(Object.isExtensible(person1));  //true
console.log(Object.isSealed(person1));      //false

Object.seal(person1);
console.log(Object.isExtensible(person1));  //false
console.log(Object.isSealed(person1));      //true

person1.sayName = function(){
  console.log(this.name);
};

console.log("sayName" in person1);  //false

Freezing Objects – 对象冻结

如果一个对象被冻结,则不能在其上添加或删除属性,不能改变属性类型,也不能写入任何数据类型。简而言之,被冻结的对象是一个数据属性都为只读的被封印对象。被冻结对象无法解冻。可以用Object.freeze()来冻结一个对象,用Object.isFrozen()来判断一个对象是否被冻结。


创建新对象

Using object initializers – 使用对象初始化器

语法如下:

var obj = { property_1:   value_1,   // property_# may be an identifier...
            2:            value_2,   // or a number...
            // ...,
            "property n": value_n }; // or a string

也可以使用对象初始化器来创建数组。

Using a constructor function – 使用构造函数

1.通过创建一个构造函数来定义对象的类型。首字母大写是非常普遍而且很恰当的惯用法,用来与非构造函数进行区分 2.通过new创建对象实例

首先,为了定义对象类型,为对象类型创建一个函数以声明类型的名称、属性和方法,例如:

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}

然后,可以通过调用new创建任意数量的car对象。

var kenscar = new Car("Nissan", "300ZX", 1992);
var vpgscar = new Car("Mazda", "Miata", 1990);

使用Object.create方法

对象也可以用Object.create()方法创建。该方法非常有用,因为它允许你为创建的对象选择其原型对象,而不用定义一个构造函数。

// Animal properties and method encapsulation
var Animal = {
  type: "Invertebrates", // Default value of properties
  displayType : function() {  // Method which will display type of Animal
    console.log(this.type);
  }
}

// Create new animal type called animal1
var animal1 = Object.create(Animal);
animal1.displayType(); // Output:Invertebrates

// Create new animal type called Fishes
var fish = Object.create(Animal);
fish.type = "Fishes";
fish.displayType(); // Output:Fishes

继承

所有的JavaScript对象继承于至少一个对象。被继承的对象被称作原型,并且继承的属性可通过构造函数的prototype对象找到。


Defining properties for an object type

You can add a property to a previously defined object type by using the prototype property. This defines a property that is shared by all objects of the specified type, rather than by just one instance of the object.

Car.prototype.color = null;
car1.color = "black";       //为所有类型为car的对象增加了color属性

定义方法

一个方法是关联到某个对象的函数,或者简单地说,方法就是对象的属性是函数(a method is a property of an object that is a function)。

objectName.methodname = function_name;

var myObj = {
  myMethod: function(params) {
    // ...do something
  }

  // 或者 这样写也可以

  myOtherMethod(params) {
    // ...do something else
  }
};

这里objectName是一个已经存在的对象,methodname是方法的名称,而function_name是函数的名称。

可以在对象的上下文中这样调用方法:

object.methodname(params);

可以在对象的构造函数中包含方法定义来为某个对象类型定义方法。

//定义了一个Person对象
function Person(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
}
//创建两个Person实例

var rand = new Person("Rand McKinnon", 33, "M");
var ken = new Person("Ken Jones", 39, "M");

//定义一个Car对象,拥有一个owner属性和displayerCar方法
function Car(make, model, year, owner) {
  this.make = make;
  this.model = model;
  this.year = year;
  this.owner = owner;
  this.displayCar = displayCar;
}

//定义方法
function displayCar() {
  var result = "A Beautiful " + this.year + " " + this.make
    + " " + this.model;
  pretty_print(result);
}

//创建两个Car实例
var car1 = new Car("Eagle", "Talon TSi", 1993, rand);
var car2 = new Car("Nissan", "300ZX", 1992, ken);

//调用方法
car1.displayCar();
car2.displayCar();

通过this引用对象

关键字this,它可以在方法中使用以指代当前对象。


定义Get与Set方法

一个getter是一个获取某个特定属性的值的方法。一个etter是一个设定某个属性的值的方法。

定义gettersetter的语法采用对象字面量语法

使用对象初始化器定义

var o = {
  a: 7,
  get b() {
    return this.a + 1;
  },
  set c(x) {
    this.a = x / 2
  }
};

console.log(o.a); // 7
console.log(o.b); // 8
o.c = 50;
console.log(o.a); // 25

o对象的属性如下:

  • o.a — 数字
  • o.b — 返回 o.a + 1getter
  • o.c — 由 o.c 的值所设置 o.a 值的 setter`

getter方法必须是无参数的,setter方法只接受一个参数

使用Object.defineProperties的方法

使用Object.defineProperties的方法可以对一个已创建的对象在任何时候为其添加gettersetter方法。这个方法的第一个参数是你想定义gettersetter方法的对象,第二个参数是一个对象,这个对象的属性名用作gettersetter的名字,属性名对应的属性值用作定义gettersetter方法的函数。

var o = { a:0 }

Object.defineProperties(o, {
    "b": { get: function () { return this.a + 1; } },
    "c": { set: function (x) { this.a = x / 2; } }
});

o.c = 10 // Runs the setter, which assigns 10 / 2 (5) to the 'a' property
console.log(o.b) // Runs the getter, which yields a + 1 or 6

删除属性

可以用delete操作符删除一个不是继承而来的属性。

//Creates a new object, myobj, with two properties, a and b.
var myobj = new Object;
myobj.a = 5;
myobj.b = 12;

//Removes the a property, leaving myobj with only the b property.
delete myobj.a;

如果一个全局变量不是用var关键字来声明的话,也可以使用delete来删除。如:g = 12; delete g;

编辑备注:

  • 2017-03-14第一次编辑
  • 2017-03-22第二次编辑

对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。