重学JavaWeb第三天(九)

简介: 重学JavaWeb第三天(九)

6.6、Closure

6.6.1、闭包引入

需求信息:点击某个按钮,提示"点击的是第n个按钮"

第一种解决方法:将btn所对应的下标保存在btn上

var btns = document.getElementsByTagName('button');
//将btn所对应的下标保存在btn上
for (var i = 0, length = btns.length; i < length; i++) {
    var btn = btns[i];
    btn.index = i;
    btn.onclick = function () {
        alert('第' + (this.index + 1) + '个');
    }
}

第二种解决方法:利用闭包延长局部变量的生命周期

var btns = document.getElementsByTagName('button');
// 利用闭包延长局部变量的生命周期
for (var i = 0, length = btns.length; i < length; i++) {
    (function (j) {
        var btn = btns[j];
        btn.onclick = function () {
            alert('第' + (j + 1) + '个');
        }
    })(i);
}

6.6.2、闭包概念如何产生闭包?

当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包

什么才是闭包?

理解一:闭包是嵌套的内部函数(绝大部分人认为)

理解二:包含被引用变量(函数)的对象(极少部分人认为)

闭包的作用?

它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中

6.6.3、闭包演示

function fun1() {
    var a = 2;
    function subFun() {
        a++;
        console.log(a);
    }
    return subFun;
}
var f1 = fun1();
f1();
f1();
console.log("===============");
function fun2() {
    var a = 2;
    function subFun() {
        a--;
        console.log(a);
    }
    return subFun;
}
var f2 = fun2();
f2();
f2();
console.log("===============");

d87de75b1e8174844b422839aa340efc.png

6.6.4、闭包生命周期

生命周期:

  1. 产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
  2. 死亡:在演示说明:
function fn1() {
    //此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
    var a = 2;
    function fn2() {
        a++;
        console.log(a);
    }
    return fn2;
}
var f = fn1();
f(); // 3
f(); // 4
f = null; //闭包死亡(包含闭包的函数对象成为垃圾对象)
  1. 嵌套的内部

6.6.5、闭包应用

闭包应用: 定义JS模块

  1. 函数成为垃圾对象时就死亡了
  • 具有特定功能的js文件
  • 将所有的数据和功能都封装在一个函数内部(私有的)
  • 只向外暴露一个包含n个方法的对象或函数
  • 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能

案例演示:

第一种格式:myModule.js

function myModule() {
    //私有数据
    var msg = 'Hello, World';
    //操作数据的函数
    function doSomething() {
        console.log('doSomething() ' + msg.toUpperCase());
    }
    function doOtherthing() {
        console.log('doOtherthing() ' + msg.toLowerCase());
    }
    //向外暴露对象(给外部使用的方法)
    return {
        doSomething: doSomething,
        doOtherthing: doOtherthing
    }
}

第一种使用:index.html

var module = myModule();
module.doSomething();
module.doOtherthing();
• 1
• 2
• 3

‘第二种格式:myModule.js

(function (window) {
    //私有数据
    var msg = 'Hello, World';
    //操作数据的函数
    function doSomething() {
        console.log('doSomething() ' + msg.toUpperCase());
    }
    function doOtherthing() {
        console.log('doOtherthing() ' + msg.toLowerCase());
    }
    //向外暴露对象(给外部使用的方法)
    window.myModule = {
        doSomething: doSomething,
        doOtherthing: doOtherthing
    }
})(window);

第二种使用:index.html

myModule.doSomething();
myModule.doOtherthing();
• 1
• 2

第七章 JavaScript新特性

7.1、ECMAScript6新特性

7.1.1、let 关键字

let 关键字用来声明变量,使用 let 声明的变量有几个特点:

  • 不允许重复声明
  • 块儿级作用域
  • 不存在变量提升
  • 不影响作用域链

注意:以后声明变量使用 let 就对了


案例演示:创建四个div,单机每一个div让其变色

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        .item {
            width: 100px;
            height: 50px;
            border: solid 1px rgb(42, 156, 156);
            float: left;
            margin-right: 10px;
        }
    </style>
