1.todolist实现
html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>todolist</title>
<link rel="stylesheet" href="./style/reset.css">
<link rel="stylesheet" href="./style/main.css">
<style>
.todoed li{
width: 300px;
}
</style>
</head>
<body>
<div class="header">
<span>工作任务</span>
<span><input type="text" id="todotitle"></span>
</div>
<h3>正在进行中</h3>
<ul class="todoing">
</ul>
<h3>已完成工作</h3>
<ul class="todoed">
</ul>
<script src="./js/jquery.js"></script>
<script src="./js/layer.js"></script>
<script src="./js/Store.js"></script>
<script src="./js/MyJson.js"></script>
<script src="./js/moment.js"></script>
<script src="./js/main.js"></script>
</body>
</html>
main.js
const store = new Store();
const key = 'todo';
const todoedKey = 'todoed';
$(function () {
//获取光标移动数据
$('#todotitle').blur(function () {
const value = $.trim($(this).val());
if (value !== '') $(this).val('');
setValue(key, value);
})
//获取键盘事件的数据
$('#todotitle').keydown(function (e) {
if (e.keyCode === 13) {
const value = $.trim($(this).val());
if (value !== '') $(this).val('');
setValue(key, value);
}
// $('todo li').remove();
// let todoListData = getStoreData(key);
// getListShow(todoListData);
})
//渲染
let todoListData = getStoreData(key);
//console.log(todoListData,"2222222");
getListShow(todoListData);
let todoedListData = getStoreData(todoedKey);
getListShow2(todoedListData);
//委托修改任务状态
$('.todoing').on('change', 'input', function () {
if ($(this).prop('checked')) {
$(this).parents('li').remove();
let todoedData = getStoreData(todoedKey);
let tmpData = todoListData[$(this).val()];
tmpData.edt = moment().format('YYYY-MM-DD HH-mm-ss');
todoedData.unshift(tmpData);
//转为json字符串存储在localstorage中
todoedData = MyJson.stringify(todoedData);
store.set(todoedKey, todoedData);
//删除任务
todoListData.splice($(this).val(), 1);
let tmpTodoListData = MyJson.stringify(todoListData);
store.set(key, tmpTodoListData); //处理好的数据存储
//渲染
$('.todoed li').remove();
let todoListData1 = getStoreData(todoedKey);
getListShow2(todoListData1);
layer.msg('success', { icon: 1, time: 1000 });
}
})
//删除正在进行的任务
$('.todoing').on('click', '.del', function () {
let self = $(this);
let index = self.attr('value');
layer.confirm('您真要删除?', {
btn: ['确定', '再想一下']
}, function () {
let todoListData = getStoreData(key);
todoListData.splice(index, 1);
self.parents('li').remove();
store.set(key, MyJson.stringify(todoListData));
getListShow(todoListData);
layer.msg('success', { icon: 1, time: 1000 })
});
})
//删除完成的任务
$('.todoed').on('click', '.del', function () {
let self = $(this);
let index = self.attr('value');
layer.confirm('您真要删除?', {
btn: ['确定', '再想一下']
}, function () {
let todoedListData = getStoreData(todoedKey);
todoedListData.splice(index, 1);
self.parents('li').remove();
store.set(todoedKey, MyJson.stringify(todoedListData));
//渲染
$('.todoed li').remove();
let todoListData1 = getStoreData(todoedKey);
getListShow2(todoListData1);
layer.msg('success', { icon: 1, time: 1000 })
});
})
})
function getListShow(todoListData) {
if (Array.isArray(todoListData)) {
$.each(todoListData, function (index, item) {
let li = $(`<li>
<span>
<input class='setState' value='${index}' type="checkbox">
</span>
<span>${item.title} -- ${item.sdt}</span>
<span value='${index}' class='del'>
删除
</span>
</li>`
)
$('.todoing').prepend(li)
})
} else {
let currIndex = $('.todoing li').length;
console.log(currIndex);
let li = $(`<li>
<span>
<input class='setState' value='${currIndex}' type="checkbox">
</span>
<span>${todoListData.title} -- ${todoListData.sdt}</span>
<span value='${currIndex}' class='del'>
删除
</span>
</li>`)
$('.todoing li:first').before(li);
//console.log("11111111");
}
}
function getListShow2(todoListData) {
if (Array.isArray(todoListData)) {
$.each(todoListData, function (index, item) {
let li = $(`<li>
<span>${item.title} -- ${item.sdt}</span>
<span value='${index}' class='del'>
删除
</span>
</li>`
)
$('.todoed').prepend(li)
})
} else {
let currIndex = $('.todoing li').length;
console.log(currIndex);
let li = $(`<li>
<span>${todoListData.title} -- ${todoListData.sdt}</span>
<span value='${currIndex}' class='del'>
删除
</span>
</li>`)
$('.todoed li:first').before(li);
//console.log("11111111");
}
}
//存储数据
function setValue(key, value) {
if (value) {
let data = getStoreData(key);
let todoData = {
title: value,
sdt: moment().format('YYYY-MM-DD HH:mm:ss'),
edt: ''
}
data.unshift(todoData);
let tmpdata = MyJson.stringify(data);
store.set(key, tmpdata);
getListShow(todoData);
layer.msg('success', { icon: 1, time: 1000 })
}
}
function getStoreData(key) {
return store.get(key) ? MyJson.parse(store.get(key)) : []
}
2.作用域与闭包
2.1作用域简单介绍
全局作用域只有一个,每个函数又都有作用域(环境)。es6+多了一个块级作用域。
作用域链只向上查找,找到全局window即终止,应该尽量不要在全局作用域中添加变量。
函数被执行后其环境变量将从内存中删除,函数每次调用都会创建一个新作用域
function count() {
let total = 0;
console.log(total)
return total
}
count();
//如果子函数被使用时父级环境将被保留 -- 闭包;
function fn() {
let n = 1;
return function() {
console.log(n++);
};
}
let a = fn()
a()
//在闭包情况下若函数一直被调用,则局部变量不会被释放
function fn() {
let n = 1
let data = []
return function () {
console.log(n++)
}
}
let method = fn()
method() //1
method() //2
method() //3
method() //4
2.2 块级作用域
es6+以后有
块级作用域:if while for 里的花括号
var没有块级作用域
2.3 闭包
闭包是函数运行的一种机制,函数执行会形成一个私有作用域(上下文),如果私有作用域的某些内容被私有作用域以外的一些事物(如:变量/事件绑定等)所占用,则当前私有作用域不能被出栈释放。创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量。
闭包的作用:
1.保护上下文中的私有变量与外界的变量互不影响
2.上下文中的私有变量和值都会被保存
闭包缺点:
会导致栈内存太大,页面渲染变慢,性能受到影响
闭包基本方法
function fn() {
let n = 1;
return function() {
console.log(++n);
};
}
let a = fn()
a()
//==============================================
let lessons = [
{
title: "Nodejs快速入门",
click: 100,
price: 3200
},
{
title: "html+css",
click: 110,
price: 1200
},
{
title: "js入门到精通",
click: 2100,
price: 2000
}
];
// lessons.sort((a, b) => b.click - a.click)
/* lessons.sort(function (a, b) {
return b.click - a.click
}) */
// 箭头函数的写法
const myOrder = fieldName => (a, b) => b[fieldName] - a[fieldName];
lessons.sort(myOrder('click'))
// 保存
function myOrder(fieldName) {
return function (a, b) {
return b[fieldName] - a[fieldName]
}
}
2.3.1列表元素绑定事件
for (var i = 0; i < liList.length; i++) {
liList[i].onclick = (function (i) {
return function () {
console.log(`当前点击按钮的索引:${i}`)
}
})(i)
}
2.3.2闭包实现模块化保护
var myModule = (function () {
var name = '张三'
function getName() { return name }
return {
getName
}
})()
//例子
const {getPhone,setPhone} = (function () {
var phone = '1232131223'
function getPhone() {
return '我的电话号码:' + phone
}
function setPhone(phoneNum){
phone = phoneNum
}
// 暴露到外部调用的方法
return {
getPhone,
setPhone
}
})()
console.log(getPhone())
setPhone('22222222222')
console.log(getPhone())
2.3.3惰性调用
function getStyle(...args) {
// ie有的
// if (document.body.currentStyle) { // ie浏览器中有
if (!('getComputedStyle' in window)) { // ie浏览器中有
getStyle = function (...args) {
return args[0].currentStyle[args[1]]
}
} else {
getStyle = function (...args) {
return window.getComputedStyle(args[0])[args[1]]
}
}
return getStyle(...args)
}
console.log(getStyle(document.getElementById('box'), 'width'))
console.log(getStyle(document.getElementById('box'), 'width'))
2.3.4函数柯里化
柯里化是一个预处理思想,使用闭包形成一个不被释放的上下文,把一些信息存储起来,以后基于作用域链,访问到事先存储的信息,然后进行相关的处理,我们把这种模式称为柯里化函数。
//例子
function curring(n = 0) {
return function (...args) {
return args.reduce((prev, curr) => prev + curr, n)
}
}
const sum = curring()
console.log(sum(1, 2, 3))
3.this指向问题
3.1this指向主要分为5类,分别是:
- 事件绑定中的this
- 普通函数执行中的this
- 箭头函数执行中的this
- 构造函数中的this
- 基于call/apply/bind强制改变中的this
3.2事件绑定中的this
IE6~8中基于attachEvent实现事件绑定,事件触发方法执行,方法中的this不在是元素本身,一般情况都是指向window
// 绑定事件
/* document.getElementById('btn').addEventListener('click', function () {
// this ==> 当前点击的dom对象
console.log(this)
}) */
// ie和w3c一样的 this ==> 当前点击的dom对象
/* document.getElementById('btn').onclick = function () {
// this ==> 当前点击的dom对象
// console.log(this.tagName)
console.log(this)
} */
// ie绑定
document.getElementById('btn').attachEvent('onclick', function () {
// this指向到window
console.log(this === window)
})
3.3普通函数执行的this
1.函数执行,看函数前面是否有“点”,有“点”,“点”前面是谁this就是谁,没有点“点”this是window,(js严格模式下是undefined)
2.自执行函数执行,其内的this一般都是window,严格模式下为undefined
3.回调函数和闭包中的this为window或undefined,除非做过特殊处理(如:数组中的forEach)
// 调用普通函数,如果前面有.,则.前的对象就是this的指向
// this => obj
// obj.fn()
// this => obj.fn2
// obj.fn2.fn3()
// 闭包中的this,一般都是指向window
//obj.fn4()()
/* var fn = obj.fn4()
window.fn() */
// 回调函数中的this一般指向window
/* [1, 2, 3].forEach(function (item,index) {
console.log(this)
}); */
/* [1, 2, 3].forEach(function (item, index) {
// this => {id:1}
console.log(this)
}, { id: 1 }); */
// 自执行函数中的this指向到window
(function(){
console.log(this)
})()
3.4箭头函数中的this
箭头函数中没有自己的this,所用到的this都是所处上下文中的this
<button id="btn">点击事件</button>
<script>
// 箭头函数,定义在window中,this指向到window
/* const fn = () => console.log(this)
fn() */
// 箭头可以保留this的指向,因为它本身没有,它只听你所在的作用域
btn.onclick = function () {
/* function fn() {
console.log(this)
} */
const fn = () => console.log(this)
fn()
}
var obj = {
// window
fn:()=>console.log(this)
}
var obj = {
fn: function () {
/* function fn2() {
console.log(this)
} */
const fn2 = () => {
const fn3 = () => {
console.log(this)
}
fn3()
}
fn2()
}
}
obj.fn()
3.5 call/apply/bind强制改变this指向
3.5.1 call方法
给参数1对象中添加一个属性,并把fn函数赋值给它(fn.call({},参数)),
把参数1对象中新添加的属性方法,并把参数赋值过去
删除刚新添加的属性方法
call和apply都是自执行,调用即执行,call的性能略好于apply,因为call不用分析参数,直接用
call的实现思路:
先给参数1添加一个属性并且让这个属性值赋值为 fn,执行自定义属性方法,删除刚刚添加的自定义属性
function fn(a, b) {
console.log(this, a, b)
}
var obj = {
id: 1000
}
// call执行的函数,如果有参数,则以,逗号方式一个个去添加
// fn.call(obj, 1, 2)
fn.call(obj, ...[1, 2])
// apply 参数函数有参数,则以数组的方式来传递
// fn.apply(obj, [10, 20])
3.5.2apply
function fn(a, b) {
console.log(this, a, b)
}
var obj = {
id: 1000
}
// call执行的函数,如果有参数,则以,逗号方式一个个去添加
// fn.call(obj, 1, 2)
fn.call(obj, ...[1, 2])
// apply 参数函数有参数,则以数组的方式来传递
// fn.apply(obj, [10, 20])
//=====================
//注意
// 在非严格模式下,如果传入的是一个null/undefined/参数1为空,都指this指向到window
// ***** 如果参数1为空,在非严格模式下面指向为window,在严格模式下,指向为undefined ****
// fn.call()
// 在严格模式下写谁就指向谁 null
// fn.call(null)
// 指向undefined
// fn.call(undefined)
3.5.3 bind
// bind,它调用的方法不会立即执行,只会给预先处理好,放在此处等待被调用
// bind绑定的函数有参数,则和call中的参数传递一样的
var obj = {
id: 1,
name: '张三'
}
function fn(a, b) {
console.log(this, a, b)
}
// console.log(fn.bind(obj))
// react中大量使用
document.querySelector('#box').onclick = fn.bind(obj,1,2)