<!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>Document</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
</ul>
</body>
<script>
// 1.Proxy的实现
// (1)、proxy的实现
var person = { "name": "zhangsan" }; // 创建一个名为 'person' 的对象,该对象包含一个名为 'name' 的属性赋值为 'zhangsan'
var pro = new Proxy(person, { // 创建一个名为 'pro' 的代理对象,它会传递给 'person' 对象和一个拦截器对象
get: function (target, propertr) { // 代理对象的拦截器钩子方法,用于拦截代理对象上的获取值操作
return "lisi"; // 当访问代理对象中的任何属性时都返回字符串 'lisi'
}
});
console.log(pro.name); // 在控制台打印出 "lisi"
// (2)、set方法
var bankAccount = { "RMB": 1000, "dollar": 0 }; // 创建一个名为 'bankAccount' 的对象,表示银行账户,由人民币和美元组成,并给出了初始值。
var banker = new Proxy(bankAccount, { // 创建一个名为 'banker' 的代理对象,并传递给它 'bankAccount' 对象和拦截器对象
get: function (target, propertr) { // 拦截获取值操作,以便在访问属性时检查余额是否充足
if (target[propertr] > 0) { // 如果需要的货币余额(即 RMB 或 dollar)大于零,则返回余额
return target[propertr];
}
else { // 如果所需余额为零或负数,则返回以下错误消息
return "余额不足"
}
},
set: function (target, propertr, value) { // 拦截设置值操作,以便在设置属性时进行检查
if (!Number.isInteger(value)) { // 如果设置的值不是整数则返回以下错误消息。
return "请设置正确的数值"
}
target[propertr] = value; // 否则更新相应的属性并返回。
}
});
console.log(banker.RMB); // 在控制台打印出银行账户中 'RMB' 货币的当前余额。由于 'RMB' 初始值为 1000 ,因此输出 "1000"
console.log(banker.dollar); // 非法获取,不符合条件 "if (target[propertr] > 0)",会在拦截器中返回 "余额不足"。
banker.dollar = "五百"; // 在代理对象 'banker' 中的 'dollar' 属性上设置字符串值 "五百"。
console.log(banker.dollar); // 检索代理对象中的 'dollar' 属性,但由于 "五百" 不是整数,设置操作会失败,因此控制台输出了错误消息 "请设置正确的数值"。
banker.dollar = "500"; // 在代理对象 'banker' 中的 'dollar' 属性上设置整数值 500。
console.log(banker.dollar); // 再次检索代理对象中的 'dollar' 属性来查看其是否成功设置,由于已经将 'dollar' 的值更改为 500,因此控制台打印出 "500"。
// (3)、ownKeys方法
// ownKeys拦截操作,拦截过滤Object.keys()对对象的属性遍历。
let person1 = { "name": "老王", "age": 40, "height": 1.8 }; // 创建一个名为 `person1` 的普通对象,表示一个人的信息。该对象有三个属性,分别是 `name`、`age` 和 `height`。
let proxy = new Proxy(person1, { // 创建一个名为 `proxy` 的代理对象,并传入 `person1` 对象和代理器函数。
ownKeys: function (target) { // 这里的代理器函数主要用于拦截内部方法 `Object.keys()` 调用。
return ["name", 'age']; // 当使用 `Object.keys()` 方法时,将返回一个代表新的可枚举属性名称的数组(只包括 name 和 age)。
}
});
console.log(Object.keys(person1)); // 输出 ['name', 'age', 'height'],因为这是属性全部可枚举的 'person1' 对象本身的方法 Object.keys(),返回该对象所有可枚举自有属性的名称,会列出所有对象属性(包括 height)
console.log(Object.keys(proxy)); // 输出 ['name', 'age'],因为这里使用的时代理对象 `proxy`,经过了代理函数过滤掉了除 name 和 age 以外其他自有属性
// (4)、has方法
// has()拦截操作:拦截key in object的操作,结果会返回一个布尔值。
var person2 = { // 创建一个名为 `person2` 的普通对象,表示一个人的信息。该对象有两个属性,分别是 `name` 和 `age`。
"name": "张三",
"age": 20
};
var proxy2 = new Proxy(person2, { // 创建一个名为 `proxy2` 的代理对象,并传入 `person2` 对象和拦截器函数。
has: function (target, prop) { // 这里的拦截器函数主要用于拦截内部方法 `in` 调用(例如 'prop in obj')。
if (target[prop] === undefined) { // 判断被查询的 `prop` 属性是否在 target 对象中存在。
return false; // 如果该属性不存在,则返回 false。
}
else {
return true // 如果该属性存在,则返回 true。
}
}
});
console.log("name" in proxy2); // 输出 true,意味着 'proxy2' 代理对象中的名为 'name' 的属性已经存在。
console.log("ahight" in proxy2); // 输出 false,意味着在 'proxy2' 代理对象中没有名为 'ahight' 的属性。
// (5)、apply方法
// 除了对象类型的变量可以被代理,函数也可以被代理。如果被代理的变量是一个函数,那么还会支持一个拦截程序:apply调用。
let fn = function () { // 定义一个函数 `fn`。
console.log("wangzhihe"); // 函数主要功能是输出字符串 `"wangzhihe"`。
};
let proxy3 = new Proxy(fn, { // 创建一个名为 `proxy3` 的代理对象,并传入 `fn` 函数和代理器函数。
apply: function () { // 这里的代理器函数主要用于拦截使用了函数调用符号 ( ) 的调用。
console.log("gebilaowang"); // 当执行函数时,将输出 "gebilaowang" 而不是原本的函数内容。
}
});
proxy3(); // 调用代理对象作为函数执行,将输出 "gebilaowang"。
// (6)、proxy.revocable方法
// 如果创建了代理之后又想取消代理的话,我们可以用Proxy.revocable()(可废止的,可撤回的;)函数来实现,它会返回一个对象,对象中含有一个proxy属性,它就是Proxy的代理实例对象;还有一个revoke属性,它是一个方法,用于取消代理。
let person3 = { "name": "zhangsan" }; // 创建一个名为 `person3` 的普通对象,并拥有一个名为 `name` 的属性(值为 `"zhangsan"`)。
let handle = { // 创建一个代理器对象 `handle`,用于重写代理对象的默认行为。
get: function (target, prop) { // 这里的拦截器函数主要用于捕获对代理对象的读取操作,例如 `proxy.name`。
return "lisi"; // 当读取代理对象中的属性时,始终返回字符串 `"lisi"` 而不是原本的属性值。
}
};
let object = Proxy.revocable(person3, handle); // 使用 `Proxy.revocable()` 创建一个可撤销的代理对象 `object`,并传入普通对象和代理器对象。
console.log(object.proxy.name); // 输出 "lisi",因为已经被代理器所拦截,始终返回 `"lisi"` 而不是原本的属性值。
object.revoke(); // 撤销代理对象
//报错 console.log(object.proxy.name); // 抛出 TypeError 异常,因为代理对象已经被撤销,无法再访问其成员。
// 2.for…of使用
// (1)、for…of使用
// for...of 一种用于遍历数据结构的方法。它可遍历的对象包括数组,字符串,set和map结构等具有iterator 接口的数据结构。
// 方式一利用for循环来遍历数组的缺点就是:代码不够简洁。
// var arr = [1, 2, 3, 4, 5,]
// for (let i = 0; i < arr.length; i++) {}
// 方式二:利用forEach循环代码量少了很多,写法更加简洁,缺点就是:无法中断停止整个循环。
// var arr = [1, 2, 3, 4, 5,]
// arr.forEach(function (value, index) { })
// 方式三: for...in循环更常用于对象的循环,如果用于数组的循环,那么就要注意了,上述代码中每次循环中得到的i是字符串类型,而不是预料中的数字类型,要想对它进行运算,那得先要进行类型转换,造成不方便。
// var arr = [1, 2, 3, 4, 5]
// for (let i in arr) { }
// for...of的优势:
// 写法比for循环简洁很多;
// 可以用break来终止整个循环,或者continute来跳出当前循环,继续后面的循环;
// 结合keys()获取到循环的索引,并且是数字类型,而不是字符串类型。
// var arr = [1, 2, 3, 4, 5]
// for (let value of arr) {
// console.log(value);
// }
// (2)、循环可以终止
// var arr = [1, 2, 3, 4, 5]
// for (let value of arr) {
// if (value == 3) {
// break
// //continue
// }
// console.log(value);
// }
// (3)、得到数字类型索引
// var arr = [1, 2, 3, 4, 5]
// for (let index of arr.keys()) {
// console.log(index);
// }
// (4)、遍历字符串 for...of 支持字符串的遍历。
// let word = "王致和"
// for (let w of words) {
// console.log(w);
// }
// (5)、遍历DOM list for...of支持类数组的遍历,例如DOM List。
let liList = document.querySelectorAll('li')
for (let li of liList) {
console.log(li);
}
// 3.Iterator遍历器
// (1)、for…of为什么不遍历object对象
console.log(Array.prototype[Symbol.iterator]);//ƒ values() { [native code] }
console.log(String.prototype[Symbol.iterator]);//ƒ [Symbol.iterator]() { [native code] }
console.log(Set.prototype[Symbol.iterator]);//ƒ values() { [native code] }
console.log(Map.prototype[Symbol.iterator]);//ƒ entries() { [native code] }
console.log(Object.prototype[Symbol.iterator]);//ndefined
// (2)、Iterator原理
// 当可遍历对象被for...of遍历的时候,[Symbol.iterator]()就会被调用,返回一个iterator对象。其中还有一个很重要的方法:next();
let arr = ["a", "b", "c"]
let iter = arr[Symbol.iterator]()
console.log(iter.next());//{value: 'a', done: false}
console.log(iter.next());//{value: 'b', done: false}
console.log(iter.next());//{value: 'c', done: false}
console.log(iter.next());//{value: undefined, done: true}
// (3)、自定义Iterator遍历器
let obj = { // 定义一个名为 `obj` 的普通对象,并拥有两个属性,分别是 `name` 和 `age`。
name: "张三",
age: 19
};
// 为 `obj` 对象定义迭代器
obj[Symbol.iterator] = function () {
let index = 0; // 初始化迭代器索引为 0
let keys = Object.keys(this); // 获取普通对象中所有可枚举的属性名称的数组
// 返回一个包含 next() 方法的迭代器对象
return {
next: () => { // next() 方法返回一个具有 value 和 done 两个属性的对象,表明目前迭代到的值和是否已经迭代完成
let value = {
key: keys[index], // 当前迭代器索引代表的属性名称
value: this[keys[index]] // 当前属性所对应的值
}
let done = index === keys.length; // 标志是否已经迭代完了所有属性(即当前索引是否超过了属性数量)
index++; // 更新迭代器索引
return {
value,
done
}
}
}
}
// 使用 for...of 循环遍历 `obj` 对象的每一个成员,并输出其 key 和 value
for (let item of obj) {
console.log(item);
}
// 4.Generator函数
// (1)、声明Generator函数
// Generator函数被调用后得到的生成器shi一个遍历器iterator,用于遍历函数内部的状态。
// Generator函数,又称生成器函数,是ES6的一个重要的新特性
function* Hello(name) {
yield `hello ${name}`
yield `how are you`
yield `bye`
}
// (2)、调用Generator函数
let ite = Hello('王老吉')
console.log(ite.next());//{value: 'hello 王老吉', done: false}
console.log(ite.next());//{value: 'how are you', done: false}
console.log(ite.next());//{value: 'bye', done: false}
console.log(ite.next());//{value: undefined, done: true}
// (3)、Generator函数的行为
// 通过上面的案例,我们知道了:Generator函数被调用后并不会一直执行到最后,它是先回返回一个生成器对象,然后hold住不动,等到生成器对象的next()方法被调用后,函数才会继续执行,直到遇到关键字yield后,又会停止执行,并返回一个Object对象,然后继续等待,直到next()再一次被调用的时候,才会继续接着往下执行,直到done的值为true
// (4)、yield语句的使用
// 而yield在这里起到了十分重要的作用,就相当于暂停执行并且返回信息。有点像传统函数的return的作用,但不同的是普通函数只能return一次,但是Generator函数可以有很多个yield。而return代表的是终止执行,yield代表的是暂停执行,后续通过调用生成器的next()方法,可以恢复执行。
// (5)、next方法接收参数
function* Helloo() {
let res = yield `hello`;
yield res;
}
let iterator = Helloo();
console.log(iterator.next());
console.log(iterator.next("沙琪玛"));
// (6)、关键字‘yield *’
// 在一个Generator函数里面,如果我们想调用另一个Generator函数,就需要用到的关键字是:yield *。
function* gen1() {
yield "gen1 start";
yield "gen1 end";
}
//声明Generator函数:gen2
function* gen2() {
yield "gen2 start";
yield "gen2 end";
}
//声明Generator函数:start
function* start() {
yield "start";
yield* gen1();
yield* gen2();
yield "end";
}
//调用start函数
var itee = start();
//创建一个生成器
console.log(itee.next());//{value: "start", done: false}
console.log(itee.next());//{value: "gen1 start", done: false}
console.log(itee.next());//{value: "gen1 end", done: false}
console.log(itee.next()); //{value: "gen2 start", done: false}
console.log(itee.next());//{value: "gen2 end", done: false}
console.log(itee.next());//{value: "end", done: false}
// (7)、Generator函数的用途
// 以上就是对Generator函数的讲解介绍,它是ES6的一个很重要的新特性。它可以控制函数的内部状态,依次遍历每个状态;可以根据需要,轻松地让函数暂停执行或者继续执行。
// 根据这个特点,我们可以利用Generator函数来实现异步操作的效果。
// 原理是:利用Generator函数暂停执行的作用,可以将异步操作的语句写到yield后面,通过执行next方法进行回调。
// 5.类基本用法
// (1)、类的属性和方法
// 声明一个类的写法
// class Animal {
// constructor(color) {
// this.color = color;
// }
// }
// (2)、类的实例化对象
// class Animal {
// constructor(name) {
// this.name = name;
// }
// getName() {
// return this.name
// }
// }
// (3)、类的自定义方法
class Animal { // 定义 Animal 类
constructor(name) { // 定义构造函数,接收一个参数 name 作为动物的名称
this.name = name; // 将传入的 name 参数保存到该实例的属性中
}
getName() { // 定义方法,用于获取实例对象的名称
return 'This is a' + this.name // 返回一个字符串文本,表示该实例的名称(在字符串前添加 "This is a")。
}
}
let dog = new Animal('dog'); // 创建一个名为 dog 的 Animal 实例对象,并将其名称设定为 'dog'
console.log(dog.name); // 打印 dog 实例对象的 name 属性值 'dog'
console.log(dog.getName()); // 调用实例对象 dog 上定义的 getName() 方法,返回字符串 This is a dog,并将其打印到控制台上。
// (4)、类的静态方法
// 如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”
class Father { // 定义 Father 类
static testMethod() { // 在 Father 类上定义一个静态方法 testMethod()
return 'hello'; // 该静态方法返回字符串 'hello'
}
}
Father.testMethod(); // 调用Father类的静态方法testMethod()
// 代码功能:定义 Father 类,并在 Father 类上定义静态方法 testMethod(),最后调用该静态方法返回字符串'hello'
// (5)、类的继承 ES6使用extends关键字来实现子类继承父类
// class Animali { }
// class dogg extends Animali {
// constructor(name, color) {
// super(name)
// this.color = color
// }
// }
class Animali { // 定义 Animali 类
constructor(name) { // 构造函数,接收一个参数 name 用于设定 Animali 实例的名称
this.name = name // 将传入的 name 参数保存到该实例对象的属性中
}
say() { //定义方法 say() ,返回该实例对象的类型(字符串文本 This is a animal)
return `This is a animal`
}
}
class Dog extends Animali { //定义 Dog 狗类,并继承 Animali 父类
constructor(name, color) { //构造函数,接收两个参数——name 用于设定 Dog 实例的名称,color 用于设定狗的颜色。
super(name) //在子类的构造函数中调用父类的构造函数并传入相应的参数。
this.color = color //将传入的 color 参数保存到该实例对象的属性中
}
getAttritube() { //定义方法 getAttritube() ,用于获取该实例对象的属性值和父类的类型信息
return `${super.say()},
name:${this.name},
color:${this.color}` // 返回一个字符串文本,包含父类中该实例对象的类型信息(通过 super 调用父类方法 say()),以及该实例对象的名称(属性 name)和颜色(属性 color)
}
}
let d = new Dog("dog", "black") // 创建一个名为“dog”、颜色为“black”的 Dog 实例对象 d
console.log(d.getAttritube()); // 调用实例对象的 getAttritube() 方法,返回一个字符串文本,包含父类中该实例对象的类型信息(通过 super 调用父类方法 say()),以及该实例对象的名称(属性 name)和颜色(属性 color),并将其打印到控制台上。
</script>
</html>