# javascript_learn **Repository Path**: yzuxqz/javascript_learn ## Basic Information - **Project Name**: javascript_learn - **Description**: notes to learn javascript - **Primary Language**: HTML - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-11-16 - **Last Updated**: 2021-11-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # JS基础 ## 简单数据类型 ### Undefined类型 - 只声明但未初始化 - 只有一个值:undefined - 用typeof检测返回undefined,typeof返回的值是一个字符串 ### NUll类型 - 表示空对象指针,用于未来会保存对象的变量初始化 - 只有一个值:null - 用typeof检测返回object,typeof返回的值是一个字符串 - ==注意==:undefined派生自null(null==undefined为true,但是null == =undefined为false) ### Boolean类型 - 只有两个值:true和false - 转型函数Boolean() 1. 数字:除了0和NaN,其余都是true 2. 字符串:除了空串都为true 3. null,undefined为false 4. 对象为true - 对任意数据类型取反两次!!可以转为布尔值 ### Number类型 - js中所有的数字都是Number类型 - 二进制0b - 八进制最高位加0 - 十六进制最高位加0x - 浮点数在小数点后没有数或小数点后都为0时会被转为整型 - ==注意==: 1. 在输出和进行算术运算时都会转换成十进制 2. ECMAScript中浮点计算(0.2+0.1)会有误差,因此不要去判断浮点数 #### 数值范围 - 超过Number.MAX_VALUE和-Number.MAX_VALUE的数值会被转成 Infinity和-Infinity - Number.MIN_VALUE表示的是>0的最小值 #### NaN - 任何涉及NaN的操作都会返回NaN(没有字符串连接) - NaN与任何值都不想等,包括NaN本身 - isNaN()用于测试传入的数值是否能转为数值,如果能则返回false - ==注意==: 1. typeof NaN为number 2. true能转化为1 - isNaN()也适用于对象,对象调用isNaN()函数时首先调用对象自己的valueOf()方法,以确定该方法的返回值是否可以转为数值。如果不能,再基于这个返回值调用tostring()方法,再次测试 #### 数值转换 ##### Number() - 可以用于任何数据类型 - Boolean值,true返回1,false返回0 - null值,返回0 - undefined值,返回NaN(Number类型) - 字符串,除空的转为0,有额外字符的转为NaN,其他都转为十进制数 - ==注意==:不能转八进制,因为前导0会忽视 ##### parseInt(,进制) - 用于将一个字符串中的有效==整数==内容取出来 - 如果第一个字符不是数值或负号,则返回NaN。(空返回NaN) - 直到解析到非数字字符(包括小数点)停止 - 能够解析八进制和十六进制,加上第二个参数,表示基数是多少进制 ==注意==: 1. 对于非字符串值会先转为String然后再操作 ##### parseFloat() - 可以解析第一个小数点 - 只能解析十进制数 - 如果传入的字符是整数,则也会返回整数 ### String类型 - 转义字符 - 拼接字符串会很慢,字符串的不可变性 - 注意比较运算符的==两边都是==字符串时不会将其转化成数字进行比较,而会分别比较字符串中字符的Unicode编码,一位一位进行比较,如果两位一样则比较下一位,所以==在比较两个字符串型的数字之前一定要转成Number== #### 转为字符串 ##### toString() - 除了null,undefined都有该方法 - 参数表示返回数值的进制 - 该方法有返回值,不会影响原变量 ##### String() - 对于Number和Boolean,实际还是调用toString()方法 - 如果值为null,返回 “null” - 如果值为undefined,返回“undefined” ##### + "" - 加一个空字符串,隐式类型转换 ## JS对象 ### 创建对象 - 字面量 ```javascript var obj = { uname: 'xqz', //里面的属性方法采取键值对的形式 sayHi: function () { //方法冒号后面跟的是一个匿名函数 console.log('hi'); } } ``` - 利用Object() ```javascript var obj = new Object(); //一个空对象 obj.uname = 'xqz'; obj.sayHi = function () { console.log('Hi'); }; ``` - 构造函数 function Person(){ } Person() //不加new是普通函数 new Person() //加new是构造函数 ```javascript function Star(uname, age, sex) { //构造函数首字母大写 this.name = uname; this.age = age; this.sex = sex; this.sing = function (songs) { console.log(songs); } } var ldh = new Star('刘德华', 19, '男'); //构造函数不需要return ldh.sing('冰雨'); ``` ==注意==: 1. 构造函数每执行一次就会创建一个新的方法,也就是说所有的实例的sing方法都是唯一的,这样占用了大量的内存空间,完全可以让所有的对象共享一个方法:对象的prototype属性中写 2. 对于不再使用的对象,必须设置为null,这样GC才会回收 ```javascript var person = new Person();//此时仍有变量在引用 var person = null; //此时栈中对堆中的引用断开,对象成为垃圾 ``` ### 使用对象 - 使用属性 obj.uname obj.['uname'] - 使用方法 obj.sayHi(); hasOwnproperty() 方法验证属性是存在于对象中,还是存在于实例中。在实例中则返回true,在构造函数的prototype中则返回false。 name in Object 只要能通过原型链找到则返回false. hasPrototypeProperty()方法 实例中重写属性后,属性就存在于实例中,原型中的属性就用不到了。 枚举(不明白) 得到所有可枚举的实例属性 Object.keys()方法 Object.assign(default,new) //属性覆盖 ### 遍历对象 ```javascript for (const key in ldh) { console.log(key);//输出属性名 console.log(ldh[key]);//输出属性值 } ``` ==注意==: 1. “属性名” in 对象:可以检查obj中是否有该属性 2. obj2=obj1;此时修改obj1中的值会影响obj2因为他们指向的是同一个堆地址,如果让obj1=null,并不会改变obj2的值,只是将obj1的指向断开,堆内存中的值仍然存在 ### 判断对象类型 1. instance of检测是否为该对象类型,Array和Function都是特殊的Object类型 2. typeof 能区别:数值/字符串/布尔值/undefined/function 不能区别:null与Object,Object和Array 3. ===只对undefined和null有效,因为只有这两种类型有固定值 4. typeof a(返回值是字符串类型) === 'undefined'也可以进行判断,注意undefined == =’undefined‘是false,一个是undefined类型,一个是字符串类型 ## 内置对象 ### Math对象 - 最大值 Math.max() - 绝对值 Math.abs() - 取整 - 向下 Math.floor() - 向上 Math.ceil() - 四舍五入 Math.round() ==注意==:负数中.5特殊,往大了取 - 随机数 - 返回[0,1)之间的小数 Math.random() - 返回两个数之间的随机整数 ```javascript //返回两个数之间的随机整数,并且包含这两个数 function getRandom(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } ``` ### 日期对象 - 返回系统当前时间 new Date() - 返回自定义的时间 - 参数为数字型 new Date(2020,6,30) ==注意==:返回的月份比实际大1月 - 参数为字符串型 new Date(‘2020–6–30 8:23:45’); - 年 date.getFullYear() - 月 date.getMouth() + 1(0~11) - 日 date.getDate() - 周几 date.getDay() (0-6) - 时 date.getHours() - 分 date,getMinutes() - 秒 date.getSeconds() - 时间戳(毫秒) ==注意==:是距离1970年1月1日过了多少毫秒 - date.valueof() - date.getTime() - +new Date() - Date.now() ### 数组对象 #### 创建数组 - 字面量 ```javascript var arr = [1, 3, 3, 3] ``` - 利用new Array() ```javascript var arr1 = new Array(2); //长度为2的空数组 var arr1 = new Array(2, 3); //等价于[2,3]表示里面有两个数组元素是2和3 ``` ==注意==: 1. 读取不存在的索引不会报错,返回undefined 2. 对于连续的数组,用length会得到数组的长度,对于非连续的数组,length获得数组的最大索引加1 #### 检测是否为数组 - arr instance of Array - Array.isArray(arr) #### 增 - 在末尾加, 返回新数组长度 arr.push() - 在开头加,返回新数组长度 arr.unshift() - 在指定位置加 splice(开始索引,0,添加的元素) //返回删除的数组,在开始的索引之后增加 #### 删 - 删末尾一个,返回删除的元素 arr.pop() - 删开头一个,返回删除的元素 arr.shift() - 删除并在删除位置添加,会改变原数组 splice(开始索引,删除个数,添加的元素)//返回删除的数组 #### 改 ​ splice(开始索引,删除个数,添加的元素)//返回删除的数组 #### 查 根据元素找索引 ```javascript console.log(arr.indexOf('A')); //从前往后找 它只返回第一个满足条件的索引号 找不到则会返回-1 console.log(arr.lastIndexOf('A')); //从后往前找 ``` findIndex(),返回一个索引 ```javascript //根据id从数组中查找元素的索引 this.books.findIndex(function (item) { return item.id == id; }) ``` #### 翻转 arr.reverse(),修改原数组 #### 排序 1. 修改原数组,默认按照unicode编码进行排序 2. sort里放一个函数如果返回大于0的数则a,b交换位置,如果返回小于0的数则a,b不交换位置 ```javascript arr.sort(function(a,b){ if(a>b){ return 1;//换位置 }else if(a0,a>b返回一个>0的数,交换位置 }); return b-a;//降序 ======= return a - b;//降序,如果a-b>0,a>b返回一个>0的数,交换位置 }); return b-a;//升序 >>>>>>> 1ceb7a2e23037ad4c54e9f577ca2057109b71222 console.log(arr); ``` #### 合并 arr1.concat(arr2) 不会改变原数组,返回新数组 #### 数组转字符串 - arr.join(连接符) //默认逗号,不会改变原数组,将转换后的字符串作为结果返回 - arr.toString() //逗号分隔 #### 数组截取 arr.slice(开始索引,结束索引(不包含)) ==注意==: 1. 如果传递一个负值,则从后往前进行计算 2. 不会改变原数组 #### 数组遍历 - array.filter(function(currentValue,index,arr), thisValue)//当前元素的值,当前元素的索引,当前元素属于的数组对象 - 不会改变原始数组 - 返回一个新数组 ```javascript //根据id查询出要编辑的数据 this.books = this.books.filter(function(item){ return item.id==id; }); ``` - some(function(){}) - 用于检测数组中的元素是否满足指定条件 - some()方法会以此执行数组的每个元素: - ==如果有一个元素满足条件,则表达式返回true==,剩余的元素不再遍历 - 如果没有满足体条件的元素,返回false ==注意==:不能对空数组进行检测 ```javascript //根据当前的id去更新数组中对应的数据 this.books.some((item) => { //箭头函数中的this指向定义这个函数的父级作用域中的this,也就是handle的this,指向vue实例 if (item.id == this.id) { item.name = this.name; //完成更新操作后需要中止some遍历 return true; } }); ``` ### 字符串对象 - string是简单数据类型,为什么有length属性? 步骤: 1. 创建基本类型的一个实例; 2. 在实例上调用指定的方法; 3. 销毁这个实例; ```javascript var str = "我是string基本类型的值"; var new_str = new String("我是string基本类型的值"); // 包装处理 var my_str = new_str.substring(5,8); new_str = null; // 方法调用之后销毁实例 var str2 = "xqz"; str2.age = 21; console.log(str.age); //undefined //可见,并非string调用了自身的方法,而是后台创建了一个基本包装类型String,从而进行下一步操作。 //基本类型的“销毁性”致使我们不能为基本类型添加属性和方法。 ``` - ==注意==:String,Number,Boolean都是基本包装类型 ```javascript var a = 123; a.hello = "hello"; //这里不报错是因为转为了基本包装类型Number(),给改对象添加了hello属性,而后让new_num=null从而销毁实例 console.log(a.hello);//这里调用a的hello属性不报错,结果为undefined,因为重新又创建了一个Number()实例,此时这个实例中并没有hello属性 ``` - 字符串的不可变性 指的是里面的值不变,其实只是地址变了,内存中开辟了一个内存空间 ```javascript var str2 = 'andy'; str2 = 'red'; //str从andy指向red,但是andy仍然存在 //因为我们字符串的不可变所以不要大量拼接字符串 var str2 = ''; for (var i = 0; i < 100; i++) { //如果i过大会很卡 str += i; } console.log(str); ``` - ==注意==:字符串的所有方法都不改变自身,都返回一个新的字符串 #### 根据值找索引 ```javascript var str = '改革春风吹满地,春天来了'; console.log(str.indexOf('春')); console.log(str.indexOf('春', 3)); //从索引号3的位置(包括3)往后找 lastindexof()同理 ``` #### 根据索引找值 - 返回字符 str.charAt() str[] - 返回ASCII值,unicode编码 str.charCodeAt() - str[] 因为在底层字符串是以字符数组的形式保存的 #### 合并 ​ str1.concat(str2) #### 截取 - str.substr(开始的索引,截取长度) //如果只有一个参数且为负数,则是从后往前 - str.substring(开始索引,结束索引) //结束索引不能为负数,负数会被转为0,如果第二个参数小于第一个参数会自动交换位置 - str.slice(开始索引,结束索引(不包含)) //可以接受负数,会将字符串长度与负数相加作为第二个参数 - 都是返回截取下来的字符串 #### 替换 ​ str.replace(要替换,替换为) //只会换一个 返回一个新的字符串 可以接收正则表达式 ```javascript str.replace(/[a-z]/g,"@");//全部替换 ``` #### 大小写 ​ toUpperCase() ​ toLowerCase() #### 去除空格 ​ str.trim() #### 是否包含 ​ str.includes() //返回bool类型(es6) #### 搜索指定内容 ​ str.search() ​ 如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到则返回-1,它可以接收正则表达式作为参数来进行搜索 ​ str.search(/a[bef]c/),不能全局匹配,只会查找第一个 #### 提取 ```javascript str = '123afd445fda5'; result = str.match(/[A-z]/);//默认情况下match智慧找到第一个符合要求的内容,找到以后就会停止检索,可以设置正则表达式为全局匹配模式 result = str.match(/[A-z]/gi);//全局匹配并且忽略大小写,返回一个新的数组 ``` #### 字符串转数组 ​ str.split(以什么分隔符分割该字符串) //返回一个数组 ==注意==: 1. 根据任意字母来将字符串拆分str.split(/[A-z]/),这个方法不设置全局匹配也会全部拆分; #### 根据字符编码获取字符 ​ var result = String.fromCharCode() ### FormData对象 https://www.jianshu.com/p/e984c3619019 ### arguments的使用 - arguments是函数的一个内置对象,只有function()才有,当不确定有几个参数传递时使用 - arguments是一个伪数组:可以通过索引访问,有length属性,但是没有数组的方法 ## JS预解析 ### 变量提升 只提升变量名(使用var定义),不提升赋值操作 ### 函数提升 只提升函数声名,不调用函数 ==注意==:先执行变量提升,再执行函数提升 - 例题 ```javascript f1(); console.log(c); console.log(b); console.log(h); function f1() { var h = b = c = 9; //相当于 Var a=9;b=9;c=9;b和c直接赋值 没有var 当全局变量看 //集体声明应该写成 var a=9,b=9,c=9; console.log(h); console.log(b); console.log(c); } //相当于 function f1() { var h; h = 9; b = 9; c = 9; console.log(h); console.log(b); console.log(c); } f1(); console.log(c); console.log(b); console.log(h);//此处报错,h没有声名,因为h的作用域只在f1函数中 ``` ## JSON 1. JS中的对象只有JS自己认识,其他的语言都不认识 2. JSON就是一个特殊格式的字符串,可以被任意语言所识别,且可以转成任意语言的对象,JSON在开发中主要用来数据的交互 3. JavaScript Object Notation JS对象表示法 ==注意==: 1. JSON字符串的属性名必须加双引号 ### 分类 1. 对象{} 2. 数组[] ### JSON中允许的值 1. 字符串 2. 数值 3. 布尔值 4. null 5. 对象 6. 数组 ### JSON转对象 JSON.parse() 1. 需要一个JSON字符串作为参数,会将该字符串转为JSON对象 ### 对象转JSON JSON.stringify() 1. 需要一个js对象作为参数,会返回一个JSON字符串 # WebAPI ## DOM ### 节点操作 ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/%E8%8A%82%E7%82%B9%E5%B1%9E%E6%80%A7.png) - nodeType 1. 元素节点 2. 属性节点 3. 文本节点 - nodeName - nodeValue ==注意==:节点操作主要操作元素节点 ##### 元素节点和元素对象的区别 1.所谓元素,即html文档里面,所有的标签都可以称之为元素,比如说

