对象字面量
首先给出对象字面量的定义语法:
1. 将对象定义在一对括号中(左大括号“{”和右大括号”}”)
2. 对象中以逗号分隔属性和方法. 每个属性或方法以key-value的形式出现, key和value之间以冒号分割.
3. 当给变量赋值时, 不要忘记或大括号后面的分号
空对象
var obj = {};
这样就定义了一个空的对象, 但它并非什么也没有, 至少它具有从Object.prototype中继承下来的属性和方法.
来自构造函数的对象
语法:
// 反模式, 不推荐这么用 Var obj = new Object(); obj.name = ‘张三’;
与字面量创建对象的方式相比, 字面量对象输入的字符更少,
而且, 当创建一个局部函数时, 解释器需要从调Object()位置开始向上查询作用域链, 直到发现全局的Object构造函数.
当使用Object()构造函数创建对象时, 直到代码运行时才能确定其类型, 借用书中的例子:
// 创建一个空对象 var o1 = new Object(); console.info(o1.constructor == Object); // true // 创建一个数值对象 var o2 = new Object(7); console.info(o2.constructor == Number); // true // 创建一个字符串对象 var o3 = new Object(‘hello world‘); console.info(o3.constructor == String); // true
总得来说, 不要使用new Object()创建对象, 应该使用对象字面量的方式.
自定义构造函数及其返回值
// 定义一个Person构造函数 function Person(name) { this.name = name; this.say = function () { console.info(‘hello!‘); }; };
当使用new调用构造函数时, 函数内部将创建一个this对象, 并将属性和方法添加到this对象中,
然后隐式的返回this对象(如果没有显式的return语句).
当然构造函数中可以显式的返回一个对象, 例如:
function Person() { this.name = ‘张三‘; // 创建一个that对象, 并返回该对象 var that = {}; that.name = ‘李四‘; return that; } var p = new Person(); console.info(p.name); // 李四
正如上面看到的, 构造函数可以返回任意对象, 只要它是一个对象.
如果构造函数试图返回一个非对象, 这虽然不会导致错误, 但还是忽略此返回值, 继续返回this, 例如:
function Person() { this.name = ‘张三‘; this.age = 21; return 0; // 试图返回一个非对象, 被忽略, 返回this } var p = new Person(); console.info(p.name); // 张三
强制使用new的模式
构造函数也是函数, 只不过它是用new操作符调用的.
如果在调用时, 忘记使用new操作符, 那么构造函数中的this将指向全局对象,
在浏览器环境下this会指向window对象, this的中所有属性或方法只能通过window对象来访问, 或是直接访问, 这显然是不想看到的情况.
// 构造函数 function Person() { this.msg = ‘hello‘; } // 定义一个对象 var p1 = new Person(); console.info(typeof p1); // object console.info(p1.msg); // hello // 反模式: 忘了使用new var p2 = Person(); console.info(typeof p2); // undefined console.info(msg); // hello console.info(window.msg); // hello
上面的问题在ECMAScript5中已经得到了解决, 但在非EC5环境下, 需要想办法解决上面的问题.
命名约定
约定开头字母大写的函数为构造函数, 必须使用new来调用, 普通函数首字母小写, 可直接调用.
使用that
约定只是约定, 保不会忘, 下面的模式可以确保即使不使用new, 属性或方法也不会添加到全局对象中.
// 定义构造函数-1 function Person1() { var that = {}; that.name = ‘张三‘; return that; } // 定义构造函数-2 function Person2() { return { name: ‘李四‘ }; } // 以上两种方式, 总会返回一个对象 var p1 = new Person1(), p2 = Person1(); console.info(p1.name); // 张三 console.info(p2.name); // 李四
但是, 上面两种模式会丢失与各自原型的链接, 当扩展其原型时, 对于对象来说都是不可用的, 例如:
// 定义构造函数-1 function Person1() { var that = {}; that.name = ‘张三‘; return that; } // 扩展Person1的原型 if(typeof Person1.prototype.say === ‘undefined‘) { Person1.prototype.say = function() { console.info(‘hello world...‘); } } var p1 = new Person1(); p1.say(); // TypeError: p1.say is not a function
自调用构造函数
为了解决上述两种模式的问题, 可以引用下面的解决方案,
就是在构造函数内部, 检查this对象是否是构造函数的一个实例, 如果不是, 那么使用new操作符再次调用构造函数创建对象, 代码如下:
// 定义构造函数 function Person() { // 注意this instanceof Person需要用括号括起来 if(!(this instanceof Person)) { return new Person(); } this.name = ‘张三‘; } // 扩展其原型 if(typeof Person.prototype.say === ‘undefined‘) { Person.prototype.say = function() { console.info(‘hello world...‘); } } var p1 = new Person(), p2 = Person(); console.info(p1.name); // 张三 p1.say(); // hello world... console.info(p2.name); // 张三 p2.say(); // hello world...
数组字面量
推荐使用字面量的方式创建数组, 主要原因一是因为数组字面量简单明确, 二是因为数组构造函数的特殊性.
当向Array()构造函数中传递单个数值时, 它不会成为数组的第一个元素, 而是设置了数组的长度,
但是该数组中没有实际的元素, 所以当访问数组的元素时返回的是undefined. 来几个小例子:
var arr1 = [7]; console.info(arr1.length); // 1 console.info(arr1[0]); // 7 var arr2 = new Array(7); console.info(arr2.length); // 7 console.info(arr2[0]); // undefined var arr3 = [3.14]; console.info(arr3.length); // 1 console.info(arr3[0]); // 3.14 var arr4 = new Array(3.14); // RangeError: invalid array length console.info(arr4.length); console.info(arr4[0]);
检查数组的性质
使用typeof对数组进行操作, 返回值为”object”, 因为数组也是对象, 但意义不大.
可以通过检查length属性或slice()方法来判断是不是数组, 但是, 其他的自定义对象同样可以拥有这些属性和方法.
ECMAScript5中定义了Array.isArray()方法, 来判断对象是不是数组, 但是不支持EC5的环境中就无法使用该方法. 但仍可以通过以下方法检测是否是数组:
if(typeof Array.isArray === ‘undefined‘) { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === ‘[object Array]‘; } }