js

JavaScript

js写法、导入包

--------------------------------------------------
1. 区分大小写
2. //单行注释「Ctrl + /」、/* 多行注释 */「Alt + Shift + A」
--------------------------------------------------
/* 行内样式 */
// 在 HTML 中推荐用双引号,JS 中推荐用单引号
<input type="button" value="点我点我" onclick="alert('千古壹号 Hello 方式1')" />

/* 内嵌样式(内联样式): 在页面的<body>标签里采用<script>标签 */
//text 表示纯文本,JavaScript也是纯文本
<script type="text/javascript">
    // 这里写JS代码
    alert('千古壹号 hello 方式2');
    console.log('qianguyihao hello 方式2');
</script>

/* 外链样式: 先在 html 页面的同级目录下新建一个a.js文件,js文件里不用写<script>标签了,直接写里面的内容,再在页面的<body>标签里引入,这里的script标签引用了文件,就不能再写js代码了 */
<script src="a.js"></script>

/* 引入包 */
//body标签里写,用cdn的方式引入decimal.js包(数学运算的包)
<script src="https://cdn.bootcdn.net/ajax/libs/decimal.js/10.2.0/decimal.min.js"></script>

输入、输出、运算符

弹窗

  1. alert('别玩了!'); 警告弹窗,有一个确认按钮
  2. confirm('满18了吗?'); 弹窗,有确定和取消按钮,确定返回true,取消返回false
  3. prompt('请输入姓名'); 弹出输入框,用户输入的都会被转成字符串

输出

  1. document.write('hello world'); 打印在网页的内容区域
  2. 打印到控制台,这些级只是一个标识
    • console.log(result); 普通级
    • console.warn('千古壹号2'); 警告级
    • console.error('千古壹号3'); 错误级
  3. str.repeat(2) 重复2次字符串
  4. eval("x * y") 输出乘积而不是字符串

运算符

m % n //模,取余; 结果的正负性,取决于m的正负性
a += 5 // a=a+5
b=a++ //a先赋值给b,a再+1
b=++a //a先+1,再把a赋值给b
=== //全等于,==会有隐式转换,全等于不会。如果类型不同,就不相等;不能判断NaN,NaN只能用isNaN()判断;如果两个值都是null,或者都是undefined,那么相等
== //如果两个值类型相同,进行 === 比较;如果两个值类型不同,null 和 undefined 能相等;其他情况隐式转换后比较
!== //不全等于
a > b //如果a、b都是字符串,则是一位一位比较字符串的unicode码,如果第一位一样,再比较下一位的
a && b //与;如果a为false,则直接返回a的值,如果a为true,则执行b,返回b的值。与的优先级比或高
a || b //或;如果a为true,则直接返回a的值,如果a为false,则执行b,返回b的值
!0 //非
if (result.resultCode == 0) {
    var a = result && result.data && result.data.imgUrl || 'http://img.smyhvae.com/20160401_01.jpg';
} //一层一层往下找,如果没imgUrl,就用这个url替代,如果连前面的都没有,就直接返回。代替if语句

数据类型

const 和 let :ES6

  1. 有块级作用域,定义在函数内的只在局部起作用。
  2. 重复声明相同变量名会报错,但函数内和函数外有相同变量名没事,但函数内只会使用函数内的变量名。
  3. 不属于顶层对象 Window。
  4. 变量需要先声明,再使用

var :ES5

  1. 有块级作用域,也就是函数内部变量,外部也可以直接调用。
  2. 有变量提升(可先使用,再声明)
/* 自定义常量 */
const name='千古壹号' //ES6。必须赋值。定义后,不可再次赋值,但对象类型的只是不能改引用地址,对象属性还是可以改的
Object.defineProperty(window, 'PI', {
    value: 3.14,
    writable: false, //不可修改
}) //ES5。

/* 变量声明与赋值 */
//ES5 var
var num2 = 20
for(var i=0; i<10; i++){} //for循环里的 i 只会声明一次
//ES6 let
let num1 = num2 = 10
for(let i=0; i<10; i++){} //for循环里的 i 循环一次声明一次

/* 判断数据类型 */
typeof a 或 typeof(a) //获取数据类型
isNaN(a) //判断是否为NaN
Array.isArray(a) //判断是否为数组

基本数据类型

基本数据类型(值类型)【参数赋值的时候,传数值】:String 字符串、Number 数值、BigInt 大型数值、Boolean 布尔值、Null 空值、Undefined 未定义、Symbol

数值型

//除了加号+,其他运算符号会自动把字符串型的数字转化成可计算的数值(隐式转换)
//虽然小数的位数不多,但js就是会出现精度问题,所以不要直接判断两个浮点数是否相等。也可以导入decimal包,然后这么写
new Decimal(0.1).add(new Decimal(0.2)).toNumber(); //add加法、sub减法、mul乘法、div除法
10 + null = 10
10 + undefined = NaN //NaN 是一个特殊的数字
num.toFixed(2)//保留两位小数,返回字符串
Number.isInteger(num)//ES6判断是否为整数
console.log(0b1010) //0b 二进制转十进制
console.log(0o56); //0o 八进制转十进制
方法 描述
Math.PI 圆周率
Math.abs() 返回绝对值
Math.random() 生成[0,1)之间的随机浮点数
Math.round(Math.random()*(y-x)+x) 生成[x,y)之间的随机浮点数
Math.floor(Math.random()*(y-x+1))+x 生成[x,y]之间的随机整数
Math.floor() 向下取整(往小取值)
Math.ceil() 向上取整(往大取值)
Math.round() 四舍五入取整(正数四舍五入,负数五舍六入)
Math.max(x, y, z) 返回多个数中的最大值
Math.min(x, y, z) 返回多个数中的最小值
Math.pow(x,y) 乘方:返回 x 的 y 次幂
Math.sqrt() 开根号
Math.trunc() 去除小数部分

字符串型

//字符串里有些字符需要转义\,js才能正确的输出
str.repeat(5) //ES6 指定重复次数
const [a, b, c, d] = 'hello' //ES6,字符串解构赋值
str.length //获取字符串长度
str+1+(2+6) //字符串拼接 str18(能和所有数据类型相加)
`我是${2+6},age:${f()}` //字符串拼接(可随意enter换行,换行也可以打印出来,注意格式,是用反引号【`】引起来的,不是引号)(ES6模板字符串)
`<ul>${nameList.map((item) => `<li>${item}</li>`).join('')}</ul>`
str.includes('a') //ES6 判断是否包含指定的字符串
str.startsWith('a') //ES6 判断是否以指定字符串开头
str.endsWith('a') //ES6 判断是否以指定字符串结尾
str.indexOf('a') //查找字符a在str中的位置,从0开始
str.indexOf('a',3) //从下标为3的位置开始查找 'a'这个字符
str.search('a') //可正则,查找,返回其第一次出现的索引,无则返回-1
str.includes('a', [position])//是否有a,去掉0-该位置的部分后再找
str.startsWith('a', [position])//是否以a开头,去掉0-该位置的部分后再找
str.endsWith('a', [position])//是否以a结尾,去掉最后一个-该位置的部分后再找
str[i] 或 str.charAt(i)//索引,从0开始
str.charCodeAt(index)//返回字符的 Unicode 编码,字符在 0-127 之间的是英文,英文占一个位置,中文占两个位置
Str.fromCharCode(72)//根据字符的 Unicode 编码获取字符
str.trim()//去除字符串前后的空白
str.toLowerCase()//转换成小写
str.toUpperCase()//转换成大写
encodeURIComponent(url)//对url进行编码
decodeURIComponent(str)//对url进行解码
//截取
str.slice(0, 5)//从0-4
str.slice(2)//从2-最后
str.slice(-3)//从倒数第3个-最后
str.slice(1, -1)//从1-倒数第1个
str.substr(2, 4)//从2开始,截取4个
str.substr(-2)//从倒数第2个开始,截取到最后
str.substring(0,1) //截取0到1之间的字符,不支持负数的下标
str.substring(1) //截取1-最后
//分割,字符串转数组
str.split(分隔符) //可正则
//替换、补全
str.replace('a','b')//可正则,把a替换成b,不会修改原字符串
str.padStart(2,'0') //如果str长度<2,会在开头补0直到长度=2
str.padEnd(2,'0') //如果str长度<2,会在末尾补0直到长度=2
//转 html 格式
str.anchor()
str.big()
str.sub()
str.sup()
str.link('http://www.baidu.com')// 创建 a 链接
str.bold()

引用数据类型

引用数据类型(引用类型)【参数赋值的时候,传地址】:Object 类型(Function、Array、Date、RegExp、Error 等)

Date

var date1 = new Date('2020/02/17 21:00:00').format("yyyy-MM-dd hh:mm:ss") //需要先用new实例化,里面没参数,date1就是当前时间,会返回英文的星期、月份、日期、年、时间,可以用format转换格式
//时间转时间戳(从 1970年1月1日,0时0分0秒 到现在所花费的总毫秒数)
const timestamp1 = +new Date()
const timestamp = new Date().getTime()
Date.now() //H5新的特性
//时间戳转时间
const date32 = new Date(timestamp)

//用Moment.js时间库
<script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.26.0/moment.min.js"></script>
<script>
    console.log(moment().format('YYYY-MM-DD HH:mm:ss'))// 按照指定的格式,格式化当前时间
    console.log(moment().add(7, 'days').format('YYYY-MM-DD hh:mm:ss'))// 按照指定的格式,获取七天后的时间
</script>
先实例 myDate=new Date()
myDate.getFullYear() 返回年份
myDate.getMonth() 返回月: 0-11,0是1月
myDate.getDate() 返回日:1-31
myDate.getDay() 返回星期:0-6,0是周日
myDate.getHours() 返回小时:0-23
myDate.getMinutes() 返回分钟:0-59
myDate.getSeconds() 返回秒:0-59
myDate.getMilliseconds() 返回毫秒,1s = 1000ms

数组