、等,也就是说元素是个统称,一个文档里面有很多的元素。 2.所谓节点,是js为了对html文档进行操作,而开发的,即DOM,文档对象模型。即每个元素都可以称之为一个节点,节点是唯一的。比方来说,《p》标签,肯定是一个p标签元素,那如果通过js对它进行样式控制的时候,就必须获取(找到)到这个元素,称之为节点,如果有好多元素,可以获得第1个、第2个或者第n个。总之,元素是统称,节点是具有唯一性的。 ##### 获取节点 | API | 说明 | | ---------------------------------- | ------------------------------------------------------------ | | document.getElementById('') | 根据id返回元素对象 | | document.getElementByClassName('') | 根据类名返回元素对象 | | document.getElementByTagName('') | 根据标签名返回元素对象集合(伪数组),可以先获取父元素在获取子元素 | | document.querySelector('') | 根据css选择器返回第一个符合条件的元素对象 | | document.querySelectorAll('') | 根据css选择器返回所有元素对象 | | document.body() | 获取body标签 | | document.documentElement | 获取html标签 | ==注意==:得到的多个都是伪数组 ##### 获取父节点 | API | 说明 | | --------------- | ---------------------------- | | node.parentNode | 得到的是离元素最近的父级节点 | ##### 获取子节点 | API | 说明 | | ------------------------------------- | -------------------------- | | node.children | 得到所有子节点(元素节点) | | node.children[0] | 得到子节点的第一个元素 | | node.children[node.children.length-1] | 得到子节点的最后一个元素 | | node.childNodes | 包括空白的所有子节点 | | node.firstChild | 包括空白的第一个子节点 | | node.firstElementchild | 不包括空白的第一个子节点 | ##### 获取兄弟节点 | API | 说明 | | --------------------------- | -------------------------- | | node.nextElementSibling | 下一个兄弟节点(元素节点) | | node.previousElementSibling | 上一个兄弟节点(元素节点) | ##### 创建节点 | API | 说明 | | -------------------------------------------- | ----------------------------------------- | | var child = document.createElement('标签名') | 创建一个节点 | | document.createTextNode() | 创建文本节点,child.appendChild(textnode) | | element.innerHTML='' | 创建并且添加 | ##### 添加节点 | API | 说明 | | ---------------------------------------------------- | -------------------------------------- | | node.appendChild(child) | 添加到父节点(node)的子节点列表末尾 | | node.insertBefore(child,指定元素(node.children[])) | 添加到父节点(node)的指定的子节点之前 | ##### 删除节点 | API | 说明 | | ----------------------- | ---------------------- | | node.removeChlid(child) | node是一个父节点 | | node.remove() | 删除自身节点及其子节点 | ##### 复制节点 | API | 说明 | | -------------------- | -------------- | | node.cloneNode() | 只复制标签 | | node.cloneNode(true) | 深拷贝,全复制 | ##### 替换节点 | API | 说明 | | --------------------------- | -------- | | node.replaceChild(new ,old) | 替换节点 | ### 元素操作 #### 操作元素内容 innerHTML和innerText的区别 - innerText不识别html标签,且会自动去除空格和换行 - innerHTML识别html标签,且保留空格和换行 - 这两个属性可读写,可以获取元素里面的内容 ```javascript //innerText和inner HTML的区别 //1.innerText 不识别HTML标签 去除空格和换行 var div =document.querySelector('div'); div.innerText='1'; //2.innerHTML 识别HTML标签 (常用) 保留空格和换行 div.innerHTML='1'; //这两个属性可读写 可获取元素里的内容 var p = document.querySelector('p'); console.log(p.innerText); console.log(p.innerHTML); ``` #### 操作元素属性 ##### 获取属性 ###### 获取元素自带属性 | API | 说明 | | -------------- | ------------------------------------------------------------ | | element.属性名 | 常见元素自带的属性:src,href,title,alt等,表单元素自带的属性:type,value,disable等,获取class属性时用className获取 | - offset系列属性 | offset系列属性 | 作用 | | -------------------- | ------------------------------------------------------------ | | element.offsetParent | 返回最近一级的带有定位的父元素,没有则返回body==区别==element.parentNode返回最近一级父亲,不管有没有定位 | | element.offsetTop | 返回元素相对带有定位父元素上方的偏移,没有则以body为准 | | element.oddsetLeft | 返回元素相对带有定位父元素左边框的偏移 | | element.offsetWidth | 返回自身包括==边框,padding,内容区==的宽度,返回数值不带单位 | | element.offsetHeight | 返回自身包括边框,padding,内容区的宽度,返回数值不带单位 | ==offset与style的区别== | offset | style | | ------------------------------------- | -------------------------------- | | 可以得到任意样式表中的样式值 | 只能得到行内样式表中的样式值 | | 返回的数值没有单位 | 返回带有单位的字符串 | | offsetWidth包括padding+border+width | style.width不包括padding和border | | offsetWidth等属性是只读属性,不能赋值 | 可读可写 | | 用于获取元素大小 | 用于更改元素样式值 | - client系列属性 | client系列属性 | 作用 | | -------------------- | ------------------------------------------------------- | | element.clientTop | 返回元素上边框的大小 | | element.clientLeft | 返回元素左边框的大小 | | element.clientWidth | 返回自身包括==padding,内容区==的宽度,返回数值不带单位 | | element.clientHeight | 返回自身包括padding,内容区的宽度,返回数值不带单位 | - scroll系列属性 | scroll系列属性 | 作用 | | -------------------- | ------------------------------------------------------------ | | element.scrollTop | 返回被卷去的上侧距离(overflow:auto;滚动条),返回数值不带单位,有padding从内容离开padding算起,否则边框下沿开始计算 | | element.scrollLeft | 返回被卷去的左侧距离,返回数值不带单位 | | element.scrollWidth | 返回自身不含边框的==实际宽度==(超出的也算进去),返回数值不带单位 | element.scrollHeight 返回自身不含边框的==实际高度==(超出的也算进去),返回数值不带单位 ==注意==: 1. 元素被卷去的头部距离:element.scrollTop获取 2. 页面被卷去的头部距离,window.pageYoffset获取 3. 说明垂直滚动条滚动到底:scrollHeight-scrollTop == clientHright //用于判断用户是否阅读完协议 ###### 获取元素自定义属性 element.getAttribute('属性名') ##### 设置属性 ###### 设置元素自带属性 | API | 说明 | | -------------------- | ---- | | element.属性名 = ''; | | ###### 设置元素自定义属性 | API | 说明 | | ------------------------------------ | ---- | | element.setAttribute('属性名','值') | | ==注意==:修改class属性时 1. 用element.==className=== ''; 2. 用element.setAttribute('==class==',''); ==注意==:H5规定自定义属性data开头作为属性名并且赋值:

