diff --git a/learn_demo/chapter1.js b/learn_demo/chapter1.js new file mode 100644 index 0000000..59bfa2c --- /dev/null +++ b/learn_demo/chapter1.js @@ -0,0 +1,10 @@ +let msg = `hello \n +world`; +console.log(msg) + + +let a +let b = null +let c = 10; +console.log(a + c) +console.log(b + c) \ No newline at end of file diff --git a/learn_demo/chapter10.js b/learn_demo/chapter10.js new file mode 100644 index 0000000..1e58bec --- /dev/null +++ b/learn_demo/chapter10.js @@ -0,0 +1,22 @@ +//事件 +//js可以监听到用户对html做的各种事件,并基于这些事件做出事件处理 + +//常见的事件如下: +//1. 鼠标事件: +//onclick 一旦点击元素,触发事件 +//onmouseover 鼠标悬浮到元素上 触发事件 +//onmouseout 鼠标移出元素 触发事件 +//onmousedown 鼠标按钮对元素长按 触发事件 +//onmouseup 鼠标按钮对元素取消长按 触发事件 +//onmousemove 鼠标移动 触发事件 + +//2. form表单 +//onfocus 用户聚焦某个元素时 触发事件 +//onSubmit 用户提交表单时 触发事件 +//onblur 焦点原理表单时 触发事件 +//onchange 当用户修改或更改表单元素的值时 触发事件 + +//window/Document 事件 +//onload 当浏览器完成页面加载时触发事件 +//onunload 当访问者离开网页,浏览器将其卸载时,触发事件 +//onresize 当浏览器窗口被调整时 触发事件 \ No newline at end of file diff --git a/learn_demo/chapter10_1.html b/learn_demo/chapter10_1.html new file mode 100644 index 0000000..2aeb261 --- /dev/null +++ b/learn_demo/chapter10_1.html @@ -0,0 +1,25 @@ + + + + + 事件处理测试 + + + +

click example

