第一种方法主要是利用字符串分割和数组操作,拿到关键的字符串,再做一下类型转换,组成最终的结果。主要技术点是要熟悉String和Array的相关API,灵活运用。
const getUrlParams = (url) => {
const arrURL = url.split('?').pop().split('#').shift().split('&');
console.log(arrURL)
let obj = {};
arrURL.forEach(item => {
const [k,v] = item.split('=');
obj[k] = v;
})
return obj;
};
const url = 'http://sample.com/?a=1&b=2&c=xx&d=2#hash';
console.log(getUrlParams(url))
第二种方法利用了 Web API 提供的 URL 和 URLSearchParams 对象实现的,用起来非常简单,缺点是兼容性不是很完美,不兼容IE系列浏览器。
const getUrlParams2 = (url) => {
const u = new URL(url)
const s = u.searchParams
let obj = {};
// 这里的
s.forEach((v,k) => {
obj[k] = v;
})
console.log(obj)
return obj;
};
const url = 'http://sample.com/?a=1&b=2&c=xx&d=2#hash';
console.log(getUrlParams2(url))
第三种方法通过正则表达式(捕获组,匹配不在集合中的字符)解析标准的query-string键值对字符串,然后巧妙地利用 string.replace方法的第二个参数可以作为回调函数进行一些操作,得到最终结果,写起来是最简洁的,但是写正则真的是头疼。
正则表达式 /([?&=]+)=([&]+)/g 匹配 URL 中的查询参数。其中 [^?&=]+ 匹配参数名,= 匹配参数名和参数值之间的等号,[^&]+ 匹配参数值。
调用 replace 方法将匹配到的字符串替换为箭头函数的返回值。这里的第一个参数 _ 表示匹配到的整个字符串,不需要使用,用下划线表示忽略该参数。第二个参数 k 表示匹配到的参数名,第三个参数 v 表示匹配到的参数值。
const getUrlParams3 = (url) => {
// 定义一个 parse url.search 的方法
url = url.split('#').shift();
const obj = {};
url.replace(/([^?&=]+)=([^&]+)/g, (_, k, v) => (obj[k] = v));
return obj;
};
const url = 'http://sample.com/?a=1&b=2&c=xx&d=2#hash';
console.log(getUrlParams3(url))
改变 this 指向用的,可以接收多个参数
delete: fn如果不及时删除,可能会导致内存泄漏,因此需要使用 delete 关键字将其从上下文对象中删除。
唯一的 Symbol 值 fn,并将其作为属性添加到 ctx 对象中,这样就避免了属性名的冲突。
Function.prototype.mycall = function(ctx, ...args){
ctx = ctx || window;
let fn = Symbol();
ctx[fn] = this;//this是函数的调用者,这里是foo
let result = ctx[fn](...args);
delete ctx[fn];//释放内存
return result
}
let obj = {
name:"张三"
}
function foo(){
return this.name;
}
// 就是把 foo 函数里的 this 指向,指向 obj
console.log(foo.mycall(obj))
原理同上,只不过 apply 接收第二个参数是数组,不支持第三个参数
使用 arguments 对象来判断是否传递了第二个参数,第一个参数是ctx,如果传递了,将其作为参数列表使用展开运算符 … 解构到一个数组中
Function.prototype.mycall = function(ctx){
ctx = ctx || window;
let fn = Symbol();
ctx[fn] = this;//this是函数的调用者,这里是foo
let result;
if(arguments[1]){
result = ctx[fn](...arguments[1]);
}else{
result = ctx[fn]();
}
delete ctx[fn];//释放内存
return result
}
let obj = {
name:"张三"
}
function foo(){
return this.name;
}
// 就是把 foo 函数里的 this 指向,指向 obj
console.log(foo.apply(obj))
bind 不会立即执行,会返回一个函数
1、函数可以直接执行并且传参,如 foo.myBind(obj, 1)(2, 3),所以需要 [ …args, …arguments ]合并参数
2、函数也可以 new,所以要判断原型 this instanceof fn
Function.prototype.myBind = function (ctx, ...args) {
const self = this
const fn = function(){}
const bind = function(){
const _this = this instanceof fn ? this : ctx
return self.apply(_this, [...args, ...arguments])
}
fn.prototype = this.prototype
bind.prototype = new fn()
return bind
}
call、apply、bind的区别
1、都可以改变 this 指向
2、call 和 apply 会立即执行,bind 不会,而是返回一个函数
3、call 和 bind 可以接收多个参数,apply 只能接受两个,第二个是数组
bind 参数可以分多次传入
接受两个参数,判断第二个参数是不是在第一个参数的原型链上
function myInstanceof(left, right) {
// 获得实例对象的原型 也就是 left.__proto__
let left1 = Object.getPrototypeOf(left)
// 获得构造函数的原型
let prototype = right.prototype
// 判断构造函数的原型 是不是 在实例的原型链上
while (true) {
// 原型链一层层向上找,都没找到 最终会为 null
if (left1 === null) return false
if (prototype === left1) return true
// 没找到就把上一层拿过来,继续循环,再向上一层找
left1 = Object.getPrototypeOf(left1)
}
}
let arr = [1,2,3]
let str = '123'
// 第一个参数是实例对象,第二个参数是构造函数
console.log(myInstanceof(str,Array));
function unique(arr){
return Array.from(new Set(arr))
}
let arr = [1,1,2,2,3,3,4,4]
console.log(unique(arr))
用空对象 let obj ={}利用对象属性不能重复的特性
map方法:
function unique(arr){
let map = new Map() // 或者用空对象 let obj ={}利用对象属性不能重复的特性
let ar = []
arr.forEach((item) => {
if(!map.has(item)){// 如果是对象的话就判断 !obj[item]
map.set(item,true) // 如果是对象的话就 obj[item] = true 其他一样
ar.push(item)
}
})
return ar;
}
let arr = [1,1,2,2,3,3,4,4]
console.log(unique(arr))
对象方法:
function unique(arr){
let obj = {}
let ar = []
arr.forEach((item) => {
if(!obj[item]){// 如果是对象的话就判断 !obj[item]
obj[item] = true // 如果是对象的话就 obj[item] = true 其他一样
ar.push(item)
}
})
return ar;
}
let arr = [1,1,2,2,3,3,4,4]
console.log(unique(arr))
includes方法:
function unique(arr){
let ar = [];
arr.forEach((item) => {
if(!ar.includes(item)){
ar.push(item)
}
})
return ar;
}
let arr = [1,1,2,2,3,3,4,4]
console.log(unique(arr))
indexOf方法:
function unique(arr){
let ar = [];
arr.forEach((item) => {
if(ar.indexOf(item) == -1){
ar.push(item)
}
})
return ar;
}
let arr = [1,1,2,2,3,3,4,4]
console.log(unique(arr))
function unique(arr){
let ar = []
ar = arr.filter((item,index) => {
return arr.indexOf(item) == index
})
return ar;
}
let arr = [1,1,2,2,3,3,4,4]
console.log(unique(arr))
1.Set数据类型:
集合 :里面的元素不能重复
Set是 一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。
常用的方法:
add : 添加一个set元素
delete : 删除一个set元素
has : 查看元素是否存在
size属性 : 查看set中的元素数量
clear : 清空set集合
Symbol.iterator :迭代数据
foreach:循环遍历数据
2.Map数据类型
map与Object非常类似。map与Object的最大区别是:map的key可以是任何数据类型,object的可以只能是字符串,JavaScript支持的所有类型都可以当作Map的key
常用的方法:
set : 添加一个map元素 内置两个参数 第一个是键 第二个是值
delete : 删除一个map元素
has : 查看元素是否存在
size属性 : 查看map中的元素数量
get : 获取一个map集合元素
clear : 清空map集合
foreach:循环遍历数据
3.map 与 set 的关系
1.Map是键值对,Set是值的集合,当然键和值可以是任何的值;
2.Map可以通过get方法获取值,而set不能因为它只有值;
3.都能通过迭代器进行for…of或者foreach遍历;
4.Set的值是唯一的可以做数组去重,Map由于没有格式限制,可以做数据存储
4.ES6中 Object 的常用方法:
Object.is() 方法判断两个值是否是相同的值
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。
Object.getOwnPropertyDescriptors() 回指定对象所有自身属性(非继承属性)的描述对象。
Object.setPrototypeOf() 为现有对象设置原型,返回一个新对象
Object.getPrototypeOf() 方法返回指定对象的原型
Object.keys() 返回所有可遍历( enumerable )属性的键名。
Object.values(),返回所有的可遍历属性的内容。
Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组。
就是把多维数组变成一维数组
let arr = [1,4,[1,[2,[3]]]]
let brr = arr.flat(Infinity);
console.log((brr))
防抖:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>debounce</title>
</head>
<body>
<p>debounce</p>
搜索 <input id="input1">
<script>
// 默认延迟200毫秒,如果传入的有值,就优先传入的值
function debounce(fn, delay = 200) {
let timer = null;
return function () {
if(timer) clearTimeout(timer)
timer = setTimeout(()=>{
fn.apply(this, arguments)//透传this和参数
timer = null;
},delay)
}
}
const input1 = document.getElementById('input1')
input1.addEventListener('keyup', debounce(()=>{
console.log("搜索", input1.value)
}),300)
</script>
</body>
</html>
节流:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>throttle</title>
</head>
<body>
<p>throttle</p>
<div id="div1" draggable="true" style="width: 100px; height: 50px; background-color: #ccc; padding: 10px;">可拖拽</div>
<script>
// 默认延迟200毫秒,如果传入的有值,就优先传入的值
function throttle(fn, delay = 200) {
let timer = null;
return function () {
if(timer) return
timer = setTimeout(()=>{
fn.apply(this, arguments)//透传this和参数
timer = null;
},delay)
}
}
const div1 = document.getElementById('div1')
div1.addEventListener('drag', throttle((e)=>{
console.log("鼠标的位置", e.offsetX, e.offsetY)
}),300)
</script>
</body>
</html>
function myNew(fn,...args){
// 不是函数不能 new
if(typeof fn !== "function"){
throw new Error('TypeError')
}
// 创建一个继承 fn 原型的对象
const newObj = Object.create(fn.prototype);
// 将 fn 的 this 绑定给新对象,并继承其属性,然后获取返回结果
const result = fn.apply(newObj, args);
// 根据 result 对象的类型决定返回结果
return result && (typeof result === "object" || typeof result == "function") ? result : newObj;
}
function mycreate(obj) {
function fn(){}
fn.prototype = obj;
return new fn()
}
1、字面量方法: let obj = {} 、Object.create方法、new方法
2、三者的区别:
function parent() {
this.name = '张三';
}
function child(){
this.age = 19;
parent.call(this);
}
let obj1 = new child()
console.log(obj1)
一定要记得写super()
注意:使用了class的是ES6继承,function的原型继承和call继承都是ES5继承
class parent{
constructor(){
this.name = '张三'
}
}
class child extends parent{
constructor(){
super()
this.age = 19
}
}
let obj = new child();
console.log(obj)
在子类的原型上new一个父类
如果光打印子类可能看不出来继承了,要特地去打印子类没有但是父类有的属性,会发现子类也有,那么就是实现继承了
function parent() {
this.name = '张三';
}
function child(){
this.age = 19;
}
child.prototype = new parent()
let obj1 = new child()
console.log(obj1,obj1.name)
1、加了{} , 使用Object.assign({},obj)层次大于2的都是浅拷贝,第一层都是深拷贝
2、如果不加{} — Object.assign(obj) — 全都浅拷贝
let obj = {name:'张三', age:19,work:['it']}
let obj1 = Object.assign({},obj);
// let obj1 = Object.assign(obj)
// 浅拷贝 -- 使用Object.assign({},obj); 层次大于2的都是浅拷贝
obj1.work[0] = 'web'
console.log(obj)
console.log(obj1)
// 深拷贝--Object.assign({},obj);第一层都是深拷贝
obj1.name = 'lisi'
console.log(obj)
console.log(obj1)
// 如果不加{} --- Object.assign(obj) --- 全都浅拷贝