</head>
<body>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<!-- 在这里写JavaScript代码,因为JavaScript是由上到下执行的 -->
<script>
    // 获取div元素对象
    let items = document.getElementsByClassName('item');
    // 遍历并绑定事件
    for (let i = 0; i < items.length; i++) {
        items[i].onclick = function () {
            // 以前的做法:this.style.background = "pink";
            items[i].style.background = "pink";
        };
    }
</script>
</body>
</html>

638ebf65d0b92e6e0b0db30c511f6dbb.png

7.1.2、const 关键字

const 关键字用来声明常量,const 声明有以下特点:

  • 不允许重复声明
  • 块儿级作用域
  • 声明必须赋初始值
  • 值不允许修改
  • 标识符一般为大写

注意:声明对象类型使用 const,非对象类型声明选择 let

// 声明常量
const MAX = 100;
console.log(MAX);
// 对于数组和对象的元素修改, 不算做对常量的修改, 不会报错
const TEAM1 = [1, 2, 3, 4];
const TEAM2 = [1, 2, 3, 4];
// 但是不能修改地址指向
// TEAM2 = TEAM1;

7.1.3、变量的解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。

注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式

数组的解构赋值:

//数组的解构赋值
const arr = ["张学友", "刘德华", "黎明", "郭富城"];
let [zhang, liu, li, guo] = arr;
console.log(zhang);
console.log(liu);
console.log(li);
console.log(guo);

5fb1ba33ac33438802ebd82fb6e5cbec.png

简单对象的解构赋值:

//对象的解构赋值
const lin = {
    name: "林志颖",
    tags: ["车手", "歌手", "小旋风", "演员"]
};
let {name, tags} = lin;
console.log(name);
console.log(tags);

8b7ce50918f011787c759d52ec51259a.png

复杂对象的解构赋值:

//复杂对象的解构赋值
let wangfei = {
    name: "王菲",
    age: 18,
    songs: ["红豆", "流年", "暧昧"],
    history: [
        {name: "窦唯"},
        {name: "李亚鹏"},
        {name: "谢霆锋"}
    ]
};
let {name, age, songs: [one, two, three], history: [first, second, third]} = wangfei;
console.log(name);
console.log(age);
console.log(one);
console.log(two);
console.log(three);
console.log(first);
console.log(second);
console.log(third);

73fdf11d7d4bd02653b1a14632e0dc70.png

7.1.4、模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:

  • 字符串中可以出现换行符
  • 可以使用 ${xxx} 形式输出变量
  • 注意:当遇到字符串与变量拼接的情况使用模板字符串

字符串中可以出现换行符:

//定义字符串
let str = `<ul>
               <li>沈腾</li>
               <li>玛丽</li>
               <li>魏翔</li>
               <li>艾伦</li>
           </ul>`;
console.log(str);

37507325bd8c4b0e180ce2c328af2fb4.png

变量拼接:

//变量拼接
let name = '小可爱';
let result = `欢迎${name}访问我的文章`;
console.log(result);
1234

5c2b00c2dee5f968ec98123b851c4fdf.png

7.1.5、简化对象写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法,这样的书写更加简洁。

注意:对象简写形式简化了代码,所以以后用简写就对了

let name = "张三";
let age = 18;
let speak = function () {
    console.log(this.name);
};
//属性和方法简写
let person = {
    name,
    age,
    speak
};
console.log(person.name);
console.log(person.age);
person.speak();

c8b45b2df4f0f02214034017c22d5438.png

7.1.6、箭头函数

ES6 允许使用「箭头」(=>)定义函数,通用写法如下:

let fn = (arg1, arg2, arg3) => {
    return arg1 + arg2 + arg3;
}

箭头函数的注意点:


如果形参只有一个,则小括号可以省略

函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果

箭头函数 this 指向声明时所在作用域下 this 的值,箭头函数不会更改 this 指向,用来指定回调函数会非常合适

箭头函数不能作为构造函数实例化

不能使用 arguments 实参

省略小括号的情况:

let fn = num => {
    return num * 10;
};

省略花括号的情况:

let fn = score => score * 20;

