javaScript3
事件
1.函数
函数主要用于封装代码块
函数封装的步骤:
1)总结复用代码,用function包装复用代码。
2)发现可变量,抽取为形参。---结合项目观察某些变量值,考量是否更改当前变量就可实现多途径使用。
3)形参尽量不要超过四个。
2.作用域
调试工具断点打的其实是:执行环境。--代码运行的时候才会产生执行环境。
执行环境导致作用域存在。所以只有代码(函数)执行的时候才会产生作用域。
一个函数就是一个 独立作用域,和this毫无关系。
this只有在函数或全局代码执行的时候才存在。
AO:activation Object 简称AO ---活动性对象,函数执行的时候才会产生,一但函数执行结束就【被销毁】
一个作用域中的变量,如果没有var,而直接赋值(只有着一种情况!),则层层作用域寻找,直到window,如果window也没有,则var一个
一个作用域中的变量,如果没有var,直接参与运算,则层层作用域寻找,但是如果直到window都没有找到的话,就报错:xxx is not defined
关于函数嵌套:
// 函数嵌套 { // 通过调试工具可以看到,每一个函数执行的时候在scope(作用域)面板中生成了如下结构: /* { num1: 10, //函数内部定义的变量 this: window // 作用域终点,JS先编译,再执行, 编译后已经知道了当前这个作用域执行后的终点 } 我们将上面的对象称为:Activation Object 简称:AO —— 活动性对象, 仅仅在函数调用的时候产生,一旦函数执行结束就被【销毁】 既然是对象,就有办法保存下来 */ function a(){ let num1=10; function b(){ let num2 = 20 num1++; console.log(num1); console.log(num2); function c(){ let num3 = 20; console.log(num2); } c(); } b(); } a(); }
箭头函数中的【怪异现象】
/* 通过调试工具可以知道,函数执行的时候发生了: 1. 首先收集全部的变量声明 2. 为变量赋值 */ function a(){ let num = 10; const obj = { name:'张三', age:20 } console.log(num) console.log(obj.age) } // 运行a可以看到效果(使用调试工具调试) // a(); /* 函数调用中的【怪异现象】: 箭头函数在运行过程中,this的指向遵循于当前定义的作用域this终点; 当箭头函数this终点指向window的时候,scope面板显示为undefined */ const obj = { name:'张三', fn:function(){ let num = 1; console.log(this.name) let fn2 = ()=>{ console.log(this) } fn2() }, fn1:()=>{ console.log(this.name) } } obj.fn(); // obj.fn1();
3.关于闭包
// 跨作用于访问 // 什么是闭包? /* 闭包第一种形态: 常规的跨作用域访问,执行结束后,所有AO都被销毁 */ function a(){ let num1 = 10; function b(){ num1++; console.log(num1) } b() } a() function c(){ let num=10; return function d(){ num++; console.log(num) // console.log(this) // script面板中展示的是closureFn,需要考证 } } // 此处执行c函数,返回了d函数的代码片段给closure let closureFn = c(); // 调用closure——使用调试工具观察scope面板 closureFn() // closureFn() // closureFn() // closureFn() closu = Object function num(){ return 1 }
闭包使用
/* 狭义的建立闭包: 1. 子级作用域访问了父级作用域变量 2. 子级作用域不随父级作用域销毁(实际父级的AO未被销毁) 执行环境栈顺序:先入后出 */ // 闭包的方式 function userManager(){ var userList = []; var admin = '张三'; var password = '12345'; return { //将函数对象抛出去了,外函数外面执行。又访问了外层变量。这就形成了闭包。 //当执行栈中执行环境依次销毁的时候,却找不到里层执行环境,而外层执行环境一直等待里面的销毁。 //这就导致外层作用域下的变量被保存下来。 auth(pwd){ if(pwd === password){ console.log('欢迎回来') } }, createUser(userName){ userList.push(userName); } } } const tools = userManager() tools.auth('12345'); tools.createUser('李四') tools.createUser('李四1') tools.createUser('李四2') tools.createUser('李四3')
4.面向对象编程
1)函数改写成对象的原则
1.尽量避免函数嵌套
2.如果有函数嵌套,提取后,一定要观察this的指向,及时修改this指向问题。
2)对象的优缺点
1.代码复用,灵活性强
2.数据不安全,任何人都可以访问对象内部方法。
5.组合寄生
// 组合寄生 // 原型链继承和寄生继承 function MyMenu(list){ // call方法——改变this指向 Menu.call(this,list) } // 原型链继承 // 1.声明一个空function,用这个function的prototype去接收父类的prototype (function(){ function A(){}; A.prototype = Menu.prototype; // 2. prototype中有constrcutor属性,指向了Menu,需要改成MyMenu A.prototype.constructor = MyMenu; // 3. 使用MyMenu去接收A的prototype MyMenu.prototype = A.prototype; })(); MyMenu.prototype.doClick = function(){ } const myMenu = new MyMenu('.list1');
6.数组内置方 法
1)concat ---连接两个或更多数组,并返回新数组,不改变原数组
let arr1=[1,2,3] let arr2=[4,5,6] arr1.concat(7,8) //[1,2,3,7,8] //直接添加数据 arr1.concat(arr2) //[1,2,3,4,5,6] //连接数组,concat参数可传入多个数组
2)reverse ---反转数组 ,改变原数组
3)push ---向数组末尾添加一个或多个元素,改变原数组
4)unshift ---向数组头部插入一个或多个元素,改变原数组
5)pop ---删除数组最后一个元素,并返回删除的元素,改变原数组
6)shift ---删除数组第一个元素,并返回删除的元素,改变原数组
7)splice() ---增删改查都可以,第一个参数为开始的索引,第二个参数为删除的个数(可选),第三个参数为添加的元素(可选),第三个参数可以有多个。如果只给一个参数,默认删除给定索引以后全部的值。改变原数组
8)sort() --数组在原数组上进行排序,不生成副本。如果不给参数,默认按照数字升序---字母升序进行排列。一个数组中同时存在数字和字母,先数字。如果一个数组同时存在数字,字符串,数组,对象:顺序为数字-数组-对象-字符串)
//数字 ***如果不给参数,sort会在每一项上调用String()转型函数,然后比较字符串来决定顺序。*** arr.sort(function(a,b){return b-a}) //数字降序排列 不给值,再反reverse转数组也可以实现 //字母 arr.sort() //字母升序a-z
9)forEach() ---数组每个元素都执行一次回调函数
注意:遍历基本数据类型值的时候,得用索引配合才能改变原数组值。
// 数组改值 let arr = [1,3,5,7,9]; arr.forEach(function(item){ item = 30; }) console.log(arr); //输出 (5) [1, 3, 5, 7, 9] // 数组改值 let arr = [1,3,5,7,9]; arr.forEach(function(item,index,arr){ arr[index] = 30; }) console.log(arr); //输出 (5) [30, 30, 30, 30, 30]
10)map() ---方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。不会检测空数组,不会改变原数组。
11)filter()---创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。参数为函数,不会检测空数组,不会改变原数组。
let ages=[18,19,14,20] ages.filter(function(age){return age>=18}) //检测数组中大于等于18的
12)every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供) --不会检测空数组,不会改变原数组。
- 如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。
- 如果所有元素都满足条件,则返回 true。
let ages=[18,19,14,20] ages.every(function(age){return age>=18}) //false
13)some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。只要有一个满足条件就返回true,剩下的不再检测。全部不满足就返回false。--不会检测空数组,不会改变原数组。
let ages=[18,19,14,20] ages.some(function(age){return age>=18}) //true
14)join() ---将数组拼接为字符串,省略参数时,默认为逗号隔开,不会改变原数组
15)indexOf() ---查找数组元素,和查找字符串一样,第一个参数要查找的元素,第二个为开始索引,找到返回索引,找不到返回-1
16)includes() ---查找是否包含指定元素,返回true或false ,也可以用于字符串。
let arr=[1,"aa",2] arr.includes("aa") //true
17)flat() 多维数组转一维数组,参数为深度,默认1;
var arr2 = [1, 2, [3, 4, [5, 6]]]; arr2.flat(); // [1, 2, 3, 4, [5, 6]] var arr3 = [1, 2, [3, 4, [5, 6]]]; arr3.flat(2); // [1, 2, 3, 4, 5, 6]
18)findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。当有元素满足,后面的不再检测。全不满足返回-1;---和indexOf差不多。 不会改变原始数组。
19)find() 返回传入一个测试条件(函数)符合条件的第一个元素值。全不满足返回undefined。
20)slice(start,end) ---提取一部分并生成新数组,两个参数,开始索引,结束索引(可选)。 只给一个参数,从开始截取到最后。 也可以用于字符串。
如果是负数,则该参数规定的是从字符串的尾部开始算起的位置。也就是说,-1 指字符串的最后一个字符,-2 指倒数第二个字符,以此类推。
例:一维数组按照固定长度拆分二维数组
split_array=(arr,len)=>{ let newArr = []; for(let i=0;i<arr.length;i+=len){ newArr.push(arr.slice(i,i+len)); } return newArr; } let data = [1,2,3,4,5,6,7]; split_array(data, 3); //[[1,2,3],[4,5,6],[7]] 方式二: function group(arr, len) { let index = 0; let newArray = []; while(index < arr.length) { newArray.push(arr.slice(index, index += len)); } return newArray; }
21)keys() ---遍历数组并拿到所有的索引生成一个新数组返回出来。用于对象的时候拿到所有的键名生成新数组返回出来。
let arr=[1,2,3,4] console.log(Object.keys(arr)) // [0,1,2,3] let obj={name:"张三",age:20} console.log(Object.keys(obj)) //["name","age"]
https://www.runoob.com/w3cnote/es6-array.html ES6 数组
22)xxx.at(索引) --es2022新增
返回指定索引值
7.字符串内置方法
1)charAt() ---返回指定索引处的字符。如:str.charAt(1)
2)charCodeAt() --- 返回指定索引的字符的Unicode编码。
Unicode编码:Unicode(又称统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。
3)concat() ---连接两个或多个字符串,返回新的字符串。和数组使用方法一样。
4)includes() ---查找字符串中是否包含指定的子字符,返回true或false
5)IndexOf() ---查找 某个子字符在字符串中的位置,有返回索引,没有返回-1
6)replace() ---查找并替换符合的字符。第一个参数为正则匹配表达式,第二个参数为替换的字符。
let str="asdfba"; str.replace(/a/g,0) //"0sdfb0"
7)slice() ---拿到开始和结束的索引字符片段,并返回一个新的字符串。和数组用法一样。两个参数,开始和结束索引。
8)split() ---按照指定符分割成数组。
9)substr() --- 截取字符串,两参数,开始索引,截取个数(可选),如果省略第二个参数,默认截取到结尾。
10)substring() ---截取字符串,两参数,开始索引(包括),结束索引(不包括)。
11)toLowerCase() ---将全部字符转换成小写
12)toUpperCase() ---将全部字符转换成大写
13)trim() ---去除字符串两边的空白。jquery方法
$.trim(str)
14) replaceAll() ---替换所有匹配字符
15) match() -- 可在字符串内检索指定值,或找到一个或多个正则表达式的匹配。
16)xxx.at(索引) --es2022新增
17)startsWith() 方法 -- startsWith() 方法用于检测字符串是否以指定的前缀开始。
18)endWith() 方法 --endsWith() 方法用来判断当前字符串是否是以指定的子字符串结尾的(区分大小写)。
8.对象内置方法
1)toLocalString() ---转换时间为字符串可用
例:new Date(time).toLocalString() //将传入的时间转换为YY/MM/DD PM hh:mm:ss的格式
2)Object assign() --浅拷贝
3)Object.entries()
返回索引/值对应的迭代器
const obj = { foo: 'bar', baz: 42 }; console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ] const obj = { a: 5, b: 7, c: 9 }; for (const [key, value] of Object.entries(obj)) { console.log(`${key} ${value}`); // "a 5", "b 7", "c 9" }
9.uni限制input只能输入数字
<input type="number" v-model="sum" @input="click" /> export default { data() { return { sum: 0, } }, methods: { click(e){ const v = e.detail.value this.sum = '1' const zero = /^(0{2,})|[^0-9]/g let final = 0 if (!v) { final = 0 } else { final = v.toString().replace(zero, (v) => { return 0 }) if (final.split('')[0] * 1 === 0) { final = final.slice(1) - 0 || 0 } } this.$nextTick(() => { this.sum = final.toString() || '0' }) }, } }
10.处理时间
var date = new Date();
date .getYear(); //获取当前年份(2位)
date .getFullYear(); //获取完整的年份(4位)
date .getMonth(); //获取当前月份(0-11,0代表1月)
date .getDate(); //获取当前日(1-31)
date .getDay(); //获取当前星期X(0-6,0代表星期天)
date .getTime(); //获取当前时间(从1970.1.1开始的毫秒数)
date .getHours(); //获取当前小时数(0-23)
date .getMinutes(); //获取当前分钟数(0-59)
date .getSeconds(); //获取当前秒数(0-59)
date .getMilliseconds(); //获取当前毫秒数(0-999)
date .toLocaleDateString(); //获取当前日期
var mytime=date .toLocaleTimeString(); //获取当前时间
date .toLocaleString( ); //获取日期与时间
//时间戳转日期 new Date(时间戳).toLocaleDateString() //计算近多少(7)天 new Date(start.getTime() - 3600 * 1000 * 24 * 7).toLocaleDateString(),
11. Array.from()
1)将类数组转换为真正的数组
let arrayLike = { 0: 'tom', 1: '65', 2: '男', 3: ['jane','john','Mary'], 'length': 4 } let arr = Array.from(arrayLike) console.log(arr) // ['tom','65','男',['jane','john','Mary']] 要将一个类数组对象转换为一个真正的数组,必须具备以下条件: 1、该类数组对象必须具有length属性,用于指定数组的长度。如果没有length属性,那么转换后的数组是一个空数组。 2、该类数组对象的属性名必须为数值型或字符串型的数字 ps: 该类数组对象的属性名可以加引号,也可以不加引号
2)将Set结构的数据转换为真正的数组
let arr = [12,45,97,9797,564,134,45642] let set = new Set(arr) console.log(Array.from(set)) // [ 12, 45, 97, 9797, 564, 134, 45642 ] Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。如下: let arr = [12,45,97,9797,564,134,45642] let set = new Set(arr) console.log(Array.from(set, item => item + 1)) // [ 13, 46, 98, 9798, 565, 135, 45643 ]
3)将字符串转换为数组:
let str = 'hello world!'; console.log(Array.from(str)) // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!"]
4)Array.from
第一个参数可以是任何可迭代结构,或者有一个length属性和可索引元素的结构。
console.log(Array.from([12,45,47,56,213,4654,154])) //浅拷贝
12.可选链运算符
可选链 ?. 语法有三种形式:
obj?.prop —— 如果 obj 存在则返回 obj.prop,否则返回 undefined。可以连用。
obj?.prop1?.prop2
obj?.[prop] —— 如果 obj 存在则返回 obj[prop],否则返回 undefined。
obj.method?.() —— 如果 obj.method 存在则调用 obj.method(),否则返回 undefined。
13.字符串转换数字=+
a=+b //b只能为字符串数字才有效
14.对象动态添加key值
例:一个数组对象中的某一个值当key 某一个当值 this.$store.state.course.forEach((item)=>{ Object.assign(this.attrdate,{[item.attrId]:item.attrName}) })
15.正则对象 new RegExp
/\//g
replaceAll("/")
new Reg("/", ''g
//判断跳转来源是否缓存页面 beforeRouteEnter(to, from, next) { const caches = [ '/order/business/detail', '/patient/patientTube/detail/roominfo', '/patient/patientTube/detail/recordInfo', '/business/order/detail', '/patient/mine/detail/roominfo', '/patient/mine/detail/recordInfo', ] const cache = caches.find(item => { return new RegExp(item).test(from.path) //查询满足条件的值 }) if (cache) { next() } else { next(vm => { vm.init() }) } },
16.选中与否判断
当点击时,传入的index和当前的请求参数index相同时候表示取消,否则选中
selectDate(index) { const type = this.queryType === index ? null : index this.queryType = type this.fetchDiagList({ type }) //计算近期时间,赋值时间选择器 if (type == null) { this.date = [] return } //顺便匹配时间,根据数组下标或者对象key来匹配,抛弃switch和if...else const timeArr = [ [this.getNearTime(0), this.getNearTime(0)], [this.getNearTime(3), this.getNearTime(0)], [this.getNearTime(7), this.getNearTime(0)], ] this.date = timeArr[index] },
17.new Map()与new Array()
Map结构提供了“值—值”的对应,它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
const getmap = new Map([['aa',1],['bb',2]]); getmap.set('aa',2); console.log(getmap.get('aa')); //2 console.log(getmap.has('aa')); //是否存在aa const map = Map.from(getmap);//深度拷贝map对象 map.set('aa',1); console.log(map.get('aa'));//1 console.log(getmap.get('aa'));//2 console.log(getmap.delete('aa'));//删除aa这个元素 map与array转换 const arr = Array.from(getmap); console.log(arr)//[['bb',2]] console.log(arr.flat());//['bb',2]
18.将 Object 转化为 Map
new Map() 构造函数接受一个可迭代的 entries 。 借助 Object.entries 方法你可以很容易的将 Object 转换为 Map: const obj2 = { foo: 'bar', baz: 'abc' }; console.log(Object.entries(obj2)); // [['foo', 'bar'], ['baz', 'abc']] const map = new Map(Object.entries(obj2)); console.log(map); // Map {'foo' => 'bar', 'baz' => 'abc'}
19.scrollIntoView导致的页面偏移解决
解决方案:放弃使用scrollIntoView
改用scrollTop
来操作滚动条。
el-scrollbar的滚动元素为this.$refs.scrollbar.wrap
//scrollIntoView方式 handleTabClick({ name }) { const el = this.$refs.tabs.$el.querySelector(`#${name}`) el && el.scrollIntoView({ behavior: 'smooth' }) this.activeName = name }, //target目标元素 --锚点跳转 var target = document.getElementById("target"); target.parentNode.scrollTop = target.offsetTop; offsetTop:元素上外边框距离父元素上内边框的距离(简单来说就是元素相对父元素上边的距离) //scrollTo方式 const scroll = this.$refs['prescription-item'][index].$el.offsetTop this.$refs['prescription-content'].scrollTo({ top: scroll, behavior: 'smooth', })
20.Infinity
Infinity 属性用于存放表示正无穷大的数值。
21.三目运用
item.medicalType === 'MZ' ? '门诊' : item.medicalType === 'ZY' ? '住院' : item.medicalType === 'TJ' ? '体检' : ''
22.空值合并操作符??符
只有当左侧为null和undefined时,才会返回右侧的数
?? 这个和 || 很像,但是它不会屏蔽掉false和 0 和 "" null ?? 1 ----1 undefined ?? 1 ----1 false ?? 1 ----false 0 ?? 1 ----0 "" ?? 1 ----""
23.尽量避免条件判断
写 if-else 不外乎两种场景:异常逻辑处理和不同状态处理。异常逻辑处理说明只能一个分支是正常流程,而不同状态处理都所有分支都是正常流程。
原则:
合并条件表达式可以有效地减少if语句数目;
减少嵌套能减少深层次逻辑;
异常条件先退出自然而然主干流程就是正常流程。
针对状态处理型重构方法有两种:
一种是把不同状态的操作封装成函数,简短 if-else 内代码行数;
另一种是利用面向对象多态特性直接干掉了条件判断。
24.Object.defineProperties()
Object.defineProperties()
方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties
var obj = {}; Object.defineProperties(obj, { 'property1': { value: true, writable: true }, 'property2': { value: 'Hello', writable: false } });
25.判断对象内是否包含某个键名
1) xxx in obj
'name' in {name:'jeck'} //true //缺点:如果属性来自对象的原型,也会返回true
2) Reflect.has(obj,xxx)
Reflect.has({name:'jeck'},'name') // true //与xxx in obj 类似,缺点同理,Reflect是 ES6 为了操作对象而提供的新 API。所有属性和方法都是内置的。
3)hasOwnProperty()
var obj = {name:'jeck'} obj.hasOwnProperty('name') //缺点:不能检索Object.create(null) 创建的对象
4)Object.prototype.hasOwnProperty()
var obj = {name:'jeck'} Object.prototype.hasOwnProperty.call(obj,'name') //true //解决了方法3)的缺点 ,直接访问的对象的内置方法,跳过了原型连
5)Object.hasOwn() --es2022新方法(推荐使用)
var obj = {name:'jeck'} Object.hasOwn(obj,'name') //true