+ + + + + + \ No newline at end of file diff --git a/learn_demo/chapter10_2.html b/learn_demo/chapter10_2.html new file mode 100644 index 0000000..398329f --- /dev/null +++ b/learn_demo/chapter10_2.html @@ -0,0 +1,30 @@ + + + + + 事件委托 + + + +
+ + + + +
+ + + + + \ No newline at end of file diff --git a/learn_demo/chapter10_3.html b/learn_demo/chapter10_3.html new file mode 100644 index 0000000..a90b83c --- /dev/null +++ b/learn_demo/chapter10_3.html @@ -0,0 +1,52 @@ + + + + + 事件捕获与事件冒泡 + +
+
+ +
+
+ + + + + + \ No newline at end of file diff --git a/learn_demo/chapter10_4.html b/learn_demo/chapter10_4.html new file mode 100644 index 0000000..02f4fa2 --- /dev/null +++ b/learn_demo/chapter10_4.html @@ -0,0 +1,27 @@ + + + + + form submi事件 + + + +
+
+ + +
+
+ + + + \ No newline at end of file diff --git a/learn_demo/chapter11_1.js b/learn_demo/chapter11_1.js new file mode 100644 index 0000000..202c9f3 --- /dev/null +++ b/learn_demo/chapter11_1.js @@ -0,0 +1,122 @@ +//ES6中新增class 且在class中新增constructor构造方法 + +class Person { + constructor(name, age) { + this.name = name + this.age = age + } +} +let tom = new Person("Tom", 12) +console.log(JSON.stringify(tom)) + +let { name, age } = tom; +console.log(name, age) + +//clone对象 +//浅clone +//1. 扩展运算符 +let address = { + city: "LA", + country: "USA" +} +tom.address = address; +let marry = { ...tom } +marry.name = "Marry" +marry.age = 16 +marry.address.city = "NewYork" +console.log(JSON.stringify(marry)) +console.log(JSON.stringify(tom)) +//2. Object.assign() +let perter = Object.assign({}, tom) +perter.name = "Peter" +perter.address.city = "WT" +console.log(JSON.stringify(tom)) +console.log(JSON.stringify(perter)) + +//其中Object.assign功能更强大 Object.assign(target, ...sources) + +const target = { a: 1, b: 2 }; +const source = { b: 3, c: 4 }; + +const returnedTarget = Object.assign(target, source); + +console.log(target); // { a: 1, b: 3, c: 5 } +console.log(returnedTarget); // { a: 1, b: 3, c: 5 } + +//深拷贝 +let john = JSON.parse(JSON.stringify(tom)) +john.name = "John" +john.address.city = "BeiJing" +john.address.country = "China" +console.log(JSON.stringify(tom)) +console.log(JSON.stringify(john)) + +//深拷贝的实现 + +function deepClone(obj) { + let cloneObj = {} + for (const property in obj) { + //深层对象,递归拷贝 + if (typeof obj[property] === "object" && obj[property] != null) { + cloneObj[property] = deepClone(obj[property]) + } else { + cloneObj[property] = obj[property] + } + } + return cloneObj +} +let jeffy = deepClone(tom) +jeffy.name = "jeffy" +jeffy.address.country = "France" +console.log(JSON.stringify(tom)) +console.log(JSON.stringify(jeffy)) + + +//map与object +let myPerson = { + name: "javascript", + age: 20 +} + +//对象遍历 +console.log("============") +for (const property in myPerson) { + if (myPerson.hasOwnProperty(property)) { + console.log(property, myPerson[property]) + } +} +for (const [key, value] of Object.entries(myPerson)) { + console.log(`${key}:${value}`) +} + +for (const value of Object.values(myPerson)) { + console.log(value) +} +for (const key of Object.keys(myPerson)) { + console.log(key) +} + +console.log("xxxxxxxxxxxxxxxxxxxxx") +let myMap = new Map() +//js中map通过get和set +myMap.set("name", "javascript") +myMap.set("age", 20) + +//这里用了解构来遍历key value +for (const [key, value] of myMap) { + console.log(key, value) +} +//只遍历key +for (const key of myMap.keys()) { + console.log(key) +} +//只遍历value +for (const value of myMap.values()) { + console.log(value) +} + + + + + + diff --git a/learn_demo/chapter11_2.js b/learn_demo/chapter11_2.js new file mode 100644 index 0000000..0395ec5 --- /dev/null +++ b/learn_demo/chapter11_2.js @@ -0,0 +1,26 @@ +//对象代理 有些类似于aop或者继承对象 然后重写父类方法 总之就是对原对象的代理增强 +class Person { + constructor(name, age) { + this.name = name + this.age = age + } +} +let tom = new Person("tom", 12) + +let proxyHandler = { + //重写原来的get + get(target, property) { + console.log("this is proxy obj get") + return target[property] + } +} + +let proxyTom = new Proxy(tom, proxyHandler) +console.log(proxyTom.name) + +//js中反射 +Person.prototype.sayHello = function (arg) { + console.log("hello i am ", this.name, arg) +} +tom.sayHello("nice to meet you") +Reflect.apply(tom.sayHello, tom, ["nice to meet you too"]) \ No newline at end of file diff --git a/learn_demo/chapter12_1.html b/learn_demo/chapter12_1.html new file mode 100644 index 0000000..cc93af4 --- /dev/null +++ b/learn_demo/chapter12_1.html @@ -0,0 +1,67 @@ + + + + + ducoment测试 + + + + + + +
+
+
+

+

