首页 > 代码库 > 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;
只要理解透彻, 思路正确, 问题总会得到解决