精华内容
下载资源
问答
  • 属性描述对象 概述 Object.getOwnPropertyDescriptor() Object.getOwnPropertyNames() Object.defineProperty(),Object.defineProperties() Object.prototype.propertyIsEnumerable() 元属性 value writable ...

    目录

    属性描述对象

    概述

    JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可写、可遍历等等。

    这个内部数据结构称为“属性描述对象”(attributes object)。每个属性都有自己对应的属性描述对象,保存该属性的一些元信息。

    {
      value: 123,
      writable: false,
      enumerable: true,
      configurable: false,
      get: undefined,
      set: undefined
    }

    (1)value

    value 是该属性的属性值,默认为 undefined。

    (2)writable

    writable是一个布尔值,表示属性值(value)是否可改变(即是否可写),默认为true。

    (3)enumerable

    enumerable 是一个布尔值,表示该属性是否可遍历,默认为true。如果设为false,会使得某些操作(比如for…in循环、Object.keys())跳过该属性。

    (4)configurable

    configurable 是一个布尔值,表示可配置性,默认为true。

    如果设为false,将阻止某些操作改写该属性,比如无法删除该属性,也不得改变该属性的属性描述对象(value属性除外)。

    也就是说,configurable属性控制了属性描述对象的可写性。

    (5)get

    get是一个函数,表示该属性的取值函数(getter),默认为undefined。

    (6)set

    set是一个函数,表示该属性的存值函数(setter),默认为undefined。

    Object.getOwnPropertyDescriptor()

    Object.getOwnPropertyDescriptor() 方法可以获取属性描述对象。

    它的第一个参数是一个对象,第二个参数是一个字符串,对应该对象的某个属性名。

    var obj = { p: 'a' };
    
    Object.getOwnPropertyDescriptor(obj, 'p')
    // Object { value: "a",
    //   writable: true,
    //   enumerable: true,
    //   configurable: true
    // }

    注意,Object.getOwnPropertyDescriptor() 只能用于对象自身的属性,不能用于继承的属性。

    var obj = { p: 'a' };
    
    Object.getOwnPropertyDescriptor(obj, 'toString')
    // undefined

    Object.getOwnPropertyNames()

    Object.getOwnPropertyNames() 返回一个数组,成员是参数对象自身的全部属性的属性名,不管该属性是否可遍历。

    var obj = Object.defineProperties({}, {
      p1: { value: 1, enumerable: true },
      p2: { value: 2, enumerable: false }
    });
    
    Object.getOwnPropertyNames(obj)
    // ["p1", "p2"]

    Object.defineProperty(),Object.defineProperties()

    Object.defineProperty() 允许通过属性描述对象,定义或修改一个属性,然后返回修改后的对象。

    它的用法如下。

    /**
    ** @param object 属性所在的对象
    ** @param propertyName 属性名(它应该是一个字符串)
    ** @param attributesObject 属性描述对象
    */
    Object.defineProperty(object, propertyName, attributesObject)
    • 定义单个属性
    var obj = Object.defineProperty({}, 'p', {
      value: 123,
      writable: false,
      enumerable: true,
      configurable: false
    });
    
    obj.p // 123
    
    obj.p = 246;
    obj.p // 123
    • 定义多个属性
    var obj = Object.defineProperties({}, {
      p1: { value: 123, enumerable: true },
      p2: { value: 'abc', enumerable: true },
      p3: { get: function () { return this.p1 + this.p2 },
        enumerable:true,
        configurable:true
      }
    });
    
    obj.p1 // 123
    obj.p2 // "abc"
    obj.p3 // "123abc"

    Object.prototype.propertyIsEnumerable()

    实例对象的 propertyIsEnumerable() 返回一个布尔值,用来判断某个属性是否可遍历。

    var obj = {};
    obj.p = 123;
    
    obj.propertyIsEnumerable('p') // true
    obj.propertyIsEnumerable('toString') // false

    元属性

    属性描述对象的各个属性称为“元属性”,因为它们可以看作是控制属性的属性。

    value

    value 属性是目标属性的值。

    var obj = {};
    obj.p = 123;
    
    Object.getOwnPropertyDescriptor(obj, 'p').value
    // 123
    
    Object.defineProperty(obj, 'p', { value: 246 });
    obj.p // 246

    writable

    writable 属性是一个布尔值,决定了目标属性的值(value)是否可以被改变。

    var obj = {};
    
    Object.defineProperty(obj, 'a', {
      value: 37,
      writable: false
    });
    
    obj.a // 37
    obj.a = 25;
    obj.a // 37

    注意,正常模式下,对writable为false的属性赋值不会报错,只会默默失败。

    但是,严格模式下会报错,即使对a属性重新赋予一个同样的值。

    enumerable

    enumerable(可遍历性)返回一个布尔值,表示目标属性是否可遍历。

    JavaScript 的早期版本,for...in 循环是基于 in 运算符的。

    我们知道,in 运算符不管某个属性是对象自身的还是继承的,都会返回 true。

    var obj = {};
    'toString' in obj // true

    configurable

    configurable(可配置性)返回一个布尔值,决定了是否可以修改属性描述对象。

    也就是说,configurable 为 false 时,value、writable、enumerable 和 configurable 都不能被修改了。

    var obj = Object.defineProperty({}, 'p', {
      value: 1,
      writable: false,
      enumerable: false,
      configurable: false
    });
    
    Object.defineProperty(obj, 'p', {value: 2})
    // TypeError: Cannot redefine property: p
    
    Object.defineProperty(obj, 'p', {writable: true})
    // TypeError: Cannot redefine property: p
    
    Object.defineProperty(obj, 'p', {enumerable: true})
    // TypeError: Cannot redefine property: p
    
    Object.defineProperty(obj, 'p', {configurable: true})
    // TypeError: Cannot redefine property: p

    存取器

    除了直接定义以外,属性还可以用存取器(accessor)定义。
    其中,存值函数称为setter,使用属性描述对象的set属性;
    取值函数称为getter,使用属性描述对象的get属性。

    一旦对目标属性定义了存取器,那么存取的时候,都将执行对应的函数。
    利用这个功能,可以实现许多高级特性,比如某个属性禁止赋值。

    var obj = Object.defineProperty({}, 'p', {
      get: function () {
        return 'getter';
      },
      set: function (value) {
        console.log('setter: ' + value);
      }
    });
    
    obj.p // "getter"
    obj.p = 123 // "setter: 123"

    JavaScript 还提供了存取器的另一种写法。

    var obj = {
      get p() {
        return 'getter';
      },
      set p(value) {
        console.log('setter: ' + value);
      }
    };

    上面的写法与定义属性描述对象是等价的,而且使用更广泛。

    对象的拷贝

    有时,我们需要将一个对象的所有属性,拷贝到另一个对象,可以用下面的方法实现。

    var extend = function (to, from) {
      for (var property in from) {
        to[property] = from[property];
      }
    
      return to;
    }
    
    extend({}, {
      a: 1
    })
    // {a: 1}

    上面这个方法的问题在于,如果遇到存取器定义的属性,会只拷贝值。

    extend({}, {
      get a() { return 1 }
    })
    // {a: 1}

    为了解决这个问题,我们可以通过 Object.defineProperty() 来拷贝属性。

    var extend = function (to, from) {
      for (var property in from) {
        if (!from.hasOwnProperty(property)) continue;
        Object.defineProperty(
          to,
          property,
          Object.getOwnPropertyDescriptor(from, property)
        );
      }
    
      return to;
    }
    
    extend({}, { get a(){ return 1 } })
    // { get a(){ return 1 } })

    控制对象状态

    有时需要冻结对象的读写状态,防止对象被改变。
    JavaScript 提供了三种冻结方法,最弱的一种是Object.preventExtensions,其次是Object.seal,最强的是Object.freeze。

    Object.preventExtensions()

    Object.preventExtensions() 可以使得一个对象无法再添加新的属性。

    var obj = new Object();
    Object.preventExtensions(obj);
    
    Object.defineProperty(obj, 'p', {
      value: 'hello'
    });
    // TypeError: Cannot define property:p, object is not extensible.
    
    obj.p = 1;
    obj.p // undefined

    上面代码中,obj对象经过 Object.preventExtensions 以后,就无法添加新属性了。

    • Object.isExtensible()

    Object.isExtensible()用于检查一个对象是否使用了Object.preventExtensions()。也就是说,检查是否可以为一个对象添加属性。

    var obj = new Object();
    
    Object.isExtensible(obj) // true
    Object.preventExtensions(obj);
    Object.isExtensible(obj) // false

    Object.seal()

    Object.seal() 使得一个对象既无法添加新属性,也无法删除旧属性。

    var obj = { p: 'hello' };
    Object.seal(obj);
    
    delete obj.p;
    obj.p // "hello"
    
    obj.x = 'world';
    obj.x // undefined

    Object.seal 实质是把属性描述对象的 configurable 属性设为 false,因此属性描述对象不再能改变了。

    Object.seal只是禁止新增或删除属性,并不影响修改某个属性的值。

    var obj = { p: 'a' };
    Object.seal(obj);
    obj.p = 'b';
    obj.p // 'b'

    上面代码中,Object.seal方法对p属性的value无效,是因为此时p属性的可写性由writable决定。

    • Object.isSealed()

    Object.isSealed() 用于检查一个对象是否使用了 Object.seal 方法。

    var obj = { p: 'a' };
    
    Object.seal(obj);
    Object.isSealed(obj) // true
    Object.isExtensible(obj) // false

    Object.freeze

    Object.freeze() 可以使得一个对象无法添加新属性、无法删除旧属性、也无法改变属性的值,使得这个对象实际上变成了常量。

    var obj = {
      p: 'hello'
    };
    
    Object.freeze(obj);
    
    obj.p = 'world';
    obj.p // "hello"
    
    obj.t = 'hello';
    obj.t // undefined
    
    delete obj.p // false
    obj.p // "hello"
    • Object.isFrozen()
    var obj = {
      p: 'hello'
    };
    
    Object.freeze(obj);
    Object.isFrozen(obj) // true
    Object.isSealed(obj) // true
    Object.isExtensible(obj) // false

    局限性

    • 原型对象

    上面的三个方法锁定对象的可写性有一个漏洞:可以通过改变原型对象,来为对象增加属性。

    var obj = new Object();
    Object.preventExtensions(obj);
    
    var proto = Object.getPrototypeOf(obj);
    proto.t = 'hello';
    obj.t
    // hello

    上面代码中,对象obj本身不能新增属性,但是可以在它的原型对象上新增属性,就依然能够在obj上读到。

    var obj = new Object();
    Object.preventExtensions(obj);
    
    var proto = Object.getPrototypeOf(obj);
    Object.preventExtensions(proto);
    
    proto.t = 'hello';
    obj.t // undefined
    • 属性值是对象

    如果属性值是对象,上面这些方法只能冻结属性指向的对象,而不能冻结对象本身的内容。

    var obj = {
      foo: 1,
      bar: ['a', 'b']
    };
    Object.freeze(obj);
    
    obj.bar.push('c');
    obj.bar // ["a", "b", "c"]

    原文地址

    原文地址

    本教程采用知识共享 署名-相同方式共享 3.0协议

    目录导航

    目录导航

    展开全文
  • 在js属性描述符这部分有几个较难理解的名词概念,本文旨在描述自己对它们的理解,主要包括:[[Get]]/[[Put]]、get/set、getter/setter几个概念的阐述,数据...属性具备了属性描述符,用来描述属性拥有的特性。 属...

    在js属性描述符这部分有几个较难理解的名词概念,本文旨在描述自己对它们的理解,主要包括:[[Get]]/[[Put]]、get/set、getter/setter几个概念的阐述,数据属性和访问器属性。

    属性

    首先我们要搞清楚属性的概念,属性是存储在特定命名位置的值,是对象的内容,属性并不直接存储在对象容器内部。属性有两种类型:数据属性和访问器属性。属性具备了属性描述符,用来描述属性拥有的特性。

    属性描述符

    属性描述符用来描述属性特性的(只有在内部才能用的特性),配置属性是否可读,是否可写,是否可枚举,值是多少,读写。

    属性描述符对象
    Object.definePrOperty(obj,‘a’,{value:2,writable:true,configurable:true})第三个参数就是属性描述符对象 ,首先它是个对象,它有自己的属性,其次它是属性a的属性描述符,用来配置属性。

    定义这些特性为了实现JavaScript引擎用的,因此再JavaScript中不能直接访问它们。为了表示特征是内部值,ES规范把它们放在了两对方括号中,例如[[Enumerable]]。

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

    数据属性

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

    • [[Configurable]]:配置,表示能否删除修改属性的特性,或者把属性修改为访问器属性。默认true
    • [[Enumerable]]:枚举,表示能否通过for-in循环返回属性。默认true
    • [[Writable]]:可写,表示能否修改属性值。默认true
    • [[Value]]:属性的数据值。读写属性值从该位置。默认undefined

    访问器属性

    访问器属性不包含数据值;它们包含一对getter和setter函数。共有四个描述其行为的特征:

    • [[Configurable]]:配置,表示能否删除修改属性的特性,或者把属性修改为访问器属性。默认true
    • [[Enumerable]]:枚举,表示能否通过for-in循环返回属性。默认true
    • [[Get]]:在读取属性值时调用的函数。默认undefined
    • [[Set]]:在写入属性值时调用的函数。默认undefined

    当个一个属性定义getter、setter或者两者都有时,这个属性就成了访问器属性

    知道属性分类之后,下面我们逐一解答开头的几个问题:

    [[Get]]/[[Put]]是什么

    首先要明确一点,[[Get]]和[[Put]]是对象默认的内置操作,可以理解为算法函数。它是在访问属性时的操作,例如通过obj.a访问a属性时就是实现了[[Get]]操作,它会先找到相同的属性名,找到才要返回属性值。
    没有找到就会按照[[Get]]算法的设计,沿着原型链找,找不到会返回undefined。
    [[Put]]被触发时,取决于许多因素,最重要的有对象中是否已经存在这个属性。如果已经存在,算法大致会检查下面这些内容:

    1.属性是否是访问描述符?如果是并且存在setter就调用setter
    2.属性的数据描述符中writable是否是false?如果是,在非严格模式下静默失败,在严格模式下抛出TypeError异常。
    3.如果都不是,将该值设置为属性的值。

    get/set和getter/setter

    [[Get]]和[[Set]]:当属性拥有这两个特性时,属性就是访问器属性。代表着在访问属性或者写入属性值时,对返回值做附加的操作。而这个操作就是getter/setter函数
    它们只能应用到单个属性上,无法应用在整个对象上。getter/setter是隐藏函数,是访问器属性默认拥有的隐藏函数。在读取访问器属性时调用getter,返回有效的值;在写入访问器属性时调用setter函数并传入新值。

    不管是对象文字语法中的get a(){…},还是的defineProperty(…)中的显式定义,二者都会在对象中创造一个不包含值得属性,对于这个属性的访问会自动调用一个隐藏函数,它的返回值会被当做属性访问的返回值:

    var myObject = {
    //给a定义一个getter
        get a(){
          return this._a_;
         },
    //给a定义一个setter
        set a(val){
          this._a_=val *2;
        }
    }
    myObject.a=2;
    myObject.a;//4
    

    设置getter会覆盖默认的[[Get]]操作,setter会覆盖默认得[[Put]],也被称为赋值操作

    实际上我们赋值([[Put]])操作中的值2存储到了另一个变量_a_中。名称_a_只是一种惯例,没有任何特殊行为。

    总结

    属性是拥有自己的特性的,主要用来描述属性是否可以进行修改枚举配置的。我们访问对象的属性时就是[[Get]]操作,写入就是[[Put]],根据算法找到
    对应的属性。如果要对属性值进行附加操作时,就需要设置get/set特性,此时属性也就会变成访问器属性,然后调用默认的隐藏的getter/setter函数对属性
    进行操作。然后返回属性值。

    展开全文
  • JavaScript中的高级特性及特别对象属性和方法一,编写构造函数可以使用 new 运算符结合像 Object()、Date() 和 Function() 这样的预定义的构造函数来创建对象并对其初始化。面向对象的编程其强有力的特征是...

    JavaScript中的高级特性及特别对象、属性和方法


    一,编写构造函数
    可以使用 new 运算符结合像 Object()、Date() 和 Function() 这样的预定义的构造函数来创建对象并对其初始化。面向对象的编程其强有力的特征是定义自定义构造函数以创建脚本中使用的自定义对象的能力。创建了自定义的构造函数,这样就可以创建具有已定义属性的对象。下面是自定义函数的示例(注意 this 关键字的使用)。

    function Circle (xPoint, yPoint, radius) {
        this.x = xPoint;  // 圆心的 x 坐标
        this.y = yPoint;  // 圆心的 y 坐标
        this.r = radius;  // 圆的半径
    }
    调用 Circle 构造函数时,给出圆心点的值和圆的半径(所有这些元素是完全定义一个独特的圆对象所必需的)。结束时 Circle 对象包含三个属性。下面是如何例示 Circle 对象。

    var aCircle = new Circle(5, 11, 99);

    二,使用原型来创建对象
    在编写构造函数时,可以使用原型对象(它本身是所有构造函数的一个属性)的属性来创建继承属性和共享方法。原型属性和方法将按引用复制给类中的每个对象,因此它们都具有相同的值。可以在一个对象中更改原型属性的值,新的值将覆盖默认值,但仅在该实例中有效。属于这个类的其他对象不受此更改的影响。下面给出了使用自定义构造函数的示例,Circle(注意 this 关键字的使用)。

    Circle.prototype.pi = Math.PI;
    function ACirclesArea () {
      return this.pi * this.r * this.r; // 计算圆面积
    }
    Circle.prototype.area = ACirclesArea; // 计算圆面积的函数现在是 Circle Prototype 对象的一个方法
    var a = ACircle.area();               // 此为如何在 Circle 对象上调用面积函数

    使用这个原则,可以给预定义的构造函数(都具有原型对象)定义附加属性。例如,如果想要能够删除字符串的前后空格(与 VBScript 的 Trim 函数类似),就可以给 String 原型对象创建自己的方法。

    // 增加一个名为 trim 的函数作为
    // String 构造函数的原型对象的一个方法。
    String.prototype.trim = function() {
      return this.replace(/(^/s*)|(/s*$)/g, "");   // 用正则表达式将前后空格
    }
    var s = "    leading and trailing spaces    "; // 有空格的字符串
    window.alert(s + " (" + s.length + ")");
    s = s.trim();                                  // 删除前后空格
    window.alert(s + " (" + s.length + ")");



    三,特别对象、属性和方法
    -------------------
    Error 对象:保存有关错误的信息。

    var newErrorObj = new Error()

    var newErrorObj = new Error(
      number
    )

    var newErrorObj = new Error(
      number,
      description
    )

    Error 对象的构造函数语法有以下部分: 

    参数:-number。与错误相联的数字值。如果省略则为零。
          -description。描述错误的简短字符串。如果省略则为空字符串。

    说明:每当产生运行时错误,就产生 Error 对象的一个实例以描述错误。该实例有两个固有属性保存错误的描述(description 属性)和错误号(number 属性)。

    错误号是 32 位的值。高 16 位字是设备代码,而低字是实际的错误代码。

    Error 对象也可以用如上所示的语法显式创建,或用 throw 语句抛掉。在两种情况下,都可以添加选择的任何属性,以拓展 Error 对象的能力。

    典型地,在 try...catch 语句中创建的局部变量引用隐式创建的 Error 对象。因此,可以按选择的任何方法使用错误号和描述。

    下面的例子演示了隐式创建 Error 对象的使用:
    try { 
      x = y;                             // 产生错误。
    } catch(e) {                         // 创建局部变量 e。
      response.write(e)                  // 打印 "[object Error]".
      response.write(e.number & 0xFFFF)  // 打印 5009。
      response.write(e.description)      // 打印 "'y' is undefined".
    }


    -------------------
    Function 对象:创建新的函数。

    语法 1
    function functionName([argname1 [, ...[, argnameN]]]) {
       //body
    }

    语法 2
    functionName = new Function( [argname1, [... argnameN,]] body );

    参数:-functionName。必选项。最新创建函数的名称
          -argname1...argnameN。可选项。函数接收的参数列表。
          -body。可选项。包含调用该函数时被执行的 JScript 代码块的字符串。

    说明:函数 JScript 中的基本数据类型。语法 1 创建在必要时由 JScript 转换为 Function 对象的函数值。JScript 在调用函数时将用语法 2 创建的函数转换为 Fnction 对象。 

    语法 1 是 JScript 中创建函数的基本方法。语法 2 是用于显式创建函数对象的另一方法。 

    例如,要创建将传递给它的两个参数相加的函数,可以用两种方法中的任一种完成: 

    例子 1
    function add(x, y) {
      return(x + y);            // 执行加法并返回结果。
    }
    例子 2
    var add = new Function("x", "y", "return(x+y)");
    在两种情况下,都可以用如下代码行调用该函数: 

    add(2, 3);
    注意   在调用函数时,请确保包含了括号和必需的参数。调用函数时不用括号导致返回函数的文本而不是函数执行的结果。


    -------------------
    Object 对象:提供所有 JScript 对象通用的功能。

    obj = new Object([value]) 

    参数:-obj。必选项。要赋值为 Object 对象的变量名。
          -value。可选项。任意一种 JScript 基本数据类型。(Number、Boolean、或 String。)如果 value 为一个对象,返回不作改动的该对象。如果 value 为 null、undefined,或者没有给出,则产生没有内容的对象。

    说明:Object 对象被包含在所有其它 JScript 对象中;在所有其它对象中它的方法和属性都是可用的。在用户定义的对象中可以重定义这些方法,并在适当的时候通过 JScript 调用。toString 方法是经常被重定义的 Object 方法的例子。



    -------------------
    arguments 属性:为当前执行的 function 对象返回一个arguments 对象。

    function.arguments

    function 参数是当前执行函数的名称,可以省略。 

    说明:通过 arguments 属性,函数可以处理可变数量的参数。 arguments 对象的 length 属性包含了传递给函数的参数的数目。对于arguments 对象所包含的单个参数,其访问方法与数组中所包含的参数的访问方法相同。

    示例:下面的例子说明了 arguments 属性的用法:

    function ArgTest() {
       var i, s, numargs = arguments.length;
       s = numargs;  
       if (numargs < 2)
         s += " argument was passed to ArgTest. It was ";
       else
         s += " arguments were passed to ArgTest. They were " ;
       for (i = 0; i < numargs; i++) {
         s += arguments[i] + " ";
       }
       return(s);
    }


    -------------------
    callee 属性:返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文。

    [function.]arguments.callee

    可选项 function 参数是当前正在执行的 Function 对象的名称。 

    说明:callee 属性是 arguments 对象的一个成员,仅当相关函数正在执行时才可用。

    callee 属性的初始值就是正被执行的 Function 对象。这允许匿名的递归函数。

    示例:
    function factorial(n) {
     if (n <= 0)
       return 1;
     else
      return n * arguments.callee(n - 1)
    }
    print(factorial(3));

    要求:版本5.5或以上。


    -------------------
    caller 属性:返回一个对函数的引用,该函数调用了当前函数。

    functionName.caller 

    functionName 对象是所执行函数的名称。

    说明:对于函数来说,caller 属性只有在函数执行时才有定义。 如果函数是由 JScript 程序的顶层调用的,那么 caller 包含的就是 null 。

    如果在字符串上下文中使用 caller 属性,那么结果和 functionName.toString 一样,也就是说,显示的是函数的反编译文本。

    下面的例子说明了 caller 属性的用法:
    function CallLevel() {
      if (CallLevel.caller == null)
        return("CallLevel was called from the top level.");
      else
        return("CallLevel was called by another function.");
    }


    -------------------
    constructor 属性:表示创建对象的函数。 

    object.constructor

    必需的 object是对象或函数的名称。 

    说明:constructor 属性是所有具有 prototype 的对象的成员。它们包括除 Global 和 Math 对象以外的所有 JScript 固有对象。constructor 属性保存了对构造特定对象实例的函数的引用。 

    例如: 
    x = new String("Hi");
    if (x.constructor == String) // 进行处理(条件为真)。
    或 
    function MyFunc {
       // 函数体。
    }
    y = new MyFunc;
    if (y.constructor == MyFunc) // 进行处理(条件为真)。


    -------------------
    description 属性:返回或设置与特定错误相联系的描述字符串。

    object.description [= stringExpression]

    description 属性的语法组成部分如下:

    参数:-object。必选项。Error 对象的任意实例。
          -stringExpression。可选项。包含错误描述的字符串表达式。

    说明:description 属性包含与特定错误相联系的错误信息字符串。使用包含在这个中的值,来警告用户发生了一个不能或不想处理的错误。


    -------------------
    prototype 属性:返回对象类型原型的引用。

    objectName.prototype

    objectName 参数是对象的名称。 

    说明:用 prototype 属性提供对象的类的一组基本功能。 对象的新实例“继承”赋予该对象原型的操作。 

    例如,要为 Array 对象添加返回数组中最大元素值的方法。 要完成这一点,声明该函数,将它加入 Array.prototype, 并使用它。 

    function array_max( ) {
      var i, max = this[0];
      for (i = 1; i < this.length; i++) {
        if (max < this[i])
          max = this[i];
      }
      return max;
    }
    Array.prototype.max = array_max;
    var x = new Array(1, 2, 3, 4, 5, 6);
    var y = x.max( );
    该代码执行后,y 保存数组 x 中的最大值,或说 6。

    所有 JScript 固有对象都有只读的 prototype 属性。可以象该例中那样为原型添加功能,但该对象不能被赋予不同的原型。然而,用户定义的对象可以被赋给新的原型。



    -------------------
    apply 方法:应用某一对象的一个方法,用另一个对象替换当前对象。

    apply([thisObj[,argArray]])

    参数:-thisObj。可选项。将被用作当前对象的对象。
          -argArray。可选项。将被传递给该函数的参数数组。

    说明:如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。

    如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递任何参数。

    要求:版本5.5或以上。


    -------------------
    call 方法:调用一个对象的一个方法,以另一个对象替换当前对象。

    call([thisObj[,arg1[, arg2[,   [,.argN]]]]])

    参数:-thisObj。可选项。将被用作当前对象的对象。
          -arg1, arg2,  , argN。可选项。将被传递方法参数序列。

    说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。

    如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。

    要求:版本5.5或以上。


    -------------------
    concat 方法 (Array):返回一个新数组,这个新数组是由两个或更多数组组合而成的。

    array1.concat([item1[, item2[, . . . [, itemN]]]])

    参数:-array1。必选项。其他所有数组要进行连接的 Array 对象。 
          -item1,. . ., itemN。可选项。要连接到 array1 末尾的其他项目。

    说明:concat 方法返回一个 Array 对象,其中包含了 array1 和提供的任意其他项目的连接。

    要加的项目(item1 … itemN)会按照从左到右的顺序添加到数组。如果某一项为数组,那么添加其内容到 array1 的末尾。如果该项目不是数组,就将其作为单个的数组元素添加到数组的末尾。

    以下为从源数组复制元素到结果数组: 

    对于从正被连接到新数组的数组中复制的对象参数,复制后仍然指向相同的对象。不论新数组和源数组中哪一个有改变,都将引起另一个的改变。 
    对于连接到新数组的数值或字符串,只复制其值。一个数组中值有改变并不影响另一个数组中的值。 

    示例:下面这个例子说明了使用数组时 concat 方法的用法: 
    function ConcatArrayDemo() {
      var a, b, c, d;
      a = new Array(1,2,3);
      b = "JScript";
      c = new Array(42, "VBScript);
      d = a.concat(b, c);  // 返回数组 [1, 2, 3, "JScript", 42, "VBScript"]
      return(d);
    }


    -------------------
    escape 方法:对 String 对象编码以便它们能在所有计算机上可读。

    escape(charString)

    必选项 charstring 参数是要编码的任意 String 对象或文字。 

    说明:escape 方法返回一个包含了 charstring 内容的字符串值( Unicode 格式)。所有空格、标点、重音符号以及其他非 ASCII 字符都用 %xx 编码代替,其中 xx 等于表示该字符的十六进制数。例如,空格返回的是 "%20" 。

    字符值大于 255 的以 %uxxxx 格式存储。 

    注意:escape 方法不能够用来对统一资源标示码 (URI) 进行编码。对其编码应使用 encodeURI 和encodeURIComponent 方法。


    -------------------
    unescape 方法:解码用 escape 方法进行了编码的 String 对象。 

    unescape(charstring) 

    必选项 charstring 参数是要解码的 String 对象。 

    说明:unescape 方法返回一个包含 charstring 内容的字符串值。所有以 %xx 十六进制形式编码的字符都用 ASCII 字符集中等价的字符代替。 

    以 %uxxxx 格式(Unicode 字符)编码的字符用十六进制编码 xxxx 的 Unicode 字符代替。 

    注意:unescape 方法不能用于解码统一资源标识码 (URI)。解该码可使用 decodeURI 和 decodeURIComponent 方法。


    -------------------
    eval 方法:检查 JScript 代码并执行。

    eval(codeString)

    必选项 codestring 参数是包含有效 JScript 代码的字符串值。这个字符串将由 JScript 分析器进行分析和执行。

    说明:eval 函数允许 JScript 源代码的动态执行。例如,下面的代码创建了一个包含 Date 对象的新变量 mydate :

    eval("var mydate = new Date();");

    传递给 eval 方法的代码执行时的上下文和调用 eval 方法的一样. 


    -------------------
    encodeURI 方法:将文本字符串编码为一个有效的统一资源标识符 (URI)。

    encodeURI(URIString)

    必选的 URIString 参数代表一个已编码的 URI。

    说明:encodeURI 方法返回一个编码的 URI。如果您将编码结果传递给 decodeURI,那么将返回初始的字符串。encodeURI 方法不会对下列字符进行编码:":"、"/"、";" 和 "?"。请使用 encodeURIComponent 方法对这些字符进行编码。

    要求:版本5.5或以上。


    -------------------
    decodeURI 方法:返回一个已编码的统一资源标识符 (URI) 的非编码形式。

    decodeURI(URIstring)

    必要的 URIstring 参数代表一个已编码 URI 的值。

    说明:使用 decodeURI 方法代替已经过时的 unescape 方法。

    decodeURI 方法返回一个字符串值。

    如果 URIString 无效,那么将产生一个 URIError。

    要求:版本5.5或以上。


    -------------------
    encodeURIComponent 方法:将文本字符串编码为一个统一资源标识符 (URI) 的一个有效组件。

    encodeURIComponent(encodedURIString)

    必选的 encodedURIString 参数代表一个已编码的 URI 组件。

    说明:encodeURIComponent 方法返回一个已编码的 URI。如果您将编码结果传递给 decodeURIComponent,那么将返回初始的字符串。因为 encodeURIComponent 方法对所有的字符编码,请注意,如果该字符串代表一个路径,例如 /folder1/folder2/default.html,其中的斜杠也将被编码。这样一来,当该编码结果被作为请求发送到 web 服务器时将是无效的。如果字符串中包含不止一个 URI 组件,请使用 encodeURI 方法进行编码。

    要求:版本5.5或以上。


    -------------------
    decodeURIComponent 方法:返回统一资源标识符 (URI) 的一个已编码组件的非编码形式。

    decodeURIComponent(encodedURIString)

    必选的 encodedURIString 参数代表一个已编码的 URI 组件。

    说明:URIComponent 是一个完整的 URI 的一部分。 

    如果 encodedURIString 无效,将产生一个 URIError。

    要求:版本5.5或以上。


    -------------------
    for...in 语句:对应于一个对象的每个,或一个数组的每个元素,执行一个或多个语句。 

    for (variable in [object | array])
       statements 

    参数:-variable。必选项。一个变量,它可以是 object 的任一属性或 array 的任一元素。 
          -object, array。可选项。要在其上遍历的对象或数组。
          -statement。可选项。相对于 object 的每个属性或 array 的每个元素,都要被执行的一个或多个语句。可以是复合语句。

    说明:在循环的每次迭代前,variable 被赋予 object 的下一个属性或 array 的下一个元素。然后可以在循环内的任一语句中使用它,就好像正在使用 object 的该属性或 array 的该元素一样。

    当在一个对象上迭代时,没有办法决定或控制把对象的成员赋给 variable 的次序。在数组内将按元素的次序执行迭代,也就是,0、1、2、......

    示例:下面示例说明了 for ... in 语句的用法,它把一个对象用作一个联合数组: 
    function ForInDemo() {
      var a, key, s = "";                                     // 创建某些变量
      a = {"a" : "Athens" , "b" : "Belgrade", "c" : "Cairo"}  // 初始化对象
      for (key in a)   {                                      // 迭代属性
         s += a[key] + "<BR/>";
      }
      return(s);
    }


    -------------------
    join 方法:返回字符串值,其中包含了连接到一起的数组的所有元素,元素由指定的分隔符分隔开来。

    arrayObj.join(separator)

    参数:-arrayObj。必选项。Array 对象。
          -separator。必选项。是一个 String 对象,作为最终的 String 对象中对数组元素之间的分隔符。如果省略了这个参数,那么数组元素之间就用一个逗号来分隔。

    说明:如果数组中有元素没有定义或者为 null,将其作为空字符串处理。

    示例:下面这个例子说明了 join 方法的用法。 
    function JoinDemo() {
      var a, b;
      a = new Array(0,1,2,3,4);
      b = a.join("-");
      return(b);
    }


    -------------------
    pop 方法:移除数组中的最后一个元素并返回该元素。

    arrayObj.pop( )

    必选的 arrayObj 引用是一个 Array 对象。

    说明:如果该数组为空,那么将返回 undefined。

    要求:版本5.5或以上。


    -------------------
    push 方法:将新元素添加到一个数组中,并返回数组的新长度值。

    arrayObj.push([item1 [item2 [. . . [itemN ]]]])

    参数:-arrayObj。必选项。一个 Array 对象。
          -item, item2,. . . itemN。可选项。该 Array 的新元素。

    说明:push 方法将以新元素出现的顺序添加这些元素。如果参数之一为数组,那么该数组将作为单个元素添加到数组中。如果要合并两个或多个数组中的元素,请使用 concat 方法。

    要求:版本5.5或以上。


    -------------------
    reverse 方法:返回一个元素顺序被反转的 Array 对象。 

    arrayObj.reverse( )

    必选项 arrayObj 参数为 Array 对象。

    说明:reverse 方法将一个 Array 对象中的元素位置进行反转。在执行过程中,这个方法并不会创建一个新的 Array 对象。 

    如果数组是不连续的,reverse 方法将在数组中创建元素以便填充数组中的间隔。这样所创建的全部元素的值都是 undefined。

    示例:下面这个例子说明了 reverse 方法的用法: 
    function ReverseDemo() {
      var a, l;                  // 声明变量。
      a = new Array(0,1,2,3,4);  // 创建数组并赋值。
      l = a.reverse();           // 反转数组的内容。
      return(l);                 // 返回结果数组。
    }


    -------------------
    slice 方法 (Array):返回一个数组的一段。

    arrayObj.slice(start, [end]) 

    参数:-arrayObj。必选项。一个 Array 对象。 
          -start。必选项。arrayObj 中所指定的部分的开始元素是从零开始计算的下标。 
          -end。可选项。arrayObj 中所指定的部分的结束元素是从零开始计算的下标。

    说明:slice 方法返回一个 Array 对象,其中包含了 arrayObj 的指定部分。 

    slice 方法一直复制到 end 所指定的元素,但是不包括该元素。如果 start 为负,将它作为 length + start处理,此处 length 为数组的长度。如果 end 为负,就将它作为 length + end 处理,此处 length 为数组的长度。如果省略 end ,那么 slice 方法将一直复制到 arrayObj 的结尾。如果 end 出现在 start 之前,不复制任何元素到新数组中。

    示例:在下面这个例子中,除了最后一个元素之外,myArray 中所有的元素都被复制到 newArray 中: 

    newArray = myArray.slice(0, -1)


    -------------------
    shift 方法:移除数组中的第一个元素并返回该元素。

    arrayObj.shift( )

    必选的 arrayObj 引用是一个 Array 对象。

    说明:shift 方法可移除数组中的第一个元素并返回该元素。

    要求:版本5.5或以上。


    -------------------
    unshift 方法:将指定的元素插入数组开始位置并返回该数组。

    arrayObj.unshift([item1[, item2 [, . . . [, itemN]]]])

    参数:-arrayObj。必选项。一个 Array 对象。
          -item1, item2,. . .,itemN。可选项。将插入到该 Array 开始部分的元素。

    说明:unshift 方法将这些元素插入到一个数组的开始部分,所以这些元素将以参数序列中的次序出现在数组中。

    要求:版本5.5或以上。


    -------------------
    splice 方法:从一个数组中移除一个或多个元素,如果必要,在所移除元素的位置上插入新元素,返回所移除的元素。

    arrayObj.splice(start, deleteCount, [item1[, item2[, . . . [,itemN]]]])

    参数:-arrayObj。必选项。一个 Array 对象。
          -start。必选项。指定从数组中移除元素的开始位置,这个位置是从 0 开始计算的。
          -deleteCount。必选项。要移除的元素的个数。
          -item1, item2,. . .,itemN。必选项。要在所移除元素的位置上插入的新元素。

    说明:splice 方法可以移除从 start 位置开始的指定个数的元素并插入新元素,从而修改 arrayObj。返回值是一个由所移除的元素组成的新 Array 对象。

    要求:版本5.5或以上。

    展开全文
  • Python 中的属性访问与描述

    千次阅读 2018-04-27 20:25:44
    在Python中,对于一个对象属性访问,我们一般采用的是点(.)属性运算符进行操作。例如,有一个类实例对象foo,它有一个name属性,那便可以使用foo.name对此属性进行访问。一般而言,点(.)属性运算符比较直观,也是...

    在Python中,对于一个对象的属性访问,我们一般采用的是点(.)属性运算符进行操作。例如,有一个类实例对象foo,它有一个name属性,那便可以使用foo.name对此属性进行访问。一般而言,点(.)属性运算符比较直观,也是我们经常碰到的一种属性访问方式。然而,在点(.)属性运算符的背后却是别有洞天,值得我们对对象的属性访问进行探讨。

    对象的属性

    class Animal(object):
        run = True
    class Dog(Animal):
        fly = False
        def __init__(self, age):
            self.age = age
        def sound(self):
            return "wang wang~"
    
    # 实例化一个对象dog
    dog = Dog(1)
    # 查看dog对象的属性
    print ('dog.__dict__:',dog.__dict__)
    # 查看类Dog的属性
    print ('Dog.__dict__:',Dog.__dict__)
    # 查看类Animal的属性
    print ('Animal.__dict__:',Animal.__dict__)

    结果:

    dog.__dict__: {'age': 1}
    Dog.__dict__: {'__doc__': None, 'fly': False, '__module__': '__main__', '__init__': <function Dog.__init__ at 0x00000220DB9CB400>, 'sound': <function Dog.sound at 0x00000220DDE4FC80>}
    Animal.__dict__: {'run': True, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}

    由上面的例子可以看出:属性在哪个对象上定义,便会出现在哪个对象的__dict__中。例如:

    • 类Animal定义了一个属性run,那这个run属性便只会出现在类Animal的__dict__中,而不会出现在其子类中。
    • 类Dog定义了一个属性fly和两个函数,那这些属性和方法便会出现在类Dog的__dict__中,同时它们也不会出现在实例的__dict__中。
    • 实例对象dog的__dict__中只出现了一个属性age,这是在初始化实例对象的时候添加的,它没有父类的属性和方法。
    • 由此可知:Python中对象的属性具有 “层次性”,属性在哪个对象上定义,便会出现在哪个对象的__dict__中。
      在这里我们首先了解的是属性值会存储在对象的__dict__中,查找也会在对象的__dict__中进行查找的。

    对象属性访问与特殊方法__getattribute__

    正如前面所述,Python的属性访问方式很直观,使用点属性运算符。在新式类中,对对象属性的访问,都会调用特殊方法getattributegetattribute允许我们在访问对象属性时自定义访问行为,但是使用它特别要小心无限递归的问题。

    class Animal(object):
        run = True
    class Dog(Animal):
        fly = False
        def __init__(self, age):
            self.age = age
        # 重写__getattribute__。需要注意的是重写的方法中不能使用对象的点运算符访问
        # 属性,否则使用点运算符访问属性时,会再次调用__getattribute__。这样就会陷入
        # 无限递归。可以使用super()方法避免这个问题。
        def __getattribute__(self, key):
            print  ("calling __getattribute__")
            return super(Dog, self).__getattribute__(key)
        def sound(self):
            return "wang wang~"
    
    # 实例化对象dog
    dog = Dog(1)
    # 访问dog对象的age属性
    print ('dog.age:',dog.age)
    # 访问dog对象的fly属性
    print ('dog.fly:',dog.fly)
    # 访问dog对象的run属性
    print ('dog.run:',dog.run)
    # 访问dog对象的sound方法
    print ('dog.sound:',dog.sound)

    运行结果:

    calling __getattribute__
    dog.age: 1
    calling __getattribute__
    dog.fly: False
    calling __getattribute__
    dog.run: True
    calling __getattribute__
    dog.sound: <bound method Dog.sound of <__main__.Dog object at 0x00000253FA4CB7B8

    由上面的验证可知,__getattribute__是实例对象查找属性或方法的入口。实例对象访问属性或方法时都需要调用到__getattribute__,之后才会根据一定的规则在各个__dict__中查找相应的属性值或方法对象,若没有找到则会调用__getattr__(后面会介绍到)。__getattribute__是Python中的一个内置方法,关于其底层实现可以查看相关官方文档,后面将要介绍的属性访问规则就是依赖于__getattribute__的。

    对象属性控制

    在继续介绍后面相关内容之前,让我们先来了解一下Python中和对象属性控制相关的相关方法。

    • __getattr__(self, name)__getattr__可以用来在当用户试图访问一个根本不存在(或者暂时不存在)的属性时,来定义类的行为。前面讲到过,当getattribute方法找不到属性时,最终会调用getattr方法。它可以用于捕捉错误的以及灵活地处理AttributeError。只有当试图访问不存在的属性时它才会被调用。
    • __setattr__(self, name, value)__setattr__方法允许你自定义某个属性的赋值行为,不管这个属性存在与否,都可以对任意属性的任何变化都定义自己的规则。关于__setattr__有两点需要说明:第一,使用它时必须小心,不能写成类似self.name = “Tom”这样的形式,因为这样的赋值语句会调用__setattr__方法,这样会让其陷入无限递归;第二,你必须区分 对象属性 和 类属性 这两个概念。后面的例子中会对此进行解释。
    • __delattr__(self, name)__delattr__用于处理删除属性时的行为。和__setattr__方法要注意无限递归的问题,重写该方法时不要有类似del self.name的写法。

    还是以上面的例子进行说明,不过在这里我们要重写三个属性控制方法:

    以下进行验证。首先是__getattr__:

    class Animal(object):
        run = True
    class Dog(Animal):
        fly = False
        def __init__(self, age):
            self.age = age
        def __getattr__(self, name):
            print ("calling __getattr__")
            if name == 'adult':
                return True if self.age >= 2 else False
            else:
                raise AttributeError
        def __setattr__(self, name, value):
            print ("calling __setattr__")
            super(Dog, self).__setattr__(name, value)
        def __delattr__(self, name):
            print ("calling __delattr__")
            super(Dog, self).__delattr__(name)
    
    
    # 创建实例对象dog
    dog = Dog(1)
    # 检查一下dog和Dog的__dict__
    print ('dog.__dict__:',dog.__dict__)
    print ('Dog.__dict__:',Dog.__dict__)
    # 获取dog的age属性
    print('dog.age:',dog.age)
    # 获取dog的adult属性。
    # 由于__getattribute__没有找到相应的属性,所以调用__getattr__。
    print ('dog.adult:',dog.adult)
    # 调用一个不存在的属性name,__getattr__捕获AttributeError错误
    print('dog.name:',dog.name)
    

    运行结果:

    calling __setattr__
    <__main__.Dog object at 0x000001D860B9B828>
    dog.__dict__: {'age': 1}
    Dog.__dict__: {'__module__': '__main__', '__setattr__': <function Dog.__setattr__ at 0x000001D860B9FD08>, '__delattr__': <function Dog.__delattr__ at 0x000001D860B9FD90>, '__init__': <function Dog.__init__ at 0x000001D85E71B400>, '__doc__': None, 'fly': False, '__getattr__': <function Dog.__getattr__ at 0x000001D860B9FC80>}
    dog.age: 1
    calling __getattr__
    dog.adult: False
    calling __getattr__
    Traceback (most recent call last):
      File "C:\Users\LiLong\Desktop\self_use.py", line 186, in <module>
        print('dog.name:',dog.name)
      File "C:\Users\LiLong\Desktop\self_use.py", line 164, in __getattr__
        raise AttributeError
    AttributeError

    可以看到,属性访问时,当访问一个不存在的属性时触发__getattr__,它会对访问行为进行控制。

    接下来是__setattr__

    # 给dog.age赋值,会调用__setattr__方法
    dog.age = 2
    print('dog.age:',dog.age)
    # 先调用dog.fly时会返回False,这时因为Dog类属性中有fly属性;
    # 之后再给dog.fly赋值,触发__setattr__方法。
    print ('dog.fly:',dog.fly)
    dog.fly = True
    # 再次查看dog.fly的值以及dog和Dog的__dict__;
    # 可以看出对dog对象进行赋值,会在dog对象的__dict__中添加了一条对象属性;
    # 然而,Dog类属性没有发生变化
    # 注意:dog对象和Dog类中都有fly属性,访问时会选择哪个呢?
    print ('dog.fly:',dog.fly)
    print ('dog.__dict__:',dog.__dict__)
    print ('Dog.__dict__:',Dog.__dict__)

    运行:

    calling __setattr__
    dog.age: 2
    dog.fly: False
    calling __setattr__
    dog.fly: True
    dog.__dict__: {'age': 2, 'fly': True}
    Dog.__dict__: {'__setattr__': <function Dog.__setattr__ at 0x0000026E819CFD08>, 'fly': False, '__doc__': None, '__init__': <function Dog.__init__ at 0x0000026EFF56B400>, '__getattr__': <function Dog.__getattr__ at 0x0000026E819CFC80>, '__delattr__': <function Dog.__delattr__ at 0x0000026E819CFD90>, '__module__': '__main__'}

    实例对象的__setattr__方法可以定义属性的赋值行为,不管属性是否存在。当属性存在时,它会改变其值;当属性不存在时,它会添加一个对象属性信息到对象的__dict__中,然而这并不改变类的属性。从上面的例子可以看出来。

    最后,看一下__delattr__

    # 由于上面的例子中我们为dog设置了fly属性,现在删除它触发__delattr__方法
    del dog.fly
    # 再次查看dog对象的__dict__,发现和fly属性相关的信息被删除
    print (dog.__dict__)

    运行:

    calling __delattr__
    {'age': 2}

    描述符

    描述符是Python 2.2 版本中引进来的新概念。描述符一般用于实现对象系统的底层功能, 包括绑定和非绑定方法、类方法、静态方法特特性等。关于描述符的概念,官方并没有明确的定义,可以在网上查阅相关资料。这里我从自己的认识谈一些想法,如有不当之处还请包涵。

    在前面我们了解了对象属性访问和行为控制的一些特殊方法,例如__getattribute____getattr____setattr____delattr__。以我的理解来看,这些方法应当具有属性的”普适性”,可以用于属性查找、设置、删除的一般方法,也就是说所有的属性都可以使用这些方法实现属性的查找、设置、删除等操作。但是,这并不能很好地实现对某个具体属性的访问控制行为。例如,上例中假如要实现dog.age属性的类型设置(只能是整数),如果单单去修改__setattr__方法满足它,那这个方法便有可能不能支持其他的属性设置。

    在类中设置属性的控制行为不能很好地解决问题,Python给出的方案是:__getattribute____getattr____setattr____delattr__等方法用来实现属性查找、设置、删除的一般逻辑,而对属性的控制行为就由属性对象来控制。这里单独抽离出来一个属性对象,在属性对象中定义这个属性的查找、设置、删除行为。这个属性对象就是描述符。

    描述符对象一般是作为其他类对象的属性而存在。在其内部定义了三个方法用来实现属性对象的查找、设置、删除行为。这三个方法分别是:

    • get(self, instance, owner):定义当试图取出描述符的值时的行为。
    • set(self, instance, value):定义当描述符的值改变时的行为。
    • delete(self, instance):定义当描述符的值被删除时的行为。
      其中:instance为把描述符对象作为属性的对象实例;
      owner为instance的类对象。
      以下以官方的一个例子进行说明:
    class RevealAccess(object):
        def __init__(self, initval=None, name='var'):
            self.val = initval
            self.name = name
        def __get__(self, obj, objtype):
            print 'Retrieving', self.name
            return self.val
        def __set__(self, obj, val):
            print 'Updating', self.name
            self.val = val
    class MyClass(object):
        x = RevealAccess(10, 'var "x"')
        y = 5

    以上定义了两个类。其中RevealAccess类的实例是作为MyClass类属性x的值存在的。而且RevealAccess类定义了__get__、__set__方法,它是一个描述符对象。注意,描述符对象的__get__、__set__方法中使用了诸如self.val和self.val = val等语句,这些语句会调用__getattribute__、__setattr__等方法,这也说明了__getattribute__、__setattr__等方法在控制访问对象属性上的一般性(一般性是指对于所有属性它们的控制行为一致),以及__get__、__set__等方法在控制访问对象属性上的特殊性(特殊性是指它针对某个特定属性可以定义不同的行为)。

    以下进行验证:

    # 创建Myclass类的实例m
    >>> m = MyClass()
    # 查看m和MyClass的__dict__
    >>> m.__dict__
    {}
    >>> MyClass.__dict__
    dict_proxy({'__dict__': <attribute '__dict__' of 'MyClass' objects>,
                '__doc__': None,
                '__module__': '__main__',
                '__weakref__': <attribute '__weakref__' of 'MyClass' objects>,
                'x': <__main__.RevealAccess at 0x5130080>,
                'y': 5})
    # 访问m.x。会先触发__getattribute__方法
    # 由于x属性的值是一个描述符,会触发它的__get__方法
    >>> m.x
    Retrieving var "x"
    10
    # 设置m.x的值。对描述符进行赋值,会触发它的__set__方法
    # 在__set__方法中还会触发__setattr__方法(self.val = val)
    >>> m.x = 20
    Updating var "x"
    # 再次访问m.x
    >>> m.x
    Retrieving var "x"
    20
    # 查看m和MyClass的__dict__,发现这与对描述符赋值之前一样。
    # 这一点与一般属性的赋值不同,可参考上述的__setattr__方法。
    # 之所以前后没有发生变化,是因为变化体现在描述符对象上,
    # 而不是实例对象m和类MyClass上。
    >>> m.__dict__
    {}
    >>> MyClass.__dict__
    dict_proxy({'__dict__': <attribute '__dict__' of 'MyClass' objects>,
                '__doc__': None,
                '__module__': '__main__',
                '__weakref__': <attribute '__weakref__' of 'MyClass' objects>,
                'x': <__main__.RevealAccess at 0x5130080>,
                'y': 5})

    上面的例子对描述符进行了一定的解释,不过对描述符还需要更进一步的探讨和分析,这个工作先留待以后继续进行。

    最后,还需要注意一点:描述符有数据描述符和非数据描述符之分。

    只要至少实现__get____set____delete__方法中的一个就可以认为是描述符;
    只实现__get__方法的对象是非数据描述符,意味着在初始化之后它们只能被读取;
    同时实现__get____set__的对象是数据描述符,意味着这种属性是可读写的。

    属性访问的优先规则

    在以上的讨论中,我们一直回避着一个问题,那就是属性访问时的优先规则。我们了解到,属性一般都在__dict__中存储,但是在访问属性时,在对象属性、类属型、基类属性中以怎样的规则来查询属性呢?以下对Python中属性访问的规则进行分析。

    由上述的分析可知,属性访问的入口点是__getattribute__方法。它的实现中定义了Python中属性访问的优先规则。Python官方文档中对__getattribute__的底层实现有相关的介绍,本文暂时只是讨论属性查找的规则,相关规则可见下图:
    这里写图片描述

    上图是查找b.x这样一个属性的过程。在这里要对此图进行简单的介绍:

    1. 查找属性的第一步是搜索基类列表,即type(b).mro,直到找到该属性的第一个定义,并将该属性的值赋值给descr;
    2. 判断descr的类型。它的类型可分为数据描述符、非数据描述符、普通属性、未找到等类型。若descr为数据描述符,则调用desc.get(b, type(b)),并将结果返回,结束执行。否则进行下一步;
    3. 如果descr为非数据描述符、普通属性、未找到等类型,则查找实例b的实例属性,即b.dict。如果找到,则将结果返回,结束执行。否则进行下一步;
    4. 如果在b.dict未找到相关属性,则重新回到descr值的判断上。
      (1)若descr为非数据描述符,则调用desc.get(b, type(b)),并将结果返回,结束执行;
      (2)若descr为普通属性,直接返回结果并结束执行;
      (3)若descr为空(未找到),则最终抛出 AttributeError 异常,结束查找。

    转自:http://python.jobbole.com/88582/

    展开全文
  • Java语言中的面向对象特性 【课前思考】  1. 什么是对象?什么是类?什么是包?什么是接口?什么是内部类?  2. 面向对象编程的特性有哪三个?它们各自又有哪些特性?  3...
  • JavaScript 属性描述

    千次阅读 2015-09-11 10:21:21
    原文地址:... 什么是属性描述符 在ES5之前,JavaScript 没有内置的机制来指定或者检查对象某个属性(property)的特性(characteristics),比如某个属性是只读(readonly)的或者不能被枚举(enu
  • 即数据属性限制使用者对对象属性数据值的读取和写入权限,有四种特性 (1)[[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性特性,或者能否把属性修改为访问器属性。像前面例子中...
  • 日常生活中的所有东西都是对象,而具有同种属性的一类事物被称为类,人、动物、植物、车都是类(是抽象的),而姚明这个人就是一个具体的对象对象是类的实例化,比如唐伯虎点...属性用来描述具体某个对象的特征。
  • Objective-C的面向对象特性(一)

    千次阅读 2014-05-30 18:47:29
    Objective-C在c语言的基础上增加了面向对象特性,都有哪些面向对象特性呢? 类和对象 Objective-C软件由许多对象构成,形成一个对象网络,对象之间通过发送消息交互。在Objective-C中一个对象通过调用...
  • 理解javascript中的对象属性

    千次阅读 2014-03-08 11:52:58
    在前面已经有稍微介绍过【 javascript引用类型之object类型 】,今天就具体的说说什么是对象。 在javascript中,对象就是一组无序的名/值对的集合。并且,对象的操作,是对其引用的操作。即,将一个对象赋给一个...
  • 你必须知道的.NET之特性属性

    千次阅读 2016-09-18 14:19:00
    特性属性
  • Java面向对象特性和跨平台性

    千次阅读 2017-02-22 22:06:06
    一、Java面向对象的三大特性:Java语言的三大特性为:封装、继承、多态 – 封装: ... 属性可以用来描述同一类事物的特征,方法可以描述一类事物的操作。封装就是把属于同一类事物的共性(即属性和方法等)
  • 黑马程序员_王康Java面向对象特性

    千次阅读 2012-02-27 09:34:24
    ----------------------Java面向对象特性 1,对象的基本概念:面向对象程序语言三个关键特点: 封装 ( Encapsulation ) 继承 ( Inheritance ) 多态 ( Polymorphism ) 在面向对象语言中,在数据类型的声明与操作...
  • Unity3D Attribute特性属性特性

    千次阅读 2018-04-08 11:37:17
    关于 Attribute特性属性特性)传送门:http://www.cnblogs.com/landeanfen/p/4642819.html 总结版本来源于unity2017;其它版本仅供参考,欢迎补全,有错误请私信me。 RangeAttribute:[Range(最小值,最大值)...
  • 1. 用来描述对象特性的一组数据,也就是若干变量,通常称为属性。 2. 用来操作对象特性的若干动作,也就是若干函数,通常称为方法。 浏览器对象 对象 含义 anchor 当前文档中设置了name属性的超链接 applet ...
  • 作用:除了可以用构造函数和字面量的方式为对象设置属性,也可以使用 object.defineProperties来添加/设置对象属性。 参数: object 必需。 对其添加或修改属性对象。 这可以是本机 JavaScript 对象或 DOM...
  • 对象是JS的基本数据类型。对象是一种复合值,ta
  • 1、 创建一个球员类,并且该类最多只允许创建十一个对象。提示利用 static 和 封装性来...// sum变量用来统计当前创建的对象个数,static修饰的变量与对象无关 // 只与类有关,为所有对象共享 static int sum = 0; /...
  • C#反射机制来获取类 属性描述

    千次阅读 2016-12-02 18:52:46
    通过C#反射获取类和类中属性描述信息 自定义特性代码 [System.Serializable] [System.AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = false)] ...
  • JavaScript学习笔记:对象属性的枚举

    千次阅读 2017-01-03 17:44:00
    JavaScript中对象属性分为两种: 数据属性 和 访问器属性 。然后根据具体的上下文环境的不同,又可以将属性分为: 原型属性 和 实例属性 。原型属性是定义在对象的原型( prototype )中的属性,而实例属性一方面...
  • 第三讲 Java语言中的面向对象特性

    千次阅读 2004-09-10 17:35:00
    面向对象编程的特性有哪三个?它们各自又有哪些特性? 3. 你知道java语言在面向对象编程方面有何独特的特点吗?难点: 1. 理解方法重载和方法重写,不要混淆了两者的使用。 2. 类变量和类方法的使用。 3. ...
  • JavaScript 对象属性特性:writable、enumerable 和 configurable
  • 什么是类 类的概念:类是具有相同属性和服务的一组对象的集合... 对象的概念:对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组服务组成。从更抽
  • 一、 名词解释抽象 封装 消息【问题解答】面向对象方法中的 ...消息是 面向对象程序设计用来描述对象之间通信 的机制。一个消息就是一个对象要求另一个对象实 施某种操作的一个请求。二、 填空题(1) 目前有 面向过...
  • c#:特性属性

    千次阅读 2007-05-29 09:56:00
    本文将介绍以下内容: • 定制特性的基本概念和用法• 属性特性的区别比较• 反射的简单介绍1. 引言attribute是.NET框架引入的有一技术亮点,因此我们有必要花点时间来了解本文的内容,走进一个发现attribute...
  • python中的类和对象属性和方法

    万次阅读 多人点赞 2018-04-26 13:18:30
    一、面向对象的概述面向对象是一种描述业务问题、设计业务实体和实体之间关系的方法二、类和对象1、类和对象得区别:类是对客观世界中事物得抽象,而对象是类实例化后的实体 例如:汽车模型就是一个类,制造出来的...
  • 面向对象的四大特性:封装、继承、多态、抽象。从一定角度来看,封装和继承几乎都是为多态而准备的。是最重要的知识点。多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用...
  • 面向对象的三大特性是"封装、"多态"、"继承",五大原则是"单一职责原则"、"开放封闭原则"、"里氏替换原则"、"依赖倒置原则"、"接口分离原则"。 什么是面向对象 面向对象(Object Oriented,OO)是软件开发方法。...
  • MediaPlayer控件属性描述

    千次阅读 2010-05-07 16:59:00
    获取当前媒体信息"Title"=媒体标题,"Author"=艺术家,"Copyright"=版权信息,"Description"=媒体内容描述,"Duration"=持续时间(秒),"FileSize"=文件大小,"FileType"=文件类型,"sourceURL"=原始地址 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 176,297
精华内容 70,518
关键字:

属性是用来描述对象特性的