//数组中的元素可以是任意的数据类型,也可以是对象,也可以是函数,也可以是数组
/* 创建 */
var arr = []
let arr = new Array(10) //一个参数的代表数组的长度
var arr4 = Array(15, 16, 17) //可new或不new,多个参数表示数组中的元素
const arr = Array.of(1, 'abc', true) //这个如果参数只有一个,那它也是数组中的元素
/* 获取元素 */
arr[0]
arr.slice(); // 获取所有元素
arr.slice(2); // 从第二个值开始提取,直到末尾
arr.slice(-2); // 提取最后两个元素
arr.slice(0, 4); // 提取下标0-3
arr.slice(4, 2); // 空
/* 赋值 */
arr[0]=10
let [a, b, c] = [1, 2, 3] // ES6,数组的解构赋值(根据位置进行一一对应)
let [a, b] = ['千古壹号'] //左边变量多了,那多了的就只声明不赋值
let [foo = true] = [] //左边的变量还可以设置默认值
let [s1, ...s2] = [1, 2, 3] //s2==[2,3],三个点会把不是数组的变数组,是数组的会解开成序列
let arr2 = [...arr1] //复制,这样不会只复制地址了
let arr3 = [...arr1, ...arr2] //合并数组
/* 删除 */
arr.splice(0, 3) //从0开始,总共删除3个元素,也就是删0,1,2;返回【被删除】的元素,会改变原数组
arr.splice(1, 3, '千古壹号', 'vae') //从1开始,删3个,再在删除的空缺处补上后面两个元素
arr.splice(arr4.indexOf('c'), 1) //删除指定元素
arr.prototype.indexOf(value) //ES5基本就都是在原来的基础上加个prototype
/* 查找&筛选 */
arr.find(function())
arr.find((item, index) => {
    return item > 4; //遍历数组arr,一旦发现有第一个元素大于4,就把这个元素返回,然后停止遍历
})
arr.findIndex((item, index) => {
    return item > 4; //如果这个item大于4,那么返回它的index
})
let arr2 = arr.filter((item) => {
    if (item > 4) {
        return true; // 将arr中大于4的元素返回,组成新的数组
    }
    return false;
})
let arr2 = arr1.filter((item) => item > 4) //简洁版
/* 遍历数组 */
for (var i = 0; i < arr.length; i++) {
    console.log(arr[i]); // 打印出数组中的每一项
}
for (let value of arr1) {
    console.log(value);
}//ES6 可以这样遍历,获取的是数组里的值
for (var index in arr1) {
    console.log(arr1[index]);
}//ES6 可以这样遍历,获取的是数组里的下标
arr.every(function (value, index, array) {
    if (value.length > 2) {
        return false;
    }
    return true;
})//判断数组里每项是否符合条件,全部真,才为真。函数里的参数array填另一个数组,可以判断该数组里所有元素是否在另一个数组里,如果有一项返回 false,则停止遍历,所以return的格式要这么写
arr.some(function (value, index, array) {
    if (value.length > 2) {
        return true;
    }
    return false;
})//判断数组里每项是否符合条件,一个真,则为真
arr.forEach((item, index, arr) => {
    console.log('item:' + item);
    console.log('index:' + index);
    console.log('arr:' + JSON.stringify(arr));
    console.log('----------');
})//没有返回值,故不要将它赋值,直接在函数里面输出。会遍历每一个元素,函数里的参数arr就是本身。改变item不会改变原数组的item,但可以用arr[index]改变。IE8 以上使用
var arr2 = arr1.map(function (item, index) {
    return item + 10; //让arr1中的每个元素加10
})//返回改变完后的数组。
arr.reduce(function (prev, currentValue, currentIndex, arr) {}, initialValue);//prev必写,是上一次调用回调函数时的返回值
arr.reduce((prev, item) => {
    return prev + item;
})//求和
function repeatCount(arr, value) {
    if (!arr || arr.length == 0) return 0;
    return arr.reduce((totalCount, item) => {
        totalCount += item == value ? 1 : 0;
        return totalCount;
    }, 0);
}//统计某个元素出现的次数
arr.reduce((prev, item) => {
    return prev > item ? prev : item;
})//求最大值
Math.max.apply(this, arr1)//求最大值


/* 其他 */
arr.length //获取长度。连续的数组,是获取元素的个数;非连续的数组,得到数组的最大索引+1
arr.length=1 //修改长度。长度变短即删除;长度变长即增加null元素
插入&删除 描述 备注
a.push() 插入,数组的最后面插入一个或多个元素,返回新数组的长度 会改变原数组
a.pop() 删除,删除数组中的最后一个元素,返回被删除的元素 会改变原数组
a.unshift() 插入,在数组最前面插入一个或多个元素,返回新数组的长度 会改变原数组
a.shift() 删除,删除数组中的第一个元素,返回被删除的元素 会改变原数组
a.fill('f', 1, 3) 填充数组,[1,3)填充f,不写后两个参数则填充整个数组;返回新数组 会改变原数组
合并&拆分
a2.concat(a1, a3) 合并数组:合并a2、a1、a3数组,返回新数组 不会改变原数组
['a', 'b', 'c', ...arr1] 合并数组
a.join('-') 连接,将数组转换为字符串 不会改变原数组
a.split(',') 分割,字符串转数组 不会改变原字符串
排序
a.reverse() 将数组进行反转 会改变原数组
a.sort() 排序,按照Unicode 编码,从小到大进行排序 会改变原数组
a2.sort((a, b) => a - b) 升序 会改变原数组
a3.sort((a, b) => b - a) 降序 会改变原数组
查找元素
a.indexOf('p',3) 从前往后索引,查找是否有指定元素,这里从下标3处开始查找,有则返回第一个的索引
a.lastIndexOf('p') 从后往前索引,查找是否有指定元素,有则返回第一个的索引
a.includes('p',2) 判断是否有该元素,默认从头开始找,也可以自己指定,这里从2开始找

集合ES6

会自动去重

/* 创建 */
const set1 = new Set() //Set 本身就是一个构造函数,可通过 new Set() 生成一个 Set 的实例
set1.size //长度
const set2 = new Set(['张三', '李四','张三'])
[...set2] //用集合去重后,再转成数组

