1、判断数据的类型
function typeOf(obj) {
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()
}
分析:
console.log(toString.call(str)); // [object String]
//这里显示绑定优先级高于隐式绑定
Object类型的每个实例都有toString()方法,返回对象的字符串表示
Object.prototype.toString最终都会返回:[Object, 具体数据类型],这是我们判断数据类型的依据
//为什么要使用call?
Object.prototype.toString(); //输出:[object Object]
这是因为Object.prototype本身也是一个对象,使用call可以让显示绑定到传入的参数执行
Object.prototype.toString.call(str); //输出:[object String]
2、实现instanceof
说明:
1、instanceof的特点:instanceof 只能用来判断两个对象是否属于实例关系,返回布尔值
2、Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)
递归实现
//obj实例
//fn构造函数
function instanceof1(obj, fn) {
//obj必须是一个对象
if(obj === null || typeOf obj !== 'object') {
return false
}
let proto = obj.__proto__
if(proto === fn.prototype) { //隐式原型指向显示原型,说明存在原型链上
return true
} else if (proto === null) { //到了尽头
return false
} else {
return instanceof1(proto, fn)
}
}
//测试代码
function People() {}
const p1 = new People()
const obj = new Object()
console.log(instanceof1(p1, People)); // true
console.log(instanceof1(obj, People)); // false
console.log(instanceof1(obj, Object)); // true
3、实现继承的方案
继承的方案有:
- 原型链继承
- 借用构造函数实现继承
- 寄生式继承
- 组合继承
- 寄生式组合继承
- class实现继承
原型链继承
原理:改写构造函数的显示原型对象
function People() {
this.name = 'linming'
}
People.prototype.running = function () {
console.log(this.name + "在跑步")
}
//Student想要继承People
function Student() {}
Student.prototype = new People() //改写Student的原型
const stu = new Student()
console.log(stu.name) //linming
stu.running() //linming在跑步
问题1:原型中包含的引用类型属性将被所有实例共享;
问题2:子类在实例化的时候不能给父类构造函数传参
借用构造函数实现继承
function People(name, age, address) {
this.name = name
this.age = age
this.address = address
}
People.prototype.running = function() {
console.log(this.name + "在跑步")
}
function Student(name, age, address, sno) {
// this = 所创建的实例
People.call(this, name, age, address)
this.sno = sno
}
//改写Student的原型对象
Student.prototype = new People()
const stu2 = new Student('小明', 223, '广州', 201803041)
console.log(stu2)
//输出:People { name: '小明', age: 223, address: '广州', sno: 201803041 }
借用构造函数实现继承解决了原型链继承的 2 个问题:引用类型共享问题以及传参问题。但是由于方法必须定义在构造函数中,所以会导致每次创建子类实例都会创建一遍方法
寄生式继承
const person = {
age: 23
}
function CreateSudent(name) {
let stu = Object.create(person) //stu等于对象的隐式原型
stu.name = name
stu.sayHello = function() {
console.log(this.name+ "hello~")
}
return stu
}
const stu = new CreateSudent('linming')
console.log(stu)
**Object.create()**方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
寄生组合式继承
function Person(name, age, firedns) {
this.name = name
this.age = age
this.firedns = firedns
}
Person.prototype.running = function() {
console.log("running~")
}
Person.prototype.eating = function() {
console.log("eating~")
}
function Student(name, age, friends, sno, score) {
Person.call(this, name, age, friends)
this.sno = sno
this.score = score
}
// 改写Student的原型对象
// 创建一个对象,该对象的隐式原型指向Person的原型对象,赋值给Student的原型
Student.prototype = Object.create(Person.prototype)
//解决Student的原型中的constructor指向Person的问题
Object.defineProperty(Student.prototype, "constructor", {
enumerable: false,
configurable: true,
writable: true,
value: Student
})
Student.prototype.studying = function() {
console.log("studying~");
}
var stu = new Student("why", 18, ['kobe'], 111, 100)
console.log(stu)
stu.studying() //studying~
stu.running() //running~
stu.eating() //eating~
class实现继承
class People {
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
class Student extends People {
constructor(name, age) {
super(name)
}
}
4、vue响应式原理
proxy实现
class Depend {
constructor() {
// 收集依赖
this.reactiveFns = new Set()
}
//添加依赖
addDepend() {
if(activeEffectFn) {
this.reactiveFns.add(activeEffectFn)
}
}
//执行依赖
notify() {
this.reactiveFns.forEach(item => {
item()
})
}
}
//封装获取dep的函数
const weakMap = new WeakMap()
function getDep(obj, key) {
let map = weakMap.get(obj)
if(!map) {
map = new Map()
weakMap.set(obj, map)
}
let dep = map.get(key)
if(!dep) {
dep = new Depend()
map.set(key, dep)
}
return dep
}
let activeEffectFn = null
const watchFn = function(fn) {
activeEffectFn = fn
fn() //调用时触发get
activeEffectFn = null
}
const reactive = function (obj) {
return new Proxy(obj, {
get(target, key) {
let dep = getDep(target, key)
dep.addDepend() //收集依赖
return target[key]
},
set(target, key, newValue) {
let dep = getDep(target, key)
target[key] = newValue
dep.notify()
}
})
}
Object.defineProperty实现方式
const reactive = function (obj) {
Object.keys(obj).forEach(key => {
let dep = getDep(obj, key)
let value = obj[key]
Object.defineProperty(obj, key, {
get() {
dep.addDepend()
return value
},
set(newValue) {
value = newValue
dep.notify()
}
})
})
return obj
}
5、手写Promise
const Promise_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECT = 'reject'
class MyPromise {
constructor(executor) {
this.status = Promise_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilled = []
this.onRejected = []
const resolve = (value) => {
queueMicrotask(() => {
if(this.status === Promise_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilled.forEach(item => {
item(this.value)
})
}
})
}
const reject = (reason) => {
queueMicrotask(() => {
if(this.status === Promise_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECT
this.reason = reason
this.onRejected.forEach(item => {
item(this.reason)
})
}
})
}
executor(resolve, reject)
}
// then方法
then(onFulfilled, onRejected) {
this.onFulfilled.push(onFulfilled)
this.onRejected.push(onRejected)
}
// 类方法——resolve
static resolve(value) {
return new MyPromise(function(resolve) {
resolve(value)
})
}
// 类方法——reject
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
// 类方法——all
static all(promises) {
return new MyPromise((resolve, reject) => {
const values = []
promises.forEach(promise => {
promise.then(res => {
values.push(res)
if(values.length === promises.length) {
resolve(values)
}
}, err => {
reject(err)
})
})
})
}
// 类方法-race
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(res => {
resolve(res)
}, err => {
reject(err)
})
})
})
}
}
6、防抖 / 节流
防抖
function debounce(fn, delay) {
let timer = null
return function(...args) {
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
const input = document.querySelector('input')
let counter = 0
let fn = function(event) {
console.log(`第${++counter}次触发`, this, event);
}
let newFn = debounce(fn, 1000)
input.oninput = newFn
节流
function throttle(fn,interval) {
let lastTime = 0
return function(...args) {
let nowTime = new Date().getTime()
const remainTime = interval - (nowTime - lastTime)
if(remainTime <= 0) {
fn.apply(this, args)
lastTime = nowTime
}
}
}
7、柯里化
function lmCurrying(fn) {
function curried(...args) {
if(args.length >= fn.length) {
return fn(...args)
} else {
//当传入参数小于原函数的参数个数时
function curried2(...args2) {
return curried(...args, ...args2) //递归调用
}
return curried2
}
}
return curried
}
8、深拷贝
function deepClone (originValue, cache = new WeakMap()) {
//判断基本类型
let type = typeof originValue
if(originValue === null) return originValue
if(type !== 'object') return originValue
//判断是否函数
if(type === 'function') return originValue
// 判断是否map和set
if(originValue instanceof Map) return new Map([...originValue])
if(originValue instanceof Set) return new Set([...originValue])
//递归时,判断map中是否有值
if(cache.has(originValue)) return cache.get(originValue)
let newObj = Array.isArray(originValue) ? [] : {}
// 避免循环引用
cache.set(originValue, newObj)
for(let key in originValue) {
newObj[key] = deepClone(originValue[key], cache)
}
return newObj
}
// 测试代码
const obj = {
name: 'lin',
friends: {
name: '小强',
age: 22,
hobbies: ['看小说', '看综艺', '看球赛']
},
run: function() {
console.log('running')
},
map: new Map(),
girlFriend: null,
}
obj.self = obj
const newObj = deepClone(obj)
console.log(newObj)
newObj.friends.name = '小婷'
console.log(obj)
9、call/apply/bind
1、call
Function.prototype.myCall = function(thisArg, ...args) {
var fn = this //this就是调用了lmcall的函数
thisArg = (thisArg !== null && thisArg !== undefined)? thisArg : window
thisArg.fn = fn
thisArg.fn(...args)
delete thisArg.fn
}
测试代码
function sum(num1, num2) {
console.log(num1, num2, this);
}
sum.myCall({ name: 'linming' }, 10, 20)
2、apply
Function.prototype.lmapply = function(thisArg, argArray = []) {
var fn = this
thisArg = (thisArg !== null && thisArg !== undefined)? thisArg : window
thisArg.fn = fn
thisArg.fn(...argArray)
delete thisArg.fn
}
3、bind
Function.prototype.myBind = function(thisObj, ...args1) {
const fn = this
thisObj = (thisObj === null || thisObj === undefined)? window : thisObj
return function(...args2) {
thisObj.fn = fn
const finallyArgs = [...args1, ...args2]
thisObj.fn(...finallyArgs)
delete thisObj.fn
}
}
10、高阶函数
1、forEach
Array.prototype.myForEach = function(fn) {
const arr = this
for(let i in arr) {
fn(arr[i], i, arr)
}
}
const arr = [1,23,4,5,6,7]
arr.myForEach((item, index) => {
// console.log(item)
console.log(index)
})
forEach不能跳出循环,使用了return只会return掉当前函数的执行,无法终止剩下的函数的执行
2、filter
Array.prototype.myFilter = function(fn) {
const arr = this
let newArr = []
for(let key in arr) {
if(fn(arr[key])) {
newArr.push(arr[key])
}
}
return newArr
}
const arr = [3,4,5,6,66,2,123,6,77,23,23,32]
const res = arr.filter((item) => {
return item > 20
})
console.log(res)
3、map
Array.prototype.myMap = function(fn) {
const arr = this
let newArr = []
for(let value of arr) {
newArr.push(fn(value))
}
return newArr
}
const arr = [2,3,4,5,6,7,8]
const newArr = arr.myMap((item) => {
return item * 10
})
console.log(newArr)
4、every
Array.prototype.myEvery = function(fn) {
const arr = this
let result = true
for(let value of arr) {
result = fn(value)
if(!result) {
break
}
}
return result
}
const arr = [2,3,4,5,6,7,8]
const res = arr.myEvery((item) => {
return item < 8
})
console.log(res)
5、some
Array.prototype.mySome = function(fn) {
const arr = this
let result = false
for(let value of arr) {
result = fn(value)
if(result) {
break
}
}
return result
}
const arr = [2,3,4,5,6,7,8]
const res = arr.mySome((item) => {
return item < 3
})
console.log(res)
11、迭代器
// 定义遍历器生成函数
function makeIterator (array) {
let nextIndex = 0;
return {
next: function () {
return nextIndex < array.length?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true}
}
}
}
let example = makeIterator(["a","b"])
console.log(example.next()); //{value: "a", done: false}
console.log(example.next()); //{value: "b", done: false}
console.log(example.next()); //{value: undefined, done: true}
12、数组去重
1、Set方法
let arr = [1,0,0,2,9,8,3,1,3,3];
console.log(...new Set(arr)); //输出:1 0 2 9 8 3
2、利用数组的indexOf方法去重
let arr = [1, 5, 6, 0, 7, 3, 0, 5, 9,5,5];
function unique(arr) {
let newArr = [] //创建一个新数组
for(let i = 0;i < arr.length;i++) {
if(newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i])
}
}
return newArr
}
console.log(unique(arr));
3、利用数组sort方法去重
let arr = [1, 5, 6, 0, 7, 3, 0, 5, 9, 5, 5];
function unique(arr) {
arr.sort() //升序排序
let newArr = []
newArr.push(arr[0])
for(let i = 1;i < arr.length;i++) {
if(arr[i] !== arr[i-1]) {
newArr.push(arr[i])
}
}
return newArr
}
console.log(unique(arr));
13、数组扁平化
数组扁平化其实就是将多维数组转换为一维数组
//利用简单递归实现功能
function myFlat(arr) {
let result = []
arr.forEach((value, index, arr) => {
if(!Array.isArray(arr[index])) {//该元素不是数组
result.push(value)
} else {//该元素是数组
result = result.concat(myFlat(arr[index])) //递归调用
}
})
return result
}
14、并发控制
const concurrencyControl = (urls, maxNum) => {
return new Promise((resolve) => {
if (!urls.length) return resolve([]);
const result = [];
let index = 0;
let finishCount = 0;
const request = async () => {
if (index >= urls.length) return;
const i = index;
const url = urls[index];
index++;
try {
console.log(url);
const res = await fetch(url);
result[i] = res;
} catch (error) {
console.log(error);
} finally {
finishCount++;
if (finishCount === urls.length) {
resolve(result);
}
request();
}
};
// 限制并发数
const times = Math.min(maxNum, urls.length);
for (let i = 0; i < times; i++) {
request();
}
});
};
