javaScript(区分大小写)
ECMAScript
定义了javascript
的规范,javascript
是ECMAScript
的一种实现完整的javascript
包含ECMAScript
,DOM
,BOM
js的引入
页面中插入:<script></script>
外部引入:
<script src="url" async="async"></script>
<script src="url" defer="defer"></script>
<!-- defer 延迟执行 页面加载完后才会执行脚本, async 异步执行脚本代码 -->
不管是内部<script></script>
还是外部<script src="XXX"/>
,都是按照代码在页面位置的顺序依次执行,且后面的script
标签中可以使用前面script
中的值,一个script
标签执行完毕后才会执行后面的代码,除非在外部引入的时候使用async
和defer
来改变执行时机
<!-- 在浏览器不支持js或js被禁用时会渲染<noscript></noscript>中的内容 -->
<noscript>
<p>This page requires a JavaScript-enabled browser.</p>
</noscript>
标识符命名规则
可以用字母、下划线、$、数字,必须以字母或 “_”, $开头
注释
单行注释://
块级注释: /* */
html注释: <!-- -->
css注释:/* */
变量
关键字 | 块级作用域 | 变量提升 | 重复声明 |
---|---|---|---|
var | 没有 | 有 | 可以重复声明 |
let | 有 | 没有 | 同一作用域不可以重复声明 ,否则会报错 |
const | 有 | 没有 | 同一作用域不可以重复声明 ,否则会报错(声明常量,不能重新赋值) |
通过var
声明的全局变量或方法会挂载到window
上,let
和const
声明的不会
五种基本数据类型
typeof: 是一个操作符不是函数,用来检测数据类型,null
为object
,undefined
为undefined
undefined: 表示变量声明过但并未赋过值。
null:表示一个空对象指针,将来可能指向一个对象 ,用于释放对象的引用
Boolean: 布尔值
除(0,-0,"",null,undefined,NaN, false)
布尔值为false
外,其他布尔值都为true
number: 数值类型
Number
对象: https://www.runoob.com/jsref/jsref-obj-number.html
浮点数 float
无穷大: Number.MAX_VALUE, 无穷小: Number.MIN_VALUE
数值范围 isFinite() // 检测参数是否为无穷数,返回boolean值,NaN、Infinity、-Infinity为false,其他
非数值 NaN, isNaN() // 判断是 “不是一个数值”
//强制转换 常用方法
Number(obj) // 将对象转换为数字,若无法转换则返回NaN, ""和null,false转换为0,undefined转换为NaN
parseInt(string,radix) // 将字符串转换为整数数值,""转换为NaN。会省略掉小数点后无意义的0
parseFloat(string) // 将字符串转换为浮点数
// Number对象常用方法。
// 注意点:不能在数字后面直接跟方法,因为浏览器不识别数字后面的'.'是小数点还是运算符。
// 3.toString()这种写法会报错
num.toString() // 没有参数,将数值型转换为字符串。会省略掉小数点后无意义的0
num.toFixed() // 一个参数 保留几位小数(四舍五入),返回结果为字符串
num.toExponential() // 一个参数 返回值以几位数返回,以科学计数法返回,返回结果为字符串
num.toPrecision() // 一个参数 返回值以几位数返回,以更精确的格式返回结果,根据参数决定调用toFixed()还是toExponential(),返回结果为字符串
string: 字符串类型
String对象:https://www.runoob.com/jsref/jsref-obj-string.html
es6字符串:https://www.runoob.com/w3cnote/es6-string.html
//强制转换 常用方法
obj.toString() // 没有参数,null与undefined没有toString()方法
String(obj) // 任何类型都可以使用String()方法
// 补零的两种方法
// 方法一
num < 10 ? '0' + num : num;
// String对象串常用方法
toUpperCase() // 将字符串转换为大写 不改变原字符
toLowerCase() // 将字符串转换为小写 不改变原字符
str.slice(start,end) // 截取字符串 返回截取的新字符串
str.substr(start,length) // 截取字符串 返回截取的新字符串
str.substring(start,end) // 截取字符串 返回截取的新字符串
str.split('分隔符') // 字符串转数组 参数为分隔符,不改变原始字符串。 返回数组
str.replace(searchvalue,newvalue) // 返回一个新的字符串 不改变原字符串
str.charAt(index) // 返回指定位置的字符 索引从0开始,最后一个字符索引(str.length-1)value
str.indexOf(searchValue,start) // 返回指定字符在字符串中首次出现的位置,start开始查找位置 可选,未找到返回-1
string.concat(string1, string2, ..., stringX) // 拼接多个字符串 没有改变原字符串 返回新字符串
// 它可以用于检索字符串中是否包含一个指定的字符或者子字符串
// 其中regexp可以是一个正则表达式对象或者一个字符串,当regexp是一个字符串时,会被隐式地转换成正则表达式对象,如果没有匹配结果,则返回null。
str.match(regexp)
String新增实例方法
// es6新增字符串拼接方法
var str = `今年是${years}年`;
str.trim() // 去除字符串首尾两端的空格 不改变原字符
trimLeft(), trimRight() // 清除左边或右边空格
let str = `!zhangsan`;
//startsWith(str): 检测参数字符串是否在原字符串的头部,返回布尔值
console.log(str.startsWith("zhan")); // false
//endsWith(str): 检测参数字符串是否在原字符串的尾部,返回布尔值
console.log(str.endsWith("san")); // true
// includes(str): 检测参数字符串是否在原字符串中,返回布尔值
console.log(str.includes("q")); // false
// repeat(n): 表示将原字符串重复n次,返回新字符串
console.log(str.repeat(2)); // !zhangsan!zhangsan
// padStart("填充后的长度","用来填充的字符"): 在原字符串的前面填充,默认用空格填充,返回修改后的新字符串,不改变原字符串
console.log(str.padStart(10,'0')); // 0!zhangsan
// padEnd("填充后的长度","用来填充的字符"): 在原字符串的后面填充,返回修改后的新字符串,不改变原字符串
console.log(str.padEnd(10,'0')); // !zhangsan0
操作符
一元操作符
--a, ++a,a--,a++ 等等,前置是先加后返回值,后置是先返回值再加
--, ++ // 有隐式转换的功能,类似于Number()
算数运算符
//乘性操作符
*, /, % 会触发Number()的隐式转换
//加性操作符
+ : 会触发toString()的隐式转换
- : 会触发Number()的隐式转换
关系运算符(返回的是Boolean值)
// >, <, >=, <=
任何值和NaN比较结果都是false
两个都是字符串比较时,不会隐式转换(即使是纯数字的字符串),比较对应位置上每个字符的字符编码值
一个数值和一个字符串比较时,字符串会触发Number()隐式转换为数值
相等运算符
等于(==),不等于(!=),全等(===),不全等(!==)
逻辑运算符
//**布尔操作符**
// 布尔操作符:与(and),或(or),非(not),进行布尔值运算的运算符,其返回值也是布尔值
!a :逻辑非,取反 返回布尔值
!!a : 两次取反返回a的真正布尔值
// 短路运算(逻辑中断)
// 当有多个表达式(值)时,左边的表达式值可以确定结果时,就不再继续运算右边的表达式的值
a && b :逻辑与,若a为false,则不再继续判断b,返回a;若a为true,则返回b
a || b :逻辑或,若a为true,则不再继续判断b,返回a;若a为false,则返回b
a = b || c : // 若b为空就将c赋给a
赋值运算符
// 复合赋值操作
+=, -=, *=, /=, %=
条件运算符
// 求num1与num2之间的较大值
var max = (num1 > num2) ? num1 : num2;
逗号操作符
// 一条语句执行多个操作
var num1 = 1, num2 = 2, num3 = 3;
// 用于赋值,返回表达式中最后一项
var num = (2,3,5,1,0) // num的值为0
语句
// break 跳出循环或代码块,continue 跳过循环中的一个迭代
// 条件判断语句
if(){...} else{...}
// 三元运算符
表达式1 ? 表达式2 : 表达式3;
// 流程控制语句,a只能是Number或String
switch(a) {
case 1:
.....;
break;
case 2:
......;
break;
default:
break;
}
// 先执行后判断
do{...} while()
// 先判断后执行
while(){...}
// for循环等同于while
for(){}
// 循环遍历对象的属性
for(key in object){}
if else
语句与switch
语句的区别
switch
语句中只能处理case
为常量的情况,if
语句使用场景更多
if
语句会逐个条件计算,只能执行到满足条件的那一个,而switch
语句是直接跳转到满足条件的哪一个,只会计算一次,因此当分支较多时,switch
的效率要比if
语句高
变量、作用域、内存问题
变量
基本数据类型:按值来访问的,储存在栈内存中
引用类型:值是保存在内存中的,不允许直接访问内存中的位置,通过引用访问。储存在堆内存中
// 基本类型 变量复制后num1,num2相互独立 互不影响
// 引用类型 变量复制后 只是复制了一个指针,obj1,obj2都指向堆内存中的同一个对象,obj1和obj2其中一个改变会影响另一个
// 基本类型num1,num2
var num1 = 5;
var num2 = num1;
// 引用类型obj1,obj2
var obj1 = new Object();
obj1.name = "zhangsan"
var obj2 = obj1;
obj2.name = "lisi";
alert(obj.name); // 弹出"lisi"
作用域
每个局部执行环境(上下文)都有一个与之关联的变量对象,该执行环境中所有的变量和函数都存在于这个变量对象上,当代码执行到某个局部执行环境(上下文)时,会创建变量对象的一个作用域链,作用域链可向上追溯到全局变量对象
全局执行环境的变量对象一直存在,局部环境的变量对象只有在函数执行的过程中存在。
通过var
声明的全局变量或方法会挂载到window
上,let
和const
声明的不会
任何变量(不管包含的是原始值还是引用值)都存在于某个执行上下文中(也称为作用域)。这个上下文(作用域)决定了变量的生命周期,以及它们可以访问代码的哪些部分。
预解析(变量提升)
通过var
声明的变量或函数会有变量提升,而通过let
和const
声明的不会(看起来不会,实际上也会)
变量提升:变量的声明会被提升到当前作用域的最前面,变量的赋值不会提升
函数提升:函数的声明会被提升到当前作用域的最前面,但不会调用函数
预解析中的一些特殊情况
if
语句中不管条件是否成立,都会对语句内部代码进行预解析。
;(function(){})()
立即执行函数不进行预解析,定义和执行一起完成。
return
语句后面的代码不执行了,但依然需要预解析,return
中的代码都是返回值,不需要预解析
那么为什么var
声明的变量会有变量提升,而let
、const
声明的变量不会有变量提升呢?
var
声明的变量会被挂在到running execution context(执行上下文)的VariableEnvironment(变量环境)上,会在当前作用域的变量对象实例化的过程中被声明并赋值为undefined。 在赋值语句执行时才会为变量赋值。
let
,const
声明的变量会被挂在到running execution context(执行上下文)的LexicalEnvironment(词法环境)上,会在当前作用域的变量对象实例化的过程中被创建,但是直到变量的语法声明代码被执行之前,他们都是不可被访问的。同样在赋值语句执行时才会为变量赋值。
所以,let
,const
声明的变量也会变量提升,只是他们在定义的语句执行前不能被访问而已
引用类型
对象:一列无序的属性和方法的集合(是一组数据和功能的集合)
hasOwnProperty(propertyName):用于判断当前对象实例(不是原型)上是否存在给定的属性。要检查的属性名必须是字符串(如 obj.hasOwnProperty("name")
)或符号。
Object类型
// 利用关键字new Object()创建
var person = new Object();
person.name = "zhangsan";
person.age = 29;
// 字面量创建法
var person = {
name : "zhangsan",
age : 29
}
//构造函数方法创建
function Create(name,age) {
this.name = name,
this.age = age
}
var obj1 = Create(name,age);
// for(let key in obj) { } 遍历数组或对象,通过in操作符会遍历出对象自身属性和原型属性
var obj = {name: 'zhangsan', age: 18, sex: '男'}
for(let key in obj){} // for...in 遍历对象、数组、字符串的key
// 访问对象属性可以通过.或者[]访问
obj.name
obj['name']
常用方法
- 解构赋值, 展开运算符
- 新增遍历方法:
Object.keys(obj)
,Object.values(obj)
,Object.entries(obj)
,新增的三个方法都只会遍历出对象实例自身的属性,不会遍历出原型上的属性,for...in...
会遍历出自身和原型上的属性 - 对象合并方法:
Object.assign(obj1,obj2)
返回一个新对象
数组(Array)
https://www.runoob.com/jsref/jsref-obj-array.html
创建数组
// 构造函数创建
var arr = new Array();
// 字面量创建
var arr = ['red','pink'];
常用方法
// 检测某一个对象是不是数组
//方法一
if(arr instanceof Array) {}
// 方法二
if(Array.isArray(arr)) {}
// 数组转字符串,join(separator) 参数为分隔符默认为",".
// 将数组转换为字符串(若其中某项为null或undefined,则以空字符串表示)·
arr.join();
// 数组添加或删除项(会改变原数组)
// 栈方法(先进后出,栈中的堆入和弹出都只发生在栈的顶部)
var arr = [1,2,3]
arr.push(4); // push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
arr.pop(); // pop() 方法用于删除数组的最后一个元素并返回删除的元素。
// 队列方法(先进先出,队列在列表的末端添加,从列表的前端移除)
arr.shift(); //方法用于把数组的第一个元素从其中删除,并返回删除的元素。
arr.unshift() // 方法可向数组的开头添加一个或更多元素,并返回新的长度。
array.slice(start, end) // 截取数组元素,也可截取字符串,不包含结束位置元素,可为负值(相当于数组长度加上这个负值),如果开始位置小于结束位置则返回空数组.返回截取的新数组
[1,2,3,4,5].slice(-4,-1) // 相当于 array.slice(1,4)
array.splice(index,howmany,item1,.....,itemX)
// splice():有删除、插入、替换功能,始终返回一个包含删除项的数组(若没有删除项则返回空数组),会改变原始数组。数组塌陷,解决办法采用从最后一项开始倒着循环
// 扩展运算符: 可以将数组或者对象转为用逗号分隔的参数序列
let arr = [1,2,3]
console.log(...arr) // => 1,2,3
// 拼接数组
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
// 方法一 扩展运算符
let ary3 = [...ary1, ...ary2];
// 方法二
ary1.push(...ary2);
// 方法三
let arr3 = ary1.concat(ary2);
Array.from(str) // 将类数组转化为数组
Array.of() // 可以把一组参数转换为数组
console.log(Array.of(1,2,3)) // => [1,2,3]
// forEach遍历数组 改变原数组 没有返回值,return 会跳出当前循环,类似continue
arr1.forEach(function(value,index,arr){})
// map 映射,返回一个新数组,对数组中所有元素进行统一操作
const arr2 = [10,20,30,40]
let newArr = arr2.map((value,index,arr)=>{return value*2}) // [20,40,60,80]
// filter 过滤 返回一个新数组,遍历数组所有元素 每次遍历必须返回布尔值 为true则将遍历元素添加到返回的新数组中,为false则不添加
const arr3 = [10,20,30,40]
let newArr = arr3.filter((value,index,arr)=>{return value>20}) // [30,40]
// array.reduce(function(total, currentValue, currentIndex, arr){}, initialValue)
// 累加器 返回计算结果
const arr = [10,20,30,40]
let total = arr.reduce((preValue,value)=>{
return preValue + value
},0)
// some 一些,用于检测数组中的元素是否满足指定条件 返回布尔值 碰到true就终止遍历
arr4.some(function(value,index,arr){ return ...})
// every 所有,用于检测数组所有元素是否都符合指定条件 返回布尔值 碰到false就终止遍历
arr5.every(function(value,index,arr){})
// find 查找指定数组项,找到则返回数组项 未找到返回undefined
// findIndex 查找指定数组项,找到则返回数组项的索引 未找到返回-1
const arr = []
arr.find(() => { return ...})
arr.findIndex(() => { return ...})
// includes 查找是否存在某个元素 如果找到返回true 没找到返回false
const arr = []
arr.includes()
arr.indexOf() // 找到返回索引值,找不到返回-1
arr.lastIndexOf() // 从最后一项开始查找
数组重排序及去重
// 冒泡排序
var arr = [5,6,12,1,9];
// 外层循环 比较的轮数
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 num = arr[j];
arr[j] = arr[j+1];
arr[j+1] = num;
}
}
}
console.log(arr);
// 重排序方法
arr.reverse(); // 方法用于颠倒数组中元素的顺序。 改变原数组
array.sort(sortfunction) // sort() 方法用于对数组的元素进行排序。
// 排序顺序可以是字母或数字,并按升序或降序。
var arr = [0,1,15,5];
// 数组冒泡排列
arr.sort(compare); // 改变原数组
function compare(value1,value2) {
// 升序
return value1 - value2;
// 降序
// return value2 - value1;
// 乱序排列
// return Math.random() - 0.5;
}
数组去重
// 简单数组去重
let arr1 = [2,4,'a','a',2]
let newArr = new Set(arr1)
// 复杂数组去重
let arr1 = [{...},{...},{...}]
let newArr = arr1.filter((item, index) => {
return arr1.slice(0, index).every((key) => key.id !== item.id);
});
Map数据结构
类似于对象,也是键值对的集合,但是键的范围更广(对象,数组都可以作为键), 与Object
的主要差异是Map
的键值对是有序的
// 对象作为键
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
// 实例化Map
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.delete('name') // 删除name键值对
map.clear() // 清空所有键值对
Set数据结构
它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成 Set 数据结构,可以接收一个数组做参数
// 数组去重
const set = [...new Set([1, 2, 3, 4, 4])]; //[1, 2, 3, 4]
// 也可用于字符串去重
const str = [...new Set('ababbc')].join('') // 'abc'
实例方法
- add(value):添加某个值,返回 Set 结构本身
- delete(value):删除某个值,返回一个布尔值,表示删除是否成功
- has(value):返回一个布尔值,表示该值是否为 Set 的成员
- clear():清除所有成员,没有返回值
var arr = new Set();
arr.add(1).add(2); // Set{1,2}, 添加已有的值无效
var flag = arr.delete(3); // false
var flag2 = arr.has(3); // false
arr.clear(); // 没有返回值
遍历 forEach()
//Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。
arr.forEach(value => console.log(value));
Date类型
https://www.runoob.com/jsref/jsref-obj-date.html
// 创建时间对象
var timer = new Date();
// 获取当前时间的毫秒数(时间戳)
var date = new Date();
var noeTime = date.getTime();
var times = +new Date();
// h5新增方法
var nowTime = Date.now();
比较两个时间的大小,一般转化为时间戳进行比较。两个时间对象也可以直接比较大小
if (+new Date(date1) < +new Date(date2) ) {
return `date2大于date1`
} else if (+new Date(date1) > +new Date(date2)) {
return `date1大于date2`
}
RegExp(正则表达式)
https://www.runoob.com/jsref/jsref-obj-regexp.html
匹配这些元字符时都需要转义:( [ { \ ^ $ | ) ] } ? * + .
// 构造函数模式 new RegExp('匹配模式','修饰符')
var regexp = new RegExp('abc');
// 字面量模式 /匹配模式/修饰符
var reg = /qwe/i;
// 修饰符
i - 修饰符是用来执行不区分大小写的匹配。
g - 修饰符是用于执行全文的搜索(而不是在找到第一个就停止查找,而是找到所有的匹配)。
gi - 即全局匹配 同时忽略大小写
特殊字符
// 边界符 ^、$
// ^ 匹配行首的字符,以xxx开始
var reg = /^abc/;
// $ 匹配行尾的字符,以xxx结尾
var reg = /abc$/;
//如果 ^和 $ 在一起,表示必须是精确匹配。
// 字符类 []、()、|
// 字符类表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内。
var reg = /[a-z0-9_-]/
var reg = /^[a-z0-9_-]$/
var reg = /^[^a-z0-9_-]$/
// 量词符 *、 +、?、{n}、{n,}、{n,m}
// 元字符 \d、\D , \w、\W , \s、\S
\d: 查找数字
\w: 查找数字、字母、下划线
常用方法
// 匹配 返回Boolean值,用来测试该模式在字符串中是否能匹配到
reg.test(str)
// replace() 替换
str.replace(/需要被替换的字符/,"新字符")
var str = "我{{是}}中国人{{是}}"
var aa = str.replace(/{{是}}/g,"***");
// 提取 exec()
var str = "我{{是}}中国人{{是}}"
var reg = /{{(是)}}/; // ()是提取分组
var str = reg.exec(str)
// 只会匹配一个,str[0]: 匹配的元素,str[1]:提取的字符串,str.input:是整个查找的字符串
函数
函数没有重载
函数没有重载,同名函数,后面的会覆盖先定义的
函数定义方法
// 使用不带括号的函数名是访问函数指针,而非调用函数
// 函数声明语法定义, 函数声明式由于函数声明提升,所以可以在定义之前调用
function fn(num1,num2) {
return num1 + num2;
}
// 函数表达式定义函数,由于变量提升,可以在声明前访问fn这个变量,但是在fn被赋值前函数还不能调用
var fn = function(num1,num2){
return num1 + num2;
};
// 箭头函数, 箭头函数不能使用arguments,箭头函数中的this是指向定义位置的上下文中的this
let fn = () => {}
函数参数
参数是按值传递的,不是按引用。
函数调用的时,是将实参变量的值拷贝一份赋值给形参变量,只是实参变量的可能时基本数据类型或引用类型
函数不关心传入参数的个数,也不关心参数的数据类型。函数内部可以使用arguments
访问参数
function fn() {console.log(arguments[0],"+",arguments[1])}
fn(1,2) // 1"+"2
// 函数绑定方法(this指向问题)
fun.call(this,'parameter1','parameter2');
fun.apply(this,['parameter01','paremeter02']);
fun.bind(this)(); // bind 返回的是函数
arguments(伪/类数组):用于储存函数的实参, length
取决于传入的参数长度,而不是函数定义时给出的命名参数个数
具有数组的length
属性
按照索引的方式进行储存
没有真正数组的一些方法,如push(), pop()
等等
arguments
对象其实还有一个 callee
属性,是一个指向arguments
对象所在函数的指针
构造函数方法:Array.from()
将伪数组或可遍历对象转换为真正的数组
默认参数
ES6
支持显式定义函数的默认参数,默认参数值可以是原始值或对象,也可以是函数返回值。
arguments
对象的值不会反映默认参数的值,只反映传入函数的参数,始终以调用函数时传入的值为准。
函数默认参数只会在函数被调用时求值,不会在函数定义的时候求值。计算默认值的函数只会在函数被调用且没有传入相应参数时才会被调用
function fn(name='zhangsan',age=18) {
return `我是${name},${age}岁`
}
参数的扩展与收集
// 参数的扩展
function fn() {
return console.log(arguments.length)
}
fn(...[1,2,3]) // 3
// 参数的收集
// 扩展操作符把不同长度的独立参数组合为一个数组,此处values为一个Array的实例
// 箭头函数无法使用arguments,但是可以使用参数收集实现arguments一样的逻辑
function fn(...values) {
return console.log(values.length)
}
fn('a','b') // 2
立即执行函数
// 两种写法 前面要带分号 防止代码融合
// (function(){})() 后面()里的是传递的参数
(function(a,b){
return a + b;
})(1,2);
// (function(){}()) 后面()里的是传递的参数
(function(a,b){
return a + b;
}(1,2));
基本包装类型
Boolean、String、Number
生命周期:每当读取一个基本类型值的时候,后台会为其自动创建一个基本包装类型对象,以便调用一些方法来操作这些数据。但是创建的基本包装类型对象会在此行代码执行完后被立即销毁。(引用类型的实例会在执行流离开当前作用域前一直保存在内存中)
内置对象
ECMA-262 对内置对象的定义是“任何由 ECMAScript 实现提供、与宿主环境无关,并在 ECMAScript程序开始执行时就存在的对象”。Object、Array
和 String
这些都是, 还有两个特殊的单例内置对象Global
和Math
.
Gobal
Gobal对象是不能被访问到的, 事实上,不存在全局变量或全局函数这种东西。在全局作用域中定义的变量和函数都会变成 Global
对象的属性 。浏览器将window对象实现为Gobal对象的代理, 因此全局的变量和函数成了window
的属性和方法.
eval(); // javascript的一个解释器
// 编码方法:
encodeURI(url) // 不会编码一些特殊字符
encodeURIComponent(url) // 会编码所有字符
// 对应的解码方法:
decodeURI(url)
decodeURIComponent(url)
Math对象
https://www.runoob.com/js/js-obj-math.html
常用方法
Math.max(num1,num2,num3) // 返回一组数中的最大值
Math.min(num1,num2,num3) // 返回一组数中的最小值
// 返回数组中的最大值和最小值
Math.max.apply(Math,arr);
Math.min.apply(Math,arr);
// 舍入方法
Math.ceil() // 向上取整
Math.floor() // 向下取整
Math.round() // 四舍五入取整
Math.random() // 返回0~1之间的随机数,包含0不包含1
// 随机数公式
function selectFrom(lowerValue, upperValue) {
let choices = upperValue - lowerValue + 1;
return Math.floor(Math.random() * choices + lowerValue);
}
数组和字符串方法的比较
// 连接多个数组或字符串 返回新的数组或字符串 不改变原数组或字符串 数组和字符串通用
arr.concat() // 参数为要被连接的数组
str.concat() // 参数为要被连接的字符串
arr.join(separator) // 数组转字符串 参数为要分隔符 默认为',' 返回一个字符串
str.split(separator,limit) // 字符串转数组 separator为指定分隔标识 limit为可返回数组最大长度,返回数组 返回的数组中不包括分隔符本身
// 查找指定数组项或字符
arr.includes(searchElement, start) // 返回布尔值,找到为true 没找到为false
arr.indexOf(searchElement,start) // 找到返回索引值 没找到返回-1
arr.lastIndexof(searchElement,start) // 从后往前查找
arr.slice(start,end) // 开始位置,结束位置,返回截取的新数组,可取负值
arr.splice(start,num,item) // 返回删除元素组成的新数组,若没有则返回空数组,会改变原数组
str.includes(searchElement, start) // 返回布尔值,找到为true 没找到为false
str.indexOf(searchElement,start) // 找到返回索引值 没找到返回-1,第二个参数表示从start开始往后查找
str.lastIndexof(searchElement,start) // 从后往前查找,第二个参数表示从start开始往前查找
str.slice(start,end) // 开始位置,结束位置,返回截取的新字符串,可取负值
str.substr(start,length) // 截取字符串 方法返回新字符串
str.substring(start,end) // 截取字符串 方法返回新字符串
数据类型的总结
// 简单数据类型:Number、String、Boolean、Null、Undefined、Symbol
// 引用类型:Object、Array、Function
// typeof 检测一个变量最基本的数据类型
console.log(typeof 123); // => number
console.log(typeof '123'); // => string
console.log(typeof null); // => object
console.log(typeof undefined); // => undefined
console.log(typeof true); // => boolean
console.log(typeof String); // => function
console.log(typeof Number); // => function
console.log(typeof Boolean); // => function
console.log(typeof Null); // => undefined
console.log(typeof Undefined); // => undefined
console.log("************************************")
console.log(typeof {}); // => object
console.log(typeof []); // => object
console.log(typeof function(){});// => function
// a instanceof B: B的原型是否在a的原型链上
console.log([] instanceof Array); // => true
console.log({} instanceof Object); // => true
console.log(function(){} instanceof Function); // => true
// ES5提供了数组特有的一个方法 检测一个对象是不是数组
console.log(Array.isArray([])); // => true
检测数据类型的几种方式
// 检测简单数据类型
typeof a
// 判断b的原型是否在a的原型链上
a instanceof b
// 判断a的原型是否指向b
a.constructor === Array
// 利用Object原型上的toString()方法检测变量的数据类型,适用于所有变量
Object.prototype.toString.call(a)
错误处理与调试
控制台打印:
console.log("")
打印的是结果,直接显示信息
console.dir("")
打印的是对象内容,显示对象的所有属性和方法
错误处理
try {
......
} catch(error) {
console.log(error)
} finally {
......
}
// 抛出错误,为每一种错误类型设置自定义错误信息
throw new Error("Something bad happend.")
应该只在确切知道接下来该做什么时捕获错误, 捕获错误的目的是阻止浏览器以其默认的方式相应,抛出错误的目的是为错误提供有关其发生原因的说明.
编码与解码
encodeURI与decodeURI
encodeURI()
是javascript
中真正用来对URL
编码的函数. 它着眼于对整个URL
进行编码,因此除了一些常见的符号以外,对其他一些在网址中有特殊含义的符号也不进行编码.例如"/ : ' @ # & ? = + $ ,"
encodeURIComponent和decodeURIComponent
它用于对URL
的组成部分进行个别编码,而不用对整个URL
进行编码. 对在encodeURI()
中不被编码的符号都会进行编码
JSON
JSON
时一种数据格式, 而不是编程语言, 并不是javascript
所独有的
序列化: JSON.stringify()
反序列化: JSON.parse()
常用插件
jquery
:javascript
函数库
axios
:请求工具
echart
:图表工具库
loadsh
:javascript
实用工具库
day.js
:轻量级的javascript
时间日期处理库
moment.js
:轻量级的javascript
时间日期处理库
Swiper
:触摸滑动插件
wow.js
:展示css3
动画的js
插件
js-cookie
:操作cookie
的插件
clipboard
: vue
组件,实现复制粘贴文本
Dom
document
节点时每个文档的根节点,html
元素时html
页面唯一的文档元素
获取元素的几种方式
// 通过id获取元素
document.getElementById('id');
// 通过标签获取元素的集合
document.getElementsTagName('element');
// *********h5新增的方式 不兼容IE8及之前的版本
// 通过类名获取元素的集合
document.getElementsByClassName("class");
// 返回指定选择器的第一个对象
document.querySelector('选择器');
// 返回指定选择器所有元素对象集合
document.querySelectorAll("选择器");
// matches()返回布尔值,元素匹配到指定的CSS选择器返回true,未匹配到返回false
document.matches()
// 重写整个页面
document.wirte('XXX')
//***********获取特殊元素
// 获取body和html、head元素
document.body
document.documentElement
document.head
// document上的几个特殊集合
document.images
document.links
// document的其他常用方法和属性
document.hasFocus() // 返回布尔值,表示文档是否拥有焦点
document.activeElement // 返回获取焦点的元素
document.readyState // 有两个值,loading表示文档加载中,complete表示文档加载完毕
// 访问内嵌iframe窗口的document和window,如果内嵌页面跨域就会报错
let iframe = document.querySelector('myIframe')
let document = iframe.contentDocument
let window = iframe.contentWindow
// jquery获取方式
$("选择器");
操作样式的几种方式
// 方法一:通过style直接操作 通过style属性只能读写行内样式
odiv.style.backgroundColor = 'pink';
// 方法二:通过类名操作
odiv.className = '类名'; // 会覆盖原有的类名
// classList()方法,不会影响原有类名
odiv.classList.add('类名')
odiv.classList.remove('类名')
odiv.classList.toggle('类名')
odiv.classList.contains('类名') // 返回布尔值,判断元素是否有这个class
// 方法三:动态添加样式表
const styleNode = document.createElement('style')
styleNode.type = 'text/css'
styleNode.innerHTML = '#dom {border: 1px solid yellow}'
document.head.appendChild(styleNode)
// jquery 方法
$("div").addClass()
$("div").removeClass()
$("div").toggleClass()
属性操作
// 获取/设置元素内置属性,也可操作自定义属性
odiv.value
// 设置/获取自定义属性,也可以设置/获取节点内置属性
odiv.getAttribute('属性名');
odiv.setAttribute('属性名','属性值');
odiv.removeAttribute('属性名');
// h5新增获取自定义属性操作 dataset只能获取data-value开头的属性
odiv.dataset.value;
// jquery 属性操作
$("li").attr(); // 操作自定义属性,只能操作通过attr添加的属性
$("li").prop(); // 操作固有属性,和通过prop添加的属性
节点操作
// 一些常用的 HTML DOM 方法:
let oul = document.querySelector('ul') // 获取ul
let oli = document.createElement('li') // 创建一个li节点
oli.innerHTML = '新增一个li'
oul.appendChild(oli) // 插入新的子节点(元素)
oul.insertBefore(oli,'指定元素') // 插入到指定元素前面
oul.removeChild(oul) // 删除子节点(元素) 返回删除的节点。
oul.remove() // 删除自己,包括子节点
replaceChild(newnode,oldnode) // 替换节点
// 一些常用的 HTML DOM 属性
innerHTML // 节点(元素)的文本值,可识别html标签
innerText // 节点的文本值
parentNode // 节点(元素)的父节点
childNodes // 节点(元素)的子节点 不仅是元素节点 还包括文本节点等
children //找子元素(亲儿子)元素节点,获取到所有亲儿子元素
firstChild // 第一个子节点
lastChild // 最后一个子节点
nextSibing // 下一个兄弟节点
previousSibing // 上一个兄弟节点
dom.contains() // 确定一个元素是否是其后代
// jquery 创建元素方法
var li = $("<li></li>") // 创建元素
$("ul").append(li); // 添加元素 添加到元素内部的最后面
$("ul").prepend(li); // 添加元素 添加到元素内部的最前面
$("li").after(li); // 添加到元素外部的后面
$("li").before(li); // 添加到元素外部的前面
$("ul").remove(); // 删除自己 包括子元素
$("ul").empty(); // 清空元素内部 未删除自己
// 属性操作
$("li").html()
$("li").text()
$("input").val()
$("li").attr(); // 操作自定义属性,只能操作通过attr添加的属性
$("li").prop(); // 操作固有属性,和通过prop添加的属性
事件注册
三要素:事件源、事件类型、执行程序
// 注册事件的两种方式
// 传统方式
odiv.onclick = function(){}
// 事件监听方式 同一个元素 同一个事件可以添加多个监听
// 通过addEventListener注册的事件必须通过removeEventListener才能删除
odiv.addEventListener('click',fn);
// 删除事件两种方式
odiv.onclick = null;
// 移除事件监听,addEventListener添加的匿名函数无法删除
odiv.removeEventListener('click',fn);
// jquery 绑定事件方法
// 一般方式
$('div').click(function(){})
$("div").on("click",function(){})
// 是什么:把需要绑定到每个子元素上的事件 统一绑定到父级上
// 原理: 冒泡,给父元素绑定事件 利用事件冒泡 当子元素触发事件时 会冒泡到父元素上 然后执行相应的行为
$("div").on("click","子元素",function(){})
// 删除事件的方式
$('div').off()
DOM事件流
事件流的三个阶段:事件捕获、目标阶段、冒泡阶段
odiv.addEventListener('click',fn,Boolean);
// 第三个参数为布尔值,默认为false 表示在事件冒泡阶段调用事件处理程序;若为true 表示在事件捕获阶段处理事件程序。有些事件没有冒泡
事件委托
原理:事件冒泡,在父亲身上统一注册事件,之后子组件通过冒泡可以出发
优点:减少了内存消耗,动态添加的元素也可以出发该事件
缺点:有的事件不支持冒泡,无法委托
js入口函数
// 页面加载事件的两种方式
// 第一种 等所有document加载完后执行(包括img、css、)
window.onload = function() {} // 只能写一次 写多次后面会覆盖前面的
window.addEventListener('load',function(){})
// 第二种 DOM加载完成后执行,不包括css,img,flash等等。
document.addEventListener('DOMContentLoaded', function() {})
// jquery入口函数,dom结构加载完就会执行,不需要等所有内容加载完
$(function(){}), $(document).ready(function(){})
// 需要等所有页面内容加载完毕
$(window).load(function(){})
事件对象
// 获取事件对象
odiv.onclick = function(e) {
console.log(e);
}
// 常用事件属性和方法
e.target // 返回触发事件对象
e.preventDefault() // 阻止默认事件
e.stopPropagation() // 阻止冒泡
// this 是事件绑定的元素,始终等于currentTarget的值
// e.target 是事件触发的元素,事件的实际目标
创建元素的三种方法
document.write()
页面重绘,会覆盖原页面
创建节点的效率问题
// innerHTML 直接创建元素的效率很低,但是如果先用一个字符串或数组将新增元素装起来 最后一次性用innerHTML添加则效率要高于 createElement
// createElement创建元素 给新增元素添加属性、样式和方法比较灵活
// 计算代码执行时间
console.time('耗时:');
console.timeEnd('耗时:');
事件类型
用户界面事件
load
: 页面或dom
元素加载完成时触发
unload
: 页面或dom
元素卸载完成时触发
resize
: 窗口大小改变时触发
scroll
: 滚动滚动条时触发
表单事件
blur
: 失去焦点时触发, 不冒泡
focus
: 获取焦点时触发, 不冒泡
input
: 表单值改变时触发,input
会事件在keyup
之前,keydown
、keypress
之后被触发
change
: 表单值改变且失去焦点时触发
常用键盘事件
onkeyup // 键盘弹起时触发
onkeydown // 键盘按下时触发
onkeypress // 键盘按下时触发 不识别功能键 区分大小写
e.keyCode // 获取按键的ASCII值, 右上左下 => 37,38,39,40
// 先keydown 后keypress 最后keyup,keydown与keypress按下不松时会一直触发
鼠标事件
click // 单击事件
dbclick // 双击事件
onmouseenter // 不会冒泡,鼠标移入元素区域
onmouseleave // 不会冒泡,是当鼠标指针离开了目标元素以及目标元素的所有子元素以后才会触发。
onmouseover // 会冒泡,鼠标指针移出某个元素到另一个元素上时发生
onmouseout // 会冒泡,只要鼠标指针离开了目标元素或者目标元素的所有子元素中的任何一个就会被触发,即使鼠标指针还在目标元素内
onmousemove // 一直触发
获取鼠标X,Y的坐标(client, page, screen)
<script>
// 鼠标事件对象 MouseEvent
document.addEventListener('click', function(e) {
// 1. client 鼠标在可视区的x和y坐标,仅仅以浏览器窗口可视区为基准
console.log(e.clientX);
console.log(e.clientY);
console.log('---------------------');
// 2. page 鼠标在页面文档的x和y坐标, 会包含文档滚动的距离
console.log(e.pageX);
console.log(e.pageY);
console.log('---------------------');
// 3. screen 鼠标在电脑屏幕的x和y坐标,和浏览器窗口大小没有关系,screen以屏幕为基准
console.log(e.screenX);
console.log(e.screenY);
})
</script>
offset、client、scroll
// offset
odiv.offsetParent // 返回有定位的父级 若父级没有定位则找到body
odiv.offsetWidth // 元素自身宽度 包含边框
odiv.offsetLeft // 元素左部到有定位父级的距离
// client
odiv.clientWidth // 元素自身宽度,不会超过视口大小,不包含边框
odiv.clientLeft // 元素content到有定位父级的距离(左边框宽度)
// scroll 可读可写
odiv.scrollHeight // 文档实际高度,包含被卷去的高度 不包含边框宽度
odiv.scrollTop // 文档向上卷去的距离
// IE9及更早版本 可读可写
document.documentElement.scrollTop
document.body.scrollTop
// IE9以上浏览器
window.pageYOffset // 页面被卷去的距离 只读属性
window.scrollTo(x,y);
// jquery 元素位置
$("div").offset() // 方法设置或返回被选元素相对于文档的偏移坐标。它返回一个带有两个属性(以像素为单位的 top 和 left 位置)的对象。
$("div").offset().top
$("div").offset().left
$("div").position() // 方法设置或返回被选元素相对于有定位父级的偏移坐标。它返回一个带有两个属性(以像素为单位的 top 和 left 位置)的对象。
$("div").position().left
$("div").position().right
$("div").scrollTop() // 元素向上卷曲的距离
$("div").scrollLeft() // 元素向左卷曲的距离
jquery元素尺寸
放大镜
遮罩的当前移动距离 / 遮罩的最大移动距离 = 大图片的当前移动距离 / 大图片的最大移动距离
offset与style的差别
// offset
可以得到任意样式表中的样式值 没有单位
offsetWidth 等属性是只读属性,只能获取不能赋值
// style
只能得到行内样式表中的样式值 有单位
style.width 是可读写属性
动画封装
// 封装动画 (动画元素,目标位置,回调函数)
function animate(ele, target,callback) {
clearInterval(ele.timer);
ele.timer = setInterval(function () {
// 缓入动画 计算步长
var step = ((target - ele.offsetLeft) / 10);
// 根据step的正负 判断向上取整还是向下取整
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (ele.offsetLeft == target) {
clearInterval(ele.timer);
// 执行回调函数
// 判断是否有回调函数 没有返回undefined
callback && callback();
} else {
ele.style.left = ele.offsetLeft + step + 'px';
}
}, 30);
}
// 缓动动画
核心算法: (目标位置 - 当前位置) / 10 每次移动的步长
触屏事件
触屏事件
touchstart
:元素触摸开始时触发
touchmove
:在触摸元素上异动时触发
touchend
:从触摸元素上离开时触发
touchcancel
:触摸被打断时触发
触屏事件对象
touches //当前屏幕上所有触摸点的列表
targetTouches //当前对象上所有触摸点的列表
changedTouches // 涉及当前(引发)事件的触摸点的列表
移动端click 300ms延迟问题
- 移动端300ms延迟问题, 这样设计是为了判断你是否要进行双击
// 方法一 禁止缩放
<meta name="viewport" content="user-scalable=no">
// 方法二 fastclick插件
fastclick.js (https://blog.csdn.net/zfy865628361/article/details/49512095)
常用插件
Swiper(https://www.swiper.com.cn/)轮播 动画图插件
zyMedia (https://github.com/ireaderlab/zyMedia) 视频插件
Bootstrap https://www.bootcss.com/ 响应式框架
BOM
BOM浏览器对象模型
顶级对象window
https://www.runoob.com/jsref/obj-window.html
window
是js
访问浏览器的接口
window
是一个全局对象,所有全局作用域中的变量和函数都是window
对象的属性和方法
top/parent属性
window.top
指最外层窗口, window.parent
指上一层窗口, 如果当前窗口就是最外层窗口,则window.top
等于window.parent
等于window
window常见属性
返回窗口的文档显示区的高度/宽度 返回数值 没有单位
window.innerWidth、 window.innerHeight
outerHeight, outerWidth
包含任务条和滚动条宽度
获取窗口滚动距离
window.pageXoffset/window.scrollX
和window.pageYoffset/window.scrollY
两组属性
操作窗口滚动距离
scroll(x,y)
, scrollTo(x,y)
, scrollBy(x,y)
滚轮监听事件
// window 方法
window.onscroll = function(){} // 监听滚轮滚动
window.scroll(x,y) // 设置X轴和Y轴坐标
// document 方法
document.onscroll = function() {} // 监听滚动
// 窗口大小监听
window.addEventListener("resize",function(){})
定时器
定时器中的this
始终指向window
,箭头函数可以改变它的this
指向。箭头函数的this
指向定义位置的上下文中的this
// 创建定时器
var timer = setInterval(fn,3000)
var timer = setTimeout(fn,3000) //只执行一次
// 清除定时器
clearInterval(timer);
clearTimeout(timer);
客户端检测
能力检测
检测某一个属性或方法是否存在,并能实现预期效果(判断对象是否存在,并校验其数据类型). 能力检测并不关心浏览器类型
用户代理字符串检测
window.navigator.userAgent
返回的字符串,用户代理检测用来判断浏览器类型
User-Agent
的约定格式是:应用名,跟一个斜线,跟版本号,剩下的是自由的格式。
Chrome
浏览器:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
location对象
https://www.runoob.com/jsref/obj-location.html
Location
对象包含有关当前 URL
的信息。
Location
对象是 window 对象的一部分,可通过 window.Location 属性对其进行访问。
通过url
传参时,应将参数转化为一个由参数名为属性的对象,通过属性访问对应的参数值
// 常见属性
location.href // 返回当前整个URL
location.host // 返回主机(域名)
location.search // 返回一个URL的查询部分(?后面的部分)
location.hash // 返回一个URL的锚部分(从#开始的部分)
// 常见方法
location.assign(url) // 载入一个新的文档 可退回上一个页面
location.replace(url) // 用一个新的文档替换当前文档 不可退回上一个页面
location.reload(Boolean); // 参数默认为false 相当于F5刷新,当参数为true时 相当于ctrl+F5强制刷新
Navigator、History
// Navigator 对象包含有关浏览器的信息
navigator.userAgent // 检测浏览器
// history 历史记录
history.length // 返回历史列表中的网址数
history.back() // 返回上一个url地址(上一页)
history.forward() // 前进到下一个url地址(下一页)
history.go(num) // 可以指定前进或后退几步
// data:要设置的history.state的值,可以是任意类型的值,可根据此值进行判断执行想要的操作。
// title:现在大多数浏览器不支持或者忽略这个参数,最好用null代替。
// url:地址栏的值,若不需要可用空来代替。
history.pushState(data,title,url) // 新增一条历史记录
// history.pushState(null, null, 'a.html')
history.replaceState(data,title,url) // 修改当前的历史记录
// history.replaceState(null, null, 'a.html')
// 浏览器的前进、后退,history的forward()、back()、go()都可以触发popstate事件。
// history的pushState()和replaceState()不会触发popstate事件
window.addEventListener('popstate', () => {
console.log(history.state, '==this==')
})
本地存储
计算机的存储单位:
1G=1024M, 1M=1024K, 1K=1024B(字节,汉字占2个字节), 1B= 8bit(位)
Cookie (同源窗口可共享)
https://www.runoob.com/js/js-cookies.html
操作方式:document.cookie = "key=value"
一般容量为4K,只能保存字符串,以文本形式保存,在发送请求时会自动添加到请求头中,且保存的Cookie
的越大,请求完成的时间越久.没有编辑和删除方法,只能通过添加一个同名的key
将原来的覆盖,记住,可能会有同名的两个key
,因为只有Domain
(域),Path
(路径),expires
(过期时间),HttpOnly
(安全属性)等属性都相同的才是同一个key
.
Domain: domain
就是域,cookie
不能跨域设置,只能设置当前域或者更高级的域中(必须是同一个根域名下的)。通过设置更高级的域可以实现多个域名共享一个cookie
Path: path
(路径),设置cookie
的路径后,只有在该路径下的页面才能访问
HttpOnly: HttpOnly
属性可以在浏览器设置,也可以在服务器设置,但只能在服务器上读取,应为javascript
无法读取设置了该属性的Cookie
会话Cookie
:没有设置Cookie
过期时间,存储在浏览器缓存中,当关闭浏览器则销毁。
持久Cookie
: 存储在内存中,等到过期时间到了才会被销毁
sessionStorage和localStorage 一般容量为5M,只能保存字符串,对于复杂数据可以用JSON对象来处理
sessionStorage (同源窗口不共享)
https://www.runoob.com/jsref/prop-win-sessionstorage.html
只能临时保存当前窗口数据,即使同源窗口也不能共享数据,存储在浏览器缓存中,当关闭浏览器数据被销毁
localStorage (同源窗口可共享)
https://www.runoob.com/jsref/prop-win-localstorage.html
保存数据可在同源窗口共享,存储在内存中,窗口关闭数据不会被销毁,除非手动清除
localStorage.setItem("key","value") // 保存数据
localStorage.getItem("key") // 获取数据
localStorage.removeItem("key") // 删除数据
localStorage.clear() // 删除所有数据
会话机制
会话指进入网站到退出网站的整个过程,会话问题源于http
请求是无状态的,无法记录每次发请求的是谁,需要一个session id
来区分请求者。常见的有两种解决方案
session+cookie
当用户第一次登录时,服务器会创建一个session
存储一些用户身份信息,并向客户端返回一个包含用户身份信息的session id
存储到cookie
中,在用户下一次发送请求时,浏览器会自动将cookie
中的所有信息添加到请求头中发送给服务器,服务器验证cookie
中的用户身份信息通过则返回数据。
Token
当用户第一次登录时,服务器通过加密算法和密钥生成一个独一无二的token
作为令牌返回给客户端,在用户下一次发送请求时,将token
令牌添加到请求头中传递给服务器,服务器通过解密验证通过后返回数据。
两种方案的区别:
session
+cookie
是有状态的,服务器需要记录生成的用户session
,增加了服务器负担,并且是不安全的,cookie
有被盗用的风险。
Token
是无状态的,服务器无需记录用户token
值,通过CUP的计算能力换取了服务器的存储压力,Token
同时可以防止CSRF
攻击,也是相对更安全的一种方案。
CSRF 和 XSS攻击
csrf
: 跨站请求伪造,利用网站A
本身的漏洞,去请求网站A
的api
。
token
可以较好的解决这个问题
xss
: 跨域脚本攻击,向网站 A
注入 JS
代码,然后执行 JS
里的代码,篡改网站A
的内容。
es6语法
https://www.runoob.com/w3cnote/es6-let-const.html
var、let、const
const
声明常量,常量就是值(内存地址)不能变化的量
var
有变量提升,let
没有
let
有自己的块级{}作用域,不能在外部被访问
// 面试题
// var, 函数调用的时候 才调用当时的i值,var声明的i没有块级作用域,可在for循环外被访问
var arr = [];
for(var i = 0; i < 2; i++) {
arr[i] = function() {
console.log(i);
}
}
arr[0](); // => 2
arr[1](); // => 2
// let, 每次循环创建一个单独块级作用域 对应的j值与之绑定,有块级作用域,for循环外不可被访问
var arr1 = [];
for(let j = 0; j < 2; j++) {
arr1[j] = function() {
console.log(j);
};
}
arr1[0](); // => 0
arr1[1](); // => 1
var、let、const的区别
关键字 | 块级作用域 | 变量提升 | 重复声明 |
---|---|---|---|
var | 没有 | 有 | 可以重复声明 |
let | 有 | 没有 | 同一作用域不可以重复声明 ,否则会报错 |
const | 有 | 没有 | 同一作用域不可以重复声明 ,否则会报错(声明常量,不能重新赋值) |
可选链
当链接的对象的引用或函数可能是undefined
或null
时,可选链提供了一种方法简化被连接值的访问.
// 语法: obj?.prop obj?.[prop] arr?.[index] func?.(args)
let obj = null
console.log(obj.a) //=> 会报错,因为当obj为null或undefined时,obj无法读取a属性.
console.log(obj?.a) //=> 返回undefined,因为可选链操作符(?.)会在访问obj.a之前先隐式校验obj是否为null或undefined,如果是则会做短路运算,返回undefined,不会报错
箭头函数
// () => {}, ():代表函数 括号内为函数参数,=>:必须要有的符号,{}:括号内为函数体
const fn = () => {}//代表把一个函数赋值给fn
箭头函数中的this指向:指向最近作用域中的this
,call/apply/bind
也无法改变箭头函数的this
// 箭头函数中的this,指向的是函数定义位置的上下文this
// 可以简单理解成,定义箭头函数中的作用域的this指向谁
const obj = { name: '张三'}
function fn () {
console.log(this);//this 指向 是obj对象
return () => {
console.log(this);//this 指向 的是箭头函数定义的位置,那么这个箭头函数定义在fn里面,而这个fn指向是的obj对象,所以这个this也指向是obj对象
}
}
const resFn = fn.call(obj);
resFn();
// 案例2
var age = 100;
var obj = {
age: 20,
say: () => {
alert(this.age)
}
}
obj.say();//箭头函数this指向的是被声明的作用域里面,而对象没有作用域的,所以箭头函数虽然在对象中被定义,但是this指向的是全局作用域
// 箭头函数内部没有arguments
// 剩余参数
function sum (first, ...args) {
console.log(first); // 10
console.log(args); // [20, 30]
console.log(Math.max(...args)); // 30
}
sum(10, 20, 30)
Array扩展方法
扩展运算符: 可以将数组或者对象转为用逗号分隔的参数序列
let ary = [1, 2, 3];
console.log(...ary); // 1 2 3,相当于下面的代码
console.log(1,2,3);
合并数组
// 方法一
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
let ary3 = [...ary1, ...ary2];
// 方法二
ary1.push(...ary2);
// 方法三
let arr3 = ary1.concat(ary2);
Array.of()
// 将一组值转换为数组
Array.of(1,2,3) // [1,2,3]
Array.from()
将一个类数组对象或者可遍历对象转换成一个真正的数组。
快速生成新数组:快速生成当前年份前后五年的数组
currentYear=new Date()
yearList = Array.from(Array(11),(value, index) => index + currentYear.getFullYear() - 5);
arr.find()
用于找出第一个符合条件的数组成员,如果没有找到返回undefined
arr.findIndex()
用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1
let ary = [{
id: 1,
name: '张三'
}, {
id: 2,
name: '李四'
},{
id: 2,
name: '李四'
}];
let target1 = ary.find((item, index) => item.id == 2);//找数组里面符合条件的值,当数组中元素id等于2的查找出来,注意,只会匹配第一个
let target2 = ary.findIndex((item, index) => item.id == 2);
console.log(target1); // {id:2,name:"李四"}
console.log(target2); // 1
arr.includes(item)
判断数组中是否有某元素,有返回true
,没有返回false
arr.flat()
// 数组扁平化,将多维数组转换为一维数组
// 默认降低一维,参数Infinity,不管是几维数组都转化为一维
let arr = [1,[2,[3,4]],5]
arr.flat() // [1,2,[3,4],5]
arr.flat(Infinity) // [1,2,3,4,5]
String扩展方法
模板字符串
// 可以解析变量、换行、调用函数
let name = `张三`;
console.log(`我的名字是${name}`);
实例方法
let str = `!zhangsan`;
//startsWith(str): 检测参数字符串是否在原字符串的头部,返回布尔值
console.log(str.startsWith("zhan")); // false
//endsWith(str): 检测参数字符串是否在原字符串的尾部,返回布尔值
console.log(str.endsWith("san")); // true
// includes(str): 检测参数字符串是否在原字符串中,返回布尔值
console.log(str.includes("q")); // false
// repeat(n): 表示将原字符串重复n次,返回新字符串
console.log(str.repeat(2)); // !zhangsan!zhangsan
// padStart("填充后的长度","用来填充的字符"): 在原字符串的前面填充,返回修改后的新数组,不改变原数组
console.log(str.padStart(10,'0')); // 0!zhangsan
// padEnd("填充后的长度","用来填充的字符"): 在原字符串的后面填充,返回修改后的新数组,不改变原数组
console.log(str.padEnd(10,'0')); // !zhangsan0
函数的扩展
箭头函数,默认参数,剩余参数
Object新增方法
- 解构赋值, 展开运算符
// 数组解构
let [a, b, c] = [1, 2, 3];
console.log(a); // 1
// 对象解构
let person = { name: 'zhangsan', age: 20 };
let { name, age } = person;
console.log(name); // zhangsan
let {name: myName, age: myAge} = person; // myName myAge 属于别名
// 多层解构
const { children: { children } } = options
- 新增遍历方法:
Object.keys(obj)
,Object.values(obj)
,Object.entries(obj)
,都只会遍历出对象自身的属性,for...in...
会遍历出自身和原型上的属性 - 对象合并方法:
Object.assign(obj1,obj2)
返回一个新对象 Object.is(value1,value2)
:判断两个值是否全等,NaN===NaN
Set数据结构
它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set
本身是一个构造函数,用来生成 Set
数据结构,可以接收一个数组做参数
// 数组去重
const set = [...new Set([1, 2, 3, 4, 4])]; //[1, 2, 3, 4]
Array.from(new Set([1, 2, 3, 4, 4])) //[1, 2, 3, 4]
// 也可用于字符串去重
const str = [...new Set('ababbc')].join('') // 'abc'
实例方法
add(value)
:添加某个值,返回Set
结构本身delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功has(value)
:返回一个布尔值,表示该值是否为Set
的成员clear()
:清除所有成员,没有返回值
var arr = new Set();
arr.add(1).add(2); // Set{1,2}, 添加已有的值无效
var flag = arr.delete(3); // false
var flag2 = arr.has(3); // false
arr.clear(); // 没有返回值
遍历 forEach()
//Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。
arr.forEach(value => console.log(value));
Map数据结构
类似于对象,也是键值对的集合,但是键的范围更广(对象,数组都可以作为键), 与Object
的主要差异是Map
的键值对是有序的
// 对象作为键
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
// 实例化Map
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.delete('name') // 删除name键值对
map.clear() // 清空所有键值对
class类
类的基础使用,关键字class
,constructor
,extends
,super
// class定义父类,constructor构造器
class Person {
constructor(name) {
this.name = name
}
say() {
console.log(this.name)
}
}
// 定义子类,并继承Person父类,extends继承,super调用父类构造器
class Son extends Person {
constructor(name,age) {
super(name)
this.age = age
}
// 添加子类自己的方法
say() {
console.log(this.age)
}
}
const son = new Son('张三', 20)
Promise
Promise
是异步编程的一种解决方案,解决多层嵌套
// promise对象初始状态PromiseState为pending,状态只会被改变一次
// promise对象初始值PromiseResult为null
const p = new Promise(function(resolve,reject){
if(true){
// 执行resolve回调后,promise状态被改为fulfilled,值PromisResult为resolve回调返回的值
resolve(params);
}else {
// 执行reject回调后,promise状态被改为rejected,值PromisResult为reject回调返回的值
reject(err);
}
})
p.then(value => {
// 异步任务成功时执行......
// value为resolve回调返回的值
}, error => {
// 异步任务失败时执行......
// error为reject回调返回的值
}).catch(err => {
// 任务异常时执行......
}).finally(() => {
// 成功或失败都会执行......
})
静态方法:Promise.all()
,Promise.race()
Promise.all([promise1,promise2,promise3]).then((res) => {
// all()方法参数为一个数组
// 数组中的对象(promise1,promise2,promise3)均为promise实例(如果不是一个promise,该项会被用`Promise.resolve`转换为一个promise
// 它的状态由这三个promise实例决定,三个都执行完才算完
// 返回值与实例一一对应,[res1,res2,res3]
// 如果有一个失败了,则返回失败的promise
console.log(res) // [res1, res2, re3]
})
Promise.race([promise1,promise2,promise3]).then((res) => {
// race 方法同样接受一个数组作参数
// 当promise1,promise2,promise3中有一个实例的状态发生改变(变为`fulfilled`或`rejected`),它的状态就跟着改变。并把第一个改变状态的promise的返回值
console.log(res) // 第一个执行完成的返回值
})
axios与fetch
axios
是通过promise
对ajax
的一种封装,支持链式调用 还可以配合async await
使用。
axios
的优点:可以设置请求拦截和响应拦截,自动转换JSON
数据
// 配置公共的请求头
axios.defaults.baseURL = 'https://api.example.com';
// 配置 超时时间
axios.defaults.timeout = 2500;
// 全局设置 axios 发送请求带上cookie
axios.defaults.withCredentials = true
// 配置公共的请求头
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// 配置公共的 post 的 Content-Type
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
// 请求拦截 对请求发送前做一些配置(校验token)
axios.interceptors.request.use(config => {
config.headers["Authorization"] = 'Bearer ' + getToken();
return config // 一定要有返回值
},(err) => {
console.log(err)
})
// 响应拦截 对响应数据做一些处理(处理token过期)
axios.interceptors.response.use(res => {
let data = res
return data // 一定要有返回值
},(err) => {
console.log(err)
})
async和await
async/await
中真正起作用的是 await
async
关键字让函数具有异步特征,但总体上还是同步求值的,如果不添加await
,则与普通函数基本无差别
await
关键字会暂停执行异步函数后面的代码,让出javascript
运行时的执行线程
要完全理解await
,就必须知道他并非只是等待一个值可用那么简单。当javascript
执行遇到await
关键字时,会记录在哪暂停的,等到await
右边的值可用的时候,javascript
运行时会向任务队列推送一个任务,这个任务会恢复异步函数的执行
try catch finally的使用
使用try catch finally
结构来进行异常处理
执行try{}
中代码,若有异常则执行catch(err){}
, finally{}
中代码始终会被执行
finally{}
中代码的执行顺序是在try
和catch
的代码之后,但是在try
和catch
的return
之前
function test() {
let str = "abc"
try{
console.log(str);
return str + "==try"
}catch(err){
return str + "==catch"
}finally{
str = str + "==finally"
return str
}
}
console.log(test()); // abc abc==finally
js重要概念
js执行机制
单线程 同一个时间只能做一件事, js
有一个主线程和调用栈(执行栈),所有js
代码都在调用栈中等待主线程执行
什么是事件循环(Event Loop)
JS
的单线程任务分为同步任务(同步代码)和异步任务(异步代码)
同步任务在执行栈(先进后出)中依次等待主线程执行,
异步任务在条件满足的时候将注册的回调函数添加到任务队列(先进先出)中等待调用,
当执行栈中的任务执行完毕后,就会到任务队列中将异步任务添加到执行栈中执行,
这种反复去任务队列轮询的机制叫做事件循环
宏任务和微任务
任务队列里的任务分为宏任务和微任务, 因为<script>
标签就是一个宏任务, 所以先执行的是宏任务, 每个宏任务执行时又会发生事件循环, 执行完里面的同步任务和微任务后,再执行下一个宏任务
宏任务: setTimeout()
, setInterval()
, 回调函数, ajax
请求
微任务: Promise.then().catch().finally()
, async/await
微任务 > dom
渲染 > 宏任务
面向对象的三大特性
封装,继承,多态
封装:将可复用的代码包装起来重复使用
继承:子对象复用父对象的属性和方法
// 继承方法一,通过原型链和借用构造函数
// 父类型
function Person(name) {
this.name = name;
}
Person.prototype.say = function() {
console.log(this.name)
}
// 子类型
function Son(name,age) {
// 借用父类的构造函数,继承父类构造函数上的属性和方法
Person.call(this,name)
// 在子类构造函数中添加自己的属性或方法
this.age = age
console.log(this.age)
}
// 将子类的原型指向父类的实例
Son.prototype = new Person()
// 再将子类原型指会子类的构造函数
Son.prototype.constructor = Son
let son = new Son('张三', 18)
// 子类的原型链构建成功后可在原型上添加自己的属性或方法
Son.prototype.say = function() {
console.log(this.age)
}
// 实现继承方法二,ES6的class语法
// class定义父类,constructor构造器
class Person {
constructor(name) {
this.name = name
}
say() {
console.log(this.name)
}
}
// 定义子类,并继承Person父类,extends继承,super调用父类构造器
class Son extends Person {
constructor(name,age) {
super(name)
this.age = age
}
// 添加子类自己的方法
say() {
console.log(this.age)
}
}
const son = new Son('张三', 20)
多态:js
中定义变量时不需要指定数据类型,使用时可以指定任意的数据类型,这就是多态的体现
构造函数创建对象的四个步骤
1.创建一个新对象(开辟内存空间)
2.将构造函数作用域赋给新对象(this指向新对象)
3.执行构造函数里的代码(给新对象添加属性和方法)
4.返回新对象
静态成员和动态成员
静态成员:static
关键字定义静态成员,在构造函数本身添加的 直接通过构造函数来调用的
实例成员: 在构造函数中通过this
添加,需要通过实例对象访问。
构造函数返回值问题
如果构造函数有return
,并且返回值是值类型,则忽略这个值,仍然
返回this
如果返回的是引用类型,则忽略this
,返回这个引用类型的值
// 构造函数创建对象
function Student(name,age) {
this.name = name;
this.age = age;
//return 123; // =>仍返回this
return [1,2,3]; // =>返回[1,2,3]
}
原型
每一个构造函数都有一个 原型或原型对象,通过构造函数创建的对象都可以访问原型中的成员
// 构造函数创建对象
function Student(name,age) {
this.name = name;
this.age = age;
}
// 用原型(prototype)创建sayHi方法, 用Student构造函数创建的对象都有此方法
Student.prototype.sayHi = function() {
console.log(`我是${this.name},${this.age}岁`);
}
var s1 = new Student("zhangsan",18)
console.log(zhangsan.sayHi() === lisi.sayHi()); // 返回true
// 当调用对象的属性和方法的时候 先到对象本身找,如果没有就去原型中找 都没找到就报错
构造函数、原型、对象的关系
console.log(s1); // 返回对象
console.log(s1.__proto__); // 返回原型对象
console.log(Student); // 返回构造函数
console.log(Student.prototype); // 返回原型对象
console.log(Student.prototype.constructor); // 返回构造函数
原型链
// 原型链的最顶级是 null
s1(用户创建对象) => s1.__proto__(原型对象) => s1.__proto__.__proto__(object原型对象) => s1.__proto__.__proto__.__proto__(null)
// 属性查找规则
// 读取属性:先在对象本身查找 => 没找到再到原型链上查找
// 设置属性:不会查找原型链,如果是对象本身已有的属性 直接覆盖,如果是没有的 直接新增一个属性
继承
主流的实现继承有两个方法:原型链+借用构造函数,class
类语法
函数调用的方法
函数内部的this
不是在声明的时候确定的 而是在调用的时候确定的
对象没有作用域 ,箭头函数的this取决于上下文的作用域环境
严格模式下:"use strict"
, 全局作用域中的this
指向window
, 全局函数中的this
指向undefined
// 1.普通函数 this指向window
function fn() {}
fn();
// 2.方法调用 this指向方法调用者
function fn() {}
obj.fn();
// 3.作为构造函数调用 this指向实例
function Person() {}
var s1 = new Person()
// 4.作为事件处理函数 this指向触发该事件的对象
btn.onclick = function(){}
// 5.定时器中的函数 this指向window
setInterval(function(){},1000)
// 在箭头函数中,this引用的是定义箭头函数的上下文
let fn = () => {}
改变函数内this指向的3种方法
// 函数绑定方法(this指向问题)
// 当第一个参数没有或为空时 相当于函数模式: 函数名()
// 当有第一个参数时 相当于方法模式:obj.函数名()
// 应用场景: 经常做继承.
fun.call(this,'parameter1','parameter2');
//应用场景: 经常跟数组有关系
// 与call类似 只是参数不同
fun.apply(this,['parameter01','paremeter02']);
// 应用场景: 改变定时器中this指向等
//bind() 方法不会调用函数,但是能改变函数内部this 指向,返回的是原函数改变this之后产生的新函数
//应用场景:不调用函数,但是还想改变this指向
fun.bind(this); // bind 返回的是函数
函数内部的其他成员
function fn() {
// arguments 传入实参的个数
console.log(fn.arguments);
// caller 函数的调用者
console.log(fn.caller);
// 函数名称
console.log(fn.name);
// 函数形参的个数
console.log(fn.length);
}
闭包
闭包是什么:当嵌套的内部函数中引用了外部函数的变量时就产生了闭包
影响:延长变量作用域、在函数的外部可以访问函数内部的局部变量,实现变量的私有。容易造成内层泄露,因为闭包中的局部变量永远不会被回收
使用场景:实现变量的私有
function outer() {
let count = 1;
function fn() {
count++;
console.log(`函数被调用了${count}次`)
}
return fn;
}
let result = outer();
result() // 2
result() // 3
// 这个例子中count是outer函数内部的局部变量,但在外部可以通过fn函数访问到count,这就是利用闭包实现了变量的私有化
闭包一定要有return
吗,闭包一定会内存泄漏吗
不一定,只有需要让外界访问闭包的变量时才需要return
。函数内部没有将闭包的变量return
出去就不会泄露
内存管理
内存溢出:系统分配给程序的内存空间被耗尽,程序会被中断。报错out of memory
内存泄漏:某个内存中的数据已经没有用了,由于某些原因没有被回收就会造成内存泄漏。
常见内存泄漏场景:没用的全局变量,没有及时清除的定时器,没有及时解绑的监听,没有及时释放的闭包
垃圾回收:引用计数和标记清除
引用计数(已经弃用):如果没有引用指向该对象(零引用),则该对象会被回收。
限制:无法解决循环引用的问题,当两个对象互相引用的时候,永远都不会被回收,因为相互之间至少存在一次引用
标记清除(主流):垃圾回收程序运行时,会给内存中所有变量加上标记,然后将所有上下文的变量以及被上下文中的变量引用的变量的标记清除,之后被标记的变量就会被回收
提升js代码性能:
及时解除(obj = null
)不再使用的全局对象,全局对象的属性,有利于垃圾收集时有效的回收
使用构造函数一次性创建对象的所有属性(这种方式可以共享一个隐藏类),避免先创建再补充的方式创建对象
动态删除属性时,最好使用将属性设置为null
(这种方式可以共享一个隐藏类,同时便于垃圾回收),少使用delete
关键字
防抖和节流函数
防抖和节流都是为了防止短时间内多次触发某个动作
防抖: 当持续触发时,一定时间内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又触发了一次事件,就重新开始延迟
// 防抖函数 触发事件=>清除之前的定时器=>重新开启新定时器等待执行
<input id='debounceBtn'>防抖输入框</input>
const debounceBtn = document.querySelector('#debounceBtn')
// 待调用函数
function fn() {
console.log('点击了防抖按钮')
}
// 触发事件
debounceBtn.addEventListener('click',debounce(fn,1000))
// 定义防抖函数
function debounce(callback, delay = 200) {
// 利用闭包是为让多个return返回的函数共用同一个timer,这样才能实现只有一个定时器在执行
let timer
// 使用高阶函数是为了防止函数在初始定义的时候就被执行一次
return function (params) {
// 触发函数时,如果前面已经触发过了,有一个定时器在执行,则清除之前的定时器,重新开启一个等待执行
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
// 调用回调函数,并让this指向调用者,event为传入的参数
callback.call(this, event)
}, delay)
}
}
节流: 当事件持续被触发时, 一段时间内事件处理函数只会执行一次,只有在事件处理函数执行完毕后再触发才会再次执行
// 节流函数 触发事件=>判断是否已有定时器在执行=>若有,则直接return;若没有,则开启一个定时器去执行
<button id='throttleBtn'>节流按钮</button>
const throttleBtn = document.querySelector('#throttleBtn')
// 待执行函数
function fn() {
console.log('调用了节流按钮')
}
// 触发事件
throttleBtn.addEventListener('click',throttle(fn,1000))
function throttle(callback, delay = 200) {
let timer
return function() {
// 如果已经有定时器在执行,则直接返回
if (timer) {
return
}
timer = setTimeout(() => {
this[callback]()
timer = null
}, delay)
}
}
递归函数
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数(一定要加退出条件,避免发生栈溢出)
arguments.callee()
指向arguments
对象所在函数的指针
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
利用递归求斐波那契数列
// 利用递归函数求斐波那契数列(兔子序列) 1、1、2、3、5、8、13、21...
// 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
// 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值
function fb(n) {
if (n === 1 || n === 2) {
return 1;
}
return fb(n - 1) + fb(n - 2);
}
console.log(fb(3));
Object.defineProperty
Object.defineProperty
设置或修改对象中的属性
Object.defineProperty(对象,修改或新增的属性名,{
value:修改或新增的属性的值,
writable:true/false,//如果值为false 不允许修改这个属性值,默认为false
enumerable: false,//enumerable 如果值为false 则不允许遍历,默认为false
configurable: false, //configurable 如果为false 则不允许删除这个属性 属性是否可以被删除或是否可以再次修改特性,默认为false
get() {}, // 读取操作
set(val) {} // 修改操作
})
Proxy 与 Reflect
Proxy
对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Reflect
是一个内置的对象,所有的属性和方法都是静态的(就像Math
)。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
let person = {
name: 'zhangsan',
age: 18
}
let p = {}
const p = new Proxy(person,{
//有人读取p的某个属性时调用
get(target,propName){
console.log(`有人读取了p身上的${propName}属性`)
return Reflect.get(target,propName)
},
//有人修改p的某个属性、或给p追加某个属性时调用
set(target,propName,value){
console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)
Reflect.set(target,propName,value)
},
//有人删除p的某个属性时调用
deleteProperty(target,propName){
console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
return Reflect.deleteProperty(target,propName)
}
})
浅拷贝与深拷贝
浅拷贝:只复制了引用地址
// 常用方法 直接赋值
const obj = {a:1,b:2}
const newObj = obj
深拷贝:在堆内存中开辟了新空间,创建了一个独立的新对象
// 常用方法
const obj = {a:1,b:2,f:{c:10},fn(){alert("对象里的函数")}}
const newObj = JSON.parse(JSON.stringify(obj)) // JSON方法 但是无法克隆对象里的函数
// 对象展开运算符,Object.asign(),for in方法,都只能实现对象第一层数据的深拷贝 深层数据任未独立
const newObj = {...obj}
const newObj = Object.assign({},obj) // 将obj赋值给一个空对象
const newObj = {}
for (let key in obj) {
newObj[key] = obj[key]
}
// 递归实现深拷贝
// 使用for-in会遍历数组所有的可枚举属性,包括原型。例如上例的原型方法method和name属性都会被遍历出来,通常需要配合hasOwnProperty()方法判断某个属性是否该对象的实例属性,来将原型对象从循环中剔除。
//`hasOwnProperty()`方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。
function deepClone(obj) {
const objClone = Array.isArray(obj) ? [] : {}
if (obj && typeof obj === 'object') {
for (let key in obj) {
// 判断这个key是不是obj自己的(非继承)
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === 'object') {
objClone[key] = deepClone(obj[key])
} else {
objClone[key] = obj[key]
}
}
}
}
return objClone
}
const newObj = deepClone(obj)
高内聚、低耦合
开发应遵循高内聚、低耦合的原则
高内聚:一个模块依靠自身完成一个特定的功能,少依赖外部代码
低耦合:不同模块之间相互关联的程度,尽量减少模块间的依赖,少使用全局变量
小程序
注意事项
- appID 必须要有
- 数据放在服务器端,服务器更改了小程序页面会立马更新,更改小程序需要重新提交审核 需要审核通过才能更新
- 不存在跨域,只支持https(调用接口时要注意)
- wxs: 小程序自己的脚本语言,常当做过滤器使用,基本类似于js, 但是不支持ES6语法,遵循CommonJs规范
- 回到顶部
监听滚动的钩子函数(onPageScroll), 可以设置滚动到哪的api(wx.pageScrollTo) - 下拉刷新
先要在全局或者局部开启下拉刷新( enablePullDownRefresh:true),
在下拉钩子函数(onPullDownRefresh)里面调用下拉函数,
下拉函数里可以用到 loading 提示框,请求完成的回调(complete)里关闭loading, 关闭下拉刷新
优化:添加节流阀 - 上拉触底
在上拉触底钩子函数(onReachBottom)里调用上拉函数,
上拉函数与下拉函数非常相似,上拉函数请求得到的数据是累加到数组中,而刷新是重置,
做一个判断,如果数据已经记载完毕,则提示已到底部 并停止发送请求 - bindtap 和 catchtap 的区别
都是绑定点击事件,bindtap有冒泡,catchtap没有冒泡 - e.target 和 e.currentTarget的区别
e.target 指的是当前触发事件的组件。e.currentTarget 指的是绑定事件的组件
生命周期
App
onLaunch // 只要小程序启动立即执行 只显示一次
onShow // 页面开始显示 只执行一次
onHide // 页面关闭时执行 只执行一次
Page
onLoad // 页面开始加载时执行 只执行一次 类似于created
onShow // 只要进入页面就会执行
onReady // 页面加载完毕后执行 只执行一次 类似于mounted
onHide // 页面只要不显示就会立刻执行
Component
lifetimes:{
created(){} // 组件实例刚创建好,可以拿到数据,但此时的setData()还不能生效
attached(){} // 组件初始化完毕,进入页面节点树,setData()已经可以使用,此时多用于发送初始化请求
ready(){} // 组件页面已经渲染完成后执行
detached(){} // 组件实例被移除节点树后出发
},
pageLifetimes:{
show () {}, // 页面被展示时触发
hide () {}, // 页面被隐藏时触发
resize () {} // 页面尺寸变化
}
常用包
- 将原始的wx.request() 转换为 异步请求,返回一个Promise
miniprogram-api-promise
html5和css3
css3新特性
// 媒体查询 配合flexible.js使用 适应多种屏幕
@media screen and (min-width:300px) {
css代码...
}
// flex布局 弹性盒子 不用固定宽度
flex-direction flex-wrap justify-content
align-items align-content align-self
// 盒模型
box-sizing:content-box
box-sizing:border-box
// css动画
// 创建动画
@keyframes anim {
from{起点...}
to{终点...}
}
// 引用动画
animation: anim 5s linear infinite 2s
animation-play-state:paused // 暂停动画
// 过渡
transition: all 1.2s linear 2s
// 2d 3d转换
transform: translate(50px,100px); /* 移动 */
transform: scale(2); /* 2D缩放 */
transform: rotate(60deg); /* 2D旋转 */
transform: skew(60deg); /* 2D倾斜 */
transform-origin /* 转换原点 */
/* 3D转换需要给父元素添加透视 对所有后代生效 */
perspective: 300px;
/* 保持3D工作空间 只对亲儿子生效 */
transform-style: preserve-3d;
// css3新增样式效果
圆角边框(border-radius) 盒子/文字阴影(box-show/text-show)
背景图片大小(background-size:cover/contain)
裁切背景图片(background-clip:content-box/border-box)
背景渐变 background-image: -webkit-linear-gradient(left,#e66465, #9198e5);
html5新特性
<!-- 语义化标签 处理兼容:引入html5shiv.js插件 -->
<header></header>
<nav></nav>
<article>
<section></section>
</article>
<aside></aside>
<footer></footer>
<!-- 新增表单类型 -->
(常用的):tel email date number
<!-- 新增表单属性 -->
(常用的): autocomplete autofocus placeholder required
<!-- 视频/音频标签 -->
<audio src="sss.mp4" controls></audio>
<video src="sss.mp3" controls></video>
<!-- 画布标签 -->
<canvas></canvas>
<!-- h5新增 web储存技术 -->
sessionStorage localStorage
<!-- h5新增接口 -->
1. 地图接口:百度地图
2. 全屏接口:requestFullScreen():开启全屏 cancelFullScreen(): 退出全屏 fullScreenElement(): 是否全屏状态
3.网络监听接口:online 当网络连接时触发 offline 当网络断开时触发
移动端问题
适配方案
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no"/>
移动端开发最重要的是如何适配页面,实现多终端兼容
常见适配方案:流式布局,flex+rem
布局,栅格系统布局,vh/vw
视口布局
目前主流:响应式布局(栅格系统),媒体查询+rem+flex
混合布局
问题:响应式布局需要设置多个响应断点,切换时会有卡顿。
flexible+rem
布局需要引入js
脚本
兼容问题
- 移动端300ms延迟问题, 这样设计是为了判断你是否要进行双击
解决方案:引入 fastclick.js , 利用FastClick.attach(document.body);
https://blog.csdn.net/zfy865628361/article/details/49512095
zepto的touch模块,tap事件也是为了解决在click的延迟问题
zepto是一个轻量级的有着与jquery类似api的插件,为什么轻量,因为他抛弃了很多兼容代码,并只保留了最基本的功能模块,jquery中含有大量的兼容代码 以及所有功能模块
- normalize: css重置文件,修改了一些浏览器bug, 保存了一些有用的浏览器默认样式
- 防止手机中网页放大和缩小 设置meta中的viewport
- IOS中input键盘事件keyup、keydown、keypress支持不是很好,可以用html5的oninput事件去代替keyup
- fixed定位缺陷 ios下fixed元素容易定位出错,软键盘弹出时,影响fixed元素定位,android兼容效果较好
解决方案: 可用iScroll插件解决这个问题
-
默认情况下,设备会自动识别任何可能是电话号码的字符串。
解决方案:
<meta name="format-detection" content="telephone=no">
<!-- format-detection 格式检测 --> <meta name=”format-detection” content=”telephone=no”> 禁止把数字转化为拨号链接 <meta name=”format-detection” content=”email=no”> 告诉设备不识别邮箱 <meta name=”format-detection” content=”adress=no”> 禁止跳转至地图
-
iphone及ipad下输入框默认内阴影
解决方案:-webkit-appearance: none;
-
ios和android下触摸元素时出现半透明灰色遮罩
解决方案:-webkit-tap-highlight-color:rgba(255,255,255,0)
常见问题
下载文件的方式
【动态a标签】结合【axios方法】的方式下载
function async downloadFile(fileUrl: string, fileName: string) => {
// 若fileUrl为文件请求地址,则需要发送请求 下载文件流转成blob数据
const response = await axios.get(fileUrl, { responseType: 'blob' })
// 使用window.URL.createObjectURL创建一个表示这个Blob对象的URL。
const url = URL.createObjectURL(new Blob([response.data]))
// 创建a标签,完成下载,删除a标签,释放URL对象
const link = document.createElement('a')
link.href = url
link.setAttribute('download', fileName) // 设置下载文件名
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
}
跨域下载图片
downLoadQR(qrCodeUrl) {
const image = new Image()
// 解决跨域 Canvas 污染问题
image.setAttribute('crossOrigin', 'anonymous')
image.onload = function() {
const canvas = document.createElement('canvas')
canvas.width = image.width
canvas.height = image.height
const context = canvas.getContext('2d')
context.drawImage(image, 0, 0, image.width, image.height)
const url = canvas.toDataURL('image/png') // 得到图片的base64编码数据
const a = document.createElement('a') // 生成一个a元素
const event = new MouseEvent('click') // 创建一个单击事件
a.download = '二维码.png' || 'photo' // 设置图片名称
a.href = url // 将生成的URL设置为a.href属性
a.dispatchEvent(event) // 触发a的单击事件
}
image.crossOrigin = '' // 切记一定要将这个放在src赋值前,要不然会在safari上报安全错误
image.src = qrCodeUrl
}
js精度丢失问题
常见有两类问题:浮点数精度问题,大数据精度问题
一般情况使用Number
的toFixed()
进行四舍五入取值,但这样的值并不精准,想得到更加精准的值,使用big.js
等比较成熟的第三方库
// 安装插件
npm install --save big.js
// 引入
import Big from 'big.js'
// 常用方法使用
// 加法
sum = parseFloat(new Big(0.1).plus(0.2))
// 加法
sum = parseFloat(new Big(0.1).minus(0.2))
// 加法
sum = parseFloat(new Big(0.1).times(0.2))
// 加法
sum = parseFloat(new Big(0.1).div(0.2))
// 加法
sum = parseFloat(new Big(0.1).abs(0.2))
// 加法
sum = parseFloat(new Big(0.1).mod(0.2))