函数

  1. 全局变量:写在函数外面的都是全局变量。只有浏览器关闭时才会被销毁,比较占内存。
  2. 局部变量
  • 声明过的局部变量(包括形参),出作用域就会被销毁
  • 没声明过的局部变量,都会成为全局变量,而且并不会提前声明。
  • 函数内可以直接访问或修改全局变量;
  • 如果函数内有和外一样的声明过的(包括形参)变量a,此时访问和修改的都是函数内的变量,这种情况想在函数内使用外面全局变量,可以用 window.a
  • 函数作用域与执行上下文:作用域是在函数定义时就已经确定了,函数执行上下文在调用函数时创建,函数执行完毕以后销毁,调用一次创一次。在 ES6 中,JS 中有块级作用域,也就是函数内部变量,外部不可以直接调用
  1. 变量声明提前:所有用var声明的变量( 比如 var a = 1),会在所有的代码执行之前被声明(但是不会赋值)。
  2. 函数声明提前:可以先调用函数,再定义函数
    • 函数声明的形式创建的函数function foo(){},会被声明提前
    • 函数表达式创建的函数var foo = function(){},不会被声明提前,因为被提前的是变量foo了
  • window属性
    1. 除函数内用var声明的变量以外的变量,都是window的属性,都可以用 window.a 这种形式访问
    2. 任何变量,如果未经声明就赋值,那么该变量是 window 的属性(比如直接写a = 1,那么a相当于window.a
  • 全局预编译顺序
    (并没有赋值,只是作为属性添加给window了)
    1. var定义的全局变量
    2. function声明的全局函数
    3. this
  • 函数预编译顺序
    1. 找形参
    2. 将实参的值赋给形参
    3. 找var定义的局部变量
    4. 找局部的函数声明
    5. 给this赋值

(所以函数内部的函数已经编译好了,按顺序执行时会跳过该函数)

  • 高阶函数:把其他函数作为参数的,或函数作为返回值的函数
  • 闭包:定义在一个函数内部的函数引用了外部函数作用域中的变量或函数。引用的变量或函数会被保护,不会被立即销毁,它的值会跟随调用不断被累加
    • chrome浏览器控制台中,调试闭包。
      Local 指的是局部作用域,Global 指的是 全局作用域;而 Closure 则是闭包,fn1 是一个闭包函数。
      image-20220207001325994
/* 固定语法 */
function fn1(){ //外层函数
    var a = 2 //受保护的变量
    function fn2(){ //内层函数
        a++ //操作受保护的变量
    }
    return fn2 //返回内层函数
}
/* 调用 */
var f = fn1() //返回内部函数fn2
f() //执行fn2,为3
f() //再执行fn2,为4
f = null //回收闭包

普通函数里的this指向
谁直接调用了函数,this 就指向谁,只认()前的对象

  1. o.b.f()调用,那么this指向b;
  2. f()调用,f() 等价于 window.f(),所以this指向window;
  3. a=o.b.f然后a(),只认圆括号前是谁调用,所以this指向window;
  4. 构造函数通过创建实例son1=new f(a, b)的方式创建son1对象,相当于son1={name:'a',age:0},然后用son1.age调用,this指向son1;如果构造函数写了return(通常不写),return返回对象的,则this指向返回对象,其他的情况this还是指向son1
  5. 定时器等异步的函数里使用的this,this指向的是window

箭头函数里的this指向
this 跟着该函数的上一层函数走。上一层是全局了,那this就指向window

/* 创建 */
//写法一,f是函数名。函数会被声明提前
function f(a, b='1', ...args){ //ES6可以有默认参数b='1'但要放在末尾;ES5只能在函数里面写var b=b||'1'。ES6可以把多余的实参全到了args里变成了数组,用三个点定义,有的话它放最后。
    console.log(f.length); //获取形参的个数
    console.log(arguments.length); //获取实参的个数,arguments只在函数中使用,是函数的内置对象
    console.log(arguments.callee) //相当于写f,递归调用函数时推荐用这个
    console.log(arguments[0]) //arguments是个伪数组
    console.log(this) //this只在函数中使用,是函数的内置对象。
    return a //设置函数的返回值,只能返回一个东西,想要返回多个的,包装成对象{a:1,b:2}形式。退出当前函数
}
//写法二,匿名函数。函数不会被声明提前
var f = function () {
    console.log("我是匿名函数中封装的代码")
}
//写法三,ES6箭头函数。更适用于那些本来需要匿名函数的地方,可简洁。并且它不能用作构造函数
let fn2 =(param1, param2, …, paramN) => { statements }//格式。当只有一个参数时,圆括号是可选的,当只有一条语句时,可省略 `return` 关键字和花括号。
materials.map(material => material.length)
/* 箭头函数的this */
var A = {
   name: 'A',
   sayHello: () => {
      console.log(this.name)
   } //箭头函数中的this指向该函数所在的作用域的对象,只有函数的花括号是局部作用域,那么该函数写在全局作用域下,this指向window。
}
function fn1() {
    console.log(this);
    return () => {
        console.log(this); // 该函数在fn1作用域下,就看谁调用了fn1,this就跟谁走。fn1.call(obj1)()第二个括号才是调用这个里面的函数,obj1调用了fn1,所以this指向fn1;fn1.call()(obj1),箭头函数的this是不能重定向的,所以这个无效,this跟着fn1走,指向window
    };
}
fn1.call(obj1)();

/* 立即执行函数 */
(function(a, b) {
    console.log("a = " + a);
})(123, 456)

/* 调用 */
f() //等价于f.call(this)
f()() //多层嵌套函数的用多个括号
f.call(obj1, a, b) //调用,相当于obj1.f(a,b),第一个参数代表这个函数内部的this指向,不需要改变this指向,则传this,后面才写函数的参数
f.apply(obj1, ['a','b']) //调用,用法和call一样,只不过传参一定要用数组的形式传
新函数 = f.bind(想要将this指向哪里, 函数实参1, 函数实参2)//返回的是函数,没调用,可改变this
f.prototype.bind(obj) //ES5的写法

面向对象

/* 创建对象 */
//方法一
var obj2 = {} //创建空对象
const obj1 = {
    name: '千古壹号',
    age: 28
} //里面可以存放嵌套对象、函数等,只是格式一定是{属性名:属性值,属性名:属性值}这样的
let { name, age } = obj1 //ES6,对象的解构赋值
let { name: myName, age: myAge }  = obj1 //也是解构赋值,只是变量name被重命名为myName,打印name会报错,打印myName是对的
({ foo } = { foo: 'smyhvae' }) //如果变量foo在解构之前就已经定义了,那么直接解构赋值会报错,加个圆括号就好了
obj1.a=456 //添加a属性
obj1['a']=456 //添加a属性的另一种方法
delete obj1.name //删除对象的属性
console.log(JSON.stringify(obj)); // 将 obj1 以字符串的形式打印出来
console.log(obj1.a) //打印属性的值
console.log('name' in obj) //检查对象 obj 中是否含有name属性
Object.freeze(obj1) //冻结对象,无法修改对象

//方法二:构造函数(用来生成对象的函数,要用new调用,不需要 return)法
function Student(name) {
    this.name = name; //这里的this是stu2
    this.sayHi = function () {
        console.log(this.name + '厉害了');
    };
}
Student.age=1
Student.prototype.age=16 //所有的函数有prototype属性,但实例对象没有 prototype 属性,有的是__proto__ 属性
var stu2 = new Student('vae'); //new代表实例化一个新对象,stu2是Object类型,Student是Function类型。任何一个函数,如果在前面加了new,那就是构造函数,Student是构造函数。
stu2.sayHi();
stu2.__proto__ === Student.prototype //stu2是实例对象,Student是函数。所有引用类型(数组、对象、函数)都有__proto__这个属性
stu2.age //返回的是原型的age,没用this添加的属性,都要用原型的方式添加,否则没用
//判断是否为构造函数构造的
console.log(对象 instanceof 构造函数) //stu2 instance of Objecet 也是true,因为instanceof是查找对象的原型链(原型的原型...)中是否有该构造函数
//判断foo的直接原型是不是Foo函数
foo.__proto__.constructor === Foo

//方法三:通过建原型,来创建对象
var p = {name:'smyhvae'};
var obj3 = Object.create(p);  //obj3是生成的对象,p是obj3的原型,构造函数是Objecet

/* 类(面向对象)*/
//方式一:用构造函数模拟类
//方式二:用class声明
class Animal2 {
    constructor() {  
        this.name = name;
    }
}
new Animal2() //实例化

/* 继承 */
//方式一:通过调用构造函数时改变this指向的方式。但Son 无法继承 Father 的原型
function Father(myName, myAge) {
    this.name = myName;
    this.age = myAge;
}
function Son(myName, myAge) {
    Father.call(this, myName, myAge); //【重要】Father里的this指向了son1
}
const son1 = new Son('千古壹号', 28);
console.log(JSON.stringify(son1));// {"name":"千古壹号","age":28}

//方法二:通过原型链实现继承。可以继承原型,但修改一个实例对象的引用数据类型,所有实例对象的该属性值都会变,基本数据类型不影响
function Parent() {
    this.name = 'Parent 的属性';
}
function Child() {
    this.type = 'Child 的属性';
}
Child.prototype = new Parent(); //【重要】函数的原型是个对象
console.log(new Child());
//方式三:构造函数 + 原型链(就是即写方式一中重要的那条语句,又写方式二中重要的那条语句)可以即继承原型,又不改变引用数据类型的值

/* 复制对象 */
//浅拷贝:把 obj1 拷贝给 obj2,如果 obj1 只有一层数据,那么obj1 和 obj2 互不影响,用浅拷贝
var obj3 = Object.assign({}, obj1, obj2);//ES6,将 obj1、obj2 的值追加到空对象中,后面的都会依次拷贝到第一个参数中。如果对象里的属性名相同,会被覆盖。
//深拷贝:如果 obj1 有第二层数据(嵌套了对象类型),第二层的数据会互相影响,要用深拷贝。就是将浅拷贝进行递归。
function deepCopy(newObj, oldObj) {
    for (let key in oldObj) {
        let item = oldObj[key];
        // 判断这个值是否是数组
        if (item instanceof Array) {
            newObj[key] = [];
            deepCopy(newObj[key], item);
        } else if (item instanceof Object) {
            // 判断这个值是否是对象
            newObj[key] = {};
            deepCopy(newObj[key], item);
        } else {
            // 简单数据类型,直接赋值
            newObj[key] = item;
        }
    }
}

/* 遍历 */
for (var key in obj) {
    console.log(key); // 键
    console.log(obj[key]); // 值
}

/* ES5创建以指定对象为原型的对象及属性 */
/* 方式一 */
var obj2 = Object.create(obj1) //obj1成为了obj2的原型,直接打印obj2会显示空,__proto__属性会显示两个关联了,obj2可调用obj1的属性
let obj2.__proto__ = obj1 //ES6用这种方式赋原型
var obj2 = Object.create(obj1, {
    sex: {//给obj2添加新的属性sex。直接打印只有sex
        value: '男',  //通过value关键字设置sex的属性值
        writable: false, //属性值是否可修改
        configurable: true, //属性是否可以被删除
        enumerable: true //属性是否能用 for in 枚举
    }
});
/* 方式二 */
Object.defineProperties(obj2, {
    fullName : { //添加fullName属性
        get : function () {
            return this.firstName + '-' + this.lastName
        }, //object自带的get,用来得到当前属性值的回调函数。fullName属性会返回另两个已有属性的连接
        set : function (data) {  //object自带的set,用来监听当前属性值变化的回调函数。赋值给fullName属性新值,set也会捕捉到,将它们赋给firstName和lastName
            var names = data.split('-');
            this.firstName = names[0];
            this.lastName = names[1];
        }
    }
});

正则对象

/* 创建正则表达式 */
//方式一
var myReg = new RegExp("A",'i') //这个变量是正则表达式,正则里用来判断有没有A字符的;i 是匹配模式,可不写,表示忽略大小写
//方式二
var myReg = /A/i
/* 调用 */
myReg.test(str); // 判断 str 是否符合 myReg 这个正则表达式
/* 其他 */
//允许正则的方法,规则都要按正则的写,尤其是想要选所有符合规则的,要加g
str.split(/[a-zA-Z]/) //根据字母分割
str.search(/a[bef]c/) //返回第一次匹配上的首字符下标,否则返回-1
str.match(/[a-z]/gi) //将符合正则的提取出来,并封装到一个数组中返回;g模式,代表全局匹配,就返回所有符合条件的;i模式,代表忽略大小写
str2.replace(/today/gi,"tomorrow") //替换所有today为tomorrow,不会修改原字符串
str.replace(new RegExp(i,'g'),"") // i是变量,直接写正则会把变量变成字符,所以要这么写。

数据类型转换

//隐式转换和显示转换 
a.toString() //不会影响到原变量,null的转换不能用这个
a.toString(2) // a如果是数字,就代表转换成二进制
String(a) //强制转换
+a 或 -a //可通过正负号将其转为数值型
Number(a) //字符串为空或空格->0;包含除数字和小数点以外的字符->NaN;null->0,undefined->NaN
parseInt(a)//转换成整数【取整】(识别字符串里的数字,遇到非数字的字符则停止,不是字符串的也会先转成字符串再转为整数),字符串如果为空或空格报错
parseInt('   81') // 功能一:转整数。开头空格会被去除,开头如果是带有正负号的数字也能转整数,遇到非数字的字符则停止。不是字符串的也会先转成字符串再转为整数。过长的数会自动转换成科学计数法的数字,再转整数,所以也会长的不一样
parseInt('0x10') //功能二:进制解析。十六进制转十进制,16
parseInt('011') //0开头的是十进制,11
parseInt(a, 16) // 十六进制的a转十进制
parseFloat(a) //转成浮点型,类似parseInt
Boolean(a) 或 !!a //空串''为false,但空数组[]或空对象{}为true
Array.from(arrayLike) //将伪数组(用数组的方法会报错的)转化为真数组
let liArr = [...mylis] // 将伪数组转为真正的数组,获取的li列表元素可以用这个转化

json

ES5的json对象

  1. js对象(数组) --> json对象(数组):JSON.stringify(obj/arr)
  2. json对象(数组) --> js对象(数组):JSON.parse(json)
//json和面向对象的格式区别:json的属性必须用双引号引起,面向对象不用
var myJson = {
    "name" : "zs", //字符串在双引号中
    "age" : 18,
    "sex" : true,
    "info" : [{"a":1,"b":2}] //数组在方括号中
};
//json遍历的方法:for...in...
for (var key in myJson) {
    console.log(key); //获取 键
    console.log(myJson[key]); //获取 值(第二种属性绑定和获取值的方法)
    console.log('------');
}

条件、循环

尽量避免深层嵌套,else if多了就是深层嵌套

/* if语句 */
a==1?a:b //a如果等于1,则返回a,否则返回b
if (条件表达式1) {
    // 条件1为真时,做的事情
} else if (条件表达式2) {
    // 条件1不满足,条件2满足时,做的事情
} else {
    // 条件1、2、3都不满足时,做的事情
}

/* switch语句 */
switch(表达式) {
    case 值1:
        语句体1;
        break;//如果符合这里的条件判断,但没用break跳出,则会从这里开始,执行后面所有case里面的语句,直到遇到break
    case 值2:
    case 值3:
    case 值4:
        语句体2;
        break;//2、3、4都会返回语句2
    default:
        语句体 n+1;
        break;
}

/* for循环 */
outer: for (var i = 1; i <= 100; i++) {// i++ 也可以是 i=i+3之类
    console.log(i);
    break outer //只能跳出循环,不能跳出if之类。如果for循环命名过,那可以指定退出哪个循环
    continue outer //只能跳出循环,不能跳出if之类。
}
for(var i in str){} //以任意顺序遍历迭代对象的可枚举属性,如 下标,json键
for(var value of str){} //遍历可迭代对象的值,json格式不能用

/* while循环 */
while(条件表达式){
	语句...
    i++
}

/* do...while 循环 */
do{
	语句...
}while(条件表达式)//循环体至少执行一次

节点(操作对象)

js的组成

  • ECMAScript:JavaScript的语法标准。包括变量、表达式、运算符、函数、if语句、for语句等。
  • DOM:文档对象模型,操作网页上的元素的API。比如让盒子移动、变色、轮播图等。
  • BOM:浏览器对象模型,操作浏览器部分功能的API。比如让浏览器自动滚动

html的节点
节点:构成 HTML 网页的最基本单元,所有的节点都是Object

  • 文档节点(文档):整个 HTML 文档。整个 HTML 文档就是一个文档节点。
  • 元素节点(标签):HTML标签。
  • 属性节点(属性):元素的属性。
  • 文本节点(文本):HTML标签中的文本内容(包括标签之间的空格、换行)。

BOM(浏览器)节点

  • Window:代表整个浏览器的窗口,同时 window 也是网页中的全局对象。下面这些节点可以直接使用,也可以作为属性使用window.screen
  • Navigator:代表当前浏览器的信息,通过该对象可以识别不同的浏览器。
  • Location:代表当前浏览器的地址栏信息,通过 Location 可以获取地址栏信息,或者操作浏览器跳转页面。
  • History:代表浏览器的历史记录,通过该对象可以操作浏览器的历史记录。由于隐私原因,该对象不能获取到具体的历史记录,只能操作浏览器向前或向后翻页,而且该操作只在当次访问时有效。
  • Screen:代表用户的屏幕信息,通过该对象可以获取用户的显示器的相关信息。
var ua = navigator.userAgent
if (/firefox/i.test(ua)) {//正则判断
    alert('是火狐浏览器');
} else if (/chrome/i.test(ua)) {
    alert('是Chrome浏览器');
} else if (/msie/i.test(ua)) {
    alert('是IE浏览器');
} else if ('ActiveXObject' in window) {
    alert('是 IE11 浏览器');
}
/* 模拟移动端浏览器 */
//可模拟不同移动端的userAgent

/* H5 历史 */
history.length //获取当次浏览器历史列表中的 url 数量
history.back() //用来回退到上一个页面,和浏览器后退按钮一样
history.forward() //用来跳转下一个页面
history.go() // 刷新当前页面
history.go(n) // n=1 表示前进;n=-1 后退;n=0 刷新。如果移动的位置超出了访问历史的边界,会静默失败,但不会报错。

window.open('url') //在新窗体中打开页面
location.href //获取当前页面的 url
location.href = 'www.baidu.com'; // 跳转到指定的页面
location.assign('www.baidu.com') // 跳转到指定的页面
location.reload() //刷新当前页面
location.reload(true); // 传true,会强制清空缓存刷新页面
location.replace('www.baidu.com') //用一个新的页面替换当前页面,并清空该页之前的历史记录,也就是不能回退了

获取节点

//获取父节点
节点.parentNode
//获取兄弟节点
下一个兄弟节点 = 节点.nextElementSibling || 节点.nextSibling
//获取单个的子节点
第一个子元素节点 = 节点.firstElementChild || 节点.firstChild
最后一个子元素节点 = 节点.lastElementChild || 节点.lastChild
//获取所有的子节点
子节点数组 = 父节点.childNodes //它只返回HTML节点,甚至不返回文本节点。在IE6/7/8中包含注释节点(在IE678中,注释节点不要写在里面)

操作节点
js可以操作css或html的属性

/* 创建节点 */
var a1 = document.createElement("li") //创建一个li标签
/* 插入节点 */
父节点.appendChild(新的子节点) //父节点的最后插入一个新的子节点,也可以实现已有的节点移动到父节点
父节点.insertBefore(新的子节点,作为参考的子节点) //在参考节点前插入一个新的节点。如果参考节点为null,那么他将在父节点里面的最后插入一个子节点
/* 删除节点 */
父节点.removeChild(子节点)
node1.parentNode.removeChild(node1)//删除自己这个节点
/* 复制节点 */
要复制的节点.cloneNode() //只复制节点本身,不复制子节点
要复制的节点.cloneNode(true) //既复制节点本身,也复制其所有的子节点

/* 获取标签节点 */
var div = document.getElementById("box1"); //方式一:通过id获取,这个标签的一整个内容
var arr1 = document.getElementsByTagName("div");     //方式二:通过 标签名 获得 html中所有是div标签的数组
var arr2 = document.getElementsByClassName("hehe");  //方式三:通过 类名 获得 标签数组

/* 获取节点属性值 */
var myNode = document.getElementsByTagName("img")[0] //获取标签节点
var attribute = myNode.getAttributeNode("id") //获取属性节点
myNode.className //方式一,注意,js的className属性对应html中的class属性
inp.value //input标签有value属性
myNode.innerHTML //会获取到一整个标签内容,包含html实体
myNode.innerText //会获取到一整个标签内容,但不包含html实体
myNode.nodeName //获取节点名
attribute.nodeValue //获取节点值
div1.style.width //获取style属性,行内样式的css
div1.style.backgroundColor //js的backgroundColor属性对应css中的background-color属性,因为在JS中-不能作为标识符,遇到有-的都这么改,去掉-,后一个字母大写
//获取内嵌样式或外链样式的css,用获取元素当前正在显示的样式的方式获取
function getStyle(ele, attr) {
        if (window.getComputedStyle) {
            return window.getComputedStyle(ele, null)[attr];
        }
        return ele.currentStyle[attr];
    } //兼容方法
getStyle(div1, "width")
myNode["className"] //方式二
box.style["width"]
myNode.getAttribute("class") //方式三,注意,这里又是class了。如果不是html本身有的标签或属性(自己造的标签名如abc)用这种方法get/set/removeAttribut操作这个标签
/* 修改属性值 */
myNode.className = "hide" //方式一,属性值要写引号
myNode.innerHTML="<h1>你好</h1>" //赋值时要包含标签实体才会显示正常
myNode.innerTextL="你好" //赋值时不用写标签实体
box1.style.cssText = "width: 300px;height: 300px;background-color: green;" //通过cssText一次性设置行内样式
div.style.opacity = "0.2" //设置背景色的透明度。单位是0.1
div.style.filter = "alpha(opacity=20)" //上一行代码的兼容性写法。注意单位是百进制
div.style.border = "" //取消样式
myNode.setAttribute("class","hide") //方式二,("属性名", "新的属性值")
/* 删除节点的属性 */
myNode.removeAttribute("class")

事件

  • 事件源:引发后续事件的html标签

  • 事件:
    image-202202090132594

  • 事件驱动程序:对样式和html的操作。也就是DOM

  • DOM事件的级别:就是三种事件的写法。DOM0(函数绑定给事件)、DOM2和DOM3(事件监听写法)

  • DOM事件模型:捕获 和 冒泡

  • DOM事件流:捕获(从 window 对象传到 目标元素)目标阶段(到达目标元素)冒泡(从目标元素传到 Window 对象)

<body>
<div id="box1"></div> <!-- 事件源 -->

<script type="text/javascript">
    /* 1、获取事件源 */
    var div = document.getElementById("box1");

    /* 2、绑定事件 */
    //方式一:直接绑定匿名函数
    div.onclick = function () {
        /* 3、书写事件驱动程序 */
        alert("我是弹出的内容");
    }
    //方式二:先单独定义函数,再绑定
    div1.onclick = fn;   //注意,这里是fn,不是fn()。fn()指的是返回值。
    function fn() {
        alert("我是弹出的内容");
    //方式三:行内绑定
    <div id="box1" onclick="fn()"></div> //这里要写fn(),要返回值的
    <script type="text/javascript">
        function fn() {
            alert("我是弹出的内容");
        }

    //方式四
    //事件监听器addEventListener。这种写法不存在响应函数被覆盖的情况。不支持 IE8 以下的浏览器。执行顺序是,先绑定的先执行
    function fn1() {
        console.log("事件1");
    }
    function fn2() {
        console.log("事件2");
    }
    btn.addEventListener("click", fn1, false); //注意是click,不是onclick;参数为true,代表事件在「捕获」阶段触发,触发途中经过的元素要是有事件,都会执行;参数为false或者不写参数,代表事件在「冒泡」阶段触发,按冒泡顺序执行事件
    btn.addEventListener("click", fn2, true);
    //事件监听器attachEvent。IE8以下版本浏览器。执行顺序是,后绑定的先执行
    btn.attachEvent('onclick', fn1);//注意是onclick
    btn.attachEvent('onclick', fn2);
    //兼容写法
    //addEventListener()中的this,是绑定事件的对象;attachEvent()中的this,是window;需要统一两个方法this
    function myBind(element , eventStr , callback){
        if(element.addEventListener){
            //大部分浏览器兼容的方式
            element.addEventListener(eventStr , callback , false);
        }else{
            //IE8以下
            element.attachEvent("on"+eventStr , function(){
                callback.call(element);//用.call将this指向element
            });
        }
    }
    myBind(btn , "click" , function(){
        alert(this);
    });//调用myBind,只是为了兼容。执行我们设置点击了btn触发的alert事件

    /* event */
    //event可以理解为js自带的事件对象,event对象绑定了很多事件
    //js事件的捕获阶段,事件依次传递的顺序是:window --> document --> html--> body --> 父元素、子元素、目标元素
    //js事件的冒泡阶段,从事件元素开始一直冒泡到DOM树的最上层。这些事件不冒泡:blur、focus、load、unload、onmouseenter、onmouseleave
    //事件委托:有多个相同事件的,可以把事件绑定到他的父层上,然后在执行事件函数的时候再去判断是否是目标元素。利用冒泡机制,减少了事件绑定的次数
    box1.onclick = function (event) {
        alert("冒泡 child");
        event = event || window.event; //兼容性写法
        event.bubbles //检查onclick事件是否会冒泡

        //阻止冒泡,兼容写法
        if (event && event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    }

    /* 鼠标键盘的事件 */
    onmousedown //当鼠标在元素上按下时,开始拖拽;
    onmousemove //鼠标一直按着,当鼠标移动时被拖拽元素跟随鼠标移动;
    onmouseup //当鼠标松开时,被拖拽元素固定在当前位置
    onmousewheel //鼠标滚轮滚动的事件,会在滚轮滚动时触发。但是火狐不支持该属性。
    DOMMouseScroll //在火狐中需要使用 DOMMouseScroll 来绑定滚动事件。注意该事件需要通过addEventListener()函数来绑定。
    //键盘事件一般都会绑定给一些可以获取到焦点的对象或者是document
    onkeydown //按键被按下
    onkeyup //按键被松开
    event.keyCode //获取按键的编码
    event.altKey //判断alt键是否被按下
    event.ctrlKey //判断ctrl键是否被按下
    event.shiftKey //判断shift键是否被按下
    document.onkeydown = function(event) {
                event = event || window.event;
                // 判断y和ctrl是否同时被按下
                if (event.ctrlKey && event.keyCode === 89) {
                    console.log('ctrl和y都被按下了');
                }
            };

    /* 自定义事件 */
    var myEvent = new Event('clickTest');
    element.addEventListener('clickTest', function () {
        console.log('smyhvae');
    });
    //元素注册事件
    element.dispatchEvent(myEvent); //注意,参数是写事件对象 myEvent,不是写 事件名 clickTest
        
    /* 设置事件优先级 */
    event.stopImmediatePropagation() //如果给某按钮同时注册了事件A、事件B,想要单击按钮时,只执行事件A,不执行事件B,在事件A的响应函数中加入这句话。
        
    /* 其他 */
    //onload事件:当页面加载(文本和图片)完毕的时候,触发onload事件
    //由于js的加载和html的加载同步,预防使用标签在定义标签之前。想把 JS 写到<head>标签中,必须用 window.onload 将 JS 代码进行包裹,意味着等页面加载完毕后再执行js
    //window.onload是js的入口函数,只能出现一次,出现多次会存在事件覆盖的问题
    window.onload = function () {
        console.log("smyhvae");  //等页面加载完毕时,打印字符串
</script>
</body>

event
image-20220210001325994
由于pageX 和 pageY的兼容性不好,我们可以这样做:
鼠标在页面的位置 = 滚动条滚动的距离 + 可视区域的坐标。

动画

js动画

/* 定时器 */
//setInterval:每隔一段时间执行一次
let num = 1;
const timer1 = setInterval(function () {
    num ++;
    console.log(num);
}, 1000);
//setTimeout:等待一段时间之后再执行(只执行一次)
//方式一:匿名函数
const timer1 = setTimeout(function() {
    console.log(1);
}, 3000);
//方式二
setInterval(fn,1000); //虽然传进来的是函数名,但是会自动调用它
//清除定时器
clearInterval(timer1)

/* offset */
var div1 = document.getElementsByTagName("div")[0] //获取节点
div1.offsetHeight //获取节点的高,Height + padding + border
div1.offsetWidth //获取节点的宽,width + padding + border
box3.offsetParent //获取当前元素的CSS定位(position为absolute、relative、fixed)最近的父元素,如果没有,获取的是body
box2.offsetLeft //只读,不能赋值。获取当前元素相对于其css定位父元素的水平偏移量,从父亲的 padding 开始算起
box2.offsetTop //只读,不能赋值。获取当前元素相对于其css定位父元素的垂直偏移量,从父亲的 padding 开始算起

/* 闪现动画 */
btn1.onclick = function () {
    div1.style.left = "500px";
}

/* 匀速动画 */
//轮播图(自己搜代码)
//方法的封装:每间隔30ms,将盒子向右移一点
function animate(ele, target) {
    //要用定时器,先清除定时器,一个盒子只能有一个定时器,这样的话,不会和其他盒子出现定时器冲突
    clearInterval(ele.timer);
    ele.timer = setInterval(function () {
    console.log(parseInt(div.style.left));
    //动画原理: 盒子未来的位置 = 盒子现在的位置 + 步长;
    //方法1:用offsetLeft获取值,用style.left赋值。
    div.style.left = div.offsetLeft + 100 + 'px';
    // 方法2:必须一开始就在DOM节点上添加 style="left: 0px;"属性,才能用方法2。否则, div.style.left 的值为 NaN
    div.style.left = parseInt(div.style.left)+100+"px"
}, 30);
}
animate(box2, 200)

/* scroll */
window.onscroll() //用鼠标滚轮滚动网页的时候,会触发这个方法
var div = document.getElementsByTagName("div")[0];
div.scrollWidth //获取元素的宽,如果盒子里面的内容超出了盒子,那么获取实际的宽,width + padding
div.scrollHeight //获取元素的高,如果盒子里面的内容超出了盒子,那么获取实际的高,width + padding

document.compatMode === "CSS1Compat"   // DTD已声明,如果==="BackCompat"则未声明。有和没有DTD声明的scrollTop写法是不一样的
//函数封装,因为要兼容,所以干脆做了封装
function scroll() {
    return { //此函数的返回值是面向对象
        left: window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop,
        right: window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft
    }
}
scroll().top //因为封装了这么写。scrollTop:获取垂直滚动条滚动的距离。(window调用,盒子也可以调用,但必须有滚动条)
scroll().left //因为封装了这么写。scrollLeft:获取水平滚动条滚动的距离

document.title //获取文档标题
document.head //获取文档的头标签
document.body //获取文档的body标签
document.documentElement //获取文档的html标签

/* 固定导航栏(自己搜代码) */

/* 缓慢动画 */
//在移动的过程中,步长越来越小
function animate(ele, target) { //封装
    //要用定时器,先清定时器。一个萝卜一个坑儿,一个元素对应一个定时器
    clearInterval(ele.timer);
    //定义定时器
    ele.timer = setInterval(function () {
        //步长设置:盒子位置 = 盒子本身位置 + (目标位置 - 盒子本身位置)/ 10  目标位置和盒子当前位置的十分之一
        var step = (target - ele.offsetLeft) / 10
        //对步长进行二次加工(大于0向上取整,小于0向下取整),因为offsetLeft获取的left值会进行四舍五入
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        ele.style.left = ele.offsetLeft + step + "px";
        console.log(step);
        //检测缓动动画有没有停止
        console.log("smyhvae");
        if (Math.abs(target - ele.offsetLeft) <= Math.abs(step)) {
            //处理小数赋值
            ele.style.left = target + "px";
            clearInterval(ele.timer);
        }
    }, 30);
}

/* 返回顶部(代码自查) */
window.scrollTo(x,y) //浏览器显示区域跳转到指定的坐标

/* client */
//clientWidth、clientHeight:只读,不可修改。获取元素的可见宽高(width/height + padding)。当body/html 调用时,获取网页可视区域宽度
//函数封装:获取屏幕可视区域的宽高
function client() {
    if (window.innerHeight !== undefined) {
        //ie9及其以上的版本的写法
        return {
            "width": window.innerWidth,
            "height": window.innerHeight
        }
    } else if (document.compatMode === "CSS1Compat") {
        //标准模式的写法(有DTD时)
        return {
            "width": document.documentElement.clientWidth,
            "height": document.documentElement.clientHeight
        }
    } else {
        //没有DTD时的写法
        return {
            "width": document.body.clientWidth,
            "height": document.body.clientHeight
        }
    }
}

window.onresize //浏览器大小变化
div.onmousemove //鼠标在盒子上移动
document.onmousemove ////给整个页面绑定

//获取显示器的分辨率。不管如何改变浏览器的窗口大小,title栏显示的值永远都是显示器分辨率:1920*1080。
window.onresize = function () {
    document.title = window.screen.width + "*" + window.screen.height;
}

jQuery库

引入库、创建对象

<!-- 引入库 -->
<script src="http://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="jquery.color.js"></script> <!-- 引入jQuery的插件 -->
<script src="jquery.lazyload.js"></script><!-- 引入懒加载插件 -->
<script>
    //获取 jQuery 库的版本号
    console.log($.fn.jquery)
    //多库共存:jQuery占用了 $ 和 jQuery 这两个变量,如果和别的库产生冲突,让jQuery放弃对它俩的使用权
    $.noConflict() //让 jQuery 放弃对 $ 的使用权
    var MrLv=$.noConflict(true) //放弃两个的使用权,并返回新的关键字赋给MrLv

    /* 类型转换 */
    $(js对象) //DOM对象转为jQuery对象,转换后可用jQuery的方法属性
    jquery对象[index] //jQuery对象转为DOM对象,转换后可用DOM的方法属性
</script>

入口函数

//入口函数:可以出现任意多次,并不存在事件覆盖问题
//文档加载完成:DOM树加载完成后,就可以操作DOM了,不用等到所有的外部资源都加载完成。
//文档加载的顺序:从上往下,边解析边执行
//方式一。文档加载完毕,不等图片加载完毕,就可以执行
$(document).ready(function () {// $ 相当于函数名。$===jQuery
    //获取元素
    var jQbtn = $("button");//根据标签名获取元素,返回数组
    var jQdiv = $("#box"); //可以用css选择器
    //绑定事件
    jQbtn.click(function () {
        $(this).children("ul").show(1000).html(1111) //链式写法。显示盒子,并在盒子里写上1111。jquery对象绑定的事件中,this指js中的dom对象
        jQdiv.css("width", 100) //可直接修改css属性。如果修改的是类标签对象,则会隐式迭代,所有该类的都会变
    });//事件是通过方法绑定的。
});
//方式二。同方式一
$(function () {
        alert(1);
})
//方式三。文档加载完毕,图片也加载完毕,才执行这个函数。
$(window).ready(function () {
    alert(1);
})

创建节点

var node = $("#box").html("<li>我是li</li>") //类似 js 中的innerHTML,在id=box里的最后插入html。node是 jQuery类
var spanNode1 = $("<span>我是一个span元素</span>") //类似 js 中的document.createElement("标签名")
/* 添加内容 */
$("ul").append($("<li>我</li>")) //在最后追加,可以是jQuery类
$("ul").append('<div></div>') //也可以是html代码
$("ul").prepend(node) //在最前追加
$("ul").after(node) //在被选元素之后,作为兄弟元素插入
$("ul").after(node) //在被选元素之前,作为兄弟元素插入
/* 清空内容 */
$("ul").empty() //清空该元素的所有子元素
$("ul").html("") //清空该元素的所有子元素
$("ul").remove() //删除自己及其所有子元素
/* 复制 */
a = $("ul").clone() //深层复制
/* 操作属性 */
$("ul").attr("title", "千古壹号") //设置属性
$("ul").prop("checked", true) //设置属性,针对checked、selected、disabled表单元素属性,要使用 prop()方法
$("ul").removeAttr("title") //移除属性
$("input").val("我是") //赋值input的value属性。表单元素用这个(例如:input、select、textarea 的值)
$("div").text("<li>我是文本内容</li>")//不识别html标签,也就是会原样输出
$("div").height(200) //设置高度
$("div").width(200)  //设置高宽度
$("div").offset({left:100, top: 150}) //设置元素相对于 document 文档的位置。设置offset后,如果元素没有定位(默认值:static),则被修改为relative
$("div").scrollTop(100) //设置元素被卷去的头部的距离
$("div").scrollLeft(100) //设置元素水平方向滚动的位置

获取、设置

/* 获取 */
$(this).index() //获取下标
$("div").css("width") //获取css的width属性,只获取第一个div的width。返回带单位的字符串型
$("div").attr("title") //获取title属性
$("input").val() //获取标签中的value属性的值。表单元素
$("div").text() // 获取双闭合标签中的文本值。(不识别标签)(类比js中的innerText)
$("div").height() //获取高度,返回数值型
$("div").width() //获取宽度
$(".box2").offset().top //获取元素相对于 document 文档的位置,和有没有定位没有关系。返回值为:{left:num, top:num}
$(".box2").position() //获取相对于其最近的带有定位的父元素的位置(获取的就是定位值,和margin/padding无关)。返回值为对象:{left:num, top:num}。只能获取,不能设置
scrollTop() //获取元素被卷去的头部的距离
scrollLeft() //获取元素水平方向滚动的位置
scrollHeight() //获取滚动内容高度
/* 操作css */
$("div").css({"width":100,"height":100,"background-color":"pink"}) //设置多个css样式
//操作class
$("div").addClass("liItem") //为div添加类(html的那个class='')
$("div").removeClass("liItem") //移除该类
$("div").removeClass() //移除该元素的所有类
$("div").hasClass("liItem") //判断是否有这个类
$("div").toggleClass("liItem") //切换,有该class则移除,没该class则添加
$(this).addClass("active").siblings("li").removeClass("active") //当前的li添加active类,其他的删除active类

/* 选择 */
$("#abc").css("width", 100)
$("li:eq(1)").css("width", 100) //选择下标为1的li元素
$("li:gt(1)").css("width", 100) //选择下标>1 的li元素
$("li:lt(1)").css("width", 100) //选择下标<1 的li元素
$("li:odd").css("width", 100) //选择li的奇数行
$("li:even").css("width", 100) //选择li的偶数行
$("li:first").css("width", 100) //选择li的第一个
$("li:last").css("width", 100) //选择li的最后一个
$("a[href]").css("width", 100) //选择所有包含href属性的a标签
$("a[href='z']").css("width", 100) //选择所有href属性为z的a标签
$("a[href^='web']").css("width", 100) //选择所有href属性以web开头的a标签
$("a[href$='cn']").css("width", 100) //选择所有href属性以cn结尾的a标签
$("a[href*='i']").css("width", 100) //选择所有href属性包含i的a标签
$("a[href][title='z']").css("width", 100) //选择所有包含href属性并且title属性为z的a标签
$("#abc").find("li").css("width", 100) //选择id为abc的所有后代li元素
$("#abc").children("li").css("width", 100) //选择id为abc的所有儿子li元素
$("#abc").next().css("width", 100) //选择id为abc的下一个兄弟元素
$("#abc").siblings().css("width", 100) //选择id为abc的所有兄弟元素
$("#abc").parent("div").css("width", 100) //选择id为abc的父亲是div的元素
$("li").eq(2).css("width", 100) //选择所有li元素中下标为2的元素
$(this).prevAll("li") //选取该元素之前的所有li
$(this).nextAll("li") //选取该元素之后的所有li

/* 链式写法 */
//链式编程原理:return this。只有设置操作才能把链式编程延续下去,因为获取操作的时候,会返回获取到的相应的值,无法返回 this
$(this).text(star_sel).prevAll("li").text(star_sel).end().nextAll("li").text(star_none) //选取也是一种获取,获取后设置完,需要用end()结束当前链最近的一次过滤操作,并返回之前的this
result && result.user && result.user.name
result?.user?.name //新特性,原本要上面那种写法的,现在可以这么写
/* 隐式迭代 */
//each:对每个元素做不同的处理
$("ul li").each(function(index,element){
    $(element).css("opacity", (index + 1) / 10)
}) //jQuery有隐式迭代,会自动循环ul里所有的li,所以这个就表示ul里的li透明度递增。递归函数里的两个参数是写死的,参数一就代表索引,参数二就代表当前元素(是js 中的DOM对象,而不是jQuery对象)

事件、动画

/* 事件 */
click //单击事件。
blur //失去焦点事件。
mouseenter //鼠标进入事件。
mouseleave //鼠标离开事件。
dbclick //双击事件。
change //改变事件,如:文本框值改变,下拉列表值改变等。
focus //获得焦点事件。
keydown //键盘按下事件。
//jQuery 1.7版本后,jQuery用on统一了所有的事件处理的方法
$(document).on("click mouseenter", ".box", {"name": 111}, function (event) {
    console.log(event.data) //获取传入的数据,就是那个json数据
    console.log(event.data.name); //得到111
});//第一个参数代表绑定了两个事件,点击和鼠标进入,用空格分开。第二个参数是执行事件的后代元素。第三个参数是传递给事件处理函数的数据,事件触发的时候通过event.data来获取
//解绑事件
$("div").off() //解绑该元素的所有事件
$("div").off("click") // 解绑该元素的所有click事件
$("div").off( "click", "**" )// 解绑所有代理的click事件,但元素的该事件不会被解绑

/* event事件对象 */
event.data //传递给事件处理程序的额外数据
event.currentTarget //等同于this,当前DOM事件对象。在事件委托中,指的是【父元素】
event.pageX //鼠标相对于文档左部边缘的位置
event.target //触发事件源,不一定===this。在事件委托中,指的是【子元素】
event.stopPropagation() //阻止事件冒泡
event.preventDefault() //阻止默认行为
event.type //事件类型:click,dbclick…
event.which //鼠标的按键类型:左1 中2 右3
event.keyCode //键盘按键代码

/* 动画 */
//这里的 .show 这些是事件了,因为有回调函数了
//显隐动画
$(this).show() //显示
$(this).show(2000) //2s后显示完毕
$(this).show("slow") //slow 慢:600ms; normal 正常:400ms; fast 快:200ms
$("div").show(5000,function () {
    alert("动画执行完毕!");
}) //动画执行完后,立即执行函数
$(this).hide() //隐藏,写法和show一样有4种
$(this).toggle() //显示和隐藏的来回切换,来回不是自动的,是点一下显示,第二下隐藏这样。写法和show一样有4种

//滑入和滑出
$("div").slideDown(speed, 回调函数) //滑入:从上往下滑。默认400毫秒。同show一样4种写法
$("div").slideUp(speed, 回调函数) //滑出:从下往上滑
$("div").slideToggle(speed, 回调函数) //滑入和滑出的来回切换

//淡入淡出
$("div").fadeIn(speed, callback) //淡入。同show一样4种写法
$("div").fadeOut(1000) //淡出
$("div").fadeToggle('fast', callback) //淡入淡出的来回切换

//改透明度
$("div").fadeTo(1000, 0.5, callback) //同样有4种写法

//自定义动画
json1={
        "width": 100,
        "background-color": "red"
}
$("div").animate(json1, speed, callback) //第一个参数为自定义动画的CSS属性

//停止动画
$("div").stop(false, false) //第一个参数clearQueue:true为删除队列剩余动画;第二个参数jumpToEnd:true为当前动画立即跳到最终样式,false为立即停止当前动画。默认两个都是false

/* 插件 */
// jquery.color.js 的animate()支持设置背景色属性,jquery的animate()不支持
$("button").on("click", function () {
    $("div").animate({"width": 200, "background-color": "red"}, 2000, function () {
        alert("动画结束");
    });
});
//jquery.lazyload.js 懒加载:看到哪个部分,哪个部分再加载
$("img").lazyload() //懒加载这个图片

jQuery 中的 Ajax

$.ajax({
    url: 'https://xxx.com/getUserInfo.php', // 接口的请求地址
    dataType: 'text', //如果数据种类是jsonp,url在php后面加上?callback1=fn
    data: 'name=fox&age=18', // 请求参数
    type: 'GET', //请求的方式
    success: function (argument) {
        // 接口请求成功时调用
        console.log('接口请求成功');
    },
    beforeSend: function (argument) {}, // 在发送请求之前调用,可以做一些验证之类的处理
    error: function (argument) {
        // 接口请求失败时调用
        console.log('接口请求失败');
    },
});

Zepto库

专门为移动端定制的框架,轻量,快。语法、API大部分同jquery一样

<script src="libs/zepto1.2.0.js"></script> <!-- 引入库 -->
<script src="libs/touch.js"></script> <!-- 引入touch插件 -->
<script>
    $('#btn').on('touchstart', function () {
        alert('hello world');
    }); //写法和 jQuery 是一致的。要将浏览器切换到手机模式,才能看到touchstart事件的效果
</script>

ES5

ES6 能沿用 ES5,但 ES5 不一定能用 ES6
Babel:ES6的语法转为ES5(为了兼容 ES5)
建立工程目录
(1)先建立一个空的工程目录 ES6Demo,并在目录下建立两个文件夹 src和 dist:
src:书写 ES6 代码,我们写的 js 程序都放在这里。
dist:利用 Babel 编译生成的 ES5 代码。我们在 HTML 页面需要引入 dist 里的 js 文件。
(2)在 src 里新建文件 index.html

<!-- 引入 ES5 中的 js 文件,而不是引入 ES6 中的 js 文件。 -->
<script src="./dist/index.js"></script>

(3)新建文件 src/index.js ,在写代码时尽量用单引号
(4)初始化项目:打开cmd,进入项目目录,输入npm init -y 。执行完后,会在项目的根目录下生成 package.json 文件
(5)在根目录下新建.babelrc,并输入

{
    "presets":[
        "es2015"
    ],
    "plugins":[]
}

(6)配置环境(只用一次):在文件 package.json 中修改键 scripts中的内容 babel src/index.js -o dist/index.js
(7)执行npm run build,dist 目录下会生成 ES5 的 js 文件

ES5的严格模式

  • 针对整个文件:将use strict放在文件的第一行,则整个文件将以严格模式运行。
  • 针对单个函数:将use strict放在函数体的第一行,则整个函数以严格模式运行。(最好用这种,因为可以将整个脚本文件放在一个立即执行的匿名函数之中)

PS:如果浏览器不支持,则这句话只会被解析为一条简单的语句,没有任何副作用。
严格模式和普通模式的区别
必须用var声明变量
函数必须声明在顶层
自定义的函数中的this指向undefined
禁止使用with语句
构造函数必须通过new实例化对象
为了让代码更安全,禁止函数内部遍历调用栈
无法删除变量
对象不能有重名的属性

同步异步

因为js是单线程的,所以要异步

异步任务

  1. 定时器:setTimeout(定时炸弹)、setInterval(循环执行)
  2. 事件绑定(比如说,按钮绑定点击事件之后,用户爱点不点。我们不可能卡在按钮那里,什么都不做。所以,应该用异步)
  3. 网络请求(含接口请求):ajax 请求、网络图片加载
  4. ES6 中的 Promise

执行顺序

  1. 同步任务:在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行下一个任务。
  2. 异步任务:会先进入 Event Table;等时间到了之后,再进入 Event Queue,然后排队(为什么要排队?因为同一时间,JS 只能执行一个任务)。比如说,setTimeout(()=> {}, 1000)这种定时器任务,需要等一秒之后再进入 Event Queue。如果将setTimeout放进循环里,那么循环结束后会等1秒,一次性执行完多个setTimeout,因为循环执行过程中,几乎同时设置了多个1秒后执行的定时器
  3. 当主线程的任务执行完毕之后,此时主线程处于空闲状态,于是会去读取 Event Queue 中的任务队列,如果有任务,则进入到主线程去执行。

Ajax请求

HTTP 请求
(1)请求的网址、请求方法 get/post。
(2)提交请求的内容数据、请求主体等。
(3)接收响应回来的内容。

Ajax 请求

/* get 请求 */
//(1)创建异步对象,即 XMLHttpRequest 对象
var xmlhttp = new XMLHttpRequest();
//(2)使用 open 方法设置请求参数
xmlhttp.open('get', '02-ajax.php'); //发送请求。参数一:请求的类型GET 或 POST;参数二:文件在服务器上的位置;参数三:默认true(异步)或 false(同步)
//(3)发送请求
xmlhttp.send();
//(4)注册事件。 onreadystatechange事件,状态改变时就会调用。如果要在数据完整请求回来的时候才调用,我们需要手动写一些判断的逻辑。
xmlhttp.onreadystatechange = function () {
    // 为了保证 数据 完整返回,我们一般会判断 两个值
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { // 4表示请求已完成,且响应已就绪
        //(5)服务端响应,获取返回的数据
        console.log('数据返回成功:' + xmlhttp.responseXML); //获得 XML 形式的响应数据
        xmlhttp.responseXML.querySelector('h1').innerHTML //获取xml的标签,xml就是由html一样的自定义标签组成的,所以获取标签就和html一样就好
    }
};

/* post 请求 */
//(1)创建异步对象
var xmlhttp = new XMLHttpRequest();
//(2)设置请求参数
xmlhttp.open('post', '02.post.php');
// 如果想要像 form 表单提交数据那样,则必须添加此行,然后在 send() 方法中添加想要发送的数据
xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
//(3)发送请求
xmlhttp.send('name=fox&age=18'); //括号里有内容的发送请求(仅用于 POST 请求)
//(4)注册事件
xmlhttp.onreadystatechange = function () {
    //(5)服务端相应
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
        alert(xmlhttp.responseText);//获得json字符串形式的响应数据
    }
};

Ajax请求封装

/* GET请求的封装 */
// 封装 Ajax为公共函数:传入回调函数 success 和 fail
function myAjax(url, success, fail) {
    // 1、创建XMLHttpRequest对象
    var xmlhttp;
    if (window.XMLHttpRequest) {
        xmlhttp = new XMLHttpRequest();
    } else {
        // 兼容IE5、IE6浏览器。不写也没关系
        xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
    }
    // 2、发送请求
    xmlhttp.open('GET', url, true);
    xmlhttp.send();
    // 3、服务端响应
    xmlhttp.onreadystatechange = function () {
        if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
            var obj = JSON.parse(xmlhttp.responseText);
            console.log('数据返回成功:' + obj);
            success && success(xmlhttp.responseText);
        } else {
            // 这里的 && 符号,意思是:如果传了 fail 参数,就调用后面的 fail();如果没传 fail 参数,就不调用后面的内容。因为 fail 参数不一定会传。
            fail && fail(new Error('接口请求失败'));
        }
    };
}

// 单次调用 ajax
myAjax('a.json', (res) => {
    console.log(res);
});//这里传进一个函数参数,只是因为myAjax里写要调用这个函数。把要执行的函数写在这里,利于维护

// 嵌套调用 ajax
myAjax('http://localhost:8888/php/user.php?name=千古', (userInfo) => {
    // 根据第一个接口返回的 userInfo.id,继续请求第二个接口
    myAjax(`http://localhost:8888/php/info.php?id=${userInfo['id']}`, (res) => {
        console.log(response);
    });
});

实际用的Ajax请求

var util = {};

//获取 ajax 请求之后的json
util.json = function (options) {
    var opt = {
        url: '',
        type: 'get',
        data: {},
        success: function () {},
        error: function () {},
    };
    
    util.extend(opt, options);
    
    if (opt.url) {
        //IE兼容性处理:浏览器特征检查。检查该浏览器是否存在XMLHttpRequest这个api,没有的话,就用IE的api
        var xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP');
        var data = opt.data,
            url = opt.url,
            type = opt.type.toUpperCase();
        dataArr = [];
    }

    for (var key in data) {
        dataArr.push(key + '=' + data[key]);
    }

    if (type === 'GET') {
        url = url + '?' + dataArr.join('&');
        xhr.open(type, url.replace(/\?$/g, ''), true);
        xhr.send();
    }

    if (type === 'POST') {
        xhr.open(type, url, true);
        // 如果想要使用post提交数据,必须添加此行
        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xhr.send(dataArr.join('&'));
    }

    xhr.onload = function () {
        if (xhr.status === 200 || xhr.status === 304) { //304表示:用缓存即可。206表示获取媒体资源的前面一部分
            var res;
            if (opt.success && opt.success instanceof Function) {
                res = xhr.responseText;
                if (typeof res === 'string') {
                    res = JSON.parse(res);  //将字符串转成json
                    opt.success.call(xhr, res);
                }
            }
        } else {
            if (opt.error && opt.error instanceof Function) {
                opt.error.call(xhr, res);
            }
        }
    };
}

Promise

回调函数:把函数 A 传给另一个函数 B 调用,那么函数 A 就是回调函数
异步函数执行传统写法:

function fun1(cb) {
    setTimeout(cb, 1000);//异步的
}
//为了让这个执行函数铁定是异步执行的,然后又要便于维护这个执行函数,就这么写。
fun1(function () {
    console.log('我是延迟执行的cb回调函数');
});

promise(ES6):本身不是异步的,但是它可以封装异步任务,并对异步操作进行良好的状态管理。
相比于传统写法(在异步函数里调用回调函数),它是(实例化promise里写同步代码、异步代码,在promise属性里写异步成功或失败执行的代码)
执行顺序:同步任务 --> 微任务 --> 宏任务

setTimeout(() => {
    // ajax和setTimeout等异步函数是宏任务
    console.log('setTimeout');
}, 0);

new Promise((resolve, reject) => {
    resolve();
    console.log('promise1'); // 同步任务
}).then((res) => {
    // 微任务
    console.log('promise then');
});

console.log('promise2'); // 同步任务
// 创建 promise 实例
let promise1 = new Promise((resolve, reject) => { //resolve, reject参数自带的且必填,new Promise实例这一行是同步的
    //进来之后,状态为pending等待中。promise的状态一旦改变,就不能再变
    console.log('同步代码'); //这行代码是同步的
    //promise里面可写异步的代码,比如ajax请求 or 开启定时器
    if (异步的ajax请求成功) {
        console.log('333');
        resolve('请求成功,并传参'); //如果请求成功了,请写resolve(),此时,promise的状态会被自动修改为fulfilled(成功状态),状态变了才能执行promise1.then
    } else {
        reject('请求失败,并传参'); //如果请求失败了,请写reject(),此时,promise的状态会被自动修改为rejected(失败状态),状态变了才能执行promise1.then
    }
});
//调用promise的then():开始处理成功和失败
promise1
    .then( //then可以写处理两个状态的参数,catch只能写处理reject状态的参数
        (successMsg) => {
            // 处理 promise 的成功状态:如果promise的状态为fulfilled,则执行这里的代码
            console.log(successMsg, '成功了'); // 这里的 successMsg 是前面的 resolve('请求成功,并传参')  传过来的参数
        },
        (errorMsg) => {
            //处理 promise 的失败状态:如果promise的状态为rejected,则执行这里的代码
            console.log(errorMsg, '失败了'); // 这里的 errorMsg 是前面的 reject('请求失败,并传参') 传过来的参数
        }
    )
    .finally(() => {
        console.log('无论接口请求成功与否,都会走这里');
    })

//错误写法
try {
    promiseA().then(onResolve);
} catch (e) { //这里的try再catch只能捕获同步函数的异常,不能捕获异步函数的异常,所以是错误写法
    onReject(e);
}

Promise 链式调用(封装多个接口)
在请求完接口1的数据data1之后,需要根据data1的数据,继续请求接口 2,获取data2。像这样的多次 Ajax 请求,可用promise封装

// 1. 封装 ajax 请求:传入回调函数 success 和 fail
function ajax(url, success, fail) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open('GET', url);
    xmlhttp.send();
    xmlhttp.onreadystatechange = function () {
        if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
            success && success(xmlhttp.responseText);
        } else {
            fail && fail(new Error('接口请求失败'));
        }
    };
}

