在js 中创建最简单的对象,然后给它添加属性或者方法 示例如下:

    var obj = new Object();
    //或者
    var obj = {};
    obj.name = '张三';
    obj.fun = function(){};

上创建的对象 中有 name 属性和一个 fun 方法,也可如下创建

    var obj = {
        name: '张三',
        fun: function(){}
    }

这个例子中的 obj 对象与前面例子中的 obj 对象是一样的,都有相同的属性和方法。这些 属性在创建时都带有一些特征值(characteristic),JavaScript 通过这些特征值来定义它们的行为。

 

1. 属性类型

  ECMA-262 第 5 版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征。 ECMA-262 定义这些特性是为了实现 JavaScript 引擎用的,因此在 JavaScript 中不能直接访问它们。为了 表示特性是内部值,该规范把它们放在了两对儿方括号中,例如[[Enumerable]]。在ECMAScript 中有两种属性:数据属性和访问器属性。

(1). 数据属性

 数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有 4 个描述其行为的特性,如下:    

Configurable   // 表示能否通过 delete 删除属性从而重新定义属性
Enumerable     //  表示能否通过 for-in 循环及枚举返回属性
Writable       //  表示能否修改属性的值
Value          //  表示这个属性的数据值,这个特性的默认值为 undefined。

对于像前面例子中那样直接在对象上定义的属性,它们的 Configurable 、Enumerable 和 Writable 特性都被设置为 true,而 Value 特性被设置为指定的值。

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

    var p = {};
    Object.defineProperty(p,"name",{
        configurable: true, 
        writable: true,
        enumerable: true,
        value: "123"
    })    
configurable 属性
    var p = {};
    Object.defineProperty(p,"name",{
        configurable: false, //如果不写该属性或者值为false delete 删除不会生效,在严格模式下会报错
        value: "123"
    })
    delete p.name
    console.log(p); //{p:'123'}

 

 

一旦把属性定义为不可配置的, 就不能再把它变回可配置了。此时,再调用 Object.defineProperty()方法修改除 writable 之外 的特性,都会导致错误:

        var p = {};
        Object.defineProperty(p,"name",{
            configurable: false,
            value: "123"
        })
        Object.defineProperty(p,"name",{
            configurable: true,
            value: "123"
        })
        //跑出错误

也就是说,可以多次调用 Object.defineProperty()方法修改同一个属性,但在把 configurable 特性设置为 false 之后就会有限制了

 

 

writable 属性


        var p = {};
        Object.defineProperty(p,"name",{
            writable: false, // 不写该属性 或者值为false 时不能修改值
            enumerable: true,
            value: "123"
        })
        p.name = 321;
          console.log(p); //{p:'123'}

enumerable 属性
        var p = {};
        Object.defineProperty(p,"name",{
            enumerable: true, // 不写该属性 或者值为false 时不能遍历得到对象属性值
            value: "123"
        })
        for(var i in p){
            console.log(p[i]); //未获得值
        }

在调用 Object.defineProperty()方法时,如果不指定,configurable、enumerable 和 writable 特性的默认值都是 false。多数情况下,可能都没有必要利用 Object.defineProperty() 方法提供的这些高级功能。不过,理解这些概念对理解 JavaScript 对象却非常有用。

 

(2). 访问器属性

    访问器属性不包含数据值;它们包含一对儿 getter 和 setter 函数(不过,这两个函数都不是必需的)。 在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值;在写入访问器属性时,会调用 setter 函数并传入新值,这个函数负责决定如何处理数据。访问器属性有如下 4 个特性。

configurable    // 表示能否通过 delete 删除属性从而重新定义属性
Enumerable    // 表示能否通过 for-in 循环返回属性
Get                // 在读取属性时调用的函数。默认值为 undefined。
Set                // 在写入属性时调用的函数。默认值为 undefined。
configurable,Enumerable同数据属性一样的。
        var p = {
            _year: 2000,
            num : 1
        };
        Object.defineProperty(p,"year",{
            get: function(){
                return this._year;
            },
            set: function(newValue){
                if(newValue > 2000){
                    this._year = newValue;
                        this.num += newValue - 2000;
                }
            }
        })
        p.year = 2001;
        console.log(p.num); //2

以上代码创建了一个 p 对象,并给它定义两个默认的属性: _year 和 num。_year 前面 的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。而访问器属性 year 则包含一个 getter 函数和一个 setter 函数。getter 函数返回_year 的值,setter 函数通过计算来确定正确的版本。因此, 把 year 属性修改为 2001 会导致_year 变成 2001,而 num 变为 2。这是使用访问器属性的常见方 式,即设置一个属性的值会导致其他属性发生变化。

不一定非要同时指定 getter 和 setter。只指定 getter 意味着属性是不能写,尝试写入属性会被忽略。 在严格模式下,尝试写入只指定了 getter 函数的属性会抛出错误。类似地,只指定 setter 函数的属性也 不能读,否则在非严格模式下会返回 undefined,而在严格模式下会抛出错误。

 

 

支持 ECMAScript 5 的这个方法的浏览器有 IE9+(IE8 只是部分实现)、Firefox 4+、Safari 5+、Opera 12+和 Chrome。在这个方法之前,要创建访问器属性,一般都使用两个非标准的方法: __defineGetter__()和__defineSetter__()。这两个方法最初是由 Firefox 引入的,后来 Safari 3、 Chrome 1 和 Opera 9.5 也给出了相同的实现。使用这两个遗留的方法,可以像下面这样重写前面的例子。

 

var p = {
    _year: 2004,
  num: 1 
};
//定义访问器的旧有方法
p.__defineGetter__("year", function(){
return this._year; }); p.__defineSetter__("year", function(newValue){ if (newValue > 2004) { this._year = newValue; this.num += newValue - 2004; } }); p.year = 2005;
alert(p.num); //2

