JS基础 一、初识js 1. 书写位置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <head> <style > </style > <!-- 2. 内嵌式的js --> <script > </script > <!-- 3. 外部js script 双标签 --> <script src ="my.js" > </script > </head> <body > </body >
2. 注释 3. 输入输出语句 1 2 3 4 5 6 7 8 9 10 <script> // 这是一个输入框 prompt('请输入您的年龄'); // alert 弹出警示框 输出的 展示给用户的 alert('计算的结果是'); // console 控制台输出 给程序员测试用的 console.log('我是程序员能看到的'); // 在页面中显示 document.write("hello,word") </script>
二、变量 1. 数据类型 1.1 简单数据类型 Number, Null, Boolean, String, Undefined,Symbol ,Bigint 1 2 3 4 5 6 7 <script> var num = 10 var str = '10' var bool = false var und = undefined var nul = null </script>
1.2 复杂数据类型 2. 数据类型的转换 2.1 转字符串 1 2 3 4 5 6 7 8 9 var num = 10 ; var str = num.toString (); console .log (str); console .log (typeof str); console .log (String (num)); console .log (num + '' );
2.2 转数字型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // 1. parseInt(变量) 可以把 字符型的转换为数字型 得到是整数 // console.log(parseInt(age)); console.log(parseInt('3.14')); // 3 取整 console.log(parseInt('3.94')); // 3 取整 console.log(parseInt('120px')); // 120 会去到这个px单位 console.log(parseInt('rem120px')); // NaN // 2. parseFloat(变量) 可以把 字符型的转换为数字型 得到是小数 浮点数 console.log(parseFloat('3.14')); // 3.14 console.log(parseFloat('120px')); // 120 会去掉这个px单位 console.log(parseFloat('rem120px')); // NaN // 3. 利用 Number(变量) var str = '123'; console.log(Number(str)); console.log(Number('12')); // 4. 利用了算数运算 - * / 隐式转换 console.log('12' - 0); // 12 console.log('123' - '120'); console.log('123' * 1);
2.3 转布尔型 1 2 3 4 5 6 7 8 9 console .log (Boolean ('' )); console .log (Boolean (0 )); console .log (Boolean (NaN )); console .log (Boolean (null )); console .log (Boolean (undefined )); console .log ('------------------------------' ); console .log (Boolean ('123' )); console .log (Boolean ('你好吗' )); console .log (Boolean ('我很好' ));
3. 获取数据类型 typeof()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var num = 10 ; console .log (typeof num); var str = 'pink' ; console .log (typeof str); var flag = true ; console .log (typeof flag); var vari = undefined ; console .log (typeof vari); var timer = null ; console .log (typeof timer); var age = prompt ('请输入您的年龄' ); console .log (age); console .log (typeof age);
三、运算符 1. 算数运算符 +
-
*
/
%
1 2 3 4 5 6 7 8 9 10 11 12 13 14 console .log (1 + 1 ); console .log (1 - 1 ); console .log (1 * 1 ); console .log (1 / 1 ); console .log (4 % 2 ); console .log (5 % 3 ); console .log (3 % 5 ); console .log (0.1 + 0.2 ); console .log (0.07 * 100 ); var num = 0.1 + 0.2 ; console .log (num == 0.3 );
2. 比较运算符 > < == >= <= != === !==
1 2 3 4 5 6 7 8 9 10 11 console .log (3 >= 5 ); console .log (2 <= 4 ); console .log (3 == 5 ); console .log ('pink老师' == '刘德华' ); console .log (18 == 18 ); console .log (18 == '18' ); console .log (18 != 18 ); console .log (18 === 18 ); console .log (18 === '18' );
3. 逻辑运算符 && || !
1 2 3 4 5 6 7 8 console .log (3 > 5 && 3 > 2 ); console .log (3 < 5 && 3 > 2 ); console .log (3 > 5 || 3 > 2 ); console .log (3 > 5 || 3 < 2 ); console .log (!true );
4. 赋值运算符 = += -= *= /=
1 2 3 4 5 6 7 8 9 var num = 10 ; num += 5 ; console .log (num); var age = 2 ; age *= 3 ; console .log (age);
5. 一元运算符 ++ --
前置递增 ++num 先自加,再返回值 后置递增 num++ 先返回原值,后自加1 6. 优先级 !> 算数 > 关系 > 与、或 > 赋值
四、选择结构 1. if 1.1 if单分支 如果 if 里面的条件表达式结果为真 true 则执行大括号里面的 执行语句
如果if 条件表达式结果为假 则不执行大括号里面的语句 则执行if 语句后面的代码
1.2 if双分支 如果表达式结果为真 那么执行语句1 否则 执行语句2
1 2 3 4 5 if (条件表达式) { } else { }
1.3 if多分支 如果条件表达式1 不满足,则判断条件表达式2 满足的话,执行语句2 以此类推
如果上面的所有条件表达式都不成立,则执行else 里面的语句
1 2 3 4 5 6 7 8 9 if (条件表达式1 ) { } else if (条件表达式2 ) { } else if (条件表达式3 ) { } else { }
1.4 三元表达式 如果条件表达式结果为真 则 返回 表达式1 的值 如果条件表达式结果为假 则返回 表达式2 的值
1 2 3 var num = 10 ; var result = num > 5 ? '是的' : '不是的
2. switch 利用我们的表达式的值 和 case 后面的选项值相匹配 如果匹配上,就执行该case 里面的语句 如果都没有匹配上,那么执行 default里面的语句
1 2 3 4 5 6 7 8 9 10 11 switch (表达式) { case value1 : 执行语句1 ; break ; case value2 : 执行语句2 ; break ; ... default : 执行最后的语句; }
特点:
我们开发里面 表达式我们经常写成变量 我们num 的值 和 case 里面的值相匹配的时候是 全等 必须是值和数据类型一致才可以 num === 1 break 如果当前的case里面没有break 则不会退出switch 是继续执行下一个case 五、循环结构 1. for 1 2 3 4 5 6 for (var i = 1 ; i <= 100 ; i++) { console .log ('你好吗' ); }
2. while 当条件表达式结果为true 则执行循环体 否则 退出循环
1 2 3 4 5 6 7 8 var num = 1 ; while (num <= 100 ) { console .log ('好啊有' ); num++; }
3. do while 跟while不同的地方在于 do while 先执行一次循环体 在判断条件 如果条件表达式结果为真,则继续执行循环体,否则退出循环
1 2 3 4 5 6 7 8 var i = 1 ; do { console .log ('how are you?' ); i++; } while (i <= 100 )
4. continue continue 关键字 退出本次(当前次的循环) 继续执行剩余次数循环
1 2 3 4 5 6 for (var i = 1 ; i <= 5 ; i++) { if (i == 3 ) { continue ; } console .log ('我正在吃第' + i + '个包子' ); }
5. break break 退出整个循环
1 2 3 4 5 6 for (var i = 1 ; i <= 5 ; i++) { if (i == 3 ) { break ; } console .log ('我正在吃第' + i + '个包子' ); }
六、数组 数组(Array) :就是一组数据的集合 存储在单个变量下的优雅方式
1. 创建数组 1 var arr = new Array(); // 利用new 创建数组
1 var arr = []; // 利用数组字面量创建数组 []
2. 获取数组元素 1 2 3 var arr1 = [1 , 2 , 'pink老师' , true ];console .log (arr1[2 ]); console .log (arr1[3 ]);
3. 数组常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 var arr = ["李白" , 123 , false , undefined , null , true , 9 , 5 , 7 , 13 ] arr.push ("剑仙" ) console .log (arr); arr.pop () console .log (arr); arr.shift () console .log (arr); arr.splice (1 , 2 ) console .log (arr); arr.splice (1 , 0 , "诗圣" ) console .log (arr); arr.splice (1 , 3 , 10 ) console .log (arr); var arr1 = arr.join ("1" ) console .log (arr1); var arr3 = arr.concat (arr1) console .log (arr3); var arr4 = arr.slice (1 , 3 ) console .log (arr4); var arr5 = arr.reverse () console .log (arr5); var arr6 = arr.sort (function (a, b ) { return a - b }) console .log (arr6);
4. 冒泡排序 1 2 3 4 5 6 7 8 9 10 11 12 var arr = [4 , 1 , 2 , 3 , 5 ]; for (var i = 0 ; i <= arr.length - 1 ; i++) { for (var j = 0 ; j <= arr.length - i - 1 ; j++) { if (arr[j] < arr[j + 1 ]) { var temp = arr[j]; arr[j] = arr[j + 1 ]; arr[j + 1 ] = temp; } } } console .log (arr);
5. 数组最大值 1 2 3 4 5 6 7 8 var a = [2 , 5 , 7 , 343 , 22 , 23 , 65 , 234 ] var max = 0 for (var i = 0 ; i < 8 ; i++) { if (a[i] > a[i + 1 ]) { max = a[i] } } console .log (max);
6. 数组去重 1 2 3 4 5 6 7 8 9 10 var arr = [1 , 2 , 3 , 4 , 5 , 2 , 3 , 4 , 2 , 23 , 4 , 5 , 2 , 2 , 3 , 54 , 3 , 3 , 4 ] for (var i = 0 ; i < arr.length - 1 ; i++) { for (var j = i + 1 ; j < arr.length - 1 ; j++) { if (arr[i] == arr[j]) { arr.splice (j, 1 ) j-- } } } console .log (arr);
7. 反转数组 1 2 3 4 5 6 var arr = ['red' , 'green' , 'blue' , 'pink' , 'purple' , 'hotpink' ]; var newArr = []; for (var i = arr.length - 1 ; i >= 0 ; i--) { newArr[newArr.length ] = arr[i] } console .log (newArr);
8. 遍历数组 1. for of 不同于forEach的是,for of 循环可以随时退出 1 2 3 4 var arr = ['red' , 'green' , 'blue' , 'pink' , 'purple' , 'hotpink' ]; for (let i of arr) { console .log (i) }
2. forEach 1 2 3 4 5 6 7 var arr = [1 , 2 , 3 , 4 , 5 , 6 ] arr.forEach ((item ) => { if (item === 3 ) { return } console .log (item) })
3. some 不会对空数组进行检测 不会改变原数组 检测数组里的每一个值是否满足条件,如果有一个满足就返回true,否则返回false 1 2 3 4 5 6 var arr = [1 , 2 , 3 , 4 , 5 , 6 ] const result = arr.some ((item ) => { console .log (item) return item === 3 }) console .log (result)
4. every 不会对空数组进行检测 不会改变原数组 检测数组里的每一个值是否满足条件,如果有一个不满足就返回false,否则返回true 1 2 3 4 5 6 var arr = [1 , 2 , 3 , 4 , 5 , 6 ] const result = arr.every ((item ) => { console .log (item) return item === 3 }) console .log (result)
5. map 不会对空数组进行检测 不会改变原数组 按照原始数组元素顺序依次处理,返回一个新数组 1 2 3 4 5 6 var arr = [1 , 2 , 3 , 4 , 5 , 6 ] const result = arr.map ((item ) => { console .log (item) return item * item }) console .log (result)
6. filter 不会对空数组进行检测 不会改变原数组 对数组进行渲染,返回符合条件的数组 1 2 3 4 5 6 var arr = [1 , 2 , 3 , 4 , 5 , 6 ] const result = arr.filter ((item ) => { console .log (item) return item > 3 }) console .log (result)
7. find() 1 2 3 4 5 6 var arr = [1 , 2 , 3 , 4 , 5 , 6 ] const result = arr.find ((item ) => { console .log (item) return item > 3 }) console .log (result)
七、函数 1. 声明函数 1 2 3 4 function sayHi ( ) { console .log ('hi~~' ); } sayHi ();
1 2 3 4 var sayHi = function ( ) { console .log ('hi~~' ); } sayHi ();
2. 带参数的函数 1 2 3 4 5 6 7 8 9 function cook (aru ) { console .log (aru); } cook ('酸辣土豆丝' ); cook ('大肘子' );
如果实参的个数和形参的个数一致 则正常输出结果 如果实参的个数多于形参的个数 会取到形参的个数 如果实参的个数小于形参的个数 多于的形参定义为undefined 最终的结果就是 NaN 3. 函数的返回值 1 2 3 4 5 6 7 8 9 10 function getResult ( ) { return 666 ; } getResult (); console .log (getResult ());
return 后面的代码不会被执行 返回的结果是最后一个值 我们的函数如果有return 则返回的是 return 后面的值,如果函数么有 return 则返回undefined 4. arguments伪数组 所有函数都内置了一个arguments对象 (只有函数才有arguments),arguments对象中存储了传递的所有实参 ;当不知道传入的实参的个数(实参个数会变,或者不清楚具体几个),就可以不设置形参,在函数体内部直接用arguments去获得传入的实参
特点:
1 2 3 4 5 function f1 ( ) { console .log (arguments ) } f1 (1 , 2 , 3 , 4 );
八、对象 对象: 无序的相关属性和方法的集合,所有的事物都是对象
1. 声明对象 1.1 字面量创建对象 1 2 3 4 5 6 7 8 9 var obj = { uname : '张三疯' , age : 18 , sex : '男' , sayHi : function ( ) { console .log ('hi~' ); } }
1.2 利用new Object() 创建对象 1 2 3 4 5 6 7 8 9 10 11 12 13 var obj = new Object (); obj.uname = '张三疯' ; obj.age = 18 ; obj.sex = '男' ; obj.sayHi = function ( ) { console .log ('hi~' ); } console .log (obj.uname ); console .log (obj['sex' ]); obj.sayHi ();
1.3 利用构造函数创建对象 1 2 3 4 5 6 7 8 9 10 11 12 13 function Star (uname, age, sex ) { this .name = uname; this .age = age; this .sex = sex; this .sing = function (sang ) { console .log (sang); } } var ldh = new Star ('刘德华' , 18 , '男' ); console .log (ldh.name ); console .log (ldh['sex' ]);
2. new关键字内部 3. 遍历对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var obj = { name : 'pink老师' , age : 18 , sex : '男' , fn : function ( ) {} } for (var k in obj) { console .log (k); console .log (obj[k]); }
4. Date对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 let myDate = new Date () console .log (myDate) console .log (myDate.getFullYear ()) console .log (myDate.getMonth ()) console .log (myDate.getDate ()) console .log (myDate.getDay ()) console .log (myDate.getHours ()); console .log (myDate.getMinutes ()); console .log (myDate.getSeconds ()); console .log (myDate.getMilliseconds ()); console .log (myDate.toLocaleDateString ()); console .log (myDate.toLocaleString ()); function getDate (date1, date2 ) { let time1 = new Date (date1) let time2 = new Date (date2) console .log (time1) console .log (time2) return (time2 - time1) / 1000 / 60 / 60 / 24 } console .log (getDate ("2021-7-12" , "2035-7-15" ))
5. for in 和for of 的区别 for-in只是获取数组的索引;而for-of会获取数组的值
for-in会遍历对象的整个原型链,性能差;而for-of只遍历当前对象,不会遍历原型链
对于数组的遍历,for-in会返回数组中所有可枚举的属性(包括原型链上可枚举的属性);for-of只返回数组的下标对应的属性值
for-of适用遍历数组/字符串/map/set等有迭代器对象的集合,但是不能遍历普通对象(obj is not iterable)
九、字符串 1. 字符串的常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 console .log (str1.length ) let str5 = '我是一个字符串' console .log (str5.charAt (3 )) console .log (str5.slice (2 , 4 )) console .log (str5.indexOf ('123' )) console .log (str5.search ('个' )) let str6 = 'sldjfsljflskjfdslfjdsl' let str7 = str6.replace ('s' , '我的' ) console .log (str7, str6) console .log (str6.split ('s' )) let str8 = ' 我的字符 ' console .log (str8.length ) console .log (str8.trim ().length ) console .log (str8.includes ('我' )) let arr = [1 , 2 , 3 , 45 , 3 , 1 , '123' , 12 ] console .log (arr.includes (123 ))
十、正则 1. 正则规则 语法 描述 i 忽略大小写 g 全局 \d 数字 [0-9] \w [0-9,a-z,A-Z] \s 空格 \D 非数字 [^0-9] \W 非单词 【^a-zA-Z0-9】 \S 非空格 \ 转义字符
+ 代表一个或者十多个 ? {0,1} 可以有可以没有 可以有0个也可以有1个 * 可以没有 有的话可以使无限个 {n} {5} 只能有5个 {n,m} 则代表 最少有n个最多有m个 {n,} 最少有n个 多则不限 | ^ $ 或者 开头 结尾 /^1[3-9]\d{9}$/ 开头第一个字符为1 第二个字符为3~9之间 第三个字符0到9的数字一定要有9个就结束了
2. 正则比较 1 2 3 4 5 6 7 8 9 10 let reg = /^1[3-9]\d{9}$/ let phoneNumber = prompt ('请输入一个手机号' )console .log ((reg.test (phoneNumber)));
DOM
总结一、 获取元素方式 1. 传统获取元素方式 方法 描述 document.getElementById()
获取指定id的一个第一个对象的引用 document.getElementsByClassName()
返回文档中所有指定类名的元素集合,作为NodeList
对象 document.getElementsByTagName()
返回带有指定标签名的对象集合 document.getElementsByName()
返回带有指定名称的对象集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <div id="mydiv" >mydiv</div> <div class ="mydivClass" > mydiv</div > <div name ="mydivName" > mydiv</div > <div > mydiv</div > <div id ="mydiv2" > <p class ="myP" > 我是P</p > <p class ="myP" > 我是P</p > <p class ="myP" > 我是P</p > <p > 我是P</p > <p > 我是P</p > </div > <script > let div = document .getElementById ('mydiv' ) let div1 = document .getElementsByClassName ('mydivClass' ) let div2 = document .getElementsByName ('mydivName' ) let div3 = document .getElementsByTagName ('div' ) </script >
2. H5
新增获取元素方式 方法 描述 document.querySelector()
获取得到的是一个节点 如果参数可以选中多个节点 那么返回该选择器匹配的第一个节点 document.querySelectorAll()
获取得到的是一个集合
1 2 let div4 = document .querySelector ('#mydiv2 p' )let div5 = document .querySelectorAll ('#mydiv2 p:not(.myP)' )
3. 两种获取元素的区别 区别:
query
方法获取元素获取的是静态节点getElement
获取元素获取的是动态节点即:getElement
获取的节点会随DOM的变化而变化,而query
获取之后就不会再改变 二、 节点操作 1. 获取子节点 方法 描述 dom.previousElementSibling
获取某个节点的哥哥元素的节点 dom.nextElementSibling
获取某个节点的弟弟元素的节点 dom.childNodes
获取所有的子节点 dom.childElementCount
返回所有的子元素的个数 dom.firstChild
获取父元素下的第一个子节点 dom.firstElementChild
获取父元素下的第一个子节点(IE
6,7,8不支持) dom.lastChild
获取父元素下的最后一个子节点 dom.lastElementChild
获取父元素下的最后一个子节点(IE
6,7,8不支持)
2. 创建节点 方法 描述 dom.createElement()
创建元素节点 dom.createTextNode()
创建文本节点 dom.appendChild()
添加节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <div id="mydiv2" > <div id ="mydiv3" onclick ="removeMydiv3(this)" > </div > </div> <button onclick ="createDiv()" > 点击创建</button > <script > function createDiv ( ) { let newP = document .createElement ('p' ) let str = document .createTextNode ('我是新加的' ) newP.appendChild (str) let mydiv1 = document .getElementById ('mydiv1' ) mydiv1.appendChild (newP) } </script >
3. 删除节点 方法 描述 dom.removeChild()
删除父元素的子元素节点 dom.remove()
删除元素节点本身
1 2 3 4 5 6 7 8 function removeDiv () { mydiv2.removeChild (mydiv3) } function removeMydiv3 (mythis) { let ziji = document .getElementById ('mydiv3' ) console .log (mythis) }
4. 添加删除 class
类 方法 描述 dom.className = 'class'
覆盖整个class类 dom.classList.add('class')
添加一个class类 dom.className = ''
删除所有的class dom.classList.remove('class')
删除指定的class类
5. 添加属性 dom.setAttribute('属性名','属性值')
1 img.setAttribute ('src' ,'./images/close-16x16.png' )
6. 添加标签 dom.innerHTML()
1 mydiv.innerHTML += `<div class='item'>${name} <img onclick = 'delZiji(this)' src="./images/close-16x16.png" alt=""></div>`
三、DOM操作 方法 描述 dom.insertBefore()
插入节点;(要插入的节点,插到那里去) dom.replaceChild()
替换节点;(新节点,旧节点) dom.cloneNode()
克隆节点;(boolen
) dom.getAttribute()
获取元素属性 dom.removeAttribute()
移除元素属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 <p id="wenzi" >1232131 </p> <div id ="mydiv" > 我是div <div id ="div1" > 我是div1</div > </div > <button onclick ="insertDiv()" > 插入节点</button > <button onclick ="replaceDiv()" > 替换节点</button > <button onclick ="cloneDiv()" > 克隆节点</button > <button onclick ="getattr()" > 获取属性</button > <button onclick ="removeAttr()" > 删除属性</button > <button onclick ="innerDiv()" > html</button > <script > function insertDiv ( ) { let mydiv = document .getElementById ('mydiv' ) let textP = document .getElementById ('wenzi' ) let div1 = document .getElementById ('div1' ) mydiv.insertBefore (textP, div1) } function replaceDiv ( ) { let mydiv = document .getElementById ('mydiv' ) let textP = document .getElementById ('wenzi' ) let div1 = document .getElementById ('div1' ) mydiv.replaceChild (textP, div1) } function cloneDiv ( ) { let mydiv = document .getElementById ('mydiv' ) let div1 = document .getElementById ('div1' ) let div2Node = div1.cloneNode (true ) mydiv.appendChild (div2Node) } function getattr ( ) { let mydiv = document .getElementById ('mydiv' ) console .log (mydiv.getAttribute ('id' )) } function removeAttr ( ) { let mydiv = document .getElementById ('mydiv' ) mydiv.removeAttribute ('id' ) } function innerDiv ( ) { let mydiv = document .getElementById ('mydiv' ) mydiv.innerHTML = '<p>我是p标签</p>' } </script >
四、绑定事件 1. 传统方法绑定 dom.事件类型 = function() {}
2. 现代方法绑定 addEventListener(事件名字,事件处理函数,布尔值)
1 2 3 4 5 6 7 8 9 10 11 12 13 let div1 = document .getElementById ('div1' ) div1.onclick = () => { console .log (11 ); } let div2 = document .getElementById ('div2' ) div2.addEventListener ('click' , () => { console .log (222 ); })
3. 区别 传统绑定会被覆盖只能绑定一次,现代绑定 可以多次绑定
4. 常见的事件类型 方法 描述 onclick
点击事件 onmousedown
鼠标按下 onmouseup
鼠标抬起 onmouseover
鼠标移入 onmouseout
鼠标移出 onkeydown
键盘按下 onkeyup
键盘抬起 onkeypress
键盘按下(只是监听非功能) onscroll
窗口改变事件 onload
页面加载完毕时间
BOM
总结一、location对象 方法 描述 location.href
返回当前页面的完整的URL地址 location.search
返回URL后面的参数(类似于”?name = lcy&age=20
“) location.protocol
返回页面使用的协议(通常是http
或https
) location.host
返回页面的域名及端口号 location.hostname
返回页面的域名 location.port
返回页面的端口号 location.pathname
返回页面中的路径或者文件名 location.origin
返回URL源地址 location.hash
返回URL 散列值(#后面的内容)
二、window对象 方法 描述 window.innerHeight
浏览器的高度 window.innerWidth
浏览器的宽度 screenLeft
浏览器距离屏幕的左侧位置 screenTop
浏览器距离屏幕的上边距位置 window.document.documentElement.scrollTop
获取滚动条位置。距离顶部 history.forward()
前进一页 history.back()
后退一页
JS高级总结 1. IIFE 概述:自运行匿名函数,该函数在值创建后就会自动运行,不需要调用也无法调用,生命周期只有一次。
特点:函数执行完毕后就会销毁,不会污染区全局。只执行一次。
1 2 3 4 5 (function (a ) { var a = 1 console .log (arguments ); console .log (a); })('自运行函数' )
2. 递归函数 概述:在函数内部通过名字调用自己本身的一种场景。满足一定的条件就必须停止函数的调用,否则容易陷入死循环。
应用:阶乘,树形菜单,省市区级联选择
1 2 3 4 5 6 7 8 9 10 11 function add (n) { if (n == 1 ) { return 1 } return n * add (n - 1 ) } const a = add (10 ) console .log (a);
3. 惰性载入 概述:主要用来减少代码每次执行时的重复性分支判断,通过对对象的重新定义来屏蔽原来的对象的分支判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 let event = { on : function (dom, type, fn ) { console .log ('先判断浏览器' ); if (window .addEventListener ) { event.on = function (dom, type, fn ) { dom.addEventListener (type, fn, false ) } } else if (window .attachEvent ) { event.on = function (dom, type, fn ) { dom.attachEvent ('on' + type, fn) } } else { event.on = function (dom, type, fn ) { dom['on' + type] = fn } } return event.on (dom, type, fn) } } event.on (btn1, 'click' , function ( ) { console .log ('btn1被点击了' ); }) event.on (btn2, 'click' , function ( ) { console .log ('btn2被点击了' ); }) event.on (btn3, 'click' , function ( ) { console .log ('btn3被点击了' ); })
4. 作用域 变量和函数所在的作用范围,离开这个范围就无法使用。
JS执行环境 :
执行环境也成为执行上下文,在程序运行的时候,会生成一个管理函数和变量的对象,他决定了变量和函数的生命周期,以及访问其他数据的权限。
全局执行环境 :
最外围的执行环境,任何不在函数中的代码都在全局只想上下文中、
函数执行环境
每当一个函数被调用的时候,都会为该函数创建一个全新的上下文环境并推入执行栈中,在代码执行完毕后,将该环境弹出(即销毁)。
根据执行环境划分作用域
全局作用域:在函数外部分代码,在任意地方都可以使用 函数作用域:只有在函数被定义是才会创建,饱汉子啊父级函数作用域/全局作用域内 块级作用域:``ES6可以通过
let、
const` 关键字声明变量,就有可能形成块级作用域。 由于作用域的限制,每段独立的执行代码块只能访问自己作用域和外层作用域中的变量,无法访问内层作用域的变量。
作用域链
:当可执行代码内部访问变量时,会先查找本地作用域,如果找到目标变量及返回,否则会去父级作用域继续查找。。。一直找到全局作用域。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 var a = 3 function fn () { console .log (a); var b = 4 console .log (b); } fn () console .log (b); var a = 3 function fn () { console .log (a); console .log (b); let b = 4 } fn () console .log (b); for (var i = 0 ; i < 5 ; i++) { console .log (i); } console .log (i); for (let j = 0 ; j < 5 ; j++) { console .log (j, '内部' ); } console .log (j, '外部' ); var a = 5 function foo () { console .log (a); var b = 3 function bar () { var b = 2 console .log (b); } bar () } foo ()const arr = [1 , 2 , 3 ]console .log (arr);
5. 闭包(计算属性) 概述:闭包是指嵌套在一个函数内部中的函数。一般情况下函数内可以访问函数外的变量,但是函数外无法访问函数内的变量,但是通过必报这个手段就可以在函数外访问函数内部的变量。
特点
:
函数嵌套函数 内层函数访问了外层函数的局部变量,导致该变量常驻内存 将内部函数暴露出去(return或者window),实现函数外访问函数内的变量 作用
:
问题
:
一般函数运行结束后,内部产生的函数和变量都会被释放,所以每次运行函数都是全新的。
但是由于使用闭包后,会导致函数中部分的变量保留到内存中,会增加内存的消耗,所以闭包不能乱用。
实现手段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 (function ( ) { var a = 100 window .getA = function ( ) { return a } window .addA = function ( ) { a++ } })() console .log (getA ()); const game = (function ( ) { var a = 100 return function ( ) { return a } })() console .log (game ());
5.1 封装一个缓存工具 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function createCache () { const data = {} return { set (key,val) { data[key] = val }, get (key) { return data[key] } } } const cache = createCache ()cache.set ('token' ,'qwer' ) cache.set ('user' ,'admin' ) console .log (data) not defined
5.2 计算属性 简易原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function toUpper (str) { console .log ('计算过程复杂' ); return str.charAt (0 ).toUpperCase () + str.substr (1 ) } function cached (fn) { const cache = {} return function (str ) { var hit = cache[str] return hit || (cache[str] = fn (str)) } } const result = cached (toUpper)console .log (result); console .log (result ('helloWord' )); result ('helloWord' )result ('helloWord' )result ('helloWord' )result ('helloWord' )result ('helloWord' )
5.3 任务队列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 function queue () { const awaiting = [] let isRunning = false function done (task, resolve, reject) { task () .then (res => { resolve (res) }) .catch (err => { reject (err) }) .finally (() => { if (awaiting.length ) { const next = awaiting.shift () done (next.task , next.resolve , next.reject ) } else { isRunning = false } }) } return function (task ) { return new Promise ((resolve, reject ) => { if (isRunning) { awaiting.push ({ task, resolve, reject }) } else { isRunning = true done (task, resolve, reject) } }) } } const task1 = function ( ) { return new Promise ((resolve, reject ) => { setTimeout (() => { resolve ('task1' ) }, 3000 ) }) } const task2 = function ( ) { return new Promise ((resolve, reject ) => { setTimeout (() => { resolve ('task2' ) }, 1000 ) }) } const task3 = function ( ) { return new Promise ((resolve, reject ) => { setTimeout (() => { resolve ('task3' ) }, 0 ) }) } const myQueue = queue ()myQueue (task1).then (res => console .log (res))myQueue (task2).then (res => console .log (res))myQueue (task3).then (res => console .log (res))
5.4 闭包案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 function a (i) { var i; alert (i); }; a (10 ); function a (i) { alert (i); alert (arguments [0 ]); var i = 2 ; alert (i); alert (arguments [0 ]); }; a (10 );var i = 10 ;function a () { alert (i); var i = 2 ; alert (i); }; a ();var i = 10 ;function a () { alert (i); function b () { var i = 2 ; alert (i); } b (); }; a ();var scope = "global scope" ;function checkscope () { var scope = "local scope" ; function f () { return scope; } return f (); } checkscope (); function checkscope2 () { var scope = "local scope" ; function f () { return scope; } return f; } checkscope2 ()(); var uniqueInteger = (function ( ) { var counter = 0 ; return function ( ) { return counter++; } }()); uniqueInteger (); function counter () { var n = 0 ; return { count : function ( ) { return n++ }, reset : function ( ) { n = 0 ; } }; } var c = counter ();var d = counter ();c.count (); d.count (); c.reset (); c.count (); d.count (); function constfunc (v) { return function ( ) { return v; }; } var funcs = [];for (var i = 0 ; i < 10 ; i++) { funcs[i] = constfunc (i); } funcs[5 ](); function constfunc () { var funcs = []; for (var i = 0 ; i < 10 ; i++) { funcs[i] = function ( ) { return i; }; } return funcs; } var funcs = constfunc ();funcs[5 ]();
6. 内存泄漏 概述
:JS
创建变量时会给变量分配内存,对于不再使用的变量没有及时释放,会导致内存占用越来越高,有时候会导致进程崩溃。(全局变量、闭包、DOM元素的引用、定时器)
原因
:内存泄漏是指我们无法通过JS访问某个对象,而垃圾回收机制却认为该对象还在被引用,因此垃圾回收机制不会释放该对象,导致该块内存永远无法使用,积少成多,系统会越来越卡以至于崩溃。
解决
:避免使用全局变量,手动删除定时器和DOM、removeEventListener
移除事件监听。
7. 垃圾回收机制 概述
:当浏览器中不再使用的变量,浏览器就会自动将他们回收,这个过程成为垃圾回收。
标记清除法
:垃圾回收机制获取根节点并标记他们,然后访问并标记所有来自它们的引用,然后在访问这些对象并标记它们的引用…如此递进结束后若发现有没有标记的(不可达的)进行删除,进入执行环境的不能进行删除
引用计数法
:当声明一个变量并赋值时,值+1,当该值被取代时-1,为0时进行删除。
8. 浅拷贝和深拷贝 浅拷贝
:只会拷贝第一层,若第一层的属性还是对象或者数组,也是直接拷贝地址。改变新数据会使原数据也跟着改变。
方法
:扩展运算符...
Object.assign()
Array.map()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var obj1 = { name : 'jack' , children : [ { name : 'john' } ] } var obj2 = { ...obj1 }var obj 2 = Object .assign ({},obj1)\obj1.age = 18 obj1.children .push ({ name : 'lily' }) console .log (obj1, obj2);
深拷贝
:在内存中重新开辟一块空间,将原数据拷贝一份存入,与原数据无关联。
方法
:JSON.parse(JSON.stringify())
lodash
的_.cloneDeep()
递归遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var obj1 = { name : 'jack' , children : [ { name : 'john' } ] } var obj2 = JSON .parse (JSON .stringify (obj1)) obj1.age = 18 obj1.children .push ({ name : 'lily' }) console .log (obj1, obj2);
9. 堆和栈 概述
:堆和栈都是运行时内存分配的一个数据区域,两者都是临时存放数据的地方。
基本数据类型
:所有的值都保存在内存中,访问的方式是按值访问。
基本数据类型的值被复制的时候,会在栈中创建一个新值,然后把值复制到新变量分配的位置上,两个值互不影响。
引用数据类型
:引用数据类型的值是保存在堆内存中的,变量中保存的是一个指针,该指针放在栈中,直接复制的时候,两者互相影响。
10. 面向对象 面向过程:以过程为中心
面向对象:以事物为中心
方法过载:在构造函数直接创建的方法,在实例化所有的对象都会拥有一个独立的方法,不同的对象的同一个方法在不同的对空间中,导致浪费内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 var obj = new Object ()var obj = { name :'jack' , age :18 } function stu(name,age) { let obj = {} obj.name = name obj.age = age return obj } let s1 = stu ()function Stu (name,age) { this .name = name this .age = age this .sayHi = function ( ) { console .log ('hello' ) } } let s1 = new Stu ('jack' ,18 )function Stu (name,age) { this .name = name this .age = age } Stu .prototype .sayHi = function ( ) { console .log ('Hi' ) } let s1 = new Stu ('jack' ,18 )
10.1 封装 概述:通过封装可以实现隐藏对象内部实现的细节,然后对外提供一致的接口
意义:1. 将不需要公开的数据进行私有化处理,外部就无法直接访问
2. 可以提供一些特权方法对属性进行访问或者处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 (function ( ) { var _age = Symbol () var _name = Symbol () function User (name, age, sex) { this [_name] = name this [_age] = age this .sex = sex this .getName = function ( ) { return _name } this .setName = function (newName ) { _name = newName } } User .prototype .getAge = function ( ) { if (this .sex === '女' ) { return '保密' } else { return this [_age] } } User .version = '1.0.0' window .User = User })() var u1 = new User ('jack' , 18 , '女' )var u2 = new User ('chou' , 38 , '男' )console .log (u1.getAge ()); console .log (u2.getAge ()); console .log (u2.name ); u2.setName ('曹操' ) console .log (u2.getName ()); console .log (u1);
10.2 继承 概述:将多个构造函数中的类似代码提取出来,从而减少冗余代码的目的
意义:1. 子类实例化的对象必须拥有弗雷所有的属性和方法
2. 子类实例化的对象也要属于父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 function Father (name, age) { this .name = name this .age = age } Father .prototype .sayHi = function ( ) { console .log (this .name + 'hi' ); } function Son (name, age, className) { this .className = className } var s1 = new Son ('李白' , 18 )s1.sayHi () console .log (s1.name ); 李白 function Son (name, age, className) { this .className = className } Son .prototype = new Father ()Son .prototype .constructor = Son var s1 = new Son ('李白' , 18 , '高三1班' )s1.sayHi () console .log (s1); function Son (name, age, className) { this .className = className Father .apply (this , arguments ) } Son .prototype = new Father ()Son .prototype .constructor = Son var s1 = new Son ('李白' , 18 , '高三1班' )s1.sayHi () console .log (s1); function Son (name, age, course) { this .course = course Father .apply (this , arguments ) } function inheritance (Child , Father ) { function F () { } F.prototype = Father .prototype Child .prototype = new F () Child .prototype .constructor = Child } inheritance (Son , Father ) var s1 = new Son ('jack' , 18 , 'js' )s1.sayHi () console .log (s1);class Student extends Person { constructor (name, age, className ) { super (name, age) this .className = className } } var s1 = new Student ('lucy' , 12 , '三年级一班' )console .log (s1);class Vue { constructor ( ) { this .state = { msg : 'hi' } } get fn () { return this .state .msg } set fn (newVal) { this .state .msg = newVal } } var vue = new Vue ()vue.fn = 'hello' console .log (vue.fn );
10.3 多态 概述:方法可以根据传递的参数类型、参数个数等进行区别,然后返回不同的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function Father () { this .show = function ( ) { console .log ('这是父级' ); } } function Son () { this .show = function ( ) { console .log ('这是子级' ); } } function input (obj) { obj.show () } input (new Father )input (new Son )
11. New关键字 自动创建了一个空对象 修改函数的上下文为空对象,其函数内部的 this 表示该空对象 执行函数体,为该对象添加属性及方法 如果没有返回其他对象,name会自动返回创建的空对象 12. 原型 概述:在JS
中,每一个函数都有一个prototype
属性,这个属性其实也是一个指针(地址),会指向函数的原型对象,原型队形上所有的属性和方法都可以被实例共享。
作用:解决方法过载的问题,对类的功能进行扩展。
__proto___
:每一个实例对象都有一个该属性,会指向它的构造函数的源相对向上。
constructor
:每一个原型对象都有一个constructor
属性,该属性指向构造函数
12.1 原型链 概述:当访问对象属性的时候,如果对象本身不存在该属性,那么就会在原型对象上查找该属性,如果原型对象上也没有,就会在原型对象的原型上查找,如此循环,指导找到该属性或者达到顶级。对象查找属性的过程所经过的过程构成一个链条,称为原型链。
13. this 13.1 概述 在函数被调用时,会创建一个活动记录(执行上下文),这个记录会包含函数在哪里调用(调用栈)。函数调用的名字、函数参数等信息,而this就是这个上下文中的一个属性,会在函数执行的过程中用到,在非严格模式下总是会指向一个对象,严格模式下可以是任意值。他指向最后调用他的对象。
13.2 this指向 1. 默认绑定 在严格模式下绑定到undefined
,非严格模式下绑定到全局对象window
1 2 3 4 5 6 function foo ( ) {console ,log (this ) } window .foo ()
2. 隐式绑定 当函数引用有上下文对象时,则会把函数中的this
绑定到这个上下文对象。
1 2 3 4 5 6 7 8 var obj = { name :'jack' , sayHi ( ) { console .log (this ) } } obj.sayHi ()
3. 构造函数的this 当函数通过new
关键字调用,函数内部的this
指向新创建的对象
1 2 3 4 5 6 7 8 9 10 function Student ( ) { this .name = '' ; } Student .prototype .sayHi = function ( ) { console .log (this .name ); } var s = new Student ();
4. 显示绑定 使用call
、bind
、apply
来指定this
指向。
4.1 fn.calll(obj,参数1,参数2,...)
将fn
中的this
指向call
的第一个参数。
会立即执行一次fn
方法
fn
中的this
临时性修改为obj
1 2 3 4 5 6 7 8 9 10 11 12 13 var p1 = { name : '周瑜' , phone (h ) { console .log (this .name + '给小乔打' + h + '个小时的电话' ); } } var p2 = { name : '曹操' } p1.phone .call (p2, 10 );
4.2 fn.apply(obj,[参数1,参数2,参数3...])
作用与call完全相同,只是传递参数的方式发生改变。
4.3 fn.bind(obj,参数1)
永久性的修改函数中this
的指向,一般用在回调函数中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var p2 = { name : '曹操' } var p1 = { name : '周瑜' , phone (h ) { setTimeout (function ( ) { console .log (this .name + '给小乔打' + h + '个小时的电话' ); }.bind (p2), 0 ) } } p1.phone (1 );
4.4 总结 call、apply都是立即执行一次 bind被动执行 bind会永久性修改this指向 call和apply的区别在于 参数传递 5. 箭头函数 箭头函数没有this
的绑定,只能通过箭头函数所在的作用域来决定他的值,所以箭头函数中的this
始终指向你定义函数时this
的指向。
1 2 3 4 5 6 7 8 9 10 var obj = { name : '章三' , sayHi ( ) { setTimeout (() => { console .log (this .name ); }, 0 ) } } obj.sayHi ();
14. JS的设计模式(原理) 14.1 简单工厂模式 概述:为了解决多个类似对象声明的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 function UserFactory (role) { if (this instanceof UserFactory ) { if (!this [role]) { throw new Erroe ('参数错误,可选参数为 SuperAdmin,Admin,User' ) } return new this [role]() }else { return new UserFactory (role) } } UserFactory .prototype .SuperAdmin = function ( ) { this .name = '超级管理员' this .viewPage = ['首页' , '权限' , '应用数据' , '学生管理' ] } UserFactory .prototype .Admin = function ( ) { this .name = '管理员' this .viewPage = ['首页' , '应用数据' , '学生管理' ] } UserFactory .prototype .User = function ( ) { this .name = '用户' this .viewPage = ['首页' ] } var a = new UserFactory ('Admin' ) console .log (a); var b = UserFactory ('Admin' ) console .log (b);
14.2 单例模式(vuex,modal) 概述:在某些情况下,一些对象只需要一个实例,而不能创建很多个,比如全局缓存、模态框、store,如果实例已经创建,则直接返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 const SingLeton = (function ( ) { let instance return function ( ) { if (instance) { return instance } modal () return instance = this } })() function modal () { var div = document .createElement ('div' ) div.style .width = '300px' div.style .height = '150px' div.style .backgroundColor = '#ccc' document .body .appendChild (div) } const btn = document .getElementById ('btn' ) btn.onclick = function ( ) { new SingLeton () } function Store (state) { this .state = { ...state } this .setState = function ( ) { } this .getState = function ( ) { } } let createStore = (function ( ) { let instance return function (state ) { if (!(this instanceof createStore)) { return new createStore (state) } if (instance) { return instance } return instance = new Store (state) } })() const store = createStore ({ num : 0 }) const store1 = new createStore ({ msg : 'hi' }) console .log (store);console .log (store1);
14.3 策略模式(element表单验证) 概述:定义了一系列的算法,把它们封装起来,并且是他们可以相互的替换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 var w = new Date ().getDay ()var week = ['星期天' , '星期一' , '星期二' , '星期三' , '星期四' , '星期五' , '星期六' ,]console .log (week[w]);var reward = {A : function (salary ) {return salary * 2 }, B : function (salary ) {return salary * 1 }, C : function (salary ) {return 0 }, D : function (salary ) {return -salary}, } console .log (reward['A' ](6000 ));console .log (reward['B' ](6000 ));console .log (reward['C' ](6000 ));console .log (reward['D' ](6000 ));const strategies = {isEmpty : function (val, err ) {if (val === '' ) {return err} }, minLength : function (val, len, errMsg ) {if (val.length < len) {return errMsg} } } document .getElementById ('btn' ).onclick = function ( ) {var user = document .getElementById ('user' ).value var pwd = document .getElementById ('pwd' ).value var userStrategy = strategies.isEmpty (user, '请输入账号' )var pwdStrategy = strategies.isEmpty (pwd, '请输入密码' )var pwdLength = strategies.minLength (pwd, 6 , '密码不能小于6位' )var err = userStrategy || pwdStrategy || pwdLengthif (err) {return alert (err)} }
14.4 观察者模式(双向数据绑定) 概述:观察者模式是指一个对象维持一系列依赖于它的对象,当有状态发生变更的时候,就会去通知这一系列的对象去更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 function Observe (name) { this .name = name } Observe .prototype .update = function (msg ) { console .log (this .name + '收到' + msg + '通知了' ); } function Subject (name) { this .name = name this .observes = [] } Subject .prototype .add = function (observer ) { this .observes .push (observer) } Subject .prototype .notify = function (msg ) { this .observes .forEach (item => { item.update (this .name + '-' + msg) }) } Subject .prototype .remove = function (observer ) { for (let i = 0 ; i < this .observes .length ; i++) { if (this .observes [i] === observer) { this .observes .splice (i, 1 ) } } } Subject .prototype .clear = function (observer ) { this .observes = [] } const jack = new Observe ('jack' )const lilei = new Observe ('李磊' )const lucy = new Observe ('lucy' )const hengda = new Subject ('恒大' )const wanda = new Subject ('万达' )hengda.add (jack) hengda.add (lucy) wanda.add (lilei) wanda.add (lucy) hengda.notify ('破产了' ) wanda.notify ('新楼盘' )
14.5 发布订阅者模式(V2原理,eventBus) 概述:是指希望接受通知的对象给予一个主题通过自定义事件订阅,发布事件的对象通过事件中心去通知所有的主题订阅者。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 function EventBus () { this .handleEvents = {} } EventBus .prototype .on = function (type,callback ) { if (!this .handleEvents [type]) { this .handleEvents [type] = [] } this .handleEvents [type].push (callback) } EventBus .prototype .emit = function (type, ...args ) { if (this .handleEvents [type]) { const handleEvent = this .handleEvents [type].slice () handleEvent.forEach (callback => { callback.apply (this , args) }); } } const bus = new EventBus () bus.on ('wanda' , function (msg ) { console .log ('lucy订阅的' + msg); }) bus.on ('wanke' , function (msg ) { console .log ('jack订阅的' + msg); }) bus.on ('hengda' , function (msg ) { console .log ('乐磊订阅的' + msg); }) bus.emit ('wanda' , '破产了' ) bus.emit ('wanke' , '有新楼盘了' ) bus.emit ('hengda' , '有新楼盘了' )
15. defineProperty 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var obj = { name : 'jack' } Object .defineProperty (obj, 'age' , { configurable : true , enumerable : true , get : function ( ) { return 20 }, set : function ( ) { } }) console .log (obj);
15.1 单个数据劫持 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 let data = { msg : 'Hi' } let vm = {}Object .defineProperty (vm, 'msg' , { get () { return data.msg }, set (newValue) { if (newValue === data.msg ) { return } data.msg = newValue document .querySelector ('#app' ).innerHTML = data.msg } })
15.2 多个数据劫持 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 let data = { msg : 'Hi' , num : 0 } let vm = {}function defineReact () { const arr = Object .keys (data) arr.forEach (key => { Object .defineProperty (vm, key, { get () { return data[key] }, set (newValue) { if (newValue === data[key]) { return } data[key] = newValue document .querySelector ('#app' ).innerHTML = data[key] } }) }) } defineReact ()
ES6总结 1. Symbol 概述
:表示独一无二的值
特点
:
Symbol()
函数返回值一定是唯一
的。Symbol()
可以作为对象属性的标识。调用Symbol()
函数不需要加new
Symbol()
作为对象属性时,不能被for in (Object.keys
) 访问。在其它模块中,只有通过定义的Symbol变量进行访问。 用法
:
1 2 3 4 5 6 7 8 9 10 11 12 13 var s1 = Symbol (); var s2 = Symbol ();console .log (s1 == s2); var s3 = Symbol ('foo' ); var s4 = Symbol ();console .log (s3 == s4); const id = Symbol ();let obj = { [id]: '183910238' }
案例
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let t1 = {name : 'jack' ,age : 12 }; let t2 = {name : 'lily' ,city : '成都' }; const t3 = {...t1, ...t2 } console .log (t3);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const name = Symbol ('name' );const age = Symbol ('age' );let t1 = {[name]: 'jack' , [age]: 12 }; let t2 = {name : 'lily' ,city : '成都' }; const t3 = {...t1, ...t2 } console .log (t3);
2. class 概述:class
是ES6
提出来的一个概念,更加接近传统语言中的类的写法,作为对象模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 function Father () { this .name = '' } Father .prototype .sayHi = function ( ) { }Father .version = '1.0.0' var p1 = new Father ()p1.name p1.sayHi () Father .version class Person { constructor (name,age ) { this .name = name this .age = age } sayHi ( ) { console .log ('这是ES6的class' ) } static version = '1.0.0' } let p2 = new Person ('jack,18' )console .log (p2.name ) console .log (Person .version ) p2.sayHi () class Son { name = '' age = '' sayHi () { console .log ('hi' ); } static version = '1.0.0' } class Student extends Person { constructor (name, age, className ) { super (name, age) this .className = className } } var s1 = new Student ('lucy' , 12 , '三年级一班' )console .log (s1);class Vue { constructor ( ) { this .state = { msg : 'hi' } } get fn () { return this .state .msg } set fn (newVal) { this .state .msg = newVal } } var vue = new Vue ()vue.fn = 'hello' console .log (vue.fn );
JS手写题总结 1. 防抖节流 防抖:触发高频事件N秒后只会执行一次,如果N秒内事件再次触发,则会重新计时。
应用场景:
search搜索联想,用户在不断输入值时,用房都来节约请求 window触发resize的时候,不断地调整浏览器窗口会不断的触发这个事件,用防抖来让其只触发一次 简洁版:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function debounce (func, wait ) { var timeout; return function ( ) { var context = this ; var args = arguments ; clearTimeout (timeout) timeout = setTimeout (function ( ){ func.apply (context, args) }, wait); } } var node = document .getElementById ('layout' )function getUserAction (e ) { console .log (this , e) node.innerHTML = count++; }; node.onmousemove = debounce (getUserAction, 1000 )
最终版:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 function debounce (func, wait, immediate ) { var timeout, result; var debounced = function ( ) { var context = this ; var args = arguments ; if (timeout) clearTimeout (timeout); if (immediate) { var callNow = !timeout; timeout = setTimeout (function ( ){ timeout = null ; }, wait) if (callNow) result = func.apply (context, args) } else { timeout = setTimeout (function ( ){ func.apply (context, args) }, wait); } return result; }; debounced.cancel = function ( ) { clearTimeout (timeout); timeout = null ; }; return debounced; }
节流:在规定的时间内,只能触发一次函数。如果这个时间内触发多次,则只会执行最后一次。
应用场景:
鼠标不断点击触发,mousedown
只触发一次。 监听滚动事件,比如是否滑到底部自动加载更多,用节流来判断 简洁版:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function throttle (func, wait ) { var context, args; var previous = 0 ; return function ( ) { var now = +new Date (); context = this ; args = arguments ; if (now - previous > wait) { func.apply (context, args); previous = now; } } }
最终版:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 function throttle (func, wait, options ) { var timeout, context, args, result; var previous = 0 ; if (!options) options = {}; var later = function ( ) { previous = options.leading === false ? 0 : new Date ().getTime (); timeout = null ; func.apply (context, args); if (!timeout) context = args = null ; }; var throttled = function ( ) { var now = new Date ().getTime (); if (!previous && options.leading === false ) previous = now; var remaining = wait - (now - previous); context = this ; args = arguments ; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout (timeout); timeout = null ; } previous = now; func.apply (context, args); if (!timeout) context = args = null ; } else if (!timeout && options.trailing !== false ) { timeout = setTimeout (later, remaining); } }; throttled.cancel = function ( ) { clearTimeout (timeout); previous = 0 ; timeout = null ; } return throttled; }
2、Promise