// 2. Promise 封装函数n (设置自定义函数 return promise),有几层嵌套请求,就封装几个这样的promise
// Promise 封装接口1
function request1() {
    return new Promise((resolve, reject) => {
        ajax('https://www.baidu.com', (res) => {
            if (res.retCode == 201) {
                // 接口请求成功时调用:这里的 res 是接口1的返回结果
                resolve('request1 success' + res);
            } else {
                // 接口请求异常时调用异常
                reject('接口1请求失败');
            }
        });
    });
}
// Promise 封装接口2
function request2() {
    return new Promise((resolve, reject) => {
        ajax('https://www.jd.com', (res) => {
            if (res.retCode == 202) {
                // 这里的 res 是接口2的返回结果
                resolve('request2 success' + res);
            } else {
                reject('接口2请求失败');
            }
        });
    });
}

// 3. 请求成功后的执行函数。先发起request1,等resolve后再发起request2
request1()
    .then((res1) => {
        // 接口1请求成功
        console.log(res1);
        return request2(); //这里返回promise,后面好接then。但其实你返回个普通的字符串数字都行,后面的then调用的时候,会自动生成一个promise实例,返回的值会传进后一个then的res2里
    })
    .then((res2) => {
        // 接口2请求成功
        console.log(res2);
        return request3();
    })
    .catch((err) => {
        // 统一处理请求失败。只要有一个请求失败了,就会马上走到 catch
        console.log(err);
    });

