《JavaScript模式》第3章 字面量和构造函数

@by Ruth92(转载请注明出处)

第3章:字面量和构造函数

一、创建对象的三种方式

// 对象字面量
var car = {goes: "far"};

// 内置构造函数(反模式)
var car = new Object();
car.goes = "far";

// 自定义构造函数
var adam = new Person("Adam");
adam.say();

☛ 对象字面量的优点:

  • 按需创建对象,在程序生命周期内的任何时候都可以修改;
  • 输入更短的字符;
  • 强调该对象仅是一个可变哈希映射,而不是从对象中提取的属性和方法;
  • 没有作用域解析

二、构造函数

☛ 构造函数的特征(使用缺点):

  • 依赖传递的参数的值,该构造函数可能会委派另一个内置构造函数来创建对象,并且返回了一个并非期望的不同对象

  • 当传递给构造函数的值是动态的,并且直到运行时才能确定其类型

      // 一个空对象
      var o = new Object();
      console.log(o.constructor === Object);  // true
      
      // 一个数值对象
      var o = new Object(1);
      console.log(o.constructor === Number);  // true
      console.log(o.toFixed(2));  // '1.00'	
    

✔ JavaScript 中没有类的概念,但是它却支持极大的灵活性,因为不必预先知道对象的一切细节,也不需要类“蓝图”。

// 构造函数的定义
var Person = function(name) {
  this.name = name;
  this.say = function() {
    return "I am " + this.name;
  };
};

✔ 当以 new 操作符调用构造函数时,函数内部将会发生以下情况:

  • 创建一个空对象并且 this 变量引用了该对象,同时还继承了该函数的原型。
  • 属性和方法被加入到 this 引用的对象中。
  • 新创建的对象由 this 所引用,并且最后隐式地返回 this(如果没有显式地返回其它对象)。

✔ 当 new 操作符调用构造函数时,相当于在后台发生了如下事情:

var Person = function(name) {

  // 使用对象字面量模式创建一个对象
  // var this = {};

  // 向 this 添加属性和方法
  this.name = name;
  this.say = function() {
    return "I am" + this.name;
  };

  // return this;
}

✔ “空”对象实际上并不空,它已经从 Person 的原型中继承了许多成员。因此,更像下面的语句:

// var this = Object.create(Person.prototype);

构造函数的返回值:

  • 当用 new 操作符创建对象时,构造函数总是返回一个对象;
  • 默认返回 this 所引用的对象,也可以返回任意其他对象;
  • 如果在构造函数中并不向 this 添加任何属性,将返回“空”对象(这里的“空”,指的是除了从构造函数的原型中所继承的成员以外);
  • 如果试图返回并非对象的值,函数会忽略该值,并返回 this 所引用的对象。

如果在调用构造函数时,忘记使用 new 操作符,会导致构造函数中的 this 指向全局对象。

☊ 自调用构造函数——解决忘记使用 new 的问题,同时还能使实例继承原型

// 自调用构造函数
function Waffle() {

  if ( !(this instanceof Waffle) ) {
    return new Waffle;
  }

  this.tastes = "yummy";

}

Waffle.prototype.wantAnother = true;

// 测试调用
var first = new Waffle(),
    second = Waffle();

console.log(first.tastes);  // "yummy";
console.log(second.tastes);  // "yummy";
console.log(first.wantAnother);   // "true";
console.log(second.wantAnother);   // "true";

另一种检测实例对象的方法:

if ( !(this instanceof arguments.callee) ) {
	return new arguments.callee();
} 

三、数组构造函数的特殊性

/**
 * 向 Array() 构造函数传递单个数字时,会设定数组的长度
 */
// 具有一个元素的数组
var a = [3];
console.log(a.length);	// 1
console.log(a[0]);	// 3

// 具有三个元素的数组
var a = new Array(3);
console.log(a.length);	// 3
console.log(typeof a[0]);	// 'undefined'

/**
 * 向 new Array() 传递浮点数,会产生范围错误
 */
// 使用数组字面量
var a = [3.14];
console.log(a[0]);	// 3.14

