前言
大家好,我是林三心,基礎是進階的前提,前面我給大家分享了本菜鳥這一年來筆記中的50個JS基礎知識點和50個JS高階知識點
- 工作中遇到的50個JavaScript的基礎知識點,滿分找我拿獎品!【閱讀:7.8k,點贊:285】
- 萬字總結」熬夜總結50個JS的高階知識點,全都會你就是神!!!【閱讀:1.5w,點贊:812】
今天就給大家分享一下我筆記中的56個JavaScript手寫知識點
註明:此文章不含演算法題
面試常考
1、實現原生的AJAX請求
const ajax = {
get(url, fn) {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)// 第三個引數非同步與否
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
fn(xhr.responeText)
}
}
xhr.send()
},
post(url, data, fn) {
const xhr = new XMLHttpRequest()
xhr.open('POST', url, true)
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded')
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
fn(xhr.responeText)
}
}
xhr.send(data)
}
}
2、手寫 new 的過程
function myNew(fn, ...args) {
const obj = {}
obj.__proto__ = fn.prototype
fn.apply(obj, args)
return obj
}
3、instanceof關鍵字
function instanceOf(father, child) {
const fp = father.prototype
var cp = child.__proto__
while (cp) {
if (cp === fp) {
return true
}
cp = cp.__proto__
}
return false
}
4、實現防抖函式
function debounce(fn, delay = 500) {
let timer;
return function () {
if (timer) {
clearTimeout(timer)
}
const args = arguments
timer = setTimeout(() => {
fn.apply(this, args) // 改變this指向為呼叫debounce所指的物件
}, delay)
}
}
5、實現節流函式
function throttle(fn, delay = 200) {
let flag = true
return function () {
if (!flag) return
flag = false
const args = arguments
setTimeout(() => {
fn.apply(this, args)
flag = true
}, delay)
}
}
6、實現陣列去重
題目描述:實現一個陣列的去重
// 第一種:Map記錄
function quchong1(arr) {
const newArr = []
arr.reduce((pre, next) => {
if (!pre[next]) {
pre[next] = 1
newArr.push(next)
}
return pre
}, {})
return newArr
}
// 第二種:Set去重
function quchong2(arr) {
return [...new Set(arr)]
}
7、用setTimeout實現setInterval
題目描述:setinterval 用來實現迴圈定時呼叫 可能會存在一定的問題 能用 settimeout 解決嗎
function mySetTimout(fn, delay) {
let timer = null
const interval = () => {
fn()
timer = setTimeout(interval, delay)
}
setTimeout(interval, delay)
return {
cancel: () => {
clearTimeout(timer)
}
}
}
// 測試
const { cancel } = mySetTimout(() => console.log(888), 1000)
setTimeout(() => {
cancel()
}, 4000)
8、用setInterval實現setTimeout
題目說明:沒有,就是想刁難你
function mySetInterval(fn, delay) {
const timer = setInterval(() => {
fn()
clearInterval(timer)
}, delay)
}
// 測試
mySetInterval(() => console.log(888), 1000)
9、實現一個compose函式
題目說明:實現以下效果
function fn1(x) {
return x + 1;
}
function fn2(x) {
return x + 2;
}
function fn3(x) {
return x + 3;
}
function fn4(x) {
return x + 4;
}
const a = compose(fn1, fn2, fn3, fn4);
console.log(a)
console.log(a(1)); // 1+2+3+4=11
實現如下:
function compose(...fn) {
if (fn.length === 0) return (num) => num
if (fn.length === 1) return fn[0]
return fn.reduce((pre, next) => {
return (num) => {
return next(pre(num))
}
})
}
10、實現一個科裡化函式
題目要求:
const add = (a, b, c) => a + b + c;
const a = currying(add, 1);
console.log(a(2,3)) // 1 + 2 + 3=6
實現如下:
function currying(fn, ...args1) {
// 獲取fn引數有幾個
const length = fn.length
let allArgs = [...args1]
const res = (...arg2) => {
allArgs = [...allArgs, ...arg2]
// 長度相等就返回執行結果
if (allArgs.length === length) {
return fn(...allArgs)
} else {
// 不相等繼續返回函式
return res
}
}
return res
}
// 測試:
const add = (a, b, c) => a + b + c;
const a = currying(add, 1);
console.log(a(2,3))
11、實現一個LRU快取函式
題目說明:
實現如下:
class LRUCache {
constructor(size) {
this.size = size
this.cache = new Map()
}
get(key) {
const hasKey = this.cache.has(key)
if (hasKey) {
const val = this.cache.get(key)
this.cache.delete(key)
this.cache.set(key, val)
return val
} else {
return -1
}
}
put(key, val) {
const hasKey = this.cache.has(key)
if (hasKey) {
this.cache.delete(key)
}
this.cache.set(key, val)
if (this.cache.size > this.size) {
this.cache.delete(this.cache.keys().next().value)
}
}
}
12、簡單實現 釋出訂閱模式
題目描述:實現一個釋出訂閱模式擁有 on emit once off
方法
class EventEmitter {
constructor() {
this.cache = {}
}
on(name, fn) {
const tasks = this.cache[name]
if (tasks) {
this.cache[name].push(fn)
} else {
this.cache[name] = [fn]
}
}
off(name, fn) {
const tasks = this.cache[name]
if (task) {
const index = tasks.findIndex(item => item === fn)
if (index >= 0) {
this.cache[name].splice(index, 1)
}
}
}
emit(name, once = false, ...args) {
// 複製一份。防止回撥裡繼續on,導致死迴圈
const tasks = this.cache[name].slice()
if (tasks) {
for (let fn of tasks) {
fn(...args)
}
}
if (once) {
delete this.cache[name]
}
}
once(name, ...args) {
this.emit(name, true, ...args)
}
}
13、實現JSON.parse
題目描述:實現 JSON.parse
function parse (json) {
return eval("(" + json + ")");
}
14、將DOM轉化成樹結構物件
題目描述:
<div>
<span></span>
<ul>
<li></li>
<li></li>
</ul>
</div>
將上方的DOM轉化為下面的樹結構物件
{
tag: 'DIV',
children: [
{ tag: 'SPAN', children: [] },
{
tag: 'UL',
children: [
{ tag: 'LI', children: [] },
{ tag: 'LI', children: [] }
]
}
]
}
實現如下:
function dom2tree(dom) {
const obj = {}
obj.tag = dom.tagName
obj.children = []
dom.childNodes.forEach(child => obj.children.push(dom2tree(child)))
return obj
}
15、將樹結構轉換為DOM
題目描述:
{
tag: 'DIV',
children: [
{ tag: 'SPAN', children: [] },
{
tag: 'UL',
children: [
{ tag: 'LI', children: [] },
{ tag: 'LI', children: [] }
]
}
]
}
將上方的樹結構物件轉化為下面的DOM
<div>
<span></span>
<ul>
<li></li>
<li></li>
</ul>
</div>
實現如下:
// 真正的渲染函式
function _render(vnode) {
// 如果是數字型別轉化為字串
if (typeof vnode === "number") {
vnode = String(vnode);
}
// 字串型別直接就是文字節點
if (typeof vnode === "string") {
return document.createTextNode(vnode);
}
// 普通DOM
const dom = document.createElement(vnode.tag);
if (vnode.attrs) {
// 遍歷屬性
Object.keys(vnode.attrs).forEach((key) => {
const value = vnode.attrs[key];
dom.setAttribute(key, value);
});
}
// 子陣列進行遞迴操作
vnode.children.forEach((child) => dom.appendChild(_render(child)));
return dom;
}
16、判斷一個物件有環引用
題目描述:驗證一個物件有無環引用
var obj = {
a: {
c: [
1, 2
]
},
b: 1
}
obj.a.c.d = obj
console.log(cycleDetector(obj)) // true
實現思路:用一個陣列儲存每一個遍歷過的物件,下次找到陣列中存在,則說明環引用
function cycleDetector(obj) {
const arr = [obj]
let flag = false
function cycle(o) {
const keys = Object.keys(o)
for (const key of keys) {
const temp = o[key]
if (typeof temp === 'object' && temp !== null) {
if (arr.indexOf(temp) >= 0) {
flag = true
return
}
arr.push(temp)
cycle(temp)
}
}
}
cycle(obj)
return flag
}
17、計算一個物件的層數
題目描述:給你一個物件,統計一下它的層數
const obj = {
a: { b: [1] },
c: { d: { e: { f: 1 } } }
}
console.log(loopGetLevel(obj)) // 4
實現如下:
function loopGetLevel(obj) {
var res = 1;
function computedLevel(obj, level) {
var level = level ? level : 0;
if (typeof obj === 'object') {
for (var key in obj) {
if (typeof obj[key] === 'object') {
computedLevel(obj[key], level + 1);
} else {
res = level + 1 > res ? level + 1 : res;
}
}
} else {
res = level > res ? level : res;
}
}
computedLevel(obj)
return res
}
18、物件的扁平化
題目描述:
const obj = {
a: {
b: 1,
c: 2,
d: {e: 5}
},
b: [1, 3, {a: 2, b: 3}],
c: 3
}
flatten(obj) 結果返回如下
// {
// 'a.b': 1,
// 'a.c': 2,
// 'a.d.e': 5,
// 'b[0]': 1,
// 'b[1]': 3,
// 'b[2].a': 2,
// 'b[2].b': 3
// c: 3
// }
實現如下:
const isObject = (val) => typeof val === "object" && val !== null
function flatten(obj) {
if (!isObject(obj)) return
const res = {}
const dfs = (cur, prefix) => {
if (isObject(cur)) {
if (Array.isArray(cur)) {
cur.forEach((item, index) => {
dfs(item, `${prefix}[${index}]`)
})
} else {
for(let key in cur) {
dfs(cur[key], `${prefix}${prefix ? '.' : ''}${key}`)
}
}
} else {
res[prefix] = cur
}
}
dfs(obj, '')
return res
}
// 測試
console.log(flatten(obj))
19、實現(a == 1 && a == 2 && a == 3)為true
題目描述: 實現(a == 1 && a == 2 && a == 3)為true
// 第一種方法
var a = {
i: 1,
toString: function () {
return a.i++;
}
}
console.log(a == 1 && a == 2 && a == 3) // true
// 第二種方法
var a = [1, 2, 3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3); // true
// 第三種方法
var val = 0;
Object.defineProperty(window, 'a', {
get: function () {
return ++val;
}
});
console.log(a == 1 && a == 2 && a == 3) // true
20、實現限制併發的Promise排程器
題目描述:JS 實現一個帶併發限制的非同步排程器 Scheduler,保證同時執行的任務最多有兩個
addTask(1000,"1");
addTask(500,"2");
addTask(300,"3");
addTask(400,"4");
的輸出順序是:2 3 1 4
整個的完整執行流程:
一開始1、2兩個任務開始執行
500ms時,2任務執行完畢,輸出2,任務3開始執行
800ms時,3任務執行完畢,輸出3,任務4開始執行
1000ms時,1任務執行完畢,輸出1,此時只剩下4任務在執行
1200ms時,4任務執行完畢,輸出4
實現如下:
class Scheduler {
constructor(limit) {
this.queue = []
this.limit = limit
this.count = 0
}
add(time, order) {
const promiseCreator = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(order)
resolve()
}, time)
})
}
this.queue.push(promiseCreator)
}
taskStart() {
for(let i = 0; i < this.limit; i++) {
this.request()
}
}
request() {
if (!this.queue.length || this.count >= this.limit) return
this.count++
this.queue.shift()().then(() => {
this.count--
this.request()
})
}
}
// 測試
const scheduler = new Scheduler(2);
const addTask = (time, order) => {
scheduler.add(time, order);
};
addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
scheduler.taskStart();
21、實現lazyMan函式
題目描述:
實現一個LazyMan,可以按照以下方式呼叫:
LazyMan(“Hank”)輸出:
Hi! This is Hank!
LazyMan(“Hank”).sleep(10).eat(“dinner”)輸出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~
LazyMan(“Hank”).eat(“dinner”).eat(“supper”)輸出
Hi This is Hank!
Eat dinner~
Eat supper~
LazyMan(“Hank”).eat(“supper”).sleepFirst(5)輸出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
實現如下:
class _LazyMan {
constructor(name) {
this.tasks = []
const task = () => {
console.log(`Hi! This is ${name}`)
this.next()
}
this.tasks.push(task)
setTimeout(() => {
this.next()
}, 0)
}
next() {
const task = this.tasks.shift()
task && task()
}
sleep(time) {
this.sleepWrapper(time, false)
return this
}
sleepFirst(time) {
this.sleepWrapper(time, true)
return this
}
sleepWrapper(time, first) {
const task = () => {
setTimeout(() => {
console.log(`Wake up after ${time}`)
this.next()
}, time * 1000)
}
if (first) {
this.tasks.unshift(task)
} else {
this.tasks.push(task)
}
}
eat(food) {
const task = () => {
console.log(`Eat ${food}`);
this.next();
};
this.tasks.push(task);
return this;
}
}
// 測試
const lazyMan = (name) => new _LazyMan(name)
lazyMan('Hank').sleep(1).eat('dinner')
lazyMan('Hank').eat('dinner').eat('supper')
lazyMan('Hank').eat('supper').sleepFirst(5)
22、實現add函式
題目描述:實現一個 add 方法 使計算結果能夠滿足如下預期:
- add(1)(2)(3)()=6
- add(1,2,3)(4)()=10
function add(...args1) {
let allArgs = [...args1]
function fn(...args2) {
if (!args2.length) return fn.toString()
allArgs = [...allArgs, ...args2]
return fn
}
fn.toString = function () {
return allArgs.reduce((pre, next) => pre + next)
}
return fn
}
// 測試
console.log(add(1)(2)(3)())
console.log(add(1, 2)(3)())
23、實現一個合格的深拷貝
推薦看我這篇:深拷貝有這5個段位,你只是青銅段位?還想漲薪?
24、實現 Promise
推薦看我這篇:看了就會,手寫Promise原理,最通俗易懂的版本!!!【閱讀:1.3w,點贊:460】
25、實現 async/await
推薦看我這篇:7張圖,20分鐘就能搞定的async/await原理!為什麼要拖那麼久?【閱讀:2.15w,點贊:460】
Array篇
定義一個測試陣列
const players = [
{ name: '科比', num: 24 },
{ name: '詹姆斯', num: 23 },
{ name: '保羅', num: 3 },
{ name: '威少', num: 0 },
{ name: '杜蘭特', num: 35 }
]
26、forEach
引數代表含義
- item:遍歷項
- index:遍歷項的索引
arr:陣列本身
Array.prototype.sx_forEach = function (callback) { for (let i = 0; i < this.length; i++) { callback(this[i], i, this) } } players.sx_forEach((item, index, arr) => { console.log(item, index) }) // { name: '科比', num: 24 } 0 // { name: '詹姆斯', num: 23 } 1 // { name: '保羅', num: 3 } 2 // { name: '威少', num: 0 } 3 // { name: '杜蘭特', num: 35 } 4
27、map
引數代表含義
- item:遍歷項
- index:遍歷項的索引
arr:陣列本身
Array.prototype.sx_map = function (callback) { const res = [] for (let i = 0; i < this.length; i++) { res.push(callback(this[i], i, this)) } return res } console.log(players.sx_map((item, index) => `${item.name}--${item.num}--${index}`)) // [ '科比--24--0', '詹姆斯--23--1', '保羅--3--2', '威少--0--3', '杜蘭特--35--4' ]
28、filter
引數代表含義
- item:遍歷項
- index:遍歷項的索引
arr:陣列本身
Array.prototype.sx_filter = function (callback) { const res = [] for (let i = 0; i < this.length; i++) { callback(this[i], i, this) && res.push(this[i]) } return res } console.log(players.sx_filter(item => item.num >= 23)) // [ // { name: '科比', num: 24 }, // { name: '詹姆斯', num: 23 }, // { name: '杜蘭特', num: 35 } // ]
29、every
引數代表含義
- item:遍歷項
- index:遍歷項的索引
arr:陣列本身
Array.prototype.sx_every = function (callback) { let flag = true for (let i = 0; i < this.length; i++) { flag = callback(this[i], i, this) if (!flag) break } return flag } console.log(players.sx_every(item => item.num >= 23)) // false console.log(players.sx_every(item => item.num >= 0)) // true
30、some
引數代表含義
- item:遍歷項
- index:遍歷項的索引
arr:陣列本身
Array.prototype.sx_some = function (callback) { let flag = false for (let i = 0; i < this.length; i++) { flag = callback(this[i], i, this) if (flag) break } return flag } console.log(players.sx_some(item => item.num >= 23)) // true console.log(players.sx_some(item => item.num >= 50)) // false
31、reduce
引數代表含義
- pre:前一項
- next:下一項
- index:當前索引
arr:陣列本身
Array.prototype.sx_reduce = function (callback, initValue) { let start = 0, pre if (initValue) { pre = initValue } else { pre = this[0] start = 1 } for (let i = start; i < this.length; i++) { pre = callback(pre, this[i], i, this) } return pre } // 計算所有num相加 const sum = players.sx_reduce((pre, next) => { return pre + next.num }, 0) console.log(sum) // 85
32、findIndex
引數代表含義
- item:遍歷項
- index:遍歷項的索引
arr:陣列本身
Array.prototype.sx_findIndex = function (callback) { for (let i = 0; i < this.length; i++) { if (callback(this[i], i, this)) { return i } } return -1 } console.log(players.sx_findIndex(item => item.name === '科比')) // 0 console.log(players.sx_findIndex(item => item.name === '安東尼')) // -1
33、find
引數代表含義
- item:遍歷項
- index:遍歷項的索引
arr:陣列本身
Array.prototype.sx_find = function (callback) { for (let i = 0; i < this.length; i++) { if (callback(this[i], i, this)) { return this[i] } } return undefined } console.log(players.sx_find(item => item.name === '科比')) // { name: '科比', num: 24 } console.log(players.sx_find(item => item.name === '安東尼')) // undefined
34、fill
用處:填充陣列
引數代表含義
- initValue:填充的值
- start:開始填充索引,預設0
- end:結束填充索引,預設length
Array.prototype.sx_fill = function (value, start = 0, end) {
end = end || this.length
for (let i = start; i < end; i++) {
this[i] = value
}
return this
}
console.log(players.sx_fill('林三心', 1, 3))
// [
// { name: '科比', num: 24 },
// '林三心',
// '林三心',
// '林三心',
// { name: '杜蘭特', num: 35 }
// ]
35、includes
用處:查詢元素,查到返回true
,反之返回false
,可查詢NaN
Array.prototype.sx_includes = function (value, start = 0) {
if (start < 0) start = this.length + start
const isNaN = Number.isNaN(value)
for (let i = start; i < this.length; i++) {
if (this[i] === value || Number.isNaN(this[i]) === isNaN) {
return true
}
}
return false
}
console.log([1, 2, 3].sx_includes(2)) // true
console.log([1, 2, 3, NaN].sx_includes(NaN)) // true
console.log([1, 2, 3].sx_includes(1, 1)) // false
36、join
用處:將陣列用分隔符拼成字串,分隔符預設為,
Array.prototype.sx_join = function (s = ',') {
let str = ''
for(let i = 0; i < this.length; i++) {
str = i === 0 ? `${str}${this[i]}` : `${str}${s}${this[i]}`
}
return str
}
console.log([1, 2, 3].sx_join()) // 1,2,3
console.log([1, 2, 3].sx_join('*')) // 1*2*3
37、flat
Array.prototype.sx_flat = function () {
let arr = this
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr)
}
return arr
}
const testArr = [1, [2, 3, [4, 5]], [8, 9]]
console.log(testArr.sx_flat())
// [1, 2, 3, 4, 5, 8, 9]
38、splice
難點
擷取長度和替換長度的比較,不同情況
Array.prototype.sx_splice = function (start, length, ...values) { length = start + length > this.length - 1 ? this.length - start : length const res = [], tempArr = [...this] for (let i = start; i < start + values.length; i++) { this[i] = values[i - start] } if (values.length < length) { const cha = length - values.length for (let i = start + values.length; i < tempArr.length; i++) { this[i] = tempArr[i + cha] } this.length = this.length - cha } if (values.length > length) { for (let i = start + length; i < tempArr.length; i++) { this.push(tempArr[i]) } } for (let i = start; i < start + length; i++) { res.push(tempArr[i]) } return res }
Object篇
定義一個測試物件
const obj = {
name: '林三心',
age: 22,
gender: '男'
}
39、entries
用處:將物件轉成鍵值對陣列
Object.prototype.sx_entries = function (obj) {
const res = []
for (let key in obj) {
obj.hasOwnProperty(key) && res.push([key, obj[key]])
}
return res
}
console.log(Object.sx_entries(obj))
// [ [ 'name', '林三心' ], [ 'age', 22 ], [ 'gender', '男' ] ]
40、fromEntries
用處:跟entries
相反,將鍵值對陣列轉成物件
Object.prototype.sx_fromEntries = function (arr) {
const obj = {}
for (let i = 0; i < arr.length; i++) {
const [key, value] = arr[i]
obj[key] = value
}
return obj
}
console.log(Object.sx_fromEntries([['name', '林三心'], ['age', 22], ['gender', '男']]))
// { name: '林三心', age: 22, gender: '男' }
41、keys
用處:將物件的key轉成一個陣列合集
Object.prototype.sx_keys = function (obj) {
const keys = []
for (let key in obj) {
obj.hasOwnProperty(key) && res.push(key)
}
return keys
}
console.log(Object.keys(obj))
// [ 'name', 'age', 'gender' ]
42、values
用處:將物件的所有值轉成陣列合集
Object.prototype.sx_values = function (obj) {
const values = []
for (let key in obj) {
obj.hasOwnProperty(key) && values.push(obj[key])
}
return values
}
console.log(Object.sx_values(obj))
// [ '林三心', 22, '男' ]
43、instanceOf
用處:A instanceOf B,判斷A是否經過B的原型鏈
function instanceOf(father, child) {
const fp = father.prototype
var cp = child.__proto__
while (cp) {
if (cp === fp) {
return true
}
cp = cp.__proto__
}
return false
}
function Person(name) {
this.name = name
}
const sx = new Person('林三心')
console.log(instanceOf(Person, sx)) // true
console.log(instanceOf(Person, sx2)) // false
44、is
用處:Object.is(a, b),判斷a是否等於b
Object.prototype.sx_is = function (x, y) {
if (x === y) {
// 防止 -0 和 +0
return x !== 0 || 1 / x === 1 / y
}
// 防止NaN
return x !== x && y !== y
}
const a = { name: '林三心' }
const b = a
const c = { name: '林三心' }
console.log(Object.sx_is(a, b)) // true
console.log(Object.sx_is(a, c)) // false
45、Object.assign
難點
- assign接收多個物件,並將多個物件合成一個物件
- 這些物件如果有重名屬性,以後來的物件屬性值為準
assign返回一個物件,
這個物件 === 第一個物件
Object.prototype.sx_assign = function (target, ...args) { if (target === null || target === undefined) { throw new TypeError('Cannot convert undefined or null to object') } target = Object(target) for (let nextObj of args) { for (let key in nextObj) { nextObj.hasOwnProperty(key) && (target[key] = nextObj[key]) } } return target } const testa = { name: '林三心' } const testb = { name: 'sunshine_lin', age: 22 } const testc = { age: 18, gender: '男' } const testd = Object.sx_assign(testa, testb, testc) console.log(testd) // { name: 'sunshine_lin', age: 18, gender: '男' } console.log(testa === testd) // true
Function篇
46、call
Function.prototype.sx_call = function (obj, ...args) {
obj = obj || window
// Symbol是唯一的,防止重名key
const fn = Symbol()
obj[fn] = this
// 執行,返回執行值
return obj[fn](...args)
}
const testobj = {
name: '林三心',
testFn(age) {
console.log(`${this.name}${age}歲了`)
}
}
const testobj2 = {
name: 'sunshine_lin'
}
testobj.testFn.sx_call(testobj2, 22) // sunshine_lin22歲了
47、apply
Function.prototype.sx_apply = function (obj, args) {
obj = obj || window
// Symbol是唯一的,防止重名key
const fn = Symbol()
obj[fn] = this
// 執行,返回執行值
return obj[fn](...args)
}
const testobj = {
name: '林三心',
testFn(age) {
console.log(`${this.name}${age}歲了`)
}
}
const testobj2 = {
name: 'sunshine_lin'
}
testobj.testFn.sx_apply(testobj2, [22]) // sunshine_lin22歲了
48、Function.prototype.bind
難點:
- bind是返回一個函式,而不是執行結果
bind返回的函式,拿來當做建構函式,該怎麼處理
Function.prototype.sx_bind = function (obj, ...args) { obj = obj || window // Symbol是唯一的,防止重名key const fn = Symbol() obj[fn] = this const _this = this const res = function (...innerArgs) { console.log(this, _this) if (this instanceof _this) { this[fn] = _this this[fn](...[...args, ...innerArgs]) delete this[fn] } else { obj[fn](...[...args, ...innerArgs]) delete obj[fn] } } res.prototype = Object.create(this.prototype) return res }
String篇
49、slice
引數代表含義
- start:開始擷取的字元索引(包含此字元)
- end:結束擷取的字元索引(不包含此字元)
注意點 - start > end:返回空字串
- start < 0:
start = 陣列長度 + start
String.prototype.sx_slice = function (start = 0, end) {
start = start < 0 ? this.length + start : start
end = !end && end !== 0 ? this.length : end
if (start >= end) return ''
let str = ''
for (let i = start; i < end; i++) {
str += this[i]
}
return str
}
console.log(str.sx_slice(2)) // nshine_lin
console.log(str.sx_slice(-2)) // in
console.log(str.sx_slice(-9, 10)) // shine_l
console.log(str.sx_slice(5, 1)) // ''
50、substr
引數代表含義
- start:開始擷取的字元索引(包含此字元)
- length:擷取的長度
注意點 - start < 0:
start = 陣列長度 + start
- length超出所能擷取範圍,需要做處理
- length < 0:返回空字串
String.prototype.sx_substr = function (start = 0, length) {
if (length < 0) return ''
start = start < 0 ? this.length + start : start
length = (!length && length !== 0) || length > this.length - start ? this.length : start + length
let str = ''
for (let i = start; i < length; i++) {
str += this[i]
}
return str
}
console.log(str.sx_substr(3)) // shine_lin
console.log(str.sx_substr(3, 3)) // shi
console.log(str.sx_substr(5, 300)) // ine_lin
51、substring
功能與slice
大致相同
區別之處
start > end:互換值
String.prototype.sx_sunstring = function (start = 0, end) { start = start < 0 ? this.length + start : start end = !end && end !== 0 ? this.length : end if (start >= end) [start, end] = [end, start] let str = '' for (let i = start; i < end; i++) { str += this[i] } return str } console.log(str.sx_sunstring(2)) // nshine_lin console.log(str.sx_sunstring(-2)) // in console.log(str.sx_sunstring(-9, 10)) // shine_l console.log(str.sx_sunstring(5, 1)) // unsh
Promise篇
52、all
- 接收一個Promise陣列,陣列中如有非Promise項,則此項當做成功
- 如果所有Promise都成功,則返回成功結果陣列
如果有一個Promise失敗,則返回這個失敗結果
function all(promises) { const result = [] let count = 0 return new MyPromise((resolve, reject) => { const addData = (index, value) => { result[index] = value count++ if (count === promises.length) resolve(result) } promises.forEach((promise, index) => { if (promise instanceof MyPromise) { promise.then(res => { addData(index, res) }, err => reject(err)) } else { addData(index, promise) } }) }) }
53、race
- 接收一個Promise陣列,陣列中如有非Promise項,則此項當做成功
哪個Promise最快得到結果,就返回那個結果,無論成功失敗
function race(promises) { return new MyPromise((resolve, reject) => { promises.forEach(promise => { if (promise instanceof MyPromise) { promise.then(res => { resolve(res) }, err => { reject(err) }) } else { resolve(promise) } }) }) }
54、allSettled
- 接收一個Promise陣列,陣列中如有非Promise項,則此項當做成功
把每一個Promise的結果,集合成陣列,返回
function allSettled(promises) { return new Promise((resolve, reject) => { const res = [] let count = 0 const addData = (status, value, i) => { res[i] = { status, value } count++ if (count === promises.length) { resolve(res) } } promises.forEach((promise, i) => { if (promise instanceof MyPromise) { promise.then(res => { addData('fulfilled', res, i) }, err => { addData('rejected', err, i) }) } else { addData('fulfilled', promise, i) } }) }) }
55、any
any與all相反
- 接收一個Promise陣列,陣列中如有非Promise項,則此項當做成功
- 如果有一個Promise成功,則返回這個成功結果
如果所有Promise都失敗,則報錯
function any(promises) { return new Promise((resolve, reject) => { let count = 0 promises.forEach((promise) => { promise.then(val => { resolve(val) }, err => { count++ if (count === promises.length) { reject(new AggregateError('All promises were rejected')) } }) }) }) } }
56、finally
- 接收一個回撥函式,但無引數接收
無論成功失敗狀態,都會執行finally
Promise.prototype.finally = function(callback) { return this.then(res => { callback() return res }, err => { callback() throw err }) }
結語
如果你覺得此文對你有一丁點幫助,點個贊,鼓勵一下林三心哈哈。或者可以加入我的摸魚群 想進學習群,摸魚群,請點選這裡摸魚,我會定時直播模擬面試,答疑解惑