Promise的静态方法

// 静态方法 Promise 的 P 要大写
Promise.resolve() //涉及到异步操作,那就需要通过new Promise的方式创建一个 Promise 实例。但有些场景下,我们并没有异步操作,但仍然想调用 promise.then,就可以用 Promise.resolve('success1') 将其包装成成功的状态,success1会返回给then的成功状态的执行函数
Promise.reject()
Promsie.all([promise1, promise2]).then(函数) //并发处理多个异步任务,所有任务都执行成功,才算成功(走到 resolve);只要有一个失败,就会马上走到 reject。参数里传的是多个 promise 实例组成的数组;如果都成功,then里返回的是多个promise执行结果的数组
Promise.race([promise1, promise2]).then(函数) //并发处理多个异步任务,返回的是第一个【执行完成】的 promise,且状态和第一个完成的任务状态保持一致。可以用来做图片请求超时等,3秒后执行请求超时,和图片加载,让它们比谁先执行完,就返回谁
Promise.allSettled() //并发处理多个异步任务,返回所有任务的执行结果(包括成功、失败)
Promise.all()
Promise.any()

async/await

async ... await (ES8)封装多个接口

var fs = require('fs'); //这是nodejs里的方法

function fsRead(path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, { flag: 'r', encoding: "utf-8" }, (err, data) => {
            if (err) {
                reject(err) //失败执行的内容
            } else {
                resolve(data) //成功执行的内容
            }
        })
    })
}
//上面是和promise写法一样的,then这里不一样,写法更简单了,但代表的意思是一样的
async function ReadList() {
    var res1 = await fsRead('hello1.txt');
    var res2 = await fsRead('hello2.txt');
    var res3 = await fsRead('hello3.txt');
}

