值类型、对象类型、函数
// 判断是否是对象或数组
function isObject(obj) {
return typeof obj === 'object' && obj !== null
}
// 全相等(深度)
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
// 值类型(注意,参与 equal 的一般不会是函数)
return obj1 === obj2
}
if (obj1 === obj2) {
return true
}
// 两个都是对象或数组,而且不相等
// 1. 先取出 obj1 和 obj2 的 keys ,比较个数
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length) {
return false
}
// 2. 以 obj1 为基准,和 obj2 一次递归比较
for (let key in obj1) {
// 比较当前 key 的 val —— 递归!!!
const res = isEqual(obj1[key], obj2[key])
if (!res) {
return false
}
}
// 3. 全相等
return true
}
// 测试
const obj1 = {
a: 100,
b: {
x: 100,
y: 200
}
}
const obj2 = {
a: 100,
b: {
x: 100,
y: 200
}
}
// console.log( obj1 === obj2 )
console.log( isEqual(obj1, obj2) )
const arr1 = [1, 2, 3]
const arr2 = [1, 2, 3, 4]
split:拆分成数组
join:拼接成字符串
console.log('1-2-3'.split('-'));//[1,2,3]
console.log([1,2,3].join('-'));//'1-2-3'
console.log('123'.split('2'));//[]1,3]
功能是什么
返回值是什么
是否会对原数组造成影响
const arr = [10, 20, 30, 40]
// pop:去最后一个元素,返回最后一个的值
const popRes = arr.pop()
console.log(popRes, arr)//40, [10, 20, 30]
// shift:取数组第一个元素,返回数组第一个元素
const shiftRes = arr.shift()
console.log(shiftRes, arr) //10, [20, 30, 40]
// push:追加一个元素,返回时数组长度
const pushRes = arr.push(50) // 返回 length
console.log(pushRes, arr) //5, [10, 20, 30, 40, 50]
// unshift:在数组前插入一个元素,返回数组长度
const unshiftRes = arr.unshift(50) // 返回 length
console.log(unshiftRes, arr) //50, [50, 10, 20, 30, 40]
数组的API有哪些纯函数?
1. 不改变源数组(没有副作用);
2. 返回一个数组
// concat:返回一个新数组,在arr基础上拼接上[50, 60, 70],arr和arr1互不影响
const arr1 = arr.concat([50, 60, 70])
// map:对arr中每个元素做出改变,*10,返回新数组
const arr2 = arr.map(num => num * 10)
// filter:过滤>25的,返回新数组
const arr3 = arr.filter(num => num > 25)
// slice:相当于对arr做了一个深拷贝,返回的值跟arr一样,但是arr变,arr1不会变
const arr4 = arr.slice()
// 非纯函数
// push pop shift unshift
// forEach
// some every
// reduce
// slice 纯函数
const arr = [10, 20, 30, 40, 50]
//相当于深拷贝,返回[10, 20, 30, 40, 50]
const arr1 = arr.slice()
//截取第1个跟第4个之间,返回[20, 30, 40]
const arr2 = arr.slice(1, 4)
//从第2个往后截取,返回[30, 40, 50]
const arr3 = arr.slice(2)
//截取后三个,[30, 40, 50]
const arr4 = arr.slice(-3)
// splice 非纯函数
const arr = [10, 20, 30, 40, 50]
//把从第1的位置,长度是2的元素剪切出来,这个位置放进后面的元素
const spliceRes = arr.splice(1, 2, 'a', 'b', 'c')
//返回:[20, 30], [10, 'a', 'b', 'c', 40, 50]
console.log(spliceRes, arr)
//返回spliceRes1:[20, 30],arr:[10, 40, 50]
const spliceRes1 = arr.splice(1, 2)
//返回spliceRes2:[],arr:[10,'a', 'b', 'c', 20, 30, 40, 50]
const spliceRes2 = arr.splice(1, 0, 'a', 'b', 'c')
const res = [10, 20, 30].map(parseInt)
console.log(res) //[10,NaN, NaN]
// 拆解
[10, 20, 30].map((num, index) => {
return parseInt(num, index)
})
call & apply的相同点:
call & apply的不同点:两者传入的列表形式不一样
apply : 和call基本一致,唯一的区别在于传参方式
apply把需要传递给fn的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于给fn一个个的传递
fn.call(obj, 1, 2)
fn.apply(obj, [1, 2])
bind
bind: 语法和call一模一样,区别在于立即执行还是等待执行,bind不兼容IE6~IE8
fn.call(obj, 1, 2); // 改变fn中的this,并且把fn立即执行
fn.bind(obj, 1, 2); // 改变fn中的this,fn并不执行
this改变为obj了,但是绑定的时候立即执行,当触发点击事件的时候执行的是fn的返回值undefined
document.onclick = fn.call(obj);
bind会把fn中的this预处理为obj,此时fn没有执行,当点击的时候才会把fn执行
document.onclick = fn.bind(obj);
事件委托,就是利用事件冒泡的特性,将本应该注册在子元素上的处理事件注册在父元素上,这样点击子元素时,发现其本身并没有相应事件就到父元素上寻找作出响应。
这样做的优势有:
事件冒泡:JS中当触发某些具有冒泡性质的事件是:首先在触发元素寻找是否有相应的注册事件,如果没有再继续向上级父元素寻找是否有相应的注册事件作出响应,这就是事件冒泡。
本质就是上级作用域内变量的生命周期,因为被下级作用域内引用,而没有被释放。就导致上级作用域内的变量,等到下级作用域执行完以后才正常得到释放
阻止原生事件
例如a链接的跳转,form标签的提交等等。
阻止默认事件使用preventDefault()函数,或者在js中return false也可以。
event.preventDefault()
阻止冒泡事件
阻止事件冒泡则使用stopPropagation()函数。
event.stopPropagation()
掘金https://juejin.cn/post/6844903889418846221
JSONP为民间提出的一种跨域解决方案,通过客户端的script标签发出的请求方式。
同源策略:是指协议(http、https、rtmp…)、域名(jd、taobao、baidu)、端口(80、8080、443)其中有一个不同都产生跨域
而所有非同源的请求(即 域名,协议,端口 其中一种或多种不相同),都会被作为跨域请求,浏览器会将其非同源的响应数据丢弃。
这里可以理解为是浏览器在搞事情,服务端确确实实有返回数据,浏览器接收到返回的数据,发现我们请求的是一个非同源的数据,浏览器再将其响应报文丢弃掉。
而通过一些标签发出的请求则不会被进行同源检查,比如script标签,img标签等等,本文讲述JSONP便是通过script标签做的请求。
流程:
1.在发请求先,准备一个全局的接收函数
window.myCallback = (res)=>{ //声明一个全局函数 'callback',用于接收响应数据
console.log(res)
}
2.在html创建script标签,发出请求
....