+
+
+ +
+ + + + \ No newline at end of file diff --git a/learn_demo/chapter12_1.js b/learn_demo/chapter12_1.js new file mode 100644 index 0000000..9bb4d9f --- /dev/null +++ b/learn_demo/chapter12_1.js @@ -0,0 +1,2 @@ +//js中的window与document对象 +//详见https://github.com/coderZoe/javascript-basics 12.1节 diff --git a/learn_demo/chapter12_2.html b/learn_demo/chapter12_2.html new file mode 100644 index 0000000..4b8521b --- /dev/null +++ b/learn_demo/chapter12_2.html @@ -0,0 +1,12 @@ + + + + + 浏览记录测试 + + + +
+ + + \ No newline at end of file diff --git a/learn_demo/chapter6.js b/learn_demo/chapter6.js new file mode 100644 index 0000000..5c4c6b5 --- /dev/null +++ b/learn_demo/chapter6.js @@ -0,0 +1,53 @@ +//字符串数组操作 + +//============= 1. slice与splice ===============// + +//slice与splice的区别 用于字符串处理 +//首先slice与splice最大的区别是slice不改变源数组,操作后会返回一个新的数组 splice是直接修改源数组 + +let languages = ["JavaScript","Python","Java","PHP","Golang"] +let slice1 = languages.slice(1,3) //Python Java +console.log(slice1) + +//splice用于插入/删除/替换元素 比较复杂 +//首先函数接收1-n个参数,splice(index,count,item1,....,itemn) +//第一个参数index是从何除开始操作(删除/添加) +//第二个参数是要删除几个元素,从index开始从后数,要删除几个参数,count可以为0,此时代表删除0个元素 +//后面的第3-n个元素代表要插入的元素,从index开始向后插入 +//举几个常见案例 +//1. 删除 +let languages2 = ["JavaScript","Python","Java","PHP","Golang"] +languages2.splice(1,1) +console.log(languages2) //JavaScript Java PHP Golang + +//2. 替换 +let languages3 = ["JavaScript","Python","Java","PHP","Golang"] +languages3.splice(1,1,"Rust") +console.log(languages3) //JavaScript Rust Java PHP Golang + +//3. 插入 +let languages4 = ["JavaScript","Python","Java","PHP","Golang"] +languages4.splice(1,0,"Rust") +console.log(languages4) //JavaScript Rust Python Java PHP Golang + + +//================2. 字串包含 ==============// +//1. 使用正则匹配,这里不再演示 + +//2. 使用indexOf +let str1 = "hello world" +let subStr1 = "world" +let isSub = str1.indexOf(subStr1) != -1 +console.log(isSub) + +//3. 使用includes +let str2 = "hello world" +let subStr2 = "wor"; +console.log(str2.includes(subStr2)) + +//=========3. 检测是否以XX开头或者结尾 ========= +let str3 = "hello world" +let startStr = "hello" +let endStr = "world" +console.log(str3.startsWith(startStr)) +console.log(str3.endsWith(endStr)) \ No newline at end of file diff --git a/learn_demo/chapter7.js b/learn_demo/chapter7.js new file mode 100644 index 0000000..74c8bb5 --- /dev/null +++ b/learn_demo/chapter7.js @@ -0,0 +1,205 @@ +//数组 + +//=======1. 数组添加删除元素=====// + + +//1. slice与splice 见chapter6 + +//2. pop和push +let fruits = ["Apple", "Orange", "Banana"] +fruits.push("Pear") +console.log(fruits) +fruits.pop() +console.log(fruits) + +//3. shift和unshift +//shift删除头元素 +fruits.shift() +console.log(fruits) +//unshift插入头元素 +fruits.unshift("Apple", "Cherry") +console.log(fruits) +//4. concat 连接两个数组 不会修改原数组,而是会返回一个新数组 +let doc = [".docs", ".xlsx", ".md"] +let array1 = doc.concat(fruits) +console.log(array1) + + +//5. map传入一个方法,方法会返回元素,源数组里的每个元素执行方法,然后返回的元素收集起来创建一个新数组 类似于Java流中的map +let sfruits = fruits.map(element => "s" + element) +console.log(sfruits) + +//6. filter 与Java流中的filter功能类似 +let filterFruits = fruits.filter(element => element.startsWith("A")) +console.log(filterFruits) + +//7. reduce reduce每个元素迭代执行,然后返回一个最终结果 +let reduceResult = fruits.reduce((result, element) => result + element) //AppleCherryOrangeBanana +console.log(reduceResult) + +//8. reduceRight 与reduce基本一样,但是倒着 从右向左使用 +let reduceRightResult = fruits.reduceRight((result, element) => result + element) //BananaOrangeCherryApple +console.log(reduceRightResult) + +//9. every 测试方法中的每个元素是否都通过了测试,返回bool,通过filter也可以实现,filter过滤结果与原数组一样就是every通过 +let everyResult = fruits.every(element => element.length > 2) //true +console.log(everyResult) +//与下面等效果 +let everyFilterResult = fruits.filter(element => element.length > 2).length == fruits.length +console.log(everyFilterResult) + +//10. some 有一个通过就行 +let someResult = fruits.some(element => element.startsWith("App")) +console.log(someResult) +//与下面等效 +let someFilterResult = fruits.filter(element => element.startsWith("App")).length > 0 +console.log(someFilterResult) + +//11. find 找到第一个满足条件的元素并返回 +let findResult = fruits.find(element => element.length > 5) +console.log(findResult) + + +//12. 排序 sort +let unsortArray = [1, 4, 2, 7, 3, 8, 5] +let sortArray = unsortArray.sort((a, b) => a - b) +console.log(sortArray) //[1, 2, 3, 4, 5, 7, 8] + +//===========2. 数组遍历 ===============// +for (let i = 0; i < fruits.length; i++) { + console.log(fruits[i]) +} + +//for of用于遍历数组 for of迭代的是值 for in迭代的是key +for (let i of fruits) { + console.log(i) +} +//或者直接调用for-each方法,这应该是数组原型链支持的方法,方法支持多个参数 +fruits.forEach((element, index) => { + console.log(element, index) +}); + + + +//3. ============ rest与spread ================= // +//rest与spread的语法都是剩余运算符... 但rest用于将多个参数合并为一个数组,而spread用于将一个数组/对象展开为单个元素,举例如下: + +//rest用于将多个元素合成一个数组,下例中就是将1,2,3,4,5元素合成数组element +function sum(start, ...element) { + return start + element.reduce((result, item) => result + item) +} + +console.log(sum(0, 1, 2, 3, 4, 5)) //15 + + +//spread用于将数组/对象拆为单个元素,比如下例中将数组sumItem自动拆为x,y,z +function sum2(x, y, z) { + return x + y + z +} +let sumItem = [1, 2, 3] +console.log(sum(...sumItem)) + +let spreadArray1 = [3, 4, 5] +let spreadArray2 = [1, 2, ...spreadArray1, 6, 7] +console.log(spreadArray2) + +let stu1 = { + age: 1, + name: "Tom", + sex: "Man" +} +let stu2 = { + ...stu1 +} +console.log(JSON.stringify(stu2)) + + +//=============4. 解构 ============// +//js中的解构主要用于从数组或对象中解析出元素 +//数组解构如下: +let animals = ["mouse", "cat", "dog"] +let [a, b, c] = animals; +console.log(a, b, c) //mouse cat dog +let [d, , e] = animals; +console.log(d, e) //mouse dog + +//对象解构如下: +let person = { + name: "Tom", + age: 18, + sex: "Man" +} +let { name, age, sex } = person; +console.log(name, age, sex) //Tom 18 Man + +//指定属性映射 +let { name: personName, age: PersonAge } = person +console.log(personName, PersonAge) //Tom 18 + +//方法参数是解构 +function consolePerson({ name, age }) { + console.log(name, age) +} +consolePerson(person) //Tom 18 + +function consoleAnimal([a, b]) { + console.log(a, b) +} +consoleAnimal(animals) //mouse cat + +//替换 +let num1 = 10; +let num2 = 20; +[num1, num2] = [num2, num1] +console.log(num1, num2) //20 10 + +//默认值 +let animals2 = ["Lion", "Tigger"] +let [a2 = "a2", b2 = "b2", c2 = "c2"] = animals2 +console.log(a2, b2, c2) //Lion Tigger c2 + +let person2 = { + name: "Marry", + sex: "Woman" +} +let { name: Person2Name = "Test", age: Person2Age = -1, sex: Person2Sex = "Test" } = person2 +console.log(Person2Name, Person2Age, Person2Sex) //Marry -1 Woman + +function consolePerson2({ name, age = 10 }) { + console.log(name, age) +} +consolePerson2(person) //Tom 18 +consolePerson2(person2) //Marry 10 + + + +//================== 5.JSON ==================// +const personData = { + "personsJsonArray": [ + { "name": "Tom", "age": 18 }, + { "name": "Marry", "age": 16 }, + { "name": "John", "age": 28 }, + { "name": "Peter", "age": 38 } + ] +} +console.log(personData.personsJsonArray[0].name) + +//parse与stringify +const personObj = [ + { + name: "Tom", + age: 18 + }, + { + name: "Marry", + age: 28 + }, + { + name: "John", + age: 38 + } +] +console.log(JSON.stringify(personObj)) //[{"name":"Tom","age":18},{"name":"Marry","age":28},{"name":"John","age":38}] + +let personJson = '[{"name":"Tom","age":18},{"name":"Marry","age":28},{"name":"John","age":38}]'; +console.log(JSON.parse(personJson)[1].name) //Marry diff --git a/learn_demo/chapter9_1.js b/learn_demo/chapter9_1.js new file mode 100644 index 0000000..9d126d6 --- /dev/null +++ b/learn_demo/chapter9_1.js @@ -0,0 +1,121 @@ +// 箭头函数 +let consoleHello = () => { + console.log("Hello") +} +consoleHello() + +//其等价于 +function consoleHello2() { + console.log("Hello") +} +consoleHello2() +//或者 +let consoleHello3 = function () { + console.log("Hello") +} +consoleHello3() + +//单参数 +let consoleNum = x => console.log(x) +consoleNum(1) + +//多参数 +let consoleNumSum = (x, y) => console.log(x + y) +consoleNumSum(1, 2) //3 + +//箭头方法使用的注意场景: +//=======1. 应该使用的场景==========// +//1. 无需this的 箭头函数与普通函数最大的区别是箭头函数没有this上下文,而普通函数有this,其中普通函数的this指向调用时的环境 举例如下: + +const person = { + name: "Tom", + say: function () { + console.log(this.name) + } +} +//由于say方法是普通函数,且调用它的环境是person,因此这里的this.name就是person.name +person.say() //Tom +//但如果要是 +const person2 = { + name: "Tom", + say: () => { + console.log(this.name) + } +} +//此时say是箭头函数,没有上下文,那它的上下文就是全局对象,因此不存在name属性 +person2.say() //undefined + +//2. 回调函数中 +const unsortedArray = [4, 7, 2, 6] +const sortedArray = unsortedArray.sort((a, b) => a - b) +console.log(sortedArray) //[2,4,6,7] +//或者 +const person3 = { + name: "Tom", + say: function () { + setTimeout(() => { + console.log(this.name) + }, 0) + } +} +//say本身是普通函数,因此具有this是person3,say里的回调函数() =>{console.log(this.name)} 本身无this,因此继承say的this,也是person3,因此可以找到name +person3.say() //Tom +//但如果是下面这样就有问题:因为person4CallBack是普通方法,他有this,它的this其实是全局对象 +function person4CallBack() { + console.log(this.name) +} + +const person4 = { + name: "Tom", + say: function () { + setTimeout(person4CallBack, 0) + } +} +person4.say() //undefined + +//3.构造方法不能用箭头函数,因为无法继承this +const Person5 = (name) => { + this.name = name +} +// const john = new Person5("John") //会报错 + +const Person6 = function (name) { + this.name = name +} +const marry = new Person6("Marry") +console.log(marry.name) //Marry + +//4. arguments js中的arguments代表所有入参,是个数组,但箭头函数没有argumenst +function argumentsTest1(a,b){ + console.log(arguments[0]) +} +argumentsTest1(1,2) //1 + +let argumentsTest2 = (a,b) =>{ + console.log(arguments[0]) +} +argumentsTest2(1,2) //undefined 这里是由于运行环境是nodejs环境,全局变量有arguments,但不是入参参数上那个,如果是浏览器环境则报错 + +//5. 添加原型链方法也不应该使用箭头函数,如: +function PersonPrototype(name ,age){ + this.name = name + this.age = age +} +PersonPrototype.prototype.say = () =>{ + console.log("say",this.name) +} +new PersonPrototype("Tom",18).say() //say undefined + +PersonPrototype.prototype.say2 = function(){ + console.log("say2",this.name) +} +new PersonPrototype("Tom",18).say2() //say2 Tom + +//6. 事件处理器 +document.getElementById('myButton').addEventListener('click', function() { + console.log(this); // 这里的 `this` 指向触发事件的元素,即 'myButton' + }) +document.getElementById('myButton').addEventListener('click', () => { + console.log(this); // 这里的 `this` 不再指向触发事件的元素,而是指向定义事件处理器时的上下文 例如,如果在全局作用域中定义,则this可能是全局对象,如window) + }) + diff --git a/learn_demo/chapter9_2.js b/learn_demo/chapter9_2.js new file mode 100644 index 0000000..927dfcc --- /dev/null +++ b/learn_demo/chapter9_2.js @@ -0,0 +1,80 @@ +//函数的属性 + +//1. arguments 函数的所有参数 +function sum(...elements){ + let count = 0; + //arguments不是严格意义上的数组,因此不能使用数组的reduce map filter等方法 + for(arg of arguments){ + count+=arg + } + return count; +} +console.log(sum(1,2,3)) //6 +//2. arguments.callee 返回正在执行的函数 +function fa(){ + console.log("fa") + return arguments.callee; +} +function fb(){ + console.log("fb") + return arguments.callee; +} +//返回方法本身,再使用()调用方法 +fa()()() +fb()()() + +//3. arguments.length 参数长度 +//4. constructor 构造函数 +//5. prototype 原型 + +//我们需要回顾一下构造方法: +function Person(name,age){ + this.name = name; + this.age = age; +} +let tom = new Person("tom",18) +console.log(JSON.stringify(tom)) +//上面我们使用了new关键字,但new的实现其实如下: +//其核心思想其实就是原型链,Object.create()方法需要传入一个原型,而每个方法都是有原型属性的 +function _new(constructor,...params){ + let obj = Object.create(constructor.prototype) + let result = constructor.apply(obj,params) + return (typeof result === 'object' && result != null) ? result : obj; +} + +let marry = _new(Person,"Marry",20) +console.log(JSON.stringify(marry)) + +//关于原型链,这里解释一下 在js中每个函数都有个原型属性 +//当这个函数是构造函数的时候,实际上new的时候就如上,let obj = Object.create(constructor.prototype) +//造出来的对象的原型就是构造函数的原型,所以函数原型其实往往只有函数是构造函数时有意义 + +//最后这里再补充下call apply和bind的区别 +//首先call和apply功能基本相似,它们都是允许你指令函数运行时的上下文(this),区别是call是正常传入参数,但apply的参数用数组组合起来,举例如下: + +function sayHello(address,sex){ + console.log(this.name,this.age,address,sex) +} +let peter = { + name: "Peter", + age: 12 +} +let zoe = { + name: "Zoe", + age: 16 +} +sayHello.call(peter,"翻斗花园","男") +sayHello.call(zoe,"青青草原","女") +//apply是将参数放在了数组里 相对来说我还是更喜欢call +sayHello.apply(peter,["翻斗花园","男"]) +sayHello.apply(zoe,["青青草原","女"]) + +//bind其实是对上面的两步拆解,先绑定一个上下文this,返回返回一个新的函数,再可以调用这个函数,举例如下: +let peterSayHello = sayHello.bind(peter) +peterSayHello("翻斗花园","男") +let zoeSayHelllo = sayHello.bind(zoe) +zoeSayHelllo("青青草原","女") + + +//可以看到无论是bind还是call或者apply 它们的核心思想都是从别的对象上借一个方法用在自己的对象上 +//bind的好处是返回一个借过后的方法,方便后续利用,而不像call和apply是一次性的 diff --git a/learn_demo/chapter9_3.js b/learn_demo/chapter9_3.js new file mode 100644 index 0000000..035aa85 --- /dev/null +++ b/learn_demo/chapter9_3.js @@ -0,0 +1,27 @@ +//柯里化函数 +function sum(a,b,c){ + return a+b+c +} +console.log(sum(1,2,3)) + +//柯里化函数其实就是将上面的多个参数函数改为一个参数的函数,并返回一个新函数继续调用 +function curryingSum(a){ + return (b) =>{ + return (c) =>{ + return a+b+c + } + } +} +console.log(curryingSum(1)(2)(3)) + +//柯里化函数主要是针对 一些代码复用场景,比如: +let base3Sum = curryingSum(1)(2) +console.log(base3Sum(4)) +console.log(base3Sum(7)) + +//自执行函数 一般都是匿名函数,主要是就用这一次的函数 但js的自执行函数坑挺多的,比如需要()括住整体表达式,还要加; +;(function (a,b){ + console.log(a+b) +})(1,2) + + diff --git a/learn_demo/chapter9_4.js b/learn_demo/chapter9_4.js new file mode 100644 index 0000000..a70e71e --- /dev/null +++ b/learn_demo/chapter9_4.js @@ -0,0 +1,112 @@ +//聊一下JS中比较关键的promise + +function getPromsie(delay){ + return new Promise((resolve,reject) =>{ + setTimeout(() => { + resolve("ok") + }, delay); + }) +} + +getPromsie(5000).then((result) => console.log(result)) //5秒后打印ok + +//上面就是一个很典型的js中promise demo 由于笔者很熟悉netty中的future-promise源码,而js得实现与此基本类似,所以类比说下理解: +//首先netty中的promise是通过setSuccess或者setFail来设置结果的,我们知道结果一旦设置就代表promise的完成 +//而一旦promise完成,就需要做两件事:1. 唤醒由于promise阻塞的线程 2. 执行注册的监听器 +//其中netty promise中的setSuccess其实就是js中的resolve,一旦执行resolve就代表设置正常结果,而resolve得参数就是结果值 +//netty promise中得setFail 其实就是js中得reject,一旦执行reject就代表设置异常结果 +//netty中注册监听器是通过addListener,而js中通过.then注册正常返回时候得监听器,而通过.catch注册异常执行时得监听器 +//netty中通过get来同步阻塞等待获取promise得结果,而js中是通过await 其中js中的await要和async函数一起使用,这是一种异步阻塞 +async function promiseHandle(){ + try { + const result = await getPromsie(10000) //10s后返回ok + console.log(result) + } catch (error) { + //promise执行异常会到这里 + console.error(error) + } +} +promiseHandle() +console.log("hello") + + +//js中的生成器 +function* gen(){ + yield 1; + yield 2; + yield 3; +} +const generator = gen() +console.log(generator.next().value) +console.log(generator.next().value) +console.log(generator.next().value) + +//生成器其实具备一个功能:暂停和继续执行代码,可以看到yield会自动暂停代码的执行,当再次调用next的时候又会继续从之前暂停处执行代码 +//这有些类似于线程的挂起和唤醒 通过这个功能,其实我们可以实现上面说的async与await(其实js底层的async和await其实就是通过生成器实现的) +function* asyncGen(){ + const result = yield new Promise(resolve =>{ + setTimeout(() => { + resolve("asyncGen Promise Finish") + }, 3000); + }); + console.log(result) +} + +const asyncGenerator = asyncGen() + +function run(generator){ + let iteration = generator.next() + if(!iteration.done){ + let promise = iteration.value + promise.then(result =>{ + generator.next(result) + }) + } +} + +run(asyncGenerator) +//如上例所示: +//核心其实是如下几处: const result = yield new Promise(...) +//这里其实会先将yield右侧处的代码返回,也即如果调用asyncGen().next()会得到一个promise +//但注意的是,只会执行yield右侧的代码,左侧的const result = yield其实不会执行,下面的console.log(result)也不会执行 +//那什么时候执行呢?下一次再调asyncGen().next()的时候。 +//所以我们可以认为生成器其实具备暂停和恢复执行的能力,有些类似于其他编程语言中线程的挂起和唤醒 +//既然具备这个能力,那只需要在promise没执行完的时候挂起,promise执行完后再恢复执行即可 +//因此下一处核心其实是 +// let iteration = generator.next() +// if(!iteration.done){ +// let promise = iteration.value +// promise.then(result =>{ +// generator.next(result) +// }) +// } +//这里先拿到promise,如果promise没完成,就注册一个监听器,监听器执行的时间必然是promise完成的时间, +//此时在这个监听器里执行generator.next(result),由于生成器被调用next会继续执行代码,也即执行下面的 +//const result = yield... 和console.log(result) 需要注意的是,我们给next方法传入了一个参数 +//而这个参数其实就可以作为上一次yield的返回结果,我们知道这个result就是promise的结果,将promise的结果作为yield的返回 +//因此const result = yield ...其实result赋值的就是promise的结果 这就很像 const result = await promise + +//最后我们总结下: +//T1时刻,通过调用生成器.next()返回一个Promise,这个promise没完成,我们给这个Promise注册一个.then监听器, +//监听器的内容是:一旦Promise完成,拿到Promise的结果,并将结果赋值给生成器的返回,并触发生成器继续执行代码对应的是.next(result) +//T2 时刻 Promise完成,此时执行监听器代码,将Promise结果返回,并将结果设置为生成器yield的返回结果 此时const result = yield...被执行 +//然后result的结果就是Promise的结果,,再然后打印result + + + + + +//js中的闭包 +function closures(){ + let count = 0 + return function(){ + return ++count + } +} + +let adder = closures() +console.log("count ",adder()) +console.log("count ",adder()) +console.log("count ",adder()) +console.log("count ",adder()) +