// 执行方法
ReadList();

跨域通信

同源是指,域名、协议、端口 完全相同
http[协议]://www.baidu.com[域名]:8080[端口]
源不同,那就是跨域了(Cookie、LocalStorage和IndexDB无法获取;无法获取和操作DOM;不能发送Ajax请求)

前后端通信方式:Ajax、WebSocket、CORS

跨域通信方式:JSONP、WebSocket、CORS、postMessage、Hash

postMessage

H5中新增的postMessage()方法,可以用来做跨域通信

/* A窗口 */
// 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息
Bwindow.postMessage('data', 'http://B.com'); //这里强调的是B窗口里的window对象

/* B窗口 */
// 在窗口B中监听 message 事件
Awindow.addEventListener('message', function (event) {   //这里强调的是A窗口里的window对象
    console.log(event.origin);  //获取 :url。这里指:http://A.com
    console.log(event.source);  //获取:A window对象
    console.log(event.data);    //获取传过来的数据
}, false);

WebSocket

var ws = new WebSocket('wss://echo.websocket.org'); //创建WebSocket的对象。参数可以是 ws 或 wss,后者表示加密。

//把请求发出去
ws.onopen = function (evt) {
    ws.send('Hello WebSockets!');
};

//对方发消息过来时,我接收
ws.onmessage = function (evt) {
    console.log('Received Message: ', evt.data);
    ws.close();
};

