首页 > 代码库 > js对象字面量属性间相互访问的问题 和 这个问题与执行环境的关系

js对象字面量属性间相互访问的问题 和 这个问题与执行环境的关系

对象字面量属性间无法相互访问的问题:

我曾经在定义一个对象时, 并出于复用性的考虑试图复用某些属性:

// test 1var person = {    firstName    : "Bill",    lastName    : "Gates",    fullName    : firstName + " " + lastName          // 这里报错}alert(person.firstName);alert(person.lastName);alert(person.fullName);

结果发现这种复用会导致错误: "ReferenceError: firstName is not defined", firstName未定义(不存在).

但试着把错误行注释, 去检测 firstName 是否存在时:

// test 2var person = {    firstName    : "Bill",    lastName    : "Gates",    // fullName    : firstName + " " + lastName        // 注释掉这行}alert(person.firstName);          // 提示 "Bill"alert(person.lastName);            // 提示 "Gates"

又发现 firstName 和 lastName 都被正确定义和赋值了, 不存在上面说的 "firstName is not defined".

接下来我又以为是语法错误, 没有明确指定属性的归属, 需要在属性前使用 this. 做前缀:

// test 3var person = {    firstName    : "Bill",    lastName    : "Gates",    fullName    : this.firstName + " " + this.lastName        // 使用 this}alert(person.firstName);              // 提示 "Bill"alert(person.lastName);                // 提示 "Gates"alert(person.fullName);                // 提示 "undefined undefined"

结果不再提示错误, 但 fullName 却没得到了 "undefined undefined" 这个我预想不到的值.

我又以为可能是 this 不够明确, 需要用 person 来调用

// test 4var person = {    firstName    : "Bill",    lastName    : "Gates",    fullName    : person.firstName + " " + person.lastName        // 使用 person, 报错}alert(person.firstName);alert(person.lastName);alert(person.fullName);

结果折腾一圈回到原点, 又是报错: "TypeError: person is undefined", person未定义(不存在).

......


最后进行过多少次修改和尝试我已经记不清楚, 我只知道我当时并没有解开.

知道最近再碰到时, 才发现这个问题已经不能再使我感到困惑. 而之前我所有的想法从根本上就是错的...

对象字面量属性间无法相互访问, 甚至会出错. 问题不是出在语法上, 也无法通过改变调用对象就能解决.

与执行环境的关系:

要理解并解决这个问题首先需要了解js的"执行环境":
    1, 执行环境的类型有两种: 全局(window)和局部(每一个函数), 没有其他类c语言中的块级作用域.
    2, 访问权限:  内围环境具有外围环境数据的访问权限,外围环境却无法访问内围环境的数据
    3, 数据的归属:使用var声明的变量会自动添加到最近的环境中, 不使用var声明的变量会默认成为全局属性.
    4, 数据的查找:数据查找跟数据归属一样遵循"就进原则", 会先从最近的环境中查找, 找不到再向外一层查找, 直到最外层.

了解这几个特性以后才能解释上面几个例子出现的问题:

    test 1 中, 由于不存在"块级作用域", 而且数据查找会从最近的环境开始, 所以js解释器在碰到
        fullName : firstName + " " + lastName
    这条语句时它会从window(假设window是最近环境)上查找firstName这个属性, 而 window.firstName 是不存在的. 直接使用一个不存在的变量去参与运算, 这里当然会报错

    test 3 使用了 this, 但在实行到 this 那条语句的时候 person 对象的创建并没有完成, 这个对象也还没存在.
    这时候 this 指向的是window对象, this.firstName 等同于 window.firstName.
    到这里, test 3 的情况似乎与 test 1 如出一辙, 但是结果为什么不一样呢? 一个报错, 一个得到undefined.
    这个解释器的错误处理机制有关, 假设有 a, b两个变量, a 经过声明(执行var a;)而b没有.
    在使用的时候, 解释器对a的理解是"值为undefined的变量", 只要有值就可以参与运算.而解释器对b的理解是"未经声明、不存在的变量", 这个变量连undefined都没有, 无法参与运算.
    对js解释器来说, 这是经过声明(哪怕不赋值)再使用和没经过声明而直接使用这两种情况的意义是不一样的.
    显式调用 window.firstName 相当于相当于经过了声明, 如果找不到就默认值为undefined, 隐式 firstName 如果在window上找不到就相当于未经声明直接调用, 属于错误.

    test 4 的 person 如 上面所说, 在执行到带有 person 的语句时, person根本就还不存在. 解释器也把这未经声明而使用的行为当成是错误来处理.


解决思路:
    
因为这个问题在字面量上没有完美的解决方法, 所以这里只能提出解决思路.

首先, 对于"执行环境"造成的问题, 要解决它自然得从执行环境入手.

既然知道 firstName 和 lastName 会从 window 对象上查找, 我们就先往 window 上添加, 好让它能找到.

var firstName = "Bill",    lastName  = "Gates";var person = {    firstName    : firstName    lastName    : lastName    fullName    : firstName + " " + lastName}alert(person.firstName);        // 提示 "Bill"alert(person.lastName);         // 提示 "Gates"alert(person.fullName);         // 提示 "Bill Gates"

结果都正确, person 还不存在, firstName 和 lastName 会从 window 上找, 根本不会造成冲突.

其次, 对于"person 对象没构建完成无法使用" 的问题, 可以想方法延迟对 firstName 和 lastName 的访问.

var person = {    firstName    : "Bill",    lastName    : "Gates",    getFullName    : function() {        return this.firstName + " " + this.lastName;    }}alert(person.getFullName);

把直接属性改成方法, 在对象构建完成才能调用, this也不再跑偏.

其他的方法还有很多, 可以使用new创建的方式避免这种问题, 或者使用字面量+传统定义方式
        person.fullName = person.firstName + " " + person.lastName;
只要理解透彻, 思路正确, 问题总会得到解决