接到 HR 的面试通知,NodeJS 还没喵完便急匆匆地翻了这本书。等了好久还是没货打折只能跟别人借了

MVC和类

什么是 MVC

MVC 是一种设计模式,将应用划分为3个部分:数据(模型),展示层(视图),用户交互层(控制器)。一个事件发生的过程是这样的

  1. 用户和应用发生交互
  • 控制器的事件处理器被触发
  • 控制器从模型中请求数据,并将棋交给视图
  • 视图将数据呈现给用户

模型

模型用来存放应用的所有数据对象,不必知晓视图和控制器的细节,只需包含数据及直接和这些数据相关的模型。当控制器从服务器抓取数据或创建新的记录时,它就将数据包装成模型实例。即数据是面向对象的

// 不推荐
var user = users["foo"];
destoryUser(user);
// 推荐
var user = User.find("foo");
user.destory();

视图

视图是呈现给用户的,用户与之产生交互

控制器

控制器是模型和视图之间的纽带。控制器从视图获得事件和输入,对它们进行处理,并相应地更视图

创建类

var Class = function(parent) {
var klass = function() {
this.init.apply(this, arguments);
};
if(parent) {
// 通过匿名函数,避免创造 parent 实例
// constructor 也被复制过去了
var subClass = function() {};
subClass.prototype = parent.prototype;
klass.prototype = new subClass();
}
klass.prototype.init = function() {};
return klass;
};
var Person = new Class();
Person.prototype.init = function() {
// 基于 Person 实例做初始化
// 此处作为构造函数使用
// 优点是所有类继承自 Class
}
// 用法
var person = new Person();
var child = new Class(Person);

控制类的作用域

// 代理,方法属于调用对象,即 this/self 指向调用对象本身
Obj.prototype.proxy = function(func) {
var self = this;
return function() {
func.apply(self, arguments);
};
};
// 绑定,方法属于函数本身,this/self 属于执行函数的当前上下文
// 不适用于改变上下文,这里可能造成误解
if (!Function.prototype.bind) {
Function.prototype.bind = function(context) {
var slice = [].slice,
args = slice.call(arguments, 1), // 绑定时传入参数
self = this, // 当前函数
nop = function() {},
bound = function() {
// 是否在当前上下文调用函数,是的话直接传入 this,否则传入 bind 时传入的上下文,并合并新传入的参数
return self.apply(this instanceof nop ? this : (context || {}), args.concat(slice.call(arguments)));
};
// 匿名函数,避免实例化当前函数
nop.prototype = self.prototype;
// bound 继承当前上下文的原型
bound.prototype = new nop();
// 返回绑定当前上下文的函数
return bound;
};
}

事件和监听

取消事件

浏览器给事件赋予了默认行为,如页面跳转、表单提交等。在事件传播阶段(之后)会触发这些默认行为,在任何一个事件处理程序中都可以调用 event 对象的 preventDefault() 阻止默认行为,同样也可以通过返回 false 实现

通过 stopPropagation() 阻止事件传播

事件对象

属性 作用
bubbles 是否通过冒泡触发
button 鼠标按下的按钮
ctrlKey Ctrl 键是否按下
altKey Alt 键是否按下
shiftKey Shift 键是否按下
metaKey Meta 键是否按下
isChar 按下的键是否表示一个字符
charCode 当前按键的 unicode 值(针对 keypress 事件)
keyCode 表示非字符按键的 unicode 值
which 当前按键的 unicode 值,不管是否表示字符
currentTarget 事件冒泡阶段所在的当前 DOM 元素
target 原始的 DOM 元素

切换上下文

使用浏览器内置的 addEventListener() 时,上下文从局部变量切换为目标 HTML 元素。可通过事件代理保持当前的上下文

委托事件

事件绑定在父元素,jQuery 的 selector.delegate() 提供实现。所有为元素动态添加的子元素都具有事件监听

自定义事件

基于中介者模式,或者 jQuery 是实现的 bind()trigger(),使代码整洁,具有扩展性。

模型和数据

MVC 和命名空间

将模型的属性保存至命名空间的做法可以确保不会发生冲突,同时符合 MVC 原则。并将真实 user 对象上得喝 user 实例相关的函数也添加进去。降入 user 记录包含一个 destroy() 函数,它是何具体的 user 相关的,则这个函数应当基于 User 实例调用

构建对象关系映射(ORM)

ORM 是一个包装了一些数据的对象层。以往 ORM 常用于抽象 SQL 数据库,但这里 ORM 只是用于抽象 Javascript 数据类型。通过这个额外的层可以添加自定义的函数和属性来增强基础数据的功能

原型继承

if(typeof Object.create !== 'function') {
Object.create = function(o) {
function F() {}
F.prototype = o;
return new F();
}
}

通过 Ajax 载入数据

Ajax 的一个限制是同源策略,它要求所有的请求必须来自同一个域名,子域名,并且地址的端口也应该一致。主要原因是出于安全考虑,因为当一个 Ajax 请示被发送,所有请求都会附带主域的 cookie 信息一起发送

CORS(cross-origin resource sharing)允许跨域请求。只需要在 HTTP 协议的响应头添加:

Access-Control-Allow-Origin: emample.com
Access-Control-Request-Method: GET, POST
// 定义认证自定义的请求头
Access-Control-Request-Headers: Authorization
var req = new XMLHttpRequest();
req.open('POST', '/example', true);
req.setRequestHeader('Authorization', oauth_signature)

本地储存数据

HTML5 本地储存包含两类,local storagesession storage。浏览器端所储存的数据是以域名分隔开的,某个域中的脚本储存的数据只能被这个域读取。通常允许每个域名 5MB

localStorage['someData'] = 'bmw';
var len = localStorage.length;
localStorage.setItem('someData') = 'bmw';
localStorage.getItem('someData'); // => 'bmw'
localStorage.removeItem('someData');
localStorage.clear();

控制器和状态

控制器可以理解为应用中视图和模型之间的纽带。只有控制器知道视图和模型的存在并将他们连接在一起。当加载页面时,控制器将时间处理器程序绑定在视图里,并适时处理回调,以及和模型必要的对接