//关闭连接
ws.onclose = function (evt) {
    console.log('Connection closed.');
};

CORS

不受同源策略的限制,支持跨域。一种新的通信协议标准。可以理解成是:同时支持同源和跨域的Ajax。跨域时,浏览器会拦截Ajax请求,并在http头中加源,所以能跨域

// url(必选),options(可选)
fetch('/some/url/', {
    method: 'get',
}).then(function (response) {  //类似于 ES6中的promise

}).catch(function (err) {
    
});

Hash

  • url #后面的内容就叫Hash,Hash改变,页面不会刷新,所以能跨域

  • url ?后面的内容叫Search,Search的改变,会导致页面刷新,因此不能做跨域通信

页面 A 通过iframe或frame嵌入了跨域的页面 B,A页面想给B页面发消息

/* A页面 */
var B = document.getElementsByTagName('iframe');
B.src = B.src + '#' + 'jsonString'; 
/* B页面 */
window.onhashchange = function () {  //通过onhashchange方法监听,url中的 hash 是否发生变化
    var data = window.location.hash;
};

JSONP

带补丁的 json,本质是利用了 <script src=""></script> 的 src 属性具有可跨域的特性。jsonp 只能通过 GET 方式进行请求

(1)A客户端的代码

<script type="text/javascript">
    function fn(data) {
        console.log(data);
    }