var a = new Array(3.14);	// RangeError: invalid array length
console.log(typeof a);	// 'undefined'

/**
 * Array() 构造函数的灵巧用法:重复字符串
 */
var white = new Array(256).join(' ');	// 返回具有255个空白字符串的字符串

检测是否为数组:

Array.isArray()

function polyfillArray(arr) {

  if (typeof Array.isArray === 'undefined') {
    Array.isArray = function(arg) {
      return Object.prototype.toString.call(arg) === "[object Array]";
    };
  }

  return Array.isArray(arr);
}

polyfillArray([]);  // 'true'

polyfillArray({
  length: 1,
  "0": 1,
  slice: function () {}
}); // false

四、JSON

JSON:代表 JavaScript 对象表示(JavaScript Object Notation)以及数据传输格式。

它是一种轻量级数据交换格式,只是一个数组和对象字面量表示方法的组合:

{ "name": "value", "some": [1, 2, 3]}

JSON 和文字对象之间的唯一语法差异:

  • 在 JSON 中,属性名称需要包装在引号中才能成为合法的 JSON;
  • 在对象字面量中,仅当属性名称不是有效标识符时才会需要引号,如字符中间有空格 { "first name": "Dave" };
  • 在 JSON 中,不能使用函数或正则表达式籽棉量。

使用 JSON

// JSON.parse();
var jstr = '{"mykey": "my value"};
var data = JSON.parse(jstr);
console.log(data.mykey);	// 'my value'

// JSON.stringify()
var dog = {
	name: 'Fido',
	dob: new Date(),
	legs: [1, 2, 3, 4]
};

var jsonstr = JSON.stringify(dog);
console.log(jsonstr);	// "{"name":"Fido","dob":"2016-09-20T10:26:24.904Z","legs":[1,2,3,4]}"

五、基本值类型包装器

五个基本的值类型:NumberStringBooleannullundefined。除了 nullundefined 以外,其他三个都具有所谓的基本包装对象。

可以使用内置构造函数 Number()String()Boolean() 创建包装对象。

包装对象包含了一些有用的属性和方法。但是这些方法在基本值类型上也能够起作用。只要调用这些方法,基本值类型就可以在后台被临时转换为一个对象,并且表现得犹如一个对象。

// 一个基本数值
var n = 100;
console.log(typeof n);  // 'number'

// 一个数值 Number 对象
var nobj = new Number(100);
console.log(typeof nobj);   // 'object'

// 用来作为对象的基本字符串
var s = 'hello';
console.log(s.toUpperCase());   // 'HELLO'

// 值本身可以作为一个对象
"monkey".slice(3, 6);   // "key"

// 与数值的方法相同
(22 / 7).toPrecision(3);    // '3.14'

使用包装对象的场景

有扩充值以及持久保存状态的需要。因为基本值类型并不是对象,它们不可能扩充值。

六、错误对象

  • JavaScript 中有一些内置错误构造函数,如 Error()SyntaxError()TypeError(),这些错误构造函数都带有 throw 语句。通过这些错误构造函数创建的错误对象具有 namemessage 属性。

  • throw 适用于任何对象,并不一定是由某个错误构造函数所创建的对象,因此可以选择抛出自己的对象。这种错误对象也可以有属性 namemessage,以及任意希望传递给 catch 语句来处理的其他类型的信息。

      try {
          // 发生以外的事情,抛出一个错误
          throw {
              name: "MyErrorType",    // 自定义错误类型
              message: 'oops',
              extra: 'This was rather embarrassing',
              remedy: genericErrorHandler // 指定应该处理该错误的函数
          };
      } catch (e) {
          // 通知用户
          alert(e.message);   // 输出 'oops'
      
          // 优美的处理错误
          e.remedy(); // 调用函数 genericErrorHandler
      }
    
  • 错误构造函数以函数的形式调用(不带 new)时,其表现行为与构造函数(带 new)相同,并且返回同一个错误对象。


posted on 2016-10-03 10:22  Ruth92  阅读(235)  评论(0编辑  收藏  举报

导航