当前位置:网站首页>14 high-frequency handwritten JS interview questions and answers to consolidate your JS foundation
14 high-frequency handwritten JS interview questions and answers to consolidate your JS foundation
2022-08-10 10:03:00 【Xiaojie learns the front end】
目录
5. Globally common data type judgment method
1. 手写深拷贝
function deepClone(startObj,endObj) {
let obj = endObj || {}
for (let i in startObj) {
if (typeof startObj[i] === 'object') {
startObj[i].constructor === Array ? obj[i] = [] : obj[i] = {}
deepClone(startObj[i],obj[i])
} else {
obj[i] = startObj[i]
}
}
return obj
}
值得注意的一点是,在递归调用的时候,需要把当前处理的 obj[i] 给传回去,否则的话 每次递归obj都会被赋值为空对象,就会对已经克隆好的数据产生影响.
Let's verify that deep copy is implemented:
const person = {
name: 'zyj',
age: 20,
sister: {
name: 'duoduo',
age: 13,
mother: {
name: 'lili',
age:45
}
}
}
const newPerson = deepClone(person)
newPerson.sister.mother.age = 50
console.log(newPerson)
// {
// name: 'zyj',
// age: 20,
// sister: { name: 'duoduo', age: 13, mother: { name: 'lili', age: 50 } }
// }
console.log(person)
// {
// name: 'zyj',
// age: 20,
// sister: { name: 'duoduo', age: 13, mother: { name: 'lili', age: 45 } }
// }
2. 防抖函数
单位时间内,Trigger an event frequently,The last trigger will prevail.
function debounce(fn,delay) {
let timer = null
return function() {
clearTimeout(timer)
timer = setTimeout(() => {
fn.call(this)
}, delay);
}
}
Let's take a look at the call flow:
<body>
<input type="text">
<script>
const input = document.querySelector('input')
input.addEventListener('input',debounce(function() {
console.log(111);
},1000))
function debounce(fn,delay) {
let timer = null
return function() {
clearTimeout(timer)
timer = setTimeout(() => {
fn.call(this)
}, delay);
}
}
</script>
</body>
可能有些同学对 fn.call(this) 不太明白,在 debounce We pass in anonymous functions as parameters,Because the execution environment of anonymous functions is global,所以它的 this 一般指向 window ,So make a change this 指向,Make it point to the caller input .
3. 节流函数
单位时间内,Trigger an event frequently,只会触发一次.
function throttle(fn,delay) {
return function () {
if (fn.t) return;//每次触发事件时,如果当前有等待执行的延时函数,则直接return
fn.t = setTimeout(() => {
fn.call(this);//Make sure to execute the functionthis指向事件源,而不是window
fn.t = null//Set after execution fn.t 为空,This will start a new timer again
}, delay);
};
}
调用流程:
<script>
//节流throttle代码:
function throttle(fn,delay) {
return function () {
if (fn.t) return;//每次触发事件时,如果当前有等待执行的延时函数,则直接return
fn.t = setTimeout(() => {
fn.call(this);//Make sure to execute the functionthis指向事件源,而不是window
fn.t = null//Set after execution fn.t 为空,This will start a new timer again
}, delay);
};
}
window.addEventListener('resize', throttle(function() {
console.log(11);
},1000));
</script>
Only output when the browser viewport is resized,And output every one second
4. 模拟 instanceof
// 模拟 instanceof
function myInstance(L, R) {
//L 表示左表达式,R 表示右表达式
let RP = R.prototype; // 取 R 的显示原型
let LP = L.__proto__; // 取 L 的隐式原型
while (true) {
if (LP === null) return false;
if (RP === LP)
// 这里重点:当 O 严格等于 L 时,返回 true
return true;
LP = LP.__proto__;
}
}
function person(name) {
this.name = name
}
const zyj = new person('库里')
console.log(myInstance(zyj,person)); // true
5. Globally common data type judgment method
function getType(obj){
let type = typeof obj;
if (type !== "object") { // 先进行typeof判断,如果是基础数据类型,直接返回
return type;
}
// 对于typeof返回结果是object的,再进行如下的判断,正则返回结果
return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1'); // 注意正则中间有个空格
}
6. 手写 call 函数
Function.prototype.myCall = function (context) {
// 先判断调用myCall是不是一个函数
// 这里的this就是调用myCall的
if (typeof this !== 'function') {
throw new TypeError("Not a Function")
}
// 不传参数默认为window
context = context || window
// 保存this
context.fn = this
// 保存参数
let args = Array.from(arguments).slice(1)
//Array.from 把伪数组对象转为数组,然后调用 slice 方法,去掉第一个参数
// 调用函数
let result = context.fn(...args)
delete context.fn
return result
}
7. 手写 apply 函数
Function.prototype.myApply = function (context) {
// 判断this是不是函数
if (typeof this !== "function") {
throw new TypeError("Not a Function")
}
let result
// 默认是window
context = context || window
// 保存this
context.fn = this
// 是否传参
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
8. bind方法
在实现手写bind方法的过程中,看了许多篇文章,The answers are all the same,准确,但是不知其所以然,So let's analyze it wellbind方法的实现过程.
我们先看一下bind函数做了什么:
bind() 方法会创建一个新函数.当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数.
Read here and we will find out,他和 apply , call 是不是很像,所以这里指定 this 功能,就可以借助 apply 去实现:
Function.prototype.myBind = function (context) {
// 这里的 this/self 指的是需要进行绑定的函数本身,比如用例中的 man
const self = this;
// 获取 myBind 函数从第二个参数到最后一个参数(第一个参数是 context)
// 这里产生了闭包
const args = Array.from(arguments).slice(1)
return function () {
// 这个时候的 arguments 是指 myBind 返回的函数传入的参数
const bindArgs = Array.from(arguments)
// 合并
return self.apply(context, args.concat(bindArgs));
};
};
Everyone should be able to understand this code,Implement principles and handwriting call , apply 都很像,因为 bind Parameters can be passed through the returned function,所以在 return 里面获取的 bindArgs 就是这个意思,然后最后通过 concat Combine the original parameters with the parameters passed in later.
我们来看一下结果:
const person = {
name: 'zyj'
}
function man(age) {
console.log(this.name);
console.log(age)
}
const test = man.myBind(person)
test(18)//zyj 18
现在重点来了,bind 区别于 call 和 apply The place is that it can return a function,Then pass this function as a constructor new 操作符来创建对象.
我们来试一下:
const person = {
name: 'zyj'
}
function man(age) {
console.log(this.name);
console.log(age)
}
const test = man.myBind(person)
const newTest = new test(18) // zyj 18
This is what we wrote above myBind The function is this result,那原生 bind 呢?
const person = {
name: 'zyj'
}
function man(age) {
console.log(this.name);
console.log(age)
}
const test = man.bind(person)
const newTest = new test(18) // undefined 18
由上述代码可见,使用原生
bind
生成绑定函数后,通过new
操作符调用该函数时,this.name 是一个 undefined,这其实很好理解,因为我们 new 了一个新的实例,Then in the constructor this It definitely points to the instance,And our code logic always points to it context ,That is, the parameters passed in.
So now we have to add a judgment logic:
Function.prototype.myBind = function (context) {
// 这里的 this/self 指的是需要进行绑定的函数本身,比如用例中的 man
const self = this;
// 获取 myBind 函数从第二个参数到最后一个参数(第一个参数是 context)
// 这里产生了闭包
const args = Array.from(arguments).slice(1)
const theBind = function () {
const bindArgs = Array.from(arguments);
// 当绑定函数作为构造函数时,其内部的 this 应该指向实例,此时需要更改绑定函数的 this 为实例
// 当作为普通函数时,将绑定函数的 this 指向 context 即可
// this instanceof fBound 的 this 就是绑定函数的调用者
return self.apply(
this instanceof theBind ? this : context,
args.concat(bindArgs)
);
};
return theBind;
};
Now we have achieved this effect,那我们的 myBind Functions are just like other natives bind 一样了吗?来看下面的代码:
const person = {
name: 'zyj'
}
function man(age) {
console.log(this.name);
console.log(age)
}
man.prototype.sayHi = function() {
console.log('hello')
}
const test = man.myBind(person)
const newTest = new test(18) // undefined 18
newTest.sayHi()
如果 newTest 是我们 new 出来的 man 实例,That is based on knowledge of the prototype chain,定义在manThe methods on the prototype object will definitely be inherited,所以我们通过 newTest.sayHi The call can be output normally hello 么?
该版代码的改进思路在于,将返回的绑定函数的原型对象的
__proto__
属性,修改为原函数的原型对象.便可满足原有的继承关系.
Function.prototype.myBind = function (context) {
// 这里的 this/self 指的是需要进行绑定的函数本身,比如用例中的 man
const self = this;
// 获取 myBind 函数从第二个参数到最后一个参数(第一个参数是 context)
// 这里产生了闭包
const args = Array.from(arguments).slice(1);
const theBind = function () {
const bindArgs = Array.from(arguments);
// 当绑定函数作为构造函数时,其内部的 this 应该指向实例,此时需要更改绑定函数的 this 为实例
// 当作为普通函数时,将绑定函数的 this 指向 context 即可
// this instanceof fBound 的 this 就是绑定函数的调用者
return self.apply(
this instanceof theBind ? this : context,
args.concat(bindArgs)
);
};
theBind.prototype = Object.create(self.prototype)
return theBind;
};
9. 模拟 new
// 手写一个new
function myNew(fn, ...args) {
// 创建一个空对象
let obj = {}
// 使空对象的隐式原型指向原函数的显式原型
obj.__proto__ = fn.prototype
// this指向obj
let result = fn.apply(obj, args)
// 返回
return result instanceof Object ? result : obj
}
There are many friends who do not understand why they should judge result 是不是 Object 的实例,我们首先得了解,在JavaScript中构造函数可以有返回值也可以没有.
1. Returns the instantiated object without a return value
function Person(name, age){
this.name = name
this.age = age
}
console.log(Person()); //undefined
console.log(new Person('zyj',20));//Person { name: 'zyj', age: 20 }
2. If there is a return value, check if the return value is a reference type,If it is a non-reference type,如(string,number,boolean,null,undefined),The cases of the above types are the same as the case of no return value,Actually returns the instantiated object
function Person(name, age){
this.name = name
this.age = age
return 'lalala'
}
console.log(Person()); //lalala
console.log(new Person('zyj',20));//Person { name: 'zyj', age: 20 }
3. If present the return value is a reference type,then actually returns that reference type
function Person(name, age){
this.name = name
this.age = age
return {
name: 'curry',
ahe: 34
}
}
console.log(Person()); //{ name: 'curry', ahe: 34 }
console.log(new Person('zyj',20));//{ name: 'curry', ahe: 34 }
10. 类数组转化为数组的方法
const arrayLike=document.querySelectorAll('div')
// 1.扩展运算符
[...arrayLike]
// 2.Array.from
Array.from(arrayLike)
// 3.Array.prototype.slice
Array.prototype.slice.call(arrayLike)
// 4.Array.apply
Array.apply(null, arrayLike)
// 5.Array.prototype.concat
Array.prototype.concat.apply([], arrayLike)
11. 组合继承
function father (name) {
this.name = name
this.age = 18
}
father.prototype.getName = function(){} // Methods are defined on the parent class prototype(公共区域)
function child () {
// 继承父类属性,可传入参数
father.call(this,'Tom')
// The following properties will be generated:
// name:'tom'
// age: 18
}
child.prototype = new father() // 重写原型对象
child.prototype.constructor = child
The prototype chain relationship here should be like this:
This method is also called伪经典继承.其核心思路是:The prototype object of the overridden subclass is an instance of the superclass,and by stealing the constructorInherit the parent class instance的属性.
12. 原型式继承
基本思路是,Makes a shallow copy of the passed in object,and assign it to an empty function
F
(临时类型)的原型对象,and returns a passF
生成的实例.这个实例的__proto__
It naturally points to the passed in object,可以理解为一个挂钩🧷的过程.
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
let father = function() {}
father.prototype.getName = function() {
console.log('zyj')
}
let son = object(father)
let daughter = object(father)
son.prototype.getName() // zyj
大概是这么个过程:
在 ECMAScript 5
中,通过增加 Object.create()
Methods normalize the concept of prototypal inheritance,That is, it replaces the above custom one object()
函数.所以对于 Object.create()
的手写实现,The core idea is similar to the above custom function,Just added some parameter verification links.
let son = Object.create(father) // 等同于上述代码
13. 实现 Object.create()
Object.myCreate = function(proto, propertyObject) {
// 参数校验
if (typeof proto !== 'object' && typeof proto !== 'function') {
throw new TypeError('Object prototype may only be an Object or null.')
// 不能传一个 null Values are given to instances as properties
if (propertyObject == null) {
new TypeError('Cannot convert undefined or null to object')
}
// The idea of prototypal inheritance:Use an empty function(That is, the initialization code of the original constructor is ignored)Create a clean instance
function F() {}
F.prototype = proto // Determine subsequent inheritance relationships
const obj = new F()
// If there is a second parameter passed in,将其设为 obj 的属性
if (propertyObject != undefined) {
Object.defineProperties(obj, propertyObject)
}
// 即 Object.create(null) 创建一个没有原型对象的对象
if (proto === null) {
obj.__proto__ = null
}
return obj
}
14. 数组去重
ES5实现:
function unique(arr) {
var res = arr.filter(function(item, index, array) {
return array.indexOf(item) === index
})
return res
}
ES6实现:
var unique = arr => [...new Set(arr)]
边栏推荐
猜你喜欢
随机推荐
WebView2 通过 PuppeteerSharp 实现爬取 王者 壁纸 (案例版)
【C语言】头文件#include <conio.h>,conio是Console Input/Output(控制台输入输出)
LeetCode Algorithm 914. 卡牌分组
用.bat文件做Airtest脚本的多设备批量运行
UE4 Sequence添加基础动画效果 (04-在序列中使用粒子效果)
「业务架构」TAGAF建模:业务服务/信息图
Development environment variable record under win
讯飞翻译机抢镜背后,跨语种沟通迈入全新时代
单例模式基类
腾讯云校园大使开始招募啦,内推名额和奖金等你来拿
keepalived:常见问题
定时任务Quartz
Swin Transformer作者曹越加入智源,开展视觉基础模型研究
「数据战略」结果驱动的企业数据策略:组织和治理
Shell脚本数组
ESP8266-Arduino编程实例-MQ-7一氧化碳传感器驱动
04 【计算属性 侦听器】
JWT: To own me is to have power
哈希表,哈希桶的实现
「微服务架构」编曲与编舞——让系统协同工作的不同模式