</script>

<!-- 使用 script标签,发送了get请求,去到了一个php页面 -->
<script type="text/javascript" src='http://192.168.141.137/01.php?callback1=fn'></script> <!-- A 客户端请求的是 B服务器上的 01.php页面。url里有个callback1=fn,callback1传递给B服务器的回调函数的名字,fn自定义的函数名称 -->

(2)B服务器端的代码

<?php
    $mycallBack = $_GET['callback1']; //这里的callback1 是A和B之间约定的回调函数名,二者必须一致
    $arr = array("zhangsan","lisi","zhaoliu");
    echo json_encode($arr) //这里的输出,会存到A 客户端的fn方法的data里。刷新A页面,输出结果是这个
?>

算法

栈、队列、链表

https://juejin.im/entry/58759e79128fe1006b48cdfd

/* 快速排序 */
var quickSort = function(arr) {
    if (arr.length <= 1) { return arr; }
    var pivotIndex = Math.floor(arr.length / 2);   //基准位置(理论上可任意选取)
    var pivot = arr.splice(pivotIndex, 1)[0];  //基准数
    var left = [];
    var right = [];
    for (var i = 0; i < arr.length; i++){
        if (arr[i] < pivot) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
    return quickSort(left).concat([pivot], quickSort(right));  //链接左数组、基准数构成的数组、右数组
};
/* 选择排序*/
function selectionSort(arr) {
    var len = arr.length;
    var minIndex, temp;
    for (var i = 0; i < len - 1; i++) {
        minIndex = i;
        for (var j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {     // 寻找最小的数
                minIndex = j;                 // 将最小数的索引保存
            }
        }
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
}
/* 希尔排序*/
function shellSort(arr) {
    var len = arr.length,
        temp,
        gap = 1;
    while(gap < len/3) {          //动态定义间隔序列
        gap = gap*3+1;
    }
    for (gap; gap > 0; gap = Math.floor(gap/3)) {
        for (var i = gap; i < len; i++) {
            temp = arr[i];
            for (var j = i-gap; j >= 0 && arr[j] > temp; j -= gap) {
                arr[j+gap] = arr[j];
            }
            arr[j+gap] = temp;
        }
    }
    return arr;
}

错误

/* 即时运行错误(代码错误) */
//方式一
try ... catch 
//方式二,全局的
Access-Control-Allow-Origin: * //在b.js文件里,加入如下 response header,表示允许跨域
//或者,引入第三方的文件b.js时,在<script>标签中增加crossorigin属性
window.onerror = function(msg, url, row, col, error) { ... }
window.addEventListener("error", fn) //是上面那行的DOM2写法

/* 资源加载错误 */
//资源加载错误,并不会向上冒泡,object.onerror捕获后就会终止(不会冒泡给window),所以window.onerror并不能捕获资源加载错误。但是不会阻止捕获,所以可在捕获阶段绑定error事件
// 方式1:object.onerror。img标签、script标签等节点都可以添加onerror事件,用来捕获资源加载的错误
// 方式2:控制台输入
performance.getEntries().forEach(function(item){console.log(item.name)}) //已经成功加载的资源
document.getElementsByTagName('img') //所有需要加载的的img集合,这个减已成功加载的就是没成功加载的
//方式三:捕获阶段
window.addEventListener("error", function(e){
    console.log('捕获',e)
}, true) 

/* 错误上报 */
//通过Image对象进行错误上报
(new Image()).src = 'http://smyhvae.com/myPath?badjs=msg';   // myPath表示上报的路径(我要上报到哪里去)。后面的内容是自己加的参数。

页面性能优化

一、资源压缩合并,减少http请求

  • 合并图片(css sprites)、CSS和JS文件合并、CSS和JS文件压缩
  • 图片较多的页面也可以使用 lazyLoad 等技术进行优化。
  • 精灵图等

二、非核心代码异步加载

  • 动态脚本加载(不会)

  • defer <script src="./defer1.js" defer></script> 在HTML解析完之后才会执行。如果是多个,则按照加载的顺序依次执行。

  • async <script src="./async1.js" async></script> 在加载完之后立即执行。如果是多个,执行顺序和加载顺序无关。

三、利用浏览器缓存

  • 强缓存:不用请求服务器,直接使用本地的缓存。浏览器第一次请求一个资源时,服务器在返回该资源的同时,会在 http 响应头中添加Expires(服务器返回的绝对时间)或Cache-Control(服务器返回的相对时间)属性,如果两个都添加了,Cache-Control的优先级高于Expires
  • 协商缓存。
    1. 第一次请求该资源,返回Last-Modified(在服务器上的最后修改时间);再次请求这个资源时,不返回Last-Modified,返回If-Modified-Since(上一次返回的Last-Modified的值),如果这个值和上次没变化,则返回304,表示不需要请求,缓存里有
    2. 第一次请求该资源,返回ETag(资源的唯一标识,资源变了,标识就会变);再次请求这个资源时,会有If-None-Match(上一次返回的ETag的值),然后比对两个值,重新生成ETag

四、使用CDN

​ 因为第一次请求资源的时候,浏览器缓存是不起作用的

五、DNS预解析

  • DNS预解析:告诉浏览器未来可能从某个特定 URL 中获取资源,浏览器之后就会快速完成DNS解析

  • 在一些高级浏览器中,页面中所有的超链接(<a>标签等),默认打开了DNS预解析。但是,如果采用的是https协议,很多浏览器是默认关闭超链接的DNS预解析。如果加了<meta http-equiv="x-dns-prefetch-control" content="on">这行代码,则表明强制打开浏览器的预解析

小技巧

/* JS设置、获取盒模型对应的宽和高 */
div.style.width/height //只能获取行内样式中设置的
window.getComputedStyle(div).width/height //通用方式
div.currentStyle.width/height //IE独有
div.getBoundingClientRect().width/height //获取一个元素的绝对位置。返回left、top、width、height

学漏的

算法:波兰式和逆波兰式

面试

https://blog.csdn.net/wuyxinu/article/details/118026083

posted @ 2022-04-12 00:21  咕咚麦当  阅读(328)  评论(0)    收藏  举报