this 指向声明时所在作用域中 this 的值:

// this 指向声明时所在作用域中 this 的值
let fn = () => {
    console.log(this);
}
fn();
let school = {
    name: "张三",
    getName() {
        let subFun = () => {
            console.log(this);
        }
        subFun();
    }
};
school.getName();


7.1.7、rest 参数

ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments 参数。

注意:rest 参数非常适合不定个数参数函数的场景

// 作用与 arguments 类似
function add(...args) {
    console.log(args);
}
add(1, 2, 3, 4, 5);
// rest 参数必须是最后一个形参
function minus(a, b, ...args) {
    console.log(a, b, args);
}
minus(100, 1, 2, 3, 4, 5, 19);

c645ff4fa324e0fa34b3c383d49301ee.png

7.1.8、spread 扩展运算符

扩展运算符(spread)也是三个点(…),它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。

展开数组:

// 展开数组
let tfboys = ["德玛西亚之力", "德玛西亚之翼", "德玛西亚皇子"];
function fn() {
    console.log(arguments);
}
fn(...tfboys);

70d077991101d5940e8c1ea182969586.png

展开对象:

// 展开对象
let skillOne = {
    q: "致命打击"
};
let skillTwo = {
    w: "勇气"
};
let skillThree = {
    e: "审判"
};
let skillFour = {
    r: "德玛西亚正义"
};
let gailun = {...skillOne, ...skillTwo, ...skillThree, ...skillFour};
console.log(gailun);

2d13041ffd16ba9d49d21022253c67f3.png

7.1.9、Symbol类型

7.1.9.1、Symbol的使用

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值,它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型,Symbol 特点如下:


Symbol 的值是唯一的,用来解决命名冲突的问题

Symbol 值不能与其它数据进行运算

Symbol 定义的对象属性不能使用 for…in 循环遍 历 ,但是可以使用 Reflect.ownKeys 来获取对象的所有键名

  • 象的所有键名
//创建 Symbol
let s1 = Symbol();
console.log(s1);
console.log(typeof s1);
//添加标识的 Symbol
let s2 = Symbol("张三");
let s2_2 = Symbol("张三");
console.log(s2);
console.log(s2_2);
console.log(s2 === s2_2);
//使用 Symbol for 定义
let s3 = Symbol.for("张三");
let s3_2 = Symbol.for("张三");
console.log(s3);
console.log(s3_2);
console.log(s3 === s3_2);
//在方法中使用 Symbol
let game = {
    name: "狼人杀",
    [Symbol('say')]: function () {
        console.log("我可以发言")
    },
    [Symbol('zibao')]: function () {
        console.log('我可以自爆');
    }
};
console.log(game);

3155cf98436a8245b33ec66c7fdd3426.png

注意:遇到唯一性的场景时要想到 Symbol

7.1.9.2、Symbol内置值

除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。

可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。

image.png

Symbol.hasInstance演示:

class Person {
    static [Symbol.hasInstance](param) {
        console.log("我被用来检测类型了");
    }
}
let o = {};
console.log(o instanceof Person);

9d92f74cc5606e72d2abdc2761387801.png

Symbol.isConcatSpreadable演示:

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr2[Symbol.isConcatSpreadable] = true;
console.log(arr1.concat(arr2));
const arr3 = [1, 2, 3];
const arr4 = [4, 5, 6];
arr4[Symbol.isConcatSpreadable] = false;
console.log(arr3.concat(arr4));

8aa58bd357e67a60af16c7fe87f0c2bf.png

7.1.10、迭代器

遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。ES6 创造了一种新的遍历命令 for…of 循环,Iterator 接口主要供 for…of 消费,原生具备 iterator 接口的数据:


Array

Arguments

Set

Map

String

TypedArray

NodeList

注意:需要自定义遍历数据的时候,要想到迭代器


工作原理:


创建一个指针对象,指向当前数据结构的起始位置

第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员

接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员

每调用 next 方法返回一个包含 value 和 done 属性的对象

案例演示:遍历数组

