<<Javascript Patterns>>阅读笔记 – 第3章 字面量和构造函数

对象字面量

首先给出对象字面量的定义语法:

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]';
    }
}

 

posted @ 2015-04-12 23:21  第七狙击手  阅读(555)  评论(0编辑  收藏  举报