jQuery 延迟对象与Promise
延迟对象 (Deferred)
jQuery的Deferred对象是一种异步编程模式,用于处理异步操作。它提供了一种优雅的方式来处理回调函数,使代码更加清晰和可维护。
// 创建延迟对象
var deferred = $.Deferred();
// 异步操作
setTimeout(function() {
var success = true;
if (success) {
deferred.resolve('操作成功');
} else {
deferred.reject('操作失败');
}
}, 1000);
// 处理结果
deferred.done(function(result) {
console.log('成功:', result);
}).fail(function(error) {
console.error('失败:', error);
}).always(function() {
console.log('操作完成');
});
Promise
Promise是Deferred对象的一个只读视图,它只提供了添加回调函数的方法,而不提供触发回调的方法。
// 创建延迟对象并返回Promise
function asyncOperation() {
var deferred = $.Deferred();
setTimeout(function() {
deferred.resolve('操作成功');
}, 1000);
return deferred.promise();
}
// 使用Promise
var promise = asyncOperation();
promise.done(function(result) {
console.log('成功:', result);
});
// 链式调用
asyncOperation()
.done(function(result) {
console.log('第一步成功:', result);
return anotherAsyncOperation();
})
.done(function(result) {
console.log('第二步成功:', result);
})
.fail(function(error) {
console.error('失败:', error);
});
Promise的高级用法
// 并行执行多个异步操作
$.when(
asyncOperation1(),
asyncOperation2(),
asyncOperation3()
).done(function(result1, result2, result3) {
console.log('所有操作成功:', result1, result2, result3);
}).fail(function(error) {
console.error('至少有一个操作失败:', error);
});
// 转换普通对象为Promise
var promise = $.when('普通值');
promise.done(function(value) {
console.log('值:', value);
});
// 处理AJAX请求
$.ajax({
url: 'data.json',
dataType: 'json'
}).done(function(data) {
console.log('数据:', data);
}).fail(function() {
console.error('请求失败');
});
自定义事件
创建和触发自定义事件
jQuery允许我们创建和触发自定义事件,这对于组件间通信非常有用。
// 绑定自定义事件
$('#element').on('customEvent', function(event, data) {
console.log('自定义事件触发:', data);
});
// 触发自定义事件
$('#element').trigger('customEvent', { message: 'Hello World' });
// 使用命名空间
$('#element').on('customEvent.namespace1', function() {
console.log('命名空间1的事件');
});
$('#element').on('customEvent.namespace2', function() {
console.log('命名空间2的事件');
});
// 只触发特定命名空间的事件
$('#element').trigger('customEvent.namespace1');
// 移除特定命名空间的事件
$('#element').off('.namespace1');
事件委托和自定义事件
// 使用事件委托处理自定义事件
$('#container').on('itemAdded', '.item', function(event, itemData) {
console.log('项目添加:', itemData);
});
// 触发事件
$('.item').trigger('itemAdded', { id: 1, name: '项目1' });
// 自定义事件的冒泡
$('div').on('customEvent', function(event) {
console.log('事件触发在:', this.tagName);
// 阻止冒泡
// event.stopPropagation();
});
// 触发事件
$('span').trigger('customEvent');
动画队列
动画队列的基本概念
jQuery的动画队列允许我们按顺序执行多个动画,而不需要嵌套回调函数。
// 动画队列
$('#box')
.animate({ left: '100px' }, 1000)
.animate({ top: '100px' }, 1000)
.animate({ width: '200px' }, 1000)
.animate({ height: '200px' }, 1000)
.animate({ opacity: 0.5 }, 1000);
// 自定义队列
$('#element')
.queue('custom', function(next) {
console.log('第一步');
next(); // 执行下一个队列项
})
.queue('custom', function(next) {
console.log('第二步');
next();
})
.queue('custom', function(next) {
console.log('第三步');
next();
})
.dequeue('custom'); // 开始执行队列
// 清除队列
$('#element').stop(true); // 清除所有队列项
// 跳转到队列末尾
$('#element').finish(); // 立即完成所有队列中的动画
模块化开发
使用立即执行函数表达式 (IIFE)
// 模块化组织代码
var myModule = (function($) {
// 私有变量
var privateVar = '私有变量';
// 私有方法
function privateMethod() {
console.log('私有方法', privateVar);
}
// 公共接口
return {
publicVar: '公共变量',
publicMethod: function() {
console.log('公共方法');
privateMethod();
},
init: function() {
console.log('模块初始化');
}
};
})(jQuery);
// 使用模块
myModule.init();
myModule.publicMethod();
console.log(myModule.publicVar);
使用jQuery的data方法存储数据
// 使用data方法存储数据
$('#element').data('user', {
id: 1,
name: '张三',
email: 'zhangsan@example.com'
});
// 获取数据
var user = $('#element').data('user');
console.log(user.name); // 输出: 张三
// 存储方法
$('#element').data('methods', {
show: function() {
console.log('显示元素');
},
hide: function() {
console.log('隐藏元素');
}
});
// 调用方法
$('#element').data('methods').show();
实践案例:TODO应用
在这个案例中,我们将创建一个完整的TODO应用,应用前面学习的高级特性。
// TODO应用
var TodoApp = (function($) {
// 私有变量
var $todoInput, $addButton, $todoList;
var todos = [];
// 初始化
function init() {
// 缓存DOM元素
$todoInput = $('#todo-input');
$addButton = $('#add-todo');
$todoList = $('#todo-list');
// 绑定事件
bindEvents();
// 加载本地存储的TODO项
loadTodos();
// 渲染TODO列表
renderTodos();
}
// 绑定事件
function bindEvents() {
$addButton.on('click', addTodo);
$todoInput.on('keypress', function(e) {
if (e.which === 13) { // 回车键
addTodo();
}
});
// 使用事件委托处理删除和完成事件
$todoList.on('click', '.delete', deleteTodo);
$todoList.on('click', '.toggle', toggleTodo);
}
// 添加TODO项
function addTodo() {
var text = $todoInput.val().trim();
if (text) {
var todo = {
id: Date.now(),
text: text,
completed: false
};
todos.push(todo);
saveTodos();
renderTodos();
$todoInput.val('');
// 触发自定义事件
$(document).trigger('todoAdded', todo);
}
}
// 删除TODO项
function deleteTodo() {
var id = parseInt($(this).closest('li').data('id'));
todos = todos.filter(function(todo) {
return todo.id !== id;
});
saveTodos();
renderTodos();
// 触发自定义事件
$(document).trigger('todoDeleted', id);
}
// 切换TODO项状态
function toggleTodo() {
var id = parseInt($(this).closest('li').data('id'));
var todo = todos.find(function(todo) {
return todo.id === id;
});
if (todo) {
todo.completed = !todo.completed;
saveTodos();
renderTodos();
// 触发自定义事件
$(document).trigger('todoToggled', todo);
}
}
// 渲染TODO列表
function renderTodos() {
$todoList.empty();
todos.forEach(function(todo) {
var $li = $('')
.data('id', todo.id)
.addClass(todo.completed ? 'completed' : '');
var $toggle = $('');
var $text = $('' + todo.text + '');
var $delete = $('');
var $actions = $('')
.append($toggle)
.append($delete);
$li.append($text).append($actions);
$todoList.append($li);
});
}
// 保存TODO项到本地存储
function saveTodos() {
localStorage.setItem('todos', JSON.stringify(todos));
}
// 从本地存储加载TODO项
function loadTodos() {
var storedTodos = localStorage.getItem('todos');
if (storedTodos) {
todos = JSON.parse(storedTodos);
}
}
// 公共接口
return {
init: init,
getTodos: function() {
return todos;
}
};
})(jQuery);
// 初始化应用
$(document).ready(function() {
TodoApp.init();
// 监听自定义事件
$(document).on('todoAdded', function(event, todo) {
console.log('TODO添加:', todo);
});
$(document).on('todoDeleted', function(event, id) {
console.log('TODO删除:', id);
});
$(document).on('todoToggled', function(event, todo) {
console.log('TODO状态改变:', todo);
});
});
尝试使用以下TODO应用:
互动练习:动画队列挑战
在这个练习中,你需要使用jQuery的动画队列创建一个复杂的动画效果。