Javascript面向对象的程序设计_理解对象

JS面向对象的程序设计_理解对象

前言:最近在细读Javascript高级程序设计,对于我而言,中文版,书中不少地方翻译的差强人意,因此用本身所理解的,尝试解读下。

若有纰漏或错误,会很是感谢您的指出。文中绝大部份内容引用自《JavaScript高级程序设计第三版》。函数

面向对象(Object-Oriented, OO)的计算机语言有一个标志,就是它们都有一个类class的概念,经过类能够建立任意多个具备相同属性和方法的对象。ECMAScript中没有类的概念,于是它的对象也与基于类的计算机语言中的对象不一样。this

ECMA-262把对象定义为: “无序属性的集合,其属性能够包含基本值、对象或者函数。” 也就是说对象是一组没有特定顺序的值。对象的每一个属性或方法都有一个名字,而每一个名字都映射(map)到一个值。因此咱们能够把ECMAScript中的对象想象成散列表,换句话说,就是名值对,其中值能够是数据或者函数。每一个对象都是基于引用类型建立的,这个值能够是原生类型,也能够是开发人员定义的类型。翻译

理解对象

若是咱们要建立自定义对象,最简单的方式就是建立一个Object类型的实例对象,而后再为它添加属性和方法。设计

//使用new关键字从Object类型中实例化出一个对象。

var person = new Object();
person.name = "Shaw";
person.age = "Secret";
person.job = "Front-end Engineer";
person.sayName = function(){
    console.log(this.name);
    // 这里的this指向person,call调用它的就是person
}

上述代码,建立了一个person对象,并为它添加了三个属性(name, age, job)和一个方法 sayName()。其中,sayName() 方法用于显示this.name(被解析为person.name)的值。 code

如今,对象字面量成为建立这种对象的首选模式。对象

// 以前的例子,能够写成这样

var person = {
    name: "Shaw",
    age: "Secret",
    job: "Front-end Engineer",
    sayName: function(){
        console.log(this.name)
    }
}

这些属性在建立时都带有一些特性值(attribute ), Javascript经过特性来定义属性(property)的行为。描述特性的字符叫作descriptor。ip

属性特性 PropertyAttribute

特性(attribute)描述了属性(property)的各类特征(也能够理解为特性值),ECMA-262第5版定义这些特性是为了实现JavaScript引擎用的,所以在JavaScript中不能直接访问到它们。为了表示特性是内部值, 该规范把它们放在两对方括号中,[[Enumerable]]。开发

ECMAScript定义对象的属性分为两种:数据属性和访问器属性。get

  1. 对象属性的数据特性

数据特性包含一个数据值的位置。在这个位置能够读取和写入值(I/O)。博客

数据特性的描述符(property attribute descriptor)

  • [[Configurable]], 可配置的: 表示可否经过delete删除属性从而从新定义属性,可否修改属性的特性或可否把属性修改成访问器属性。像上面例子那样直接在对象上定义的属性,这些属性的这个特性默认值为true(能够删改定义属性)。
  • [[Enumerable]], 枚举: 表示可否经过for-in循环返回属性。像上面例子那样直接在对象上定义的属性,对象的属性的这个Enumerable特性值默认为true。
  • [[Writable]], 改写: 表示可否修改属性的值。像上面例子那样直接在对象上定义的属性,对象的属性的这个writable特性值默认为true。
  • [[value]], 输入读取属性值:读取属性值的时候,从这个位置开始读; 写入属性的时候,把新值保存在这个位置。这个特性的默认值为undefined。

对于上面的那个例子中直接在对象上定义的属性, 属性们的特性值[[Configurable]]、[[Enumberable]]、[[Writable]]的默认值都为true,而[[value]]特性被设置为指定的值。

//例子
var person = {
    name: "Shaw"
    //伪代码
    //name: { value: "Shaw"}
};

//这里建立一个名为name的属性,为它指定的值是"Shaw"
//也就是说, [[value]]特性被设置为“Shaw”,而对这个值的任何修改都将反应在这个位置。

要修改属性默认的特性,必须使用ECMAScript5的Object.defineProperty()方法。 这个方法接收三个参数: 属性所在的对象、属性的名字和一个描述符对象。 其中, 描述符(descriptor)对象的属性必须是: configurable、enumerable、writable和value。 设置其中的一或多个值,能够修改对应的特性值。请看下面的例子:

var person = {};

Object.defineProperty(person, "name", {
    writable: false,
    value: "Shaw"
});