//声明一个数组
const xiyou = ["唐僧", "孙悟空", "猪八戒", "沙僧"];
//使用 for...of 遍历数组
for (let v of xiyou) {
    console.log(v);
}
console.log("===============");
//获取迭代器对象
let iterator = xiyou[Symbol.iterator]();
//调用对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

2c88d67700c490a8ed8d7bf839ae6d32.png

案例演示:自定义遍历数据

//声明一个对象
const banji = {
    name: "五班",
    stus: [
        "张三",
        "李四",
        "王五",
        "小六"
    ],
    [Symbol.iterator]() {
        //索引变量
        let index = 0;
        let _this = this;
        return {
            next: function () {
                if (index < _this.stus.length) {
                    const result = {value: _this.stus[index], done: false};
                    //下标自增
                    index++;
                    //返回结果
                    return result;
                } else {
                    return {value: undefined, done: true};
                }
            }
        };
    }
}
//遍历这个对象
for (let v of banji) {
    console.log(v);
}

a1d759503631ad3fcef18fd771313f50.png

7.1.11、生成器

生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

7.1.11.1、生成器函数使用

代码说明:

  • * 的位置没有限制
  • 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到 yield 语句后的值
  • yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next 方法,执行一段代码
  • next 方法可以传递实参,作为 yield 语句的返回值
function * gen() {
    /*代码1开始执行*/
    console.log("代码1执行了");
    yield "一只没有耳朵";
    /*代码2开始执行*/
    console.log("代码2执行了");
    yield "一只没有尾巴";
    /*代码3开始执行*/
    console.log("代码3执行了");
    return "真奇怪";
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log("===============");
//遍历
for (let v of gen()) {
    console.log(v);
}

2ce536a541a29fbf134d12f0b25847c1.png

7.1.11.2、生成器函数参数


function * gen(arg) {
    console.log(arg);
    let one = yield 111;
    console.log(one);
    let two = yield 222;
    console.log(two);
    let three = yield 333;
    console.log(three);
}
//执行获取迭代器对象
let iterator = gen('AAA');
console.log(iterator.next());
//next方法可以传入实参
console.log(iterator.next('BBB'));
console.log(iterator.next('CCC'));
console.log(iterator.next('DDD'));

81714a5ed62dc020940d56314b3856cd.png

7.1.11.3、生成器函数实例

案例演示:1s后控制台输出 111,2s后输出 222,3s后输出 333

function one() {
    setTimeout(() => {
        console.log(111);
        iterator.next();
    }, 1000)
}
function two() {
    setTimeout(() => {
        console.log(222);
        iterator.next();
    }, 2000)
}
function three() {
    setTimeout(() => {
        console.log(333);
        iterator.next();
    }, 3000)
}
function * gen() {
    yield one();
    yield two();
    yield three();
}
//调用生成器函数
let iterator = gen();
iterator.next();

db5cc0bf454d8398fe084910b042ffb1.png

案例演示:模拟获取 ,用户数据 ,订单数据 ,商品数据

function getUsers() {
    setTimeout(() => {
        let data = "用户数据";
        iterator.next(data);
    }, 1000);
}
function getOrders() {
    setTimeout(() => {
        let data = "订单数据";
        iterator.next(data);
    }, 1000);
}
function getGoods() {
    setTimeout(() => {
        let data = "商品数据";
        iterator.next(data);
    }, 1000);
}
function * gen() {
    let users = yield getUsers();
    console.log(users);
    let orders = yield getOrders();
    console.log(orders);
    let goods = yield getGoods();
    console.log(goods);
}
//调用生成器函数
let iterator = gen();
iterator.next();

d0f59a8e44c836b3b8c2e577ae359fc7.png

7.1.12、Promise

Promise 是 ES6 引入的异步编程的新解决方案,语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。

7.1.12.1、Promise基本使用

//实例化 Promise 对象
const p = new Promise(function (resolve, reject) {
    setTimeout(function () {
        // 成功调用resolve()处理
        let data = "数据读取成功";
        resolve(data);
        // 失败调用reject()处理
        let err = "数据读取失败";
        reject(err);
    }, 1000);
});
//调用 promise 对象的 then 方法
p.then(function (value) {
    console.log(value);
}, function (reason) {
    console.error(reason);
});

7.1.12.2、Promise案例演示

案例演示:

// 接口地址: https://api.apiopen.top/getJoke
const p = new Promise((resolve, reject) => {
    //1. 创建对象
    const xhr = new XMLHttpRequest();
    //2. 初始化
    xhr.open("GET", "https://api.apiopen.top/getJoke");
    //3. 发送
    xhr.send();
    //4. 绑定事件, 处理响应结果
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            //判断响应状态码 200-299
            if (xhr.status >= 200 && xhr.status < 300) {
                //表示成功
                resolve(xhr.response);
            } else {
                //如果失败
                reject(xhr.status);
            }
        }
    }
});
//指定回调
p.then(function (value) {
    console.log(value);
}, function (reason) {
    console.error(reason);
});