在 不 支 持 Object.defineProperty() 方 法 的 浏 览 器 中 不 能 修 改  Configurable  和 Enumerable 。

2. 定义属性
  Object.defineProperty()
//方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
        var p = {};
        Object.defineProperty(p,"name",{
            enumerable: true, 
            value: "123"
        })
  
  
Object.defineProperties() //方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。可以定义哥多个属性

  
var p = {};
Object.defineProperties(p, {
  name: {
    value: true,
    writable: true
  },
  name2: {
    value: 'Hello',
    writable: false
  }
});

 

 

3 读取属性的特性

  使用 ECMAScript 5 的 Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述 符。这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,如果 是访问器属性,这个对象的属性有 configurable、enumerable、get 和 set;如果是数据属性,这 个对象的属性有 configurable、enumerable、writable 和 value.

     var p = {
            num : 1
        };
        Object.defineProperties(p,{
            _year: {
                value : 2000
            },
            year:{
                get: function(){
                    return this._year;
                },
                set: function(newValue){
                    if (newValue > 2004) {
                        this._year = newValue;
                        this.num += newValue - 2004;
                    }
                }
            }
        })
        var obj = Object.getOwnPropertyDescriptor(p,'_year');
        console.log(obj.value);  // 2000
        console.log(obj.configurable); //false
        console.log(typeof obj.get); //undefined
        
        var obj1 = Object.getOwnPropertyDescriptor(p, "year");
        console.log(obj1.value);  // 2000
        console.log(obj1.enumerable); //false
        console.log(typeof obj1.get); //"function"

对于数据属性_year,value 等于最初的值,configurable 是 false,而 get 等于 undefined。 对于访问器属性 year,value 等于 undefined,enumerable 是 false,而 get 是一个指向 getter 函数的指针。

在 JavaScript 中,可以针对任何对象——包括 DOM 和 BOM 对象,使用 Object.getOwnPropertyDescriptor()方法。

 

 

 4.创建对象

 4.1 工厂模式

            function person(name, sex, age){
                var obj = new Object();
                obj.name = name;
                obj.sex = sex;
                obj.age = age;
                obj.sayName = function(){
                    console.log(this.name);
                }
                return obj;
            }
            var zs = person('zs','M',21);
            var ls = person('ls','F',22);

函数 person()能够根据接受的参数来构建一个包含所有必要信息的 对象。可以无 数次地调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建 多个相似对象的问题,但却没有解决对象识别的问题

 

4.2构造函数模式

            function Person(name, sex, age){
                this.name = name;
                this.sex = sex;
                this.age = age;
                this.sayName = function(){
                    console.log(this.name);
                }
            }
            var zs = new Person('zs','M',21);
       var ls = new Person('ls','M',22);
       console.log(zs.constructor === Person);//true

 

Person()中的代码 除了与 person()中相同的部分外,还存在以下不同之处:

  没有显式地创建对象;   

  直接将属性和方法赋给了 this 对象;   

  没有 return 语句。

要创建 Person 的新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4 个步骤:

  (1) 创建一个新对象;
  (2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
  (3) 执行构造函数中的代码(为这个新对象添加属性);
  (4) 返回新对象。
  在前面例子的最后,zs 保存着 Person 的一个的实例。这个对象有一个 constructor(构造函数)属性,该属性指向 Person,如下所示。

  console.log(zs.constructor === Person);//true


  1. 将构造函数当作函数

    构造函数与其他函数的唯一区别,就在于调用它们的方式不同。不过,构造函数毕竟也是函数,不 存在定义构造函数的特殊语法。任何函数,只要通过 new 操作符来调用,那它就可以作为构造函数;而 任何函数,如果不通过 new 操作符来调用,那它跟普通函数也不会有什么两样。例如,前面例子中定义 的 Person()函数可以通过下列任何一种方式来调用。

// 当作构造函数使用
var person = new Person("Nicholas", "M", 21); person.sayName(); //"Nicholas"
     // 作为普通函数调用
Person("Greg", "M", 21); // 添加到window window.sayName(); //"Greg"
// 在另一个对象的作用域中调用
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse"); o.sayName(); //"Kristen"

2. 构造函数的问题

  构造函数模式虽然好用,但也并非没有缺点。使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍,zs 和 ls 都有一个名为 sayName()的方法,但那 两个方法不是同一个 Function 的实例.

this.sayName = new Function("console.log(this.name)"); // 与声明函数在逻辑上是等价的

  以这种方式创建函数,会导致不同的作用域链和标识符解析,但 创建 Function 新实例的机制仍然是相同的。因此,不同实例上的同名函数是不相等的.  

console.log(person1.sayName == person2.sayName); //false

  创建两个完成同样任务的 Function 实例的确没有必要;况且有 this 对象在,根本不用在 执行代码前就把函数绑定到特定对象上面。因此,大可像下面这样,通过把函数定义转移到构造函数外 部来解决这个问题。

    function Person(name, sex, age){
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.sayName = sayName;
    }
    function sayName(){
        console.log(this.name);
    }
    var zs = new Person('zs','M',21);
    var ls = new Person('ls','M',21);
    console.log(  zs.sayName == ls.sayName ); //true

在这个例子中,我们把 sayName()函数的定义转移到了构造函数外部。而在构造函数内部,我们 将 sayName 属性设置成等于全局的 sayName 函数。这样一来,由于 sayName 包含的是一个指向函数 的指针,因此 zs 和 ls 对象就共享了在全局作用域中定义的同一个 sayName()函数。




 

  

 

 

posted on 2018-09-15 22:21  浅唱年华1920  阅读(145)  评论(0编辑  收藏  举报