• 对象 Object


    在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()函数。




      

  • 相关阅读:
    华为为什么再发布2016年就已经对外露脸甚至商用的欧拉操作系统。
    更安全,仅允许当前用户运行脚本法:vscode运行python时提示无法加载文件xxx.venvScriptsactivate.ps1
    ubuntu下安装odoo 14.0框架
    安利: Swagger工具, 一个REST APIs文档生成工具
    关注Brython 项目,在浏览器中运行python,部分替代javascript
    2021年最火的前端框架
    2021 最受欢迎的前端 八 个 UI 框架
    取代os.path的模块pathlib
    Java中Int转byte分析
    基于Java的时间转换:Date、Timestamp和String时间转化
  • 原文地址:https://www.cnblogs.com/bruce-gou/p/9649571.html
Copyright © 2020-2023  润新知