这一篇打算重刷一遍常考手写代码题,方便以后翻出来记忆。
废话少说,直接刷~
实现一个new
实现new需要几步?:
- 创建一个对象
- 获取其构造函数,确保原型指向正确
- this指向
- 返回这个对象
- 代码:
function my_new(){
// 1. 创建一个对象
let obj = {}
// 2. 获取构造函数
let F = [].shift.call(arguments)
// 3. 将obj的原型指向构造函数的原型
obj.__proto__ = F.prototype
// 4. this指向该新创建对象,以及参数
let result = F.apply(obj,arguments)
// 5. 确保返回是个对象
return typeof result === 'object' ? result : obj
}
- 测试:
实现一个call
call做了什么事?
- 改变this指向
- 执行了该函数
- 代码:
Function.prototype.my_call = function (context) {
// this参数如果传null或者不传,应该指向window
context = context || window
// symbol保证context.fn的唯一性
let fn = Symbol('fn')
// 获取调用call的函数,用this获取
context[fn] = this
let args = []
// 获取传入的参数
for(let i = 1; i < arguments.length; i++){
args.push('arguments['+ i +']')
}
// 执行函数
let result = eval('context[fn](' + args + ')')
// 删除该函数
delete context[fn]
return result
}
- 测试:
let obj = {
name: 'boom'
}
function test(value) {
console.log('my call 执行啦 —— ' + value)
}
test.my_call(obj, '2022')
实现一个apply
apply跟call相似,区别就是传入参数不一样
- 代码:
Function.prototype.my_apply = function (context) {
context = context || window
let fn = Symbol('fn')
context[fn] = this
let result = arguments[1] ? context[fn](...arguments[1]) : context[fn]();
delete context[fn]
return result
}
- 测试
let obj = {
name: 'boom'
}
function test(v1, v2, v3) {
console.log('my apply 执行啦 —— ' + v1 + v2 + v3)
}
test.my_apply(obj, ['2022', '01', '01'])
实现一个bind
bind 做了什么?
- 返回一个函数
- this绑定为传入的第一个参数
- 注意bind返回的函数的使用方式:如果是作为构造函数new,this指向实例;如果是作为普通函数,this指向window
- 代码
Function.prototype.my_bind = function (context) {
if (typeof this !== "function") {
throw new Error("not a function");
}
let self = this;
let args = Array.prototype.slice.call(arguments, 1);
let fNOP = function () {};
let fBound = function () {
// 合并两次的参数
var bindArgs = Array.prototype.slice.call(arguments);
// 如果是返回的函数是通过new方式,this指向实例,此时的this为fBound函数,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
// 如果返回的函数普通方式调用,this为window,将绑定函数的 this 指向 context
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
- 测试
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'haha';
var bindFoo = bar.my_bind(foo, 'boom');
var obj = new bindFoo('18');
let o = bindFoo('11')
判断数组类型
let arr = [1, 2, 3];
// 方法一:instanceof方法
console.log(arr instanceof Array);
// 方法二:isArray方法
console.log(Array.isArray(arr));
// 方法三:Object.prototype方法
console.log(Object.prototype.toString.call(arr) === '[object Array]');
// 方法二:constructor方法
console.log(arr.constructor === Array);
// 方法五:Array.__proto__方法
console.log(arr.__proto__ === Array.prototype);
// 方法六:Object.getPrototypeOf方法
console.log(Object.getPrototypeOf(arr) === Array.prototype);
// 方法七:Array.prototype.isPrototypeOf方法
console.log(Array.prototype.isPrototypeOf(arr));
sort快速打乱数组
- 代码
let arr = [1,2,3,4,5,6,7,8,9,10]
arr.sort(()=>{ return Math.random() - 0.5})
// 利用sort,返回结果为大于等于0时被交换位置,小于0时交换位置
- 测试
但是由于v8 在处理 sort 方法时,当目标数组长度小于 10 时,使用插入排序;反之,使用快速排序和插入排序的混合排序。存在乱序不彻底的问题
第二种乱序更彻底
- 代码
function shuffle(a){
for(let i = a.length; i; i--){
let j = Math.floor(Math.random() * i);
[a[i-1], a[j]] = [a[j], a[i-1]];
}
return a;
}
- 测试
防抖
【重点清零】使用setTimeout来辅助实现,延迟运行需要执行的代码。如果方法多次触发,就把上次记录的延迟执行代码清掉,重新开始计时。若计时器件事件没有被重新触发,等延迟时间计时完毕,则执行目标代码。
防抖需要注意什么?
- 先清除定时器
- 注意修改this指向,因为直接return函数里面的this指向的时window,而不是目标DOM
- 注意获取event参数
function debounce(func, wait) {
var timer;
return function () {
// 获取this
var context = this;
// 获取event
var args = arguments;
// 清零
clearTimeout(timer)
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
节流
节流原理:如果持续触发事件,每隔一段时间,只执行一次事件。有两种主流的实现方式,一种是使用【时间戳】,一种是【设置定时器】
- 时间戳
function throtte(func, wait) {
var context, args
var before = 0
return function() {
var now = +new Date() // 隐式转换,相当于ToNumber。将字符串直接转化为number类型
context = this
args = arguments
if (now - before > wait) {
func.apply(context, args)
before = now
}
}
}
- 定时器
function throtte(func, wait) {
var timer
var context, args
return function() {
context = this
args = arguments
if (!timer) {
timer = setTimeout(function(){
timer = null
func.apply(this, args)
}, wait)
}
}
}
container.onmousemove = throtte(getUserAction, 3000)
实现一个instanceof
instanceof的原理就是根据原型链
- 代码
function instanceOf (A, B){
let p = A
while(p){
if (p === B.prototype){
return true
}
p = p.__proto__
}
return false
}
- 测试
JSONP的实现
JSONP的原理:script标签不受同源策略限制,用来进行跨域请求,优点兼容性比较好,缺点是只能get请求
- 代码
const jsonp = (url, param, callback) => {
function dealUrl () {
let data = ''
for(let key in param){
console.log(param)
if (param.hasOwnProperty(key)){
data += `${key}=${param[key]}&`
}
}
data += `callback=${callback}`
return `${url}?${data}`
}
return new Promise((resolve, reject)=>{
const script = document.createElement('script')
script.src = dealUrl()
document.body.appendChild(script)
window[callback] = data => {
resolve(data)
document.body.removeChild(script)
}
})
}
- 测试
实现一个简单的发布订阅模式
- 代码
class Subject{
constructor(name){
this.name = name
this.observer = []
this.weather = 'sunny'
}
on(observer){
this.observer.push(observer)
}
trigger(data){
this.weather = data
this.observer.forEach((item)=>{
item.update(data)
})
}
off(observer){
let index = this.observer.findIndex(item => item === observer)
if (index !== -1){
this.observer.splice(index, 1)
}
}
}
class Observer{
constructor(name){
this.name = name
}
update(msg){
console.log(`观察者${this.name}: ${msg}`)
}
}
- 测试
let s = new Subject('msg')
let o1 = new Observer('haha')
let o2 = new Observer('boom')
s.on(o1)
s.on(o2)
s.trigger('rainny')
s.off(o1)
s.trigger('hot')
console.log(s)
实现一个Promise.all
- 代码
Promise.my_all = (promises) => {
return new Promise ((resolve, reject) => {
let result = []
let count = 0
if(promises.length === 0){
return resolve(result)
}
promises.forEach((item, index) => {
Promise.resolve(item).then((res) => {
result[index] = res
count++
if (count === promises.length){
resolve(result)
}
}).catch(reject)
});
})
}
- 测试
const p1 = Promise.resolve(1)
const p2 = new Promise((resolve) => {
setTimeout(() => resolve(2), 1000)
})
const p3 = new Promise((resolve) => {
setTimeout(() => resolve(3), 3000)
})
const p4 = Promise.reject('err4')
const p11 = Promise.my_all([ p1, p2, p3 ])
.then(data => {console.log(data)})
.catch(err => {console.log(err)})
const p12 = Promise.my_all([ p1, p2, p3, p4 ])
.then(data => {console.log(data)})
.catch(err => {console.log(err)})
实现一个Promise.race
- 代码
Promise.my_race = (promises) => {
return new Promise((resolve, reject) => {
promises.forEach(item => {
Promise.resolve(item).then(resolve).catch(reject)
});
})
}
- 测试
const p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 1)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 2)
})
Promise.my_race([p1, p2]).then((value) => {
console.log(value) // 2
})
Promise.my_race([p1, p2, 3]).then((value) => {
console.log(value) // 3
})
实现一个promise
class MyPromise{
constructor(fn){
this.resolvedcallback = []
this.rejectedCallback = []
this.state = 'pending'
this.value = ''
fn(this.resolve.bind(this), this.reject,bind(this))
}
resolve(value){
if(this.state === 'pending'){
this.state = 'resolved'
this.value = value
this.resolvedcallback.map(item => item(value))
}
}
reject(value){
if(this.state === 'panding'){
this.state = 'rejected'
this.value = value
this.rejectedCallback.map(item => item(value))
}
}
then(onfulfill, onreject){
if(this.state === 'pending'){
this.resolvedcallback.map(item => item(value))
this.resolvedcallback.map(item => item(value))
}
if(this.state === 'resolved'){
onfulfill(this.value)
}
if(this.state === 'rejected'){
onreject(this.value)
}
}
}