##### 移除属性 ###### 移除元素自定义属性 | API | 说明 | | --------------------------- | ---- | | element.removeAttribute('') | | ##### 判断是否有该属性 | API | 说明 | | ---------------------- | ----------------------------------------- | | element.hasAttribute() | 判断元素属性,而不是css样式表中的样式属性 | #### 操作元素样式 ##### 获取样式属性 | API | 说明 | | ------------------------------ | ------------------------------------------------------------ | | getComputedStyle(element).属性 | 可以获取css样式表的样式,需要两个参数1.需要获取样式的元素2.可传递一个伪元素,一般都传null,该方法会返回一个对象,对象中封装了当前元素对应的样式。 | | element.style.样式名 | 只能获取行内样式(这里的style就是元素的一个属性,将将样式名修改为驼峰命名) | ##### 设置样式属性 | API | 说明 | | ------------------------- | ------------------------------------------------------------ | | element.className='' | 会覆盖原来的class,用 += 可以防止覆盖,注意要加空格className += ‘ b2’ | | element.style.样式名='值' | 添加的是内联样式,所以如果在样式中写了important,js就无法修改了(这里的style就是元素的一个属性) | ### 动画函数的封装 #### 动画函数原理 定义一个setInterval的定时器,函数里对元素的样式进行操作 ```javascript var div = document.querySelector('div'); var time = setInterval(function () { if (div.offsetLeft >= 400) { clearInterval(time); } else { div.style.left = div.offsetLeft + 1 + 'px'; } }, 1) ``` #### 动画函数简单封装 ```html
``` #### 缓动动画函数封装 ```javascript function animate(obj, target, callback) { //先清除以前的定时器 clearInterval(obj.time); //给不同的元素指定不同的定时器 obj.time = setInterval(function () { //把步长值,上下取整看正负值 var x = (target - obj.offsetLeft) / 10; if (x >= 0) { x = Math.ceil(x); } else { x = Math.floor(x); } if (obj.offsetLeft == target) { clearInterval(obj.time); //回调函数 // if (callback) { // callback(); // } callback && callback();//&&具有短路的功能 } else { obj.style.left = obj.offsetLeft + x + 'px'; } }, 15) } ``` ### 三种动态创建元素的区别 1. document.write()会导致文档流重绘 2. element.innerHTML = '' - 拼接字符串,很慢 解决方法:存入数组中,一次性渲染,很快 ```javascript var inner = document.querySelector('.inner'); inner.innerHTML = '百度'; // 创建多个字符串拼接的时候特别慢效率低 // 但是用数组的形式拼接是最快的 var arr = []; var date1 = Date.now(); for (var i = 0; i < 1000; i++) { arr.push('9'); } inner.innerHTML = arr.join('');//数组转字符串 var date2 = Date.now(); console.log(date2 - date1); ``` 3. var child = document.createElement(); node.appendChild(child); 比2慢,比1快 ### 事件 #### 鼠标事件 | 事件 | 说明 | | ------------------------ | ------------------------------------------ | | onclick | 点击 | | onmouseover/onmouseenter | 移入 | | onmouseout/onmouseleave | 移出 | | onmousedown | 按下 | | onmouseup | 松开 | | onmousemove | 移动 | | onwheel | 鼠标滚轮,e.datail(火狐)或者e.wheelDelta | ==注意==:mouseover和mouseenter的区别:mouseenter不会冒泡,简单的说,它不会被它本身的子元素的状态影响到.但是mouseover就会被它的子元素影响到,在触发子元素的时候,mouseover会冒泡触发它的父元素.(想要阻止mouseover的冒泡事件就用mouseenter) #### 键盘事件 | 事件 | 说明 | | ---------- | ------------------------------------------------------------ | | onkeyup | 松开,不区分大小写 | | onkeydown | 会在文字还没进入文本框前就添加事件,不区分大小写,一直按着某个按键不松手,事件会连续触发,第一次和第二次之间会间隔稍微长一点,其他会特别快 | | onkeypress | 不识别功能按键,如ctrl。区分大小写,触发比onkeydown慢 | #### 表单事件 | 事件 | 说明 | | -------- | ----------------------------------------------------- | | onfocus | 获得焦点,无冒泡 | | onblur | 失去焦点,无冒泡 | | onchange | 在元素值改变时触发,适用于input,textarea,select元素 | | onselect | 在文本框内容被选中时触发,适用于单选框,多选框 | #### 编辑事件 | 事件 | 说明 | | ------------- | -------------------------------------------------- | | oncopy | 在拷贝时触发 | | onselect | 在文本框内容选中时触发,return false让用户无法选中 | | oncontentmenu | 鼠标右击触发,用e.preventDefault()来禁止鼠标右键 | #### 页面事件 | 事件 | 说明 | | -------------- | -------------------------------- | | onload | 等待文档流加载完成后触发 | | onbeforeunload | 即将离开页面(刷新或关闭)时触发 | #### 滚动事件 | 事件 | 说明 | | -------- | ------------------------------------------------------ | | onscroll | 滚动条滚动触发该事件,给添加了overflow:auto的元素绑定 | #### 传统事件 ##### 注册 element.事件类型 = function(){} ##### 删除 element.事件类型 = null #### 事件监听器 ##### 区别传统 相比传统的onclick=function(){},addEventListener可以让同一元素同一事件可以注册多个监听器,按注册顺序依此执行,而传统的只能写一个,如果写了多个以最后一个为准 ##### 注册 | API | 说明 | | -------------------------------------------------------- | ------------------------------------------------------------ | | element.addEventListener(''",function(e){},[true/false]) | 第一个参数事件类型,不用加on,第三个参数默认false,冒泡阶段,true,捕获阶段 | ##### 删除 | API事件 | 说明 | | ---------------------------------------------------- | ------------------------------------------------------------ | | element.removeEventListener(''",函数名,[true/false]) | 第一个参数事件类型,不用加on,第三个参数默认false,冒泡阶段,true,捕获阶段 | #### 事件对象 ##### 事件对象属性 | 属性 | 说明 | | -------- | ------------------------------------------------------------ | | e.type | 事件类型 | | e.target | 真正触发事件的那个元素(事件委托:ul和li利用冒泡只需给ul添加点击事件,用e.target获取点击的li) | ##### 事件对象方法 | 方法 | 说明 | | ------------------- | ------------ | | e.preventDefault() | 阻止默认行为 | | e.stopPropagation() | 阻止事件冒泡 | ##### 鼠标事件对象 | 属性 | 说明 | | ----------- | -------------------------------------------------- | | e.pageX/Y | 相对于文档页面(图片跟随鼠标移动,图片要绝对定位) | | e.clientX/Y | 相对于浏览器的可视窗口 | | e.screenX/Y | 相对于电脑屏幕 | | e.offsetX/Y | 相对于带有定位的父盒子的xy坐标 | ##### 键盘事件对象 | 属性 | 说明 | | ---------- | ----------------- | | e.altKey | | | e.ctrlKey | | | e.shiftKey | 是否按下shift | | e.keyCode | 键盘输入的ascll码 | ##### DOM事件流 传统和监听器一般都是得到冒泡阶段,把addEventListener第三个参数改为true则能得到捕获阶段 ###### 捕获阶段 document=>html=>body=>father=>son(如果father有监听事件,先执行father的,再执行son的) ###### 冒泡阶段 于捕获相反,取消冒泡:e.cancelBubble=true,点击了子元素,点击事件冒泡到父元素 ==注意==:onblur,onfocus,onmouseenter,onmousemove没有冒泡 ##### 事件委托 原理:将事件监听器设置在父节点上,利用冒泡原理影响每个子节点,用e.target获取按下的那个元素 ```html ``` ## BOM Window对象是浏览器的顶级对象 1. 它是JS访问浏览器窗口的一个接口 2. 它是一个全局对象。定义在作用域中的变量,函数会变成window对象的属性和方法 ==注意==:window下的一个特殊属性:window.name ### window对象 ==浏览器内核常见模块:== js引擎模块、html,css文档解析模块、DOM\CSS模块、布局和渲染模块、定时器模块、事件响应模块、网络请求模块。 #### location属性 location是一个对象,用于获取或设置窗体的URL,并且可以用于解析,如果直接打印location,返回的是地址栏的信息 - 属性 | 属性 | 说明 | | ---------------------- | ----------------------------------------------------- | | window.location.href | 返回当前页面的URL | | location.host | 返回主机域名 | | location.port | 返回端口号 | | location.pathname | 返回路径 | | location.hash | 返回片段 #后面的内容 常见于链接 锚点 | | window.lacation.search | 返回参数:?键=值(var arr=substr(1).split(“=”)) | | | | - 方法 | 方法 | 说明 | | --------- | ------------------------------------------------------------ | | assign() | 与改变href属性一样可以跳转页面,可以后退,和直接修改location一样 | | replace() | 替换当前页,不记录历史,无法后退 | | reload() | 重新加载页面,若参数为true会强制刷新,相当于浏览器上的刷新按钮 | ```html ``` #### navigator属性 包含有关浏览器的信息,常用window.navigator.userAgent,返回由客户机发送服务器的user-agent头部的值,用正则进行匹配 ==注意==: 1. ie11已经不能用useragent判断浏览器了,但是ie中有ActiveXObject对象属性,通过判断页面一这个属性来知道是不是ie浏览器 #### histroy属性 | 方法 | 说明 | | --------- | --------------------------------------------- | | back() | 后退,相当于浏览器上的按钮 | | forward() | 前进 | | go(1/-1) | 1前进一个页面,-1后退一个页面,-2后退两个页面 | | length | 当此访问的链接的数量 | #### 其他常见属性 | 属性 | 说明 | | ------------------------ | ----------------------------------- | | window.page(Y/X)offset | 获取页面卷去的头部/左侧 | | window.scroll(x,y) | x,y不加单位,滚定至文档中的特定位置 | | window.innerWidth | 返回窗口宽度,无单位 | ### window事件 | 事件 | 说明 | | ------------------------------------------------------------ | --------------------------------------------- | | window.onload=funtion(){}/window.addEventListenter('load',function(){}) | 在文档内容(css,js,html)全部加载完成后触发 | | window.onDOMContentLoaded | 在DOM加载完成后就触发 | | window.onresize | 调整窗口大小时触发 | ### 定时器 #### setTimeout - 创建 会返回一个定时器ID给变量 var time = window.setTimeout(回调函数,[延迟的毫秒数])//在定时器到期后执行回调函数,==只调用一次== - 清除 clearTimeout(定时器ID) #### setInterval - 创建 var time = window.setInterval(回调函数,[延迟的毫秒数])//每隔延迟的时间就调用一次,==会重复调用== - 清除 clearInterval(定时器ID),可以接收任何参数,如果参数是undefined或者null则什么也不做 ```javascript setInterval(fn1,500) setInterval(fn1(),500)//如果没有返回的函数则只执行一次 1 22222.。。 function fn1() { console.log(1) return function () { console.log(2) } } ``` ### this指向问题 1. 在全局作用域,定时器,普通函数中this指向window,==注意==:箭头函数中的this指向上下文对象 2. 方法中的this指向调用者 3. 构造函数中的this指向实例 ### JS执行对列 同步任务在主线程执行,形成一个执行栈。当有异步任务时,如ajax,DOM待触发事件,定时器。放入异步进程处理,触发后放入任务队列(异步队列),等待主线程执行完毕后,通过事件循环机制来任务队列中取出任务执行 ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/JS%E6%89%A7%E8%A1%8C%E9%98%9F%E5%88%97.png) # JS高级 ## 类和对象 - 关键字:class 注意:类中所有函数不需要加function关键字,多个方法间不加逗号分隔. - 方法中的this指向:方法的调用者 ### 构造器 - 关键字:constructor() - 作用:用于传递参数,返回实例对象,new生成对象实例时自动调用。 注意:如果未定义会自动创建一个constructor() - this指向:实例对象 ```html ``` 注意: this的指向问题 ```html ``` ## 类的继承 ### 关键字 - ### entends - 注意:ES6中类没有变量提升,必须先定义类,才能实例化 ### 继承属性 调用父类构造函数: - 关键字:super(x,y) 注意:在constructor中调用,且必须在子元素的this之前调用 ```html ``` ### 继承方法 - 子类实例会自动继承父类的方法 - 在子类中调用父类方法: - 关键字:super.父类方法名() ```html ``` ## 构造函数和对象 1. 利用构造函数创建对象,首字母要大写(之前还有利用new Object(), 字面量的方法创建对象) 2. 通过this添加实例成员和实例方法 3. 构造函数名.属性名 || 方法名,添加静态成员和静态方法 ==注意==:静态成员和方法只能通过构造函数来访问 ```javascript function Star(uname, age) { this.uname = uname;//实例成员 this.age = age; this.sing = function () { console.log('我会唱歌'); } } var ldh = new Star('刘德华', 13); Star.sex = '男';//静态成员 Star.dance = function () { console.log('dance'); }//静态方法 console.log(Star.sex); ``` ### 构造函数原型:prototype(原型对象) - 构造函数通过原型分配的函数是所有对象所共享的 - 每一个构造函数都有一个prototype属性,这个属性就是一个对象,它所拥有的所有属性和方法,都会被构造函数所拥有,包函constructor指回构造函数,和proto属性指向父级的prototype - 每一个实例对象也都有一个_proto_属性,也就是对象原型,指向构造函数的prototype 注意:一般公共的属性定义到构造函数里面,公共的方法放入原型对象中 ```javascript function Star(uname, age) { this.uname = uname; this.age = age; } //Star.prototype被对象赋值覆盖了,所以没有了constructor属性 Star.prototype = { constructor: Star,//重新将构造器指回构造函数 sing: function () { console.log('我会唱歌'); }, movie: function () { console.log('我演电影'); } } ``` ### 对象原型:_proto_ - 每个实例对象都会有一个属性proto向构造函数的prototype,但最终又通过prototype里的constructor指回构造函数 - 每一个构造函数的prototype(原型对象)中也有一个proto属性,指向父级构造函数的prototype ### 方法查找规则 1. 先看实例对象身上有没有a方法 2. 如果没有,通过proto指向去构造函数的prototype里找有没有 3. 如果没有,通过prototype执向父级构造函数的prototype里找有没有,直到找到object为止 注意: 1. 通过proto到prototype中找到的属性用person.hasOwnProperty()去查找属性返回false,通过in查找返回true 2. 在打印对象时会自动掉用Object中的toString()方法,如果不想用Object中的方法,可以给对象添加toString()方法 ### constructor - proto中有一个constructor,最终通过prototype指回构造函数 - prototype中有一个constructor,指回构造函数 注意:如果prototype被覆盖了,需要重新将constructor:构造函数名(重新指回构造函数) ### 原型链 ![原型链](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/%E5%8E%9F%E5%9E%8B%E9%93%BE.png) ```java function Star(uname, age) { this.uname = uname; this.age = age; } //Star.prototype被对象赋值覆盖了,所以没有了constructor属性 Star.prototype = { constructor: Star, sing: function () { console.log('我会唱歌'); }, movie: function () { console.log('我演电影'); } } var ldh = new Star('刘德华', 12); console.log(Star.prototype); console.log(Star.prototype.__proto__ === Object.prototype); console.log(Object.prototype.__proto__); ``` ==注意==: 1. Object是对象也是函数,Object.proto===Function.prototype,Object是Function的实例 2. function也是==Function(构造函数)==的实例 new Function(),Star.proto->Function.prototype 3. Function也是Function的实例,Function.proto = Function.prototype 4. Star.prototype.proto->Object.prototype 5. ldh.proto->Star.prototype,Star.prototype.proto->Object.prototype ### 成员查找机制 1. 当访问一个对象的属性(包括方法时),首先查找这个对象自身有没有该属性 2. 如果没有就查找它的原型(也就是proto指向的prototype原型对象) 3. 如果还没有就查找原型对象的原型 4. 以此类推直到Object为止(null) 5. proto对象原型的意义就在于为对象成员查找机制提供一个方向路线 ### instance of ![原型链2](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/%E5%8E%9F%E5%9E%8B%E9%93%BE2.png) 通俗一点来讲,`instanceof`的判断规则是:**`instanceof`会检查整个原型链,将沿着A的`__proto__`这条线来一直找,同时沿着B的`prototype`这条线来一直找,直到能找到同一个引用,即同一个对象,那么就返回`true`。如果找到终点还未重合,则返回`false`**。即上图中的 `f1`-->`__proto__` 和 `Foo`-->`prototype` 指向同一个对象,`console.log(f1 instanceof Foo)`为`true`。 ### this指向 1. 在构造函数中,里面的this指向的是实例对象 2. prototype中的this也指向实例对象,因为实例对象调用原型对象里面的方法 ```javascript var that; function Star(uname, age) { this.uname = uname; this.age = age; } //Star.prototype被对象赋值覆盖了,所以没有了constructor属性 Star.prototype = { constructor: Star, sing: function () { that = this; console.log('我会唱歌'); }, movie: function () { console.log('我演电影'); } } var ldh = new Star('刘德华','12'); ldh.sing(); console.log(that === ldh);//true console.log(Star); ``` ## 改变this指向 ### this指向的4种形式 1. 一般函数,this指向全局对象window 2. 在严格模式下,为undefined 3. 对象的方法里调用,this指向调用该方法的对象 4. 构造函数里的this,指向创建出来的实例 5. 箭头函数中this指向上下文 ### call - 第一个参数:改变this指向 - 第二个参数:实参 - 使用时候会自动执行函数 ```javascript //A.call( B,x,y ):就是把A的函数放到B中运行,x 和 y 是A方法的参数。 var obj={} function fun(){} fun.call(obj) ``` ### apply - 第一个参数:改变this指向,在函数运行时才会改变this指向 - 第二个参数:数组(里面为实参) - 使用时候会自动执行函数 - 主要应用:Math.max.apply(Math,arr)//这里this的指向还是Math不过可以比较数组中的最大值 ==注意==: 1. call和apply这两个方法都是函数对象的方法,需要通过函数对象来调用 2. 调用call和apply会将一个对象指定为第一个参数,此时这个对象会称为函数执行时的this 3. call(obj,2,3)方法可以将实参在对象之后依次传递,apply需要将实参封装到数组中统一传递 ### bind - 第一个参数:改变this指向 - 第二个参数之后:实参 - 返回值为一个新的函数 - 使用的时候需要手动调用下返回 的新函数(==不会自动执行==) ## 原型继承 ### 继承属性 通过构造函数+原型对象模拟实现继承,称为组合继承 **原理**:同call()把父类型的this改为子类型的this ```javascript //通过借用父构造函数来继承属性 //1。父构造函数 function Father(uname, age) { //this指向父构造函数的对象实例 this.uname = uname; this.age = age; } //2.子构造函数 function Son(uname, age) { //this指向子构造函数的对象实例,在Father执行时指定this值,这个this是子构造函数中的 Father.call(this, uname, age); } var ldh = new Son('刘德华', 12); console.log(ldh); ``` ### 继承方法 直接给构造函数添加的是静态方法,给构造函数的prototype添加的是实例方法 **原理**:创建一个父类的实例,让子类的原型对象执向这个实例所在的内存空间,但是子类的prototype里的constructor被覆盖了,需要重新指向子类构造函数,这样就能通过父类实例的中的proto找到父类中的方法,且在子类中添加自己的私有方法也不会影响父类。 ```javascript //通过借用父构造函数来继承属性 //1。父构造函数 function Father(uname, age) { //this指向父构造函数的对象实例 console.log(this); this.uname = uname; this.age = age; } Father.prototype.money = function () { console.log(100000); } //2.子构造函数 function Son(uname, age) { //this指向子构造函数的对象实例 Father.call(this, uname, age); } //Son.prototype = Father.prototype; Son.prototype = new Father(); Son.prototype.constructor=Son Son.prototype.exam = function () { console.log('孩子要考试'); } // var zxy = new Father('张学友', 12) var ldh = new Son('刘德华', 12); console.log(ldh); // console.log(zxy); ``` ### 扩展内置对象 ```javascript Array.prototype.sum = function () { var sum = 0; for (var i = 0; i < this.length; i++) { sum += this[i]; } return sum; } var arr1 = new Array(11, 22, 33); console.log(arr1.sum()); ``` ## Web Workers 1. 主线程才能操作dom,web workers可以将大计算量的代码另外开一个线程去计算,从而防止冻结用户界面 ```html Title ``` ```javascript onmessage = function (event) { postMessage(f(event.data)) } function f(n) { return n <= 2 ? 1 : f(n - 1) + f(n - 2) } ``` ### 缺点 1. 慢 2. 不能跨域加载js 3. worker内代码不能访问DOM 4. 浏览器兼容问题 ## 函数 1. 函数也是一个对象 2. 函数不会检查实参类型和数量,少了则为undefined,多了则忽略 3. return后的语句不会执行,return;后面不跟值相当于返回undefined,不写return也返回undefined。使用return结束整个函数 4. 函数对象也可以return 5. 实参会存到arguments(伪数组)中,函数中不定义形参也能使用,arguments有一个属性callee,是当前正在执行得函数对象 ### 定义 1. 命名函数 function fn(){ } 2. 匿名函数 var fun = function() {} 3. new Function('参数1',‘参数2’,‘函数体’) var f = new Function(“xxxx ”); ==注意==:所有函数都是Function的实例 ![function](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/function.png) ### 执行上下文栈 1. 在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象 2. 在全局执行上下文(window)确定后,将其添加到栈中 3. 在函数执行上下文创建后,将其添加到栈中(压栈) 4. 在当前函数执行完后,将栈顶的对象移除(出栈) 5. 当所有的代码执行完后,栈中只剩下window ```javascript //进入全局执行上下文 var a = 10 var bar = function(x){ var b = 5 foo(x+b) } //进入foo执行行下文 var foo = function(y){ var c = 5 console.log(a+c+y) } //进入bar函数执行上下文 bar(10) ``` ### 调用方式和this指向 - 解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的上下文对象 1. 普通函数 fn();//实际上是windows.fn() this指向windows 2. 对象的方法 obj.xxx(); this指向对象 3. 构造函数里的方法 function xxx(){} new xxx(); this指向实例,包括构造函数的prototype原型对象里的this也指向实例 4. 事件函数 btn.onclick=function(){} this执向调用者btn 5. 定时器函数 setInterval(function(){},1000)//实际上是windows.setInterval this指向window,只能用bind(this)改变this指向,因为call和apply是立即执行。 6. 立即执行函数 (function(){})()或者(function (){} ()) this 指向windows ## 高阶函数 它接收函数作为参数,将函数的返回值作为输出 ```javascript /** * 数值转换 * @param {Number} val 要被处理的数值 * @param {Function} fn 处理输入的val * @return {Number || String} */ const toConvert = function(val, fn) { return fn(val); }; const addUnitW = function(val) { return val + 'W'; }; toConvert(123.1, Math.ceil); // 124 toConvert(123.1, addUnitW); // "123.1W" ``` 另外,JS的回调函数同样是以实参形式传入其他函数中,这也是高阶函数(在函数式编程中回调函数被称为 lambda表达式): ```javascript [1, 2, 3, 4, 5].map(d => d ** 2); // [1, 4, 9, 16, 25] // 以上,等同于: const square = d => d ** 2; [1, 2, 3, 4, 5].map(square); // [1, 4, 9, 16, 25] ``` ## 闭包 ### 变量作用域 - 局部变量(函数作用域) 1. 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁 2. 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的 3. 在函数作用域中可以访问全局变量,反之不行 4. 当在函数作用域中操作一个变量时,有则直接使用。没有,则去上一级作用域中去寻找 5. 定义形参就相当于var 定义变量 - 全局变量(全局作用域) 1. 在打开页面时创建,关闭页面时销毁 2. 直接编写在script标签中的js代码,都在全局作用域 3. 在函数中不用var定义的变量也在全局作用域中 4. 全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用 5. 创建的变量都变成window对象的属性,创建的函数都变成window对象的方法 - 当函数执行完毕,本作用域内的局部变量就会自动销毁 ### 闭包的产生 1. 当一个嵌套的内部函数引用了嵌套的外部函数的变量(函数),就产生了闭包 2. 通过chrome的debug调试查看closure就是闭包 ### 闭包的定义 指有权访问另一个函数作用域中变量的函数 ### 闭包的作用 在函数a执行完毕并返回后,闭包使得Js的垃圾回收机不会回收a所占用的资源,因为a的内部函数b需要依赖a中的变量,从而延伸了变量的作用范围(生命周期)。 ### 闭包的生命周期 产生:在嵌套内部函数定义执行完成时就产生了(不是在调用时) 死亡:在嵌套的内部函数成为垃圾对象时 ###### demo ```javascript 1.function fn() { var num = 10; return function fun() { console.log(num); }; } //fn外面的作用域访问fn()内部的局部变量 fn()();//10 2. window.onload = function () { function fn() { let a = 2 function add() { a++ console.log(a) } function minus() { a-- console.log(a) } return {add, minus} } //没有触发闭包 fn().add()//3 fn().minus()//1 //区别 let fn1 = fn() //触发闭包 fn1.add()//3 fn1.minus()//2 } //如果fn()写成var f = fn() //那么再执行完后就不会释放闭包中的a,因为f变量指向fn(),return的函数中有对a的引用 //当f=null时,闭包死亡,因为包函闭包的函数对象成为垃圾对象 ``` ### 闭包的例子 ###### 获取ul中li的索引号 - 最初的方法 ```javascript var lis = document.querySelector('.nav').querySelectorAll('li'); for (var i = 0; i < lis.length; i++) {//注意:这里lis是一个伪数组,循环中lis.length会计算多次,所以写成for(var i = 0 ,length=lis.length; i < length ;i++)这样能加快运行效率 lis[i].setAttribute('index', i); lis[i].addEventListener('click', function () { console.log(this.getAttribute('index')); }) }//在循环的同时给每个li添加自定义属性index,需要时再读取属性值 ``` - 利用闭包 ```javascript for (var i = 0; i < lis.length; i++) { //创建了4个立即执行函数 (function (num) { lis[num].onclick = function () { console.log(num); }func })(i) } ``` 这里面的闭包指的是匿名函数,通过(i)把值保存到了num中。每个点击事件都有一个局部变量num,num保存的是相应的i值。 - 错误写法 ```javascript for (var i = 0; i < lis.length; i++) { lis[i].addEventListener('click', function () { console.log(i); }) }//无论点击哪个li都输出4 ``` 每个li标签的onclick事件执行时,本身onclick绑定的function的作用域中没有变量i,i为undefined,则解析引擎会寻找父级作用域,发现父级作用域中有i,且for循环绑定事件结束后,i已经赋值为4,所以每个li标签的onclick事件执行时,log的都是父作用域中的i,也就是4。 ###### 经典例子 ```javascript function fn() { var num = 3; return function () { var n = 0; console.log(++n); console.log(++num); } } fn()();// 1 4 fn()();//1 4 var f1 = fn(); f1();//1 4 f1();//1 5 ``` 直接调用fn函数,此时fn执行完后,就连同它的变量num一同销毁,但是如果将fn的返回值赋给f1,这时候相当于f1=function(){var n = 0};并且匿名函数内部引用这fn里面的变量num,所以变量num无法被销毁,而变量n在调用完后会销毁,在每次调用时新建,于是最后只剩下num,所以再次调用时num是在4的基础上+1。 ### 定时器中的闭包函数 ```javascript //3秒后同时打印所有li元素的内容 var lis = document.querySelector('.nav').querySelectorAll('li'); for (var i = 0; i < lis.length; i++) { (function (a) { setTimeout(function () { console.log(lis[a].innerHTML); }, 3000) })(i); } //每隔3秒打印一个li元素的内容 var lis = document.querySelector('.nav').querySelectorAll('li'); for (var i = 0; i < lis.length; i++) { (function (a) { setTimeout(function () { console.log(lis[a].innerHTML); }, a*3000) })(i); } ``` ### 闭包的应用:定义js模块 ```html Title ``` ```javascript function myModule() { let msg = 'my msg' function doSomething() { console.log(msg.toLowerCase()) } function doSomething2() { console.log(mag.toUpperCase()) } return { doSomething, doSomething2 } } //如果是匿名函数的自调用,则暴露在window对象中 (function myModule2() { let msg = 'my msg' function doSomething() { console.log(msg.toLowerCase()) } function doSomething2() { console.log(mag.toUpperCase()) } window.myModule2 = { doSomething, doSomething2 } })() ``` ### 闭包的缺点 因为闭包会使得Js的垃圾回收机不会回收占用的资源,滥用闭包会造成内存泄露,所以在必要时,我们要及时释放这个闭包函数 ### 内存溢出与内存泄漏 内存溢出:栈溢出,堆溢出 内存泄露:内存泄漏积累多了就容易导致内存溢出 常见的内存泄漏: 1. 意外的全局变量 2. 没有及时清理的计时器或回调函数 3. 闭包 ### Let的出现 将上面的错误写法中的var改为let,let会产生块级作用域 ```javascript for (let i = 0; i < lis.length; i++) { lis[i].addEventListener('click', function () { console.log(i); }) }//点击li输出相应的索引 ``` ## 递归 函数内部自己调用自己,作用和循环一样 注意:防止发生栈溢出,所以要加退出条件return ```html ``` ## 浅拷贝和深拷贝 - 浅拷贝只是拷贝一层,更深层次的对象级别的只拷贝引用(修改数据会修改原来的) ```javascript var obj1 = { name: 'zs', age: 18, sex: '男', dog: { name: '金毛', age: 2, yellow: '黄色' } } var obj2 = {}; function copy(o1, o2) { for (var key in o1) { o2[key] = o1[key]; } } copy(obj1, obj2); // 修改obj1中的成员 obj1.name = 'xxxx'; obj1.dog.name = '大黄'; console.dir(obj2); // name 属性深拷贝,不改变;dog对象浅拷贝, dog.name 随之发生改变 ``` ES6语法糖:Object.assign(new,old) - 深拷贝拷贝多层,每一级的数据都会拷贝 ```javascript //深拷贝 function deepcopy(newer, old) { for (var k in old) { //判断属性值属于声明类型 // 1.获取 var item = old[k]; // 2.判断是否为数组 if (item instanceof Array) { newer[k] = []; deepcopy(newer[k], item) } else if (item instanceof Object) { // 3.是否为对象 newer[k] = {}; deepcopy(newer[k], item); } else { // 4.属于简单数据类型 newer[k] = item; } } } deepcopy(b, obj); console.log(b); b.msg.age = 30; console.log(obj); ``` ==注意==:先判断数组再判断对象,因为数组也属于对象,如果对象中没有function和RegExp且为json对象可以先用 JSON.stringify转为字符串再用JSON.parse转为新的对象 ## 正则 ### 创建 ```javascript var regexp = new RegExp("正则表达式","匹配模式"); //正则表达式为字符串,匹配模式包括i(忽略大小写),g(全局匹配模式) var regexp = / /i; //字面量创建 ``` ### 检测 ```javascript regexp.test(str); //返回布尔值 ``` ### 特殊字符 #### 边界符 - ^:行首文本以谁开始 - &:行尾文本以谁结束 - \bxxx\b:表示单词边界 /^abc$/ #### 字符类 - [ ]:中括号里的内容是或的关系 ```javascript /[abc]/ 表示包含有a或b或c的一个或多个 /^[abc]$/ 表示有且仅有其中一个 /[^abc]/ 除了中括号里的 ``` #### 量词符 用于设定每个模式出现的次数 | 量词 | 说明 | | ----- | ----------------------------- | | * | 重复0次或更多次,相当于{0,} | | + | 重复一次或更多次,相当于{1,} | | ? | 重复0次或一次,相当于{0,1} | | {n} | 重复n次 | | {n,} | 重复n次或更多次 | | {n,m} | 重复n次到m次(中间无空格) | ```javascript /^a*$/ /^a{3,8}$/ /^abc{3}$/ /^[abc]{3}$/ //[]内的单个内容可以重复三次 /^(abc){3}$/ //()内的重复三次 ``` ### 正则中的预定义类 | 预定义类 | 说明 | | -------- | ------------------------------------------------------------ | | \d | 匹配0-9之间的任一数字 | | \D | 匹配0-9以外的字符,相当于/[^0-9]/ | | \w | 匹配任意的字母,数字和下划线,相当于/[A-Za-z0-9_]/ | | \W | 除了所有字母,数字和下划线以外的字符,相当于/[^A-Za-z0-9_]/ | | \s | 匹配空格(包括换行符,制表符,空格符等),相当于[\t\r\\n\v\f],去除开头和结尾空格/^\s*\|\s*$/ g | | \S | 匹配非空格的字符,相当于/[^\t\r\n\v\f]/ | ### 正则中的替换 - replace(//,'')只替换第一个,加上/g全局替换 ```javascript btn.onclick = function () { div.innerHTML = text.value.replace(/激情|gay/g, '**'); } ``` ### 修饰符 - /g:全局匹配 - /i:忽略大小写 - /gi:全局匹配且忽略大小写 | | | | --------- | ------------------------------------- | | [ ] | 中括号里的内容是或的关系 | | [ab] | a\|b | | [a-z] | 任意小写字母 | | [A-Z] | 任意大写字母 | | [A-z] | 任意字母 | | /a[bde]c/ | 检测一个字符串中是否含有abc或adc或aec | | /[^]/ | 除了中括号里的都行 | # ES5 ## 严格模式 为脚本开启‘use strict’写在script标签第一行 为函数开启写在函数体第一行 ### 特点 1. 变量必须先声名 ==区别:==标准模式中 ​ a=2||this.a=2是给window对象的一个属性,可以用delete删除 ​ var a=2也是给window对象一个属性,但是不能用delete删除 2. 全局作用域中this是undefined,而不是windows对象 3. 构造函数不加new调用(不创建实例)的话会报错,因为严格模式中this为undefined,要new之后this才有指向的实例对象,标准模式中构造函数没有实例对象,它的this也是指向window对象的 ==注意:==定时器的this还是指向window 4. 参数不能同名 ## JSON对象 写法: - 只能用双引号 - 所有名字都必须用双引号包起来 #### JSON对象转JSON字符串 JSON.stringify(json) #### JSON字符串转JSON对象 JSON.parse(json) #### JSON的简写 名字和值一样时保留一样即可 ```javascript let a = 12; let b = 15; let c = 30; let json = {a,b,c} //相当于 let json = {a:a,b:b,c:c} ``` json中方法的简写 ES6中可以和面向对象中的方法一样把function删除 ```javascript let json = { "name":"xqz", show(){ alert(this.a) } } //相当于 let json = { "name":"xqz", show: function(){ alert(this.a) } } ``` ## Object扩展 常用的三个静态方法 ### Object.create(prototype,[descriptors]) 作用: 1. 以指定对象为原型创建新的对象(通过__proto__可以找到) 2. 为新的对象指定新的属性,并对属性进行描述 value:指定值 writable:标识当前属性值是否可以修改,默认为false configurable:标识当前属性是否可以被删除,默认为false enumberable:标识当前属性是否能用for in 枚举 默认为 false ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/ES5Object%E6%89%A9%E5%B1%951.png) ### Object.defineProperty() - 定义对象中新属性或修改原有的属性 - obj:目标对象 - prop:要增加或修改的属性名字 - descriptor:增加或修改的属性所持有的特性 1. value:设置属性的值,默认为undefined 2. writable:值是否可以重写,默认为false,遍历时就无法遍历出来 3. enumerable:目标属性是否可以被枚举,默认为false也遍历不出来 4. configurable:目标属性是否可以别删除或者再次修改特性,默认false ```javascript Object.defineProperty(obj, 'num', { value: 20000, writable: false, enumerable: false, configurable: false }) delete obj.num; console.log(obj); ``` ### Object.defineProperties(object,descriptors) 作用: 1. 为指定对象定义扩展多个属性 2. get:用来获取当前属性值的回调函数 3. set:用来修改当前属性值的触发回调函数,并且实参即为修改后的值 4. 存取器属性:setter,getter一个用来存,一个用来取 ```javascript let obj = {firstName:'x',lastName:'qz'} Object.defineProperties(obj,{ fullName:{ get:function () { return this.firstName+this.lastName//获取属性时自动调用get方法 }, set:function (data) {//设置属性时自动调用set方法 let arr = data.split(',') this.firstName=arr[0] this.lastName=arr[1] } } }) console.log(obj.fullName) obj.fullName='w,xx' console.log(obj.fullName) ``` 对象本身的两个方法 get propertyName(){} set propertyName(){} ```javascript let obj2={ firstName:'x', lastName:'qz', get fullName(){ return this.firstName+''+this.lastName }, set fullName(data){ let arr = data.split(' ') this.firstName = arr[0] this.lastName = arr[1] } } console.log(obj2) obj2.fullName='w xx' ``` ## Array扩展 ### indexOf Array.prototype.indexOf(value) 得到值在数组中的第一个下标 ### lastIndexOf Array.prototype.lastIndexOf(value) 得到值在数组中的最后一个下标 ### forEach Array.prototype.forEach(function(item,index){}) 遍历数组 1. array.forEach(function(currentValue,index,arr)) 遍历每个元素,将每个元素的返回值给回调函数 2. 数组中有几个元素函数就会执行几次,每次执行时,浏览器会将遍历到的元素以实参的形式传递进来,我们可以来定义形参来读取这些内容 3. 浏览器会在回调函数中传递三个参数 - currentValue:数组当前项的值 - index:数组当前项的索引 - arr:数组对象本身 ==**注意**==:如果return false会阻止函数继续向下执行,但不会结束遍历,会继续遍历下一个元素,无返回值 ```javascript var arr = ['red', 'green', 'blue', 'pink']; arr.forEach(function (value) { if (value == 'green') { console.log('找到'); return false; } console.log(11); }) ``` Array.prototype.map(function(item,index){}) 遍历数组返回一个新数组,返回加工后的值 ### map 映射,给多少处理完后还多少 map() 方法==返回一个新数组==,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。 ```javascript var array1 = [1,4,9,16]; const map1 = array1.map(x => x *2); console.log(map1); ``` ### filter Array.prototype.filter(function(item,index){}) 遍历过滤出一个新的子数组,返回条件为true的值 1. array.filter(function(currentValue,index,arr))会==返回一个新的数组==,新数组中是通过检查指定数组中符合条件的所有元素,主要用于筛选数组 2. 通过return true或false来决定保留不保留 **注意**:直接返回新数组 ```javascript var arr = [23, 13, 545, 52]; var newArr = arr.filter(function (value, index, array) { return value >= 20; }) console.log(newArr);//[23,545,52] ``` ### some array.some(function(currentValue,index,arr))用于查找数组中是否有满足条件的元素 ==**注意**==: - 返回的是布尔值,找到返回true且找到后不会再继续执行,找不到返回false。 ```javascript var arr = [3, 2, 3, 5, 5, 4, 23, 55, 45, 435, 3, 4524, 5, 245, 2, 52]; var arr1 = arr.some(function (value, index, array) { return value >= 20; }); console.log(arr1);//true ``` **区别**:foreach和filter不会终止迭代,some检测到符合要求的元素后会停止,且返回值三者不同 ### reduce(参数一,参数二) 参数一是回调函数 参数二是tmp的默认值,0 进去一堆,出来一个 1. tmp为中间结果,是函数返回的结果,当遍历到数组最后一个元素是=0时,函数的返回值则为最终结果 2. item为数组中的每一项 3. index为数组索引 ```javascript var arr = [12,34,3,4,2]; var result = arr.reduce(fucntion(tmp,item,index){ if(index != arr.length-1){ return tmp*item;//求和 }else{ return tmp*item/arr.length;//最后一次求平均数 } }) ``` ## Function扩展 Function.prototype.bind() ![bind](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/bind.png) # ES6 ### let 1. 产生块级作用域 2. for循环中防止i变成全局变量 3. let无变量提升(js预解析会把var和function(){}提到最前面) 4. 暂时性死区 5. 不能重复声明相同的变量名 ```javascript var tmp = 123; if (true) { tmp = 'avd'; // let tmp;如果在if中需要用一样的变量名,用let声名 } console.log(tmp); ``` ```html      ``` ### const 1. 用来声名常量,值无法更改 2. 也具有块级作用域,不存在变量提升 ```javascript if (true) { const a = 10; if(true){ const a=20; console.log(a); } console.log(a); } ``` 3. 必须赋初始值 4. 赋值后的内存地址不能修改,但是复杂数据类型内的值可以改 ```javascript const arr=[100,100]; arr[0]='a';//这里没有修改内存地址,所以是可以修改的 arr=['a',100];//这里修改了内存地址,所以是无法修改的 ``` ### 解构赋值 如果解构后无值则为undefined ```javascript //数组解构 let [a, b, c] = [1, 2, 3]; console.log(a); let person = { name: 'xuqianzhou', age: 13 }; let { name, age } = person; console.log(name); ``` ### 箭头函数 - 不能使用argunments来获取参数 - 函数体只有一句可以省略大括号 - 只有一个参数可以省略括号 - 箭头函数不绑定this关键字,箭头函数中的this指向的是函数定义位置上下文的this(所处的对象) 1. 如果箭头函数外层有函数,箭头函数的this和外层函数的this一摸一样 2. 如果外层没有函数,指向window ```javascript ``` 注意: ```javascript var age = 100; var obj = { age: 20, say: () => { console.log(this);//这里的this指向window alert(this.age); } } obj.say(); ``` ### 形参默认值 ```javascript function show(a,b=5,c=10){ //当不传b的值,默认为5 } ``` ### 三点运算符 - 将不定数量的参数表示为一个数组,只能放在最后 ```javascript const sum = (num, ...arr) => { console.log(num); console.log(arr); let total = 0; arr.forEach(item => total += item); return total;//50 }; console.log( sum(10, 20, 30)); ``` - 可以配合解构使用 ```javascript var obj={xxx,xxx,xxx}; let {s1,...s2}=obj;//s1是obj对象中的第一个数据,剩下的在s2数组中 ``` ![三点运算符](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/%E4%B8%89%E7%82%B9%E8%BF%90%E7%AE%97%E7%AC%A6.png) ### JSON 写法: - 只能用双引号 - 所有名字都必须用双引号包起来 #### JSON对象转JSON字符串 JSON.stringify(json) #### JSON字符串转JSON对象 JSON.parse(json) #### JSON的简写 名字和值一样时保留一样即可 ```javascript let a = 12; let b = 15; let c = 30; let json = {a,b,c} //相当于 let json = {a:a,b:b,c:c} ``` json中方法的简写 ES6中可以和面向对象中的方法一样把function删除 ```javascript let json = { "name":"xqz", show(){ alert(this.a) } } //相当于 let json = { "name":"xqz", show: function(){ alert(this.a) } } ``` ### Array的扩展方法 #### 扩展运算符 将数组或对象转为用逗号分隔的参数序列,其实就是和剩余参数一个相反的过程,这里用逗号拆开,剩余参数是合并为一个数组 ```javascript let arr=[1,2,3]; log(...arr)//1 2 3(逗号被log当作参数分隔符) ``` - 可以用于合并数组 ```javascript arr1=[1,2,3]; arr2=[4,5,6]; arr3=[...arr1,...arr2]; //或者 arr1.push(...arr2); ``` - 可以将伪数组转为真正的数组 ```javascript //方法一: arr2=[...arr1];//arr1是伪数组; //方法二: let arr2=Array.form(arr1,item=>item+1);//将arr1伪数组转为真正的数组,并且每个数组元素加1 ``` #### Array.form() 将伪数组转为数组且对元素进行加工 #### Array.find() 用于找出第一个符合条件的数组成员,返回对象,如果没有找到则返回==undefined== ```javascript let arr[{ id:1, name:'张三' },{ id:2, name:'李四' }]; let target = arr.find((item,index)=>item.id==2);//函数用于返回查找的条件 ``` #### Array.findindex() 用于找出第一个符合条件的数组成员的==位置==,如果没有返回==-1== ```javascript let arr=[1,5,9,15]; let index=arr.findindex((value,index)=>value>9);//函数用于返回查找的条件 log(index);//2 ``` ### String的扩展方法 #### 模板字符串 - 可以解析变量 ```javascript `hello,my name is${变量名}` ``` - 可以换行 - 可以调用函数 ```javascript const sayhello = function(){ return....; } let agree = `hello,my name is ${sayhello()}` ``` #### includes() 判断是否包含指定的字符串 #### startsWith() #### startsWith() 是否以某字符开头,返回布尔值 startsWith('Hello') #### endsWith() 是否以某字符结尾,返回布尔值 endsWith('!') #### repeat() 将原字符重复n次,返回一个新的字符串 str.repeat(3); #### trim() 删除字符串两边的空格,不影响原字符串,==返回一个新的字符串==(防止用户输入空格) ### Number的扩展方法 二进制与八进制数值表示法:二进制0b,八进制0o #### isFinite() 判断是否是有限大的数 #### isNaN() 判断是否是NaN #### isInteger() 判断是否是整数 #### parseInt() 判断是否是整数 #### trunc() 将字符串转换为对应的数值 ### Object的扩展方法 1. #### Object.is(v1,v2) 判断2个数据是否相等 1. #### Object.assign(target,source1,source2..) 将源对象的属性赋值到目标对象上,如果没有则使用原来的默认值 2. #### 直接操作_proto_属性 let obj2 = {} obj2._proto_ = obj1 3. #### Object.keys() - 用于获取对象自身所有的属性 - 返回由属性名组成的数组 ```javascript var obj = { id: 1, pname: '小米', price: 1999, num: 2000 } var arr = Object.keys(obj); console.log(arr); ``` ### Promise 作用:将异步操作写成同步代码,避免了回调地狱 promise对象的3个状态 1. pending:初始化状态 2. fullfilled:成功状态 3. rejected:失败状态 过程: 1. 首先new promise对象 2. 在异步代码执行成功时,调用resolve()方法,改变对象状态,传的参数在promise实例的then方法中的第一个函数中获取 3. 异步代码执行失败时,调用reject()方法,改变对象状态,传的参数在promise实例的then方法中的第二个函数中获取 ```javascript let promise = new Promise((resolve,reject)=>{ //初始化promise状态:pending:初始化 console.log('111') //执行异步操作,通常是发送ajax请求,开启定时器 setTimeout(()=>{ console.log(333) //根据异步任务的返回结果去修改promise的状态 //异步任务执行成功 resolve(data) //修改promise的状态为 fullfilled:成功的状态 //异步任务执行失败 // reject(err) //修改promise的状态为 rejected:失败的状态 },2000) }) promise.then((data)=>{ //成功的回调 console.log('成功') },(err)=>{ //失败的回调 console.log('失败') }) ``` ### symbol ![symbol](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/symbol.png) ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/symbol2.png) ==注意==: 1. for in 和for of 不能遍历symbol属性 对象的Symbol.iterator属性:指向对象的默认遍历器方法(fenerator) ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/%E5%AF%B9%E8%B1%A1%E7%9A%84iterator%E6%8E%A5%E5%8F%A3.png) ==注意==: 1. 三点运算符和解构赋值实际上就是实现了iterator接口 ### iterator遍历器 概念:是一种接口机制,为各种不同的数据结构提供统一的访问机制 #### 作用: 1. 为各种数据结构,提供一个统一的,简便的访问接口 2. 使得数据结构的成员能够按照某种次序排列 3. ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费 #### 工作原理: ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/iterator%E6%8E%A5%E5%8F%A3.png) #### 底层实现: 实际上这里的nextIndex使用了闭包 ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/iterator%E6%9C%BA%E5%88%B6.png) #### 实现了iterator接口的数据结构 ![](assets/%E5%AE%9E%E7%8E%B0%E4%BA%86interator%E7%9A%84%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.png) ==注意==: 1. 对象没有iterator接口,无法用for of去遍历循环 ### generator ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/generator.png) 普通函数:一路到底 generator函数:函数加*,中间能停,next一次走一步,yield处停止 1. yield的值是next中传入的参数,==注意==:执行函数的第一个next不能传参,因为传了也不能赋值给变量,第二个next传的参数是第一个yield的返回值 2. 调用next方法的返回值是函数return的值,或者yield后面的值 3. ==生成遍历器对象==,给对象的Symbol.iterator属性添加一个遍历器对象后,就能用for of遍历 ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/generator2.png) #### yield 可以传参,返回 看成把一个函数分成多个子函数,show_1,show_2 ```javascript //可以传参 function* show() { alert('a') let a = yield alert('b') console.log(a) } let genObj = show()//yield之前函数传参 genObj.next(12)//没法给yield传参,执行函数开始到第一个yield,此时yield有值,但是并没有给变量a genObj.next(5)//执行yield往后的代码,let a是在yield之后 //可以返回 function* show2() { alert('a') yield 12 alert('b') return 55 } let genObj2 = show2() let res1 = genObj2.next() console.log(res1) let res2 = genObj2.next()//这里的结果是通过函数中的return得到的 console.log(res2) ``` ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/generatorAjax.png) ==注意==: 1. 在getNews里面调用next方法并且传参,这样能在generator中得到url的值 ### class类和继承 #### 类和对象 - 关键字:class 注意:类中所有函数不需要加function关键字,多个方法间不加逗号分隔. - 方法中的this指向:方法的调用者 #### 构造器 - 关键字:constructor() - 作用:用于传递参数,返回实例对象,new生成对象实例时自动调用。 注意:如果未定义会自动创建一个constructor() - this指向:实例对象 ```html ``` ==注意==: this的指向问题 ```html ``` #### 类的继承 ##### 关键字 - ### entends - 注意:ES6中类没有变量提升,必须先定义类,才能实例化 ##### 继承属性 调用父类构造函数: - 关键字:super(x,y) 注意:在constructor中调用,且必须在子元素的this之前调用 ```html ``` ##### 继承方法 - 子类实例会自动继承父类的方法 - 在子类中调用父类方法: - 关键字:super.父类方法名() ```html ``` ### 深度克隆 1. arr.concat():数组浅拷贝 2. arr.slice():数组浅拷贝 3. JSON.parse(JSON.stringify(arr/obj)):数组或对象深拷贝,拷贝的数据里不能有函数,这里实际上拷贝的是json字符串,是基本数据类型 4. Object.assign();浅拷贝 5. 浅拷贝拷贝引用,拷贝以后的数据会影响原数据 6. 深拷贝拷贝值,拷贝以后的数据不会影响原数据 #### 封装深拷贝函数 ```javascript let arr = [1, 2, {userName: 'xqz'}, 3, 2] function clone(a) { if (a instanceof Array) { let arr1 = [] for (let i = 0; i < arr.length; i++) { if (arr[i] instanceof Object) { arr1[i] = clone(arr[i]) } else { arr1[i] = arr[i] } } return arr1 } else if (a instanceof Object) { let obj = {} for (const key in a) { obj[key] = a[key] } return obj } } let newArr = clone(arr) console.log(newArr) newArr[2].userName = 'hhh' console.log(arr) console.log(newArr) ``` ### set容器 1. 无序的,不可重复的多个value的集合体 - 类似于数组,但是成员不能重复,会自动去重 ```javascript const s = new Set(); const s = new Set([1,2,3,4,4])//可以接收数组初始化,会自动去重4 ``` - 数据数量 s.size(); - 可以用于数组去重 ```javascript var arr = [1,2,3,4,4]; const s = new Set(str); var arr = [...s];//这里用扩展运损符将s用逗号分隔开 ``` #### 实例方法 - add(value):添加某个值,返回Set结构本身 - delete(value):删除某个值,返回布尔值,表示是否和删除成功 - has(value):返回布尔值,表示该值是否为set的成员 - clear():清除所有成员,没有返回值 #### Set遍历 和数组一样有foreach方法,无返回值 ##### s.forEach(value=>log(value)) ### map容器 1. 无序的key,不重复的多个key-value的集合体 Map() Map(array) set(key,value)//添加 get(key) delete(key) has(key) clear() size ```javascript let map = new Map([['username',25],[36,'age']]) console.log(map) map.set(78,'haha') console.log(map) map.delete(78) console.log(map) ``` ### for of 1. 遍历数组 2. 遍历set 3. 遍历map 4. 遍历字符串 5. 遍历伪数组 # ES7 ## async/await - ES7的新语法,可以更加方便得进行异步操作 - async用于函数上(async函数得返回值时promise对象) - await用于saync函数中(await可以得到当前异步的结果) ==注意==:因为async返回的也是个promise对象,在调用时也可以用then获得函数的返回值 ```javascript async function queryData() { var ret = await axios.get('adata'); console.log(ret.data); return ret.data; } queryData().then(data => { console.log(data); }); ``` ==注意==:await后面必须跟promise实例对象,才能获取异步的结果 ```javascript async function num() { var ret = await new Promise(function (resolve, reject) {//resolve成功,reject失败 setTimeout(function () { resolve('nihao') }, 1000) }); console.log(ret); return ret; }; num().then(data => { console.log(data); }) ``` - 处理多个异步任务 桉顺序写即可 ```javascript axios.defaults.baseURL = 'http://localhost:3000'; async function queryData() { var info = await axios.get('async1'); var ret = await axios.get('async2?info=' + info.data);//用info作为参数 return ret.data; } queryData().then(ret=>{ console.log(ret); }) ``` ## Array.includes() 表示某个数组是否包含给定的值,返回布尔值 ```javascript arr=[1,2,3]; arr.includes(2);//true ``` ## 指数运算符 ** # 模块化规范 ## namespace模式 ```javascript let obj ={ msg:'module', foo(){ console.log('foo()',this.msg) } } ``` ```html Title ``` ## IIFE模式 ```javascript //有独立的作用域 (function (window) { let msg = 'module' function foo() { console.log('foo()', msg) } //给全局的window对象添加属性 window.module={foo} })(window)//闭包 ``` ```html Title ``` ## IIFE模式增强 ```javascript //有独立的作用域 (function (window, $) { let msg = 'module' function foo() { console.log('foo()', msg) } window.module = foo $('body').css('background', 'red') })(window, jQuery) ``` ```html Title ``` ## CommonJS ### 规范 ![Commmonjs](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/Commmonjs.png) ![commonjs](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/commonjs.png) ### 基于服务器端 文件结构 ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/CommonJS-node.png) ```javascript //1 module.exports={ msg:'module1', foo(){ console.log(this.msg) } } //2 module.exports = function () { console.log('module2') } //3 exports.foo=function () { console.log('foo() module3') } exports.bar=function () { console.log('bar() module3') } exports.arr=[1,2,3,5,4,3,3,5] ``` ```javascript //app.js //引入第三方库 let uniq =require('uniq') //将其他的模块汇集到主模块 let module1 = require('./modules/module1') let module2 = require('./modules/module2') let module3 = require('./modules/module3') module1.foo() module2() module3.foo() module3.bar() let result = uniq(module3.arr) console.log(result) ``` ==注意==: 1. npm install xxx --save-dev 标识开发依赖包 ### 基于浏览器端 文件结构 ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/Commonjs-browserify.png) 1. 首先和服务端一样,用module.epxorts进行暴露,require进行引入 2. 其次 全局下载browserify包:npm install browerify 再局部下载开发依赖:npm install browerify --save-dev 3. 其次命令行打包处理: **错误内容:** `'browserify' 不是内部或外部命令,也不是可运行的程序 或批处理文件。` **已解决:** 命令行前面加上browserify的路径即可, `node_modules\.bin\browserify js/src/app.js -o js/dist/build.js` ==注意==: 1. 前面的是原文件 2. -o表示output 3. 后面的是输出文件的目录以及文件名字 4. 然后在index.html中引入输出的新文件,即可在浏览器端使用require ## AMD ### 规范 ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/AMD.png) 文件结构 ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/RequireJS.png) 下载requireJS 定义没有依赖的模块 ```javascript //没有依赖的模块 define(function () { let name = 'dataService' function getName() { return name } //暴露模块 return {getName} }) ``` 定义有依赖的模块 ```javascript //定义有依赖的模块 define(['dataService'],function (dataService) { let msg = 'alerter.js' function showMsg() { console.log(msg,dataService.getName()) } //暴露模块 return {showMsg} }) ``` main.js ```javascript (function () { requirejs.config({ baseUrl: 'js/',//基本的路径,出发点在根目录下 paths: {//配置路径 dataService: './modules/dataService',//属性名和之前定义的模块名一样 alerter: './modules/alerter' } }) requirejs(['alerter'], function (alerter) { alerter.showMsg() }) })() ``` ==注意==: 1. 引入jquery时要小写 index.html ```html Title //先通过src找到require第三方库,然后通过data-main找到main.js,再从main中的path找到相应的模块路径 ``` ## CMD ## ES6 ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/ES6.png) 步骤: 1. 定义package.json文件 2. 安装babel-cli,babel-preset-es2015和browserify - npm install babel-cli browserify -g - npm install babel-preset-es2015 --save-dev - preset 预设(将es6转为es5的所有插件打包) 3. 定义.babelrc文件, ```javascript { "presets": ["es2015"] } ``` 4. 编码 文件结构 ### ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/ES6babel.png) ```javascript //暴露模块 分别暴露 export function foo() { console.log('foo() module1') } export function bar() { console.log('bar() module1') } export let arr = [1,3,4,5] ``` ### 统一暴露 ```javascript //统一暴露 function fun() { console.log('fun() module2') } function fun2() { console.log('fun2() module2') } export {fun,fun2} ``` ### 默认暴露 ```javascript //默认暴露 可以暴露任意数据类型 暴露什么数据接收到的就是什么数据 //只能一次 // export default ()=>{ // console.log("默认") // } export default { msg:'默认暴露', foo(){ console.log(this.msg) } } ``` main ```javascript //引入其他的模块 //语法:import xxx from ‘路径’ import {foo,bar} from './module1' import {fun,fun2} from './module2' import module3 from './module3' console.log(module3.foo()) console.log(foo(),bar(),module2) console.log(module1,module2) ``` ​ ==注意==: 1. 要使用解构赋值的方式来引入模块 5.编译 - 使用babel将ES6编译为ES5代码:==babel js/src -d js/lib== ​ 源文件目录 编译后的文件目录 改成require那种es5的,但是require仍然需要转换 - 使用Browserify编译js:==browserify js/lib/main.js -o js/lib/bundle.js== 6.引入:index.html中使用bundle文件 ​ ==注意==: 1. 一旦修改了js文件就需要重新编译打包 打包完成后文件结构 ![](https://raw.githubusercontent.com/yzuxqz/pic-bed/master/notes-img/ES6%E6%89%93%E5%8C%85%E5%90%8E.png)