《JS高级程序设计-第3版》的读书笔记关于对象的属性

ECMAScript与其他OO语言对象的区别:js中没有类的概念,但是有对象的概念,ECMA-262把对象定义为,无序属性的集合,其属性可以包含基本值、对象或者函数。这里我将其当成java中的HashMap<Key, Object>,一个HashMap对应一个对象。应为没有类的概念所以对象的创建也有一些不同。在js里每个对象都是基于一个引用类型创建的(或者说是'对象定义')。可以是js原生类型(例如:Object、Array、Date)也可以是自定义的引用类型。

对象的创建:对象的创建的统一方式是new操作符后跟构造函数。例如:var person = new Object()。但是对于不同的类型还可能存在着不同的字面量表示法。例如Object和Array类型:

Object类型,使用字面量表示法创建对象
var person = {
    "name" : "abc",    //属性名也可以是字符串
    age : 18,        
    5 : true        //最后一个属性后没有逗号,不同版本浏览器可能会出错,数值5转化为字符串
};    //这有个分号,js中表达式上下文指的是能够返回一个值(表达式),这里的{}及其内部构成表达式

var person ={}; //与new Object()相同,现在版本这句话不会调用Object构造函数,以前的可能。
person.name = "abc";
person.age = 18;

Array类型,使用字面量表示法创建对象
var colors = ["red", "blue", "green"]; //创建一个包含3个字符串的数组
var names = [];                      //创建一个空数组 
var values = [1,2,];                  // 不要这样!这样会创建一个包含2或者3项的数组
var options = [,,,,,];                  // 不要这样!这样会创建一个包含5或者6项的数组

在js中可以用person.name来访问属性,也可以用person["name"]这种方括号语法,方括号语法可以访问一些特别的属性名,类如person["first name"],或者访问属性名是关键字或保留字的属性。里面必须要用引号,单引号和双引号都行。

对象的属性:js中有两种属性,数据属性和访问器属性。不同的属性有不同的特性,这些特性描述了属性的各种特征。(类比为,属性为成员变量,特性为public、static、final这些修饰符),特性是给js引擎用的,不能直接访问。

数据属性:数据属性包含一个数据值的位置,在这个位置可以读写。即可以通过person.name这样访问和赋值的属性。该属性有四个特性:

[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把苏醒修改为访问器属性 。对于直接在对象上定义的属性,如上例该值默认为true。

[[Enumerable]]:表示能否通过for-in循环返回属性。对于直接在对象上定义的属性,如上例该值默认为true。

[[Writable]]:表示能够修改属性的值。对于直接在对象上定义的属性,如上例该值默认为true。

[[Value]]:包含这个属性的数据值。读写都是在这个位置。默认为undefined。

 属性的特性可以修改,但是要使用ECMAScript5的Object.defineProperty()方法。这个方法就收三个参数:属性所在对象、属性的名字和一个描述符对象。其中描述符对象必须为上面的特性的一个或多个(注意首字母小写,js是区别大小写的)。

var person = {}; 
Object.defineProperty(person, "name", {
    writable: false,     //即便删除这一行,下面输出部分的结果也是一样的。
    value: "Nicholas"  
}); 
 
alert(person.name);  //"Nicholas" 
person.name = "Greg"; 
alert(person.name);  //"Nicholas"

在非严格模式会忽略,在严格模式会报错。这里值得注意的地方有两个。其一、如果这里修改的特性如果为configurable,即将configurable修改为false,那么你就没有办法修改回来了。其二、这里了传入的特性虽然只有一个,但是默认为传入四个值,并且默认值都是false(value默认为undefined)。这也就意味着一旦defineProperty运行,只有传入的特性可以操控,其余的都是false。即上面的方法调用后,其实即便删除writable: false。其结果也是一样的。

访问器属性:访问器属性不包含数据值。它们包含一对getter和setter函数(不过,这两个函数都不是必需的)。这两句话我的理解是,这个属性因为它包含两个函数,为了方便使用这两函数,用一个标识符来代替这两个函数,以达到用"对象名.访问器属性"这种方式就能访问这两个函数的程度。这个标识符就是访问器属性。该属性有四个特性如下。

[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,(访问器也可以直接定义?)默认为true。

[[Enumerable]]:表示能否通过for-in循环返回属性。对于直接在对象上定义的属性,(访问器也可以直接定义?)默认为true。

[[Get]]:在读取属性时调用的函数。默认为undefined。

[[Set]]:在写入属性时调用的函数。默认为undefined。

访问器属性不能直接定义,必须用Object.defineProperty()来定义,例子如下:

var book = {     
    _year: 2004, //_year前面的下划线是一种常用的记号,用于表达只能通过对象方法访问的属性。
    edition: 1   //当然这是一种约定,你要是非要用点访问法访问,也是可以访问的
}; 
 
Object.defineProperty(book, "year", {
    //enumerable: true,     //取消注释才能返回year
    get: function(){         
        return this._year;     //这里说明函数被book调用,合理
    },     
    set: function(newValue){ 
        if (newValue > 2004) {             
            this._year = newValue;             
            this.edition += newValue - 2004;         
        }     
    } 
}); 
for(let a in book){
    console.log(a);    //输出_year和edition,没有year
}
book.year = 2005; 
alert(book.edition);  //2

上面例子就是访问器属性的常见用法,即设置一个属性的值导致其他属性发生变化。其在调用defineProperty()方法后同样存在不操作的属性设置为false。(get和set属性设置为undefined的情况,get和set还不知道怎么验证所以括起来,有可能get和set为指向getter和setter的指针,也有可能为null)。但是只对访问器属性有效果,例如上例,enumerable和configurable变成了false,但是只对year这个属性起作用,在for-in中不返回year属性,delete book.year; 无效果。另外,不一定非要同时指定getter和setter。只指定getter意味着属性不能写,尝试写入会被忽略,严格模式下会报错。只指定setter函数的属性不能读,否则返回undefined,严格模式下会报错。这里尝试在非严格模式下,给getter和setter添加参数,给getter添加参数没法传参,给setter添加参数没法给第二个参数传参。没报错。

两个遗留的方法创建访问器属性:

var book = {     
    _year: 2004,     
    edition: 1 
}; 

book.__defineGetter__("year", function(){  //遗留的__defineGetter__()方法
    return this._year; 
}); 
 
book.__defineSetter__("year", function(newValue){ //一流的__defineSetter__()方法
    if (newValue > 2004) {         this._year = newValue;         
        this.edition += newValue - 2004;     
    } 
}); 
 
book.year = 2005; alert(book.edition);  //2

ECMAScript 5 中有个Object.defineProperties()的方法,用于复制属性和属性的特性。第一个参数时要添加和修改的对象,第二个是模板,第二个对象的属性和第一个对象的某个属性相同时应该就是修改了。

var book = {
    x:100
}; 

var fee = {     
    _year: {         //由下面for-in的输出结果可以看出,这种属性后加{}来定义属性的方式
        value: 2004    //会使其余特性会变成false,因为不是在对象上直接定义
    },          
    edition: {         
        value:  1
    }, 
    say: {
        value: function(){         
                    alert("123");     
                } 
    },
    year: {         
        get: function(){ 
            return this._year;         
        }, 
        set: function(newValue){             
            if (newValue > 2004) {                 
                this._year = newValue;                 
                this.edition += newValue - 2004;             
            }         
        }     
    } 
}

Object.defineProperties(book, fee); //fee可以用fee等号右边的整个来代替
for(var y in book){
    console.log(y);        //只输出了属性x,因为其余属性的enumerable都为false
}
book.say();      //输出123,复制成功。
console.log(book.year);
console.log(book.x);    //原本的book.x并没有发生改变

ECMAScript 5 中的Object.getOwnPropertyDescriptor()用于读取属性的特性,属性不同对应不同的特性,第一个参数为对象,第二个参数为属性名称。返回一个以特性为属性的对象,属性值为原本的特性值。如下

var book = {}; 
 
Object.defineProperties(book, {     
    _year: {          
        value: 2004     
    }, 
    edition: {         
        value: 1     
    }, 
    year: {         
        get: function(){             
            return this._year;         
        }, 
        set: function(newValue){             
            if (newValue > 2004) {                 
                this._year = newValue;                 
                this.edition += newValue - 2004;             
            }         
        }     
    } 
}); 
 
var descriptor = Object.getOwnPropertyDescriptor(book, "_year"); 
alert(descriptor.value);         //2004 
alert(descriptor.configurable); //false 
alert(typeof descriptor.get);    //"undefined" 
var descriptor = Object.getOwnPropertyDescriptor(book, "year"); 
alert(descriptor.value);        //undefined 
alert(descriptor.enumerable);   //false 
alert(typeof descriptor.get);   //"function" 

 访问对象中的属性

var person = {
    "name" : "abc",
    age : 18,
    methods: function(){
        alert(this.age);    //需要加this才能访问到对象的属性
        alert(age);        //会报未定义的错
    }
};  
person.methods();

 

posted @ 2019-07-27 16:07  缓步徐行静不哗  阅读(244)  评论(0)    收藏  举报