603ce242bd13f7b7ffdf005d207035b7.png

7.1.12.3、Promise-then方法

调用 then 方法,then 方法的返回结果是 Promise 对象,对象状态由回调函数的执行结果决定,如果回调函数中返回的结果是 非 promise 类型的属性,状态为成功,返回值为对象的成功的值

//创建 promise 对象
const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("用户数据");
    }, 1000)
});
//链式调用+箭头函数
p.then(value => {
    console.log(value);
    return value;
}).then(value => {
    console.log(value);
});

993ef0fb925a3817260fcb4d227cebf4.png

7.1.12.4、Promise-catch方法

如果只想处理错误状态,我们可以使用 catch 方法

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        //设置 p 对象的状态为失败, 并设置失败的值
        reject("出错啦!");
    }, 1000);
});
p.catch(function (reason) {
    console.error(reason);
});

b412a745a372657808f79c344e98dd00.png

7.1.13、Set

ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历,集合的属性和方法:

  • size:返回集合的元素个数
  • add():增加一个新元素,返回当前集合
  • delete():删除元素,返回 boolean 值
  • has():检测集合中是否包含某个元素,返回 boolean 值
  • clear():清空集合,返回 undefined
//创建一个空集合
let s = new Set();
//创建一个非空集合
let s1 = new Set([1, 2, 3, 1, 2, 3]);
//集合属性与方法
//返回集合的元素个数
console.log(s1.size);
//添加新元素
console.log(s1.add(4));
//删除元素
console.log(s1.delete(1));
//检测是否存在某个值
console.log(s1.has(2));
//清空集合
console.log(s1.clear());

1665802849a49c938c1c84f9677cb0d7.png

目录
相关文章
|
5月前
|
存储 前端开发 JavaScript
基于JavaWeb实现停车场管理系统
基于JavaWeb实现停车场管理系统
|
5月前
|
前端开发 JavaScript Java
图书借阅管理平台|基于JavaWeb实现图书借阅系统
图书借阅管理平台|基于JavaWeb实现图书借阅系统
116 1
|
2月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
422 37
|
1月前
|
前端开发 Java 应用服务中间件
Javaweb学习
【10月更文挑战第1天】Javaweb学习
33 2
|
1月前
|
安全 Java Android开发
JavaWeb解压缩漏洞之ZipSlip与Zip炸弹
JavaWeb解压缩漏洞之ZipSlip与Zip炸弹
51 5
|
2月前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
5月前
|
前端开发 Java 关系型数据库
JavaWeb开发简介
JavaWeb开发简介
55 0
|
2月前
|
安全 Java Android开发
JavaWeb解压缩漏洞之ZipSlip与Zip炸弹
JavaWeb解压缩漏洞之ZipSlip与Zip炸弹
108 2
|
2月前
|
SQL JSON JavaScript
JavaWeb基础9——VUE,Element&整合Javaweb的商品管理系统
Vue 指令、生命周期、this和$、vue脚手架进行模块化开发/ElementUI框架、综合案例,element商品列表展示增删改查
JavaWeb基础9——VUE,Element&整合Javaweb的商品管理系统
|
5月前
|
SQL Java 数据库连接
JavaWeb Mapper代理开发
JavaWeb Mapper代理开发
下一篇
无影云桌面