console.log(person.name); //"Shaw"
person.name = "roc";
console.log(person.name); // 仍是"Shaw", 由于经过Object.defineProperty()方法,设置该属性的特性writable的值为false,即不可改写。

这个例子建立一个名为name的属性,它的值“Shaw"是不可改写的,即只能读取。若是尝试为它指定新值,赋值操做会被忽略;在严格模式下,赋值操做会抛出错误。

相似的规则也适用于属性的可配置特性。例如:

var person = {};
Object.defineProperty(person, "name", {
    configurable: false,
    value: "Shaw"
});
delete person.name; // false, 由于configurable为false; 
console.log(person.name);

把属性的特性configurable设置为false,意味着该属性是不可配置的,也就是不能从对象中删除这个属性。若是对这个属性调用delete, 在非严格模式下什么都不会发生, 在严格模式下, 会抛出一个错误。 ==注意==, 一旦把属性的特性定义为不可配置的,就不能再把它变回可配置了。此时,再调用Object.defineProperty()方法,会抛出错误。

var person = {};
Object.defineProperty(person, "name", {
    configurable: false,
    value: "Shaw"
    //不指定的话, 其他的特性值为默认值,也就是说特性writable的默认值为 false,
    //特性 enumerable的默认值为 false
})

//Uncaught TypeError: Cannot redefine property: name
Object.defineProperty(person, "name", {
    configurable: true,
    value: "Shaw"
})

换句说,能够屡次调用Object.defineProperty()方法来修改该属性, 可是设置属性的特性configurable的值为false(不可配置)后,其结果是不可逆的, 就会有限制了。

在调用Object.defineProperty()方法来建立一个新的属性时候,若是不指定,configurable、writable、enumerable特性的默认值都为false。

若是调用Object.defineProperty()方法只是修改已定义的属性的特性, 则无此限制。 由于已经定义的属性的特性configurable、enumerable、writable默认都为true。好比以前的那个例子:

var person = {
    name: "Shaw"
    // 伪代码 
    /*propertyAttribute = {
        configurable(descriptor): true,
        enumerable(descriptor): true,
        writable(descriptor): true,
        value(descriptor): "Shaw"*/
    }
}

多数状况下, 咱们可能都用不到Object.defineProperty()提供的这些高级功能。

不过,理解这些概念对理解JavaScript对象有很大的帮助。

  1. 对象属性的访问器特性

顾名思义就是定义了对象内部,该属性的特性是有权限访问或设置对象内部其余属性。
访问器属性不包含数据值;它们包含一对儿getter和setter函数。

在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值; 在写入访问器属性时,会调用setter函数并传入新值,这个函数负责如何处理数据。 访问器属性有以下4个特性:

  • [[configurable]],是否可配置。 表示可否经过delete删除属性从而从新定义属性,可否修改属性的特性。或者可否把属性修改成数据属性。对于直接在对象上定义的属性, 这个属性的该特性的默认值为true。
  • [[Enumerable]], 是否可枚举。 表示可否经过for-in循环返回属性。 对于直接在对象上定义的属性,这个属性的该特性的默认值为true。
  • [[Get]], 读取属性函数。 读取属性时调用的函数。默认值为undefined。
  • [[Set]], 设置属性函数。 设置属性时调用的函数。默认值为undefined。

访问器属性不能直接定义, 必须使用Object.defineProperty()来定义。

var cat = {
    _age: 1, 
    adoptionYear: 2016
};

Object.defineProperty(cat, 'age', {
    get: function() {
        return this._age;
    },
    set: function(newAge) {
        if(newAge>1) {
            this._age = newAge; //5
            this.adoptionYear +=  newAge - 1;
        }
    }
});

window.alert(cat.age); //1
cat.age = 5;
window.alert(cat.age); //5
window.alert(cat.adoptionYear); // 2020

以上代码建立了一个cat对象,并给它定义两个默认的属性: _age和adoptionYear。 _age 是一种约定俗成的记号,表示只能经过对象属性的访问器特性方法才能访问到的属性(能够想象成对象的成员属性)。 而属性year的访问器特性则包含一个get函数和set函数。get函数返回_age的值, set函数经过计算来肯定猫咪的收养年份。所以,把age的属性值修改成5,而adoptionYear变为2020,即已经收养了四年。这是使用访问器属性的常见方法,即设置一个属性的值而后改变另一个属性的值。

在这个定义对象属性的访问器特性以前,Object.defineProperty(object, 'property', { get: function() {} , set: function(){}}) 。 要建立对象属性的访问器特性,通常都使用两个非标准方法: __defineGetter__() 和 __defineSetter__(), ==注意这里是双下划线==。 这两个方法最初是由Firefox引入的,后来Safari 3,Chrome 1和Opera9.5也给出相同的实现。 使用这两个遗留的方法,也能够实现对象属性的访问器特性设置。

// 使用例子以下
var cat = {
    _age: 1,
    adoptionYear: 2016
}

cat.__defineGetter__('age', function(){
    return this._age;
});

cat.__defineSetter__('age', function(newAge){
    if(newAge > 1) {
        this._age = newAge;
        this.adoptionYear += this._age - 1;
    }
})

alert(cat.age); //1
alert(cat.adoptionYear); //2016
cat.age = 5;
alert(cat.age); //5
alert(cat.adoptionYear); //2020

同时定义对象多个属性的特性的方法

为对象同时定义多个属性特性的可能性很大,ECMAScript5又定义了一个Object.defineProperties(object, ObjectDescriptor)方法,用这个方法能够经过一次性定义对象的多个属性特性。 该方法接收两个对象参数: 第一个对象参数是要添加和修改属性特性值的对象,第二个对象参数的属性与第一个对象中要添加或修改的属性一一对应。

// 仍是代码比较直观

var cat = {};
Object.defineProperties(cat, {
    _name: {
        writable: true,
        value: 'Meow'
    },
    _age: {
        writable: true,
        value: 1
    },
    adoptionYear: {
        writable: true,
        value: 1
    },
    age: {
        get: function() {
            return this._age;
        },
        set: function(newAge) {
            if(newAge <= 1) {
                this._age = 1;
            };
            this._age = newAge;
            this.adoptionYear += newAge -1;
        }
    }
})

alert(cat.age); // 1
cat.age = 3;
alert(cat.age); // 3
alert(cat.adoptionYear); // 3 从小就收养了。

读取属性的特性

ECMAScript5定义了Object.getOwnPropertyDescriptor(object, 'property')方法, 该方法取得给定属性的特性描述符。

这个方法接收两个参数:

  1. 第一个参数为: 属性所在的对象。
  2. 第二个参数为: 须要读取其特性值(PropertyAttribute) 的对象属性。

该方法的返回值是一个对象,因此能够经过.语法读取到值,若是是属性的访问器特性,返回的这个对象的属性有:configurable、enumerable、get, set; 若是是数据属性, 这个对象的属性有configurable、enumerable、writable和value。

var person = {};
Object.defineProperties(person, {
    _name: {
        writable: true,
        value: '请使用person.name = ?,给我定义一个名字呀!'
    },
    _age: {
        writable: true,
        value: '请使用person.age = ?,给我定义一个年龄呀!'
    },
    _gender: {
        writable: true,
        value: '请使用person.gender = ?,给我定义一个性别呀!'
    },
    name: {
        get: function(){
            return this._name;
        },
        set: function(newName) {
            this._name = newName;
        }
    },
    age: {
        get: function(){
            return this._age;
        },
        set: function(newAge) {
            this._age = newAge;
        }
    },
    gender: {
        get: function(){
            return this._gender;
        },
        set: function(newGender) {
            this._gender = newGender;
        }
    }
});

var objPropAttrDescriptor = Object.getOwnPropertyDescriptor(person, '_age');
alert(objPropAttrDescriptor.value); //请使用person.gender = ?,给我定义一个性别呀!
alert(objPropAttrDescriptor.configurable); //false;
alert(typeof objPropAttrDescriptor.get); // undefined;

var objPropAttrDescriptor = Object.getOwnPropertyDescriptor(person, 'age');
alert(objPropAttrDescriptor.value);// undefined;
alert(objPropAttrDescriptor.configurable); //false;
alert(typeof objPropAttrDescriptor.get); // function

在JavaScript中,能够针对任何对象 - 包括DOM和BOM对象,使用Object.getOwnPropertyDescript(object, 'objProperty')方法,。

总结

若是英语水平足够好的话,建议看英文原版书籍或者国外大师的博客。毕竟翻译过来的文字,不少东西都变了味, 并且看英文,会让你注意力更加集中,不容易跑神。

接下来我会按期翻译一些国外的精品博客。 立个Flag,促使本身进步。

相关文章
相关标签/搜索