前言
本系列主要整理前端面试中需要掌握的知识点。本节介绍JS基础中常见的手写代码汇总。
文章目录
一、手写Object.create
具体的方法解释见博客:
【前端知识之JS】Object.create和new的区别以及Object.create的实现
// Object.create能够创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
// 代码重点:
function mycreate(obj){
// 创建一个对象
function F(){}
// 使用现有的对象来提供新创建的对象的__proto__
F.prototype = obj
// 返回F的实例
return new F()
}
//测试代码
function A() {
this.name = 'abc';
}
A.prototype.a = 'a';
A.prototype.showName = function () {
return this.name;
}
var a1 = new A();
var a2 = Object.create(A.prototype);
var a3 = mycreate(A.prototype)
console.log('new方法:');
console.log(a1); //name: "abc" [[Prototype]]: Object
console.log('create方法:');
console.log(a2); // [[Prototype]]: Object
console.log('mycreate方法:');
console.log(a3); //[[Prototype]]: Object
二、手写instanceof方法
数据类型判断的具体解释见博客
【前端知识之JS】typeof 与 instanceof 区别以及instanceof的手写
// instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
// left:实例
// right:数据类型
function myInstanceof(left,right){
// 1. getPrototypeOf是获取实例原型的
// 2.prototype用于建立由 new C() 创建的对象的原型。
let proto = Object.getPrototypeOf(left)
let prototype = right.prototype
while(true){
if(!proto) return false
if(proto === prototype) return true
else{
proto = Object.getPrototypeOf(proto)
}
}
}
console.log(myInstanceof('123',Array)); //false
console.log(myInstanceof([1,2,3],Array)); //true
三、手写new操作符
具体关于new的解释见博客
【前端知识之JS】JS中new关键字的具体操作
function myNew(Func,...args){
// 1.创建一个新对象
const obj = {};
// 2.使新对象的原型等于传入对象的原型
obj.__proto__ = Func.prototype;
// 3.this指向当前对象
let result = Func.apply(obj,args);
// 4.判断返回值是什么,不是对象就返回新建的对象
return result instanceof Object ? result : obj
}
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayName = function(){
console.log(this.name);
}
let xiaoming = myNew(Person,'xiaoming',19)
四、手写防抖函数
防抖的解释与应用见博客
【前端知识之JS】防抖知识
/*
防抖思路:事件触发后,要等待n秒才能触发下次,如果n秒内被触发了,就要重新计时
总体思路:首先要定义一个定时器,用来保留上一次的定时器,每次触发都提前清空定时器,然后重新用延时函数包裹触发事件
完善思路:由于每个防抖内部包含的回调事件以及参数都是不同的,因为这些最好在外部定义,在防抖内部直接调用即可。
*/
var input = document.getElementById('input')
// 两个参数分别是延时时间,以及触发事件的回调函数
function debounce(delay,fn){
// 保留上次的定时器
let timer;
// 用闭包延长timer的变量周期
const _debounce = function (value){
// 清除定时器
clearTimeout(timer);
// 重新计时
timer = setTimeout(function(){
fn(value);
},delay)
}
return _debounce
}
// 定义回调函数
const fn = function(value){
console.log(value);
}
var debounceFunc = debounce(2000,fn)
input.addEventListener('keyup',function(e){debounceFunc(e.target.value)})
五、手写节流函数
节流的解释与应用见博客
【前端知识之JS】节流知识
/*
节流思路:在一段时间内,同一个事件只能被触发一次
总体思路:首先用timer保存上次点击的定时器,然后判断timer是否为空,如果timer为空,那么才执行回调函数,并且同时生成一个定时器
完善思路:将回调函数放在节流的外面进行定义。
*/
var btn = document.getElementById('button')
function throttle(wait,fn){
// 用timer保存上次点击的定时器
let timer;
return function(value){
if(!timer){
timer = setTimeout(()=>{
fn(value);
// 等待时间过后就把定时器清空
timer = null;
},wait)
}
}
}
const fn = function(value){
console.log(value);
}
const throttleFunc = throttle(2000,fn)
btn.addEventListener('click',function(e){
throttleFunc(e.target.innerText)
})
六、手写类型判断函数
/*
思路:先判断null,再判断Object,最后判断基本数据类型,但是typeof能够把function的类型识别出来,所以function是和基本数据类型一起判断的。
*/
function getType(value){
if(value === null) return null+"";
else if(typeof value === "object"){
let valueClass = Object.prototype.toString.call(value).split(" ")[1].split("]")[0]
return valueClass
}
else{
return typeof value
}
}
console.log(getType(123)) //number
七、手写call函数
call , apply , bind几种函数的具体解释见博客
【前端知识之JS】bind, call, apply的区别,以及如何实现bind
/*
call函数功能:第一个参数是this的目标,后面几个参数是参数列表
1.判断调用对象是否为函数;
2.判断传入上下文对象是否存在,不存在就设置为window;(传入的上下文就是this的新指向)
3.截取参数slice(1),call和apply的区别就是参数的类型不同。
4.将函数作为上下文对象的一个属性;
5.使用上下文对象来调用这个方法,并保存返回结果
6.删除刚刚新增的属性
7.返回结果
*/
Function.prototype.myCall = function(context){
if(typeof this !== "function"){
console.error("type error");
}
let args = [...arguments].slice(1);
let result = null;
context = context || window;
context.fn = this;
result = context.fn(...args)
delete context.fn;
return result
}
function fn(...args) {
console.log(this, args);
}
let obj = {
myname: "anada"
}
fn.myCall(obj, 1, 2, 3)
八、手写apply函数
/*
apply函数功能:第一个参数是this的目标,第二个参数是一个数组,里面是函数的参数
1.判断调用对象是否为函数;
2.判断传入上下文对象是否存在,不存在就设置为window;(传入的上下文就是this的新指向)
3.截取参数[...arguments][1],call和apply的区别就是参数的类型不同。
4.将函数作为上下文对象的一个属性;
5.使用上下文对象来调用这个方法,并保存返回结果
6.删除刚刚新增的属性
7.返回结果
*/
Function.prototype.myApply = function(context){
if(typeof this !== "function"){
console.error("type error");
}
context = context || window;
let args = [...arguments][1];
let result = null;
context.fn = this;
result = context.fn(...args);
delete context.fn;
return result;
}
function fn(...args) {
console.log(this, args);
}
let obj = {
myname: "anada"
}
fn.myApply(obj,[1,2,3])
九、手写bind函数
/*
bind函数功能:第一个参数是this的目标指向,接下来是一个参数列表,bind不能立即执行,但是可以分次执行。
实现思路:
1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
2. 保存当前函数的引用,获取其余传入参数值。
3. 创建一个函数返回
4. 函数内部使用 apply 来绑定函数调用,需要判断函数作为构造函数的情况,这个时候需要传入当前函数的 this 给 apply 调用,其余情况都传入指定的上下文对象。
*/
Function.prototype.myBind = function(context){
if(typeof this !== "function"){
console.error("type error");
}
let args = [...arguments].splice(1);
let fn = this;
return function Fn(){
return fn.apply(this instanceof Fn ? this : context,args.concat(...arguments))
}
}
function fn(...args){
console.log(this,args);
}
const obj = {
name:'ananda'
}
bindFunc = fn.myBind(obj,1,2,3)
bindFunc()
bindFunc(6,7,8)
十、手写ajax请求
关于ajax相关的知识见博客
【前端知识之JS】ajax原理及其手写
/*
1.创建一个XMLHttpRequest对象;
2.在这个对象上使用open方法创建一个HTTP请求,open方法所需要的参数是请求的方法、请求的地址、是否异步和用户的认证信息
3.在发起请求之前,可以为这个对象添加一些信息和监听函数。通过setRequestHeader方法来为请求添加头信息。
4.XMLHttpRequest状态变化时会触发onreadystatechange事件,可以通过设置监听函数,来处理请求成功后的结果。
当对象的readystate变为4时,服务器接受数据完成,如果时2xx或者304的话则代表返回正常,可以通过response中的数据来对页面进行更新了。
5.当对象的属性和监听函数设置完成后,最后调用sent方法来向服务器发起请求,可以传入参数作为发送的数据体。
*/
let xhr = new XMLHttpRequest();
xhr.open('GET',url,true);
xhr.responseType = "json"
xhr.setRequestHeader("Accept","application/json")
xhr.onreadystatechange = function(){
if(this.readyState !== 4) return;
if(this.status === 200){
handle(this.response)
}
else{
console.error(this.statusText);
}
}
xhr.onerror = function(){
console.error(this.statusText);
}
xhr.send(null)
十一、使用Promise对ajax进行封装
function getJSON(url){
let promise = new Promise(function(resolve,reject){
let xhr = new XMLHttpRequest();
xhr.open('GET',url,true);
xhr.responseType = "json";
xhr.setRequestHeader("Accept","application/json");
xhr.onreadystatechange = function(){
if(this.readyState !== 4) return;
if(this.status === 200){
resolve(this.response)
}
else{
reject(this.statusText)
}
}
xhr.onerror = function(){
reject(this.statusText)
}
xhr.send(null)
})
return promise
}
getJSON('./data.json').then(function(result){
console.log(result);
})
十二、手写实现浅拷贝
关于深浅拷贝的知识见博客
【前端知识之JS】JS中的深拷贝与浅拷贝
/*
浅拷贝:一个新的对象对原始对象的属性值进行精确地拷贝,如果拷贝的是基本数据类型,拷贝的就是基本数据类型的值,如果是引用数据类型,拷贝的就是内存地址。
如果其中一个对象的引用内存地址发生改变,另一个对象也会跟着改变。
1. Object.assign 是浅拷贝
2. 扩展运算符是浅拷贝
3. slice是浅拷贝
4. concat是浅拷贝
*/
// 浅拷贝的实现
function shallowCopy(object){
// 如果不是对象就不拷贝
if(!object || typeof object !== "object") return;
let newObject = Array.isArray(object)?[]:{}
for(let key in object){
if(object.hasOwnProperty(key)){
newObject[key] = object[key];
}
}
return newObject;
}
console.log(shallowCopy([1,2,3]));
十三、手写实现深拷贝
/*
深拷贝:新建一个引用类型并将对应的值赋值给它,因此对象获得一个新的引用类型而不是一个原有类型的引用。
JSON.stringfy()是深拷贝
函数库lodash的_.cloneDeep方法是深拷贝
*/
// 深拷贝的实现
function deepCopy(object){
if(!object || typeof object !== "object") return;
var newObject = Array.isArray(object)? []:{};
for(let key in object){
if(object.hasOwnProperty(key)){
newObject[key] = typeof object[key] === "object" ? deepCopy(object[key]) : object[key]
}
}
return newObject;
}
let object = {b:2,c:{d:4}}
let target = deepCopy(object)
object.c.d = 88;
console.log(object);
console.log(target);
十四、手写Object.assign
/*
Object.assign的作用是将几个对象整理到目标对象中,使用的是浅拷贝
*/
Object.myAssign = function(target,...source){
if(target == null || target == undefined) console.error('Cannot convert undefined or null to object');
let result = Object(target)
source.forEach(function(obj){
if(obj!=null){
for(let key in obj){
if(obj.hasOwnProperty(key)){
result[key] = obj[key]
}
}
}
})
return result
}
obj0 = {g:1}
obj1 = {a:1,b:{c:2,d:5}}
obj2 = Object.myAssign(obj0,obj1)
console.log(obj2);
版权声明:本文为weixin_44337386原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。