Reference article:
JS Common handwritten code questions (1)
JS common handwritten code questions (2)
Write the call,apply,bind functions by hand
32 hand-written JS_ Single eyelid bears -CSDN blog
This article from the above article, excerpt summarizes a part of the common and need to have a good understanding of the JS handwritten code question type, interview necessary, on demand
For more detailed explanations, please refer to the source above
- call
- apply
- bind
- new
- Array flattening
- Array to heavy
- Prototype inheritance
- Promise, promise. All, promise. Race
- instanceof
- ajax
- Closure cache
- Shallow copy, deep copy
- String turns hump
- Lazy loading of images
- The rolling load
JS implements a call
Fun. call(obj, argument 1, argument 2…) , the first value is to change this to obj, followed by the parameter queue. Call immediately executes the fun method
The definition and usage of call
// The first argument to the call method refers to this; Accepts a list of arguments; Method executed immediately
/ / Function. The prototype. The call () sample
function fun(arg1, arg2) {
console.log(this.name)
console.log(arg1 + arg2)
}
const _this = { name: 'YIYING' }
// This in fun points to _this, and then executes immediately, from which YIYING is output
fun.call(_this, 1.2)
/ / output
YIYING
3
Copy the code
Implement call by hand
Funcion.protoType.mockCall = function (context = window. args) {
const key = Symbol()
context[key] = this
constresult = context[key](... args)delete context[key]
returnResult} or:Function.prototype.myCall = function(context) {
if (typeof context === "undefined" || context === null) {
context = window
}
/ / the context = context | | window the same as the above code
context.fn = thisContext. fn = this = myFun) //(context.fn = this = myFun) //(context.fn = this = myFun)
const args = [...arguments].slice(1)// The first argument is context, to be removed
constresult = context.fn(... args)delete context.fn
return result
}
Copy the code
Implementation analysis
- Context is optional. If not, the default context is Window
- Next create a unique property for the Content (represented by Symbol) and set the value to the function you want to call
- Because call can pass multiple arguments as arguments to the calling function, we use… Extended operator
- The function is then called and removed from the object
JS implements an apply
Fun. apply(obj, [parameter 1, parameter 2… Change this to obj and execute fun immediately
Apply takes two arguments, the first being the value to bind to this and the second an array of ** arguments. ** The apply and Call implementations are similar, except for the handling of parameters
Function.protoType.mockApply = function (context = window, args) {
const key = Symbol()
context[key] = this
constresult = context[key](... args)delete context[key]
return result
}
Copy the code
JS implements a bind
Function.prototype.bind the first argument is a reference to this, and the second argument is a list of received arguments. The difference from call is that the bind method returns values from the function and the use of a list of arguments that BIND receives.
Implementation idea:
- Use closures to save this from a call to bind, where this is the original function
- Specify this using call/apply
- Returns a binding function
- When the returned binding function is called by the new operator, the context of the binding refers to the object created by the new operator
- Change the binding function’s prototype to the original function’s prototype
Function.protoType.mockBind = function (context = window. initArgs) {
const foo = this
var bindFoo = function (. args) {
if(this instanceof bindFoo){
return newfn(... initArgs, ... args) }returnfoo.call(context, ... initArgs, ... args) }returnBindFoo}Function.prototype.mockBind = function(ctx){
let fn = this
return function(){
fn.apply(ctx, arguments) //arguments are arguments passed when a function is called}}Copy the code
The first argument is the reference to this, and the second argument is the list of arguments received. The difference is that the bind method returns a function and uses a list of arguments that BIND receives.
Write a new implementation by hand
Normal use of new
function Dog(name){
this.name = name
}
Dog.prototype.sayName = function(){
console.log('name'.this.name)
}
var dog1 = new Dog('dog')
dog1.sayName() // Output the name dog
Copy the code
What does the new operator do?
- Create a new object
- The new object will be executed
__proto__
Link, associated with the constructor.prototype
Property, which is a stereotype used by the constructor to call the methods on the stereotype - The function call’s this is bound to the new object
- If the function returns no other object, the function call in the new expression automatically returns the new object
Handwriting new implementation
function mockNew (foo, ... args) {
if (typeoffoo ! = ='function') {
throw Error('foo is not a constructor')}const obj = Object.create(foo.protoType)
const result = foo.apply(obj, args)
return typeOf result === 'object'&& result ! = =null ? result : obj
}
newSpecific steps of1.Create an empty objectvar obj = {}
2.Modify the obj. __proto__ = t prototype3.Only changethisPoint to and pass the parameter,call or apply4.According to the specification, returnnull 和 undefinedNo processing, still return obJCopy the code
5. Array flattening
Method 1: ES6 Flat method
var arr = [1.2[3.4[5.6[7]]]]
arr.flat(Infinity) / /,2,3,4,5,6,7 [1]
Copy the code
Method two: recursion
var flatArr = function(arr1) {
let newArr = [];
function getChild(arr) {
for(let i = 0; i<=arr.length; i++) {if(arr[i] instanceof Array= = =false && arr[i]) {
newArr.push(arr[i])
} else if(arr[i]){
getChild(arr[i])
}
}
}
getChild(arr1);
return newArr;
}
/ / call:
var a = [[1.2.2], [6.7.8[11.12[12.13[14]]].10]].console.log('Utilities', flatArr(a))
// [1, 2, 2, 6, 7, 8, 11, 12, 12, 13, 14, 10]
Copy the code
Method three: re
const res2 = JSON.stringify(arr).replace(/\[|\]/g.' ').split(', ');
res2.map(item= > parseInt(item))
Copy the code
Array deduplication
Method 1: ES6 Set
var arr = [1.2.3.3.4.4.5]
var newArr = Array.from(new Set(arr)); / / [1, 2, 3, 4, 5]
// or arr = [...set] array.from () converts a pseudo-array to an Array
Copy the code
Method two: loop through the number group
function filterArr(arr){
var newArr = [];
arr.forEach(item= > {
if(! newArr.includes(item)) {// Can also be! newArr.indexOf(item)
newArr.push(item)
}
})
return newArr
}
Copy the code
Method 3: Hash table
let arr = [1.1.2.3.2.1.2]
function unique(arr){
let obj = {}
arr.forEach((item) = > {
obj[item] = true
})
let keys = Object.keys(obj)
keys = keys.map(item= > parseInt(item)) // Convert to a number
return keys
}
console.log(unique(arr))
Copy the code
Vii. Archetypal Inheritance (Parasitic combination Inheritance)
I'm just going to say that there's a parasitic combination inheritance, and there's a couple of descendents in between but they all have some flawsfunction Parent() {
this.name = 'parent';
}
function Child() {
Parent.call(this);
this.type = 'children';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Copy the code
Write a Promise by hand
Full parse (well written and worth a look) :
zhuanlan.zhihu.com/p/103651968
function Promise(executor) {
let self = this
this.status = 'pending' // Current status
this.value = undefined // Store successful values
this.reason = undefined // Cause of storage failure
this.onResolvedCallbacks = []// Store a successful callback
this.onRejectedCallbacks = []// Storage failed callback
function resolve(value) {
if (self.status == 'pending') {
self.status = 'resolved'
self.value = value
self.onResolvedCallbacks.forEach(fn= >fn()); }}function reject(error) {
if (self.status == 'pending') {
self.status = 'rejected'
self.reason = error
self.onRejectedCallbacks.forEach(fn= > fn())
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
Promise.prototype.then = function (infulfilled, inrejected) {
let self = this
let promise2
infulfilled = typeof infulfilled === 'function' ? infulfilled : function (val) {
return val
}
inrejected = typeof inrejected === 'function' ? inrejected : function (err) {
throw err
}
if (this.status == 'resolved') {
promise2 = new Promise(function (resolve, reject) {
//x can be a promise, or it can be a normal value
setTimeout(function () {
try {
let x = infulfilled(self.value)
resolvePromise(promise2, x, resolve, reject)
} catch(err) { reject(err) } }); })}if (this.status == 'rejected') {
promise2 = new Promise(function (resolve, reject) {
//x can be a promise, or it can be a normal value
setTimeout(function () {
try {
let x = inrejected(self.reason)
resolvePromise(promise2, x, resolve, reject)
} catch(err) { reject(err) } }); })}if (this.status == 'pending') {
promise2 = new Promise(function (resolve, reject) {
self.onResolvedCallbacks.push(function () {
//x can be a promise, or it can be a normal value
setTimeout(function () {
try {
let x = infulfilled(self.value)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
});
})
self.onRejectedCallbacks.push(function () {
//x can be a promise, or it can be a normal value
setTimeout(function () {
try {
let x = inrejected(self.reason)
resolvePromise(promise2, x, resolve, reject)
} catch(err) { reject(err) } }); })})}return promise2
}
function resolvePromise(p2, x, resolve, reject) {
if(p2 === x && x ! =undefined) {
reject(new TypeError('Type error'))}// It can be a promise, see if the object has a then method, if it has ~ then it is a promise
if(x ! = =null && (typeof x === 'object' || typeof x === 'function')) {
try {// To prevent {then:11}, determine if then is a function
let then = x.then
if (typeof then === 'function') {
then.call(x, function (y) {
//y may still be a promise, so parse until a normal value is returned
resolvePromise(p2, y, resolve, reject)
}, function (err) {
reject(err)
})
} else {// If then is not function then may be object or constant
resolve(x)
}
} catch (e) {
reject(e)
}
} else {// Indicates a normal value
resolve(x)
}
}
Copy the code
Promise.all
Promise.all supports chained calls, essentially returning a Promise instance that changes its state through resolve and reject.
Promise.myAll = function(promiseArr) {
return new Promise((resolve, reject) = > {
const ans = [];
let index = 0;
for (let i = 0; i < promiseArr.length; i++) {
promiseArr[i]
.then(res= > {
ans[i] = res;
index++;
if (index === promiseArr.length) {
resolve(ans);
}
})
.catch(err= >reject(err)); }})}Copy the code
Promise.race
Promise.race = function(promiseArr) {
return new Promise((resolve, reject) = > {
promiseArr.forEach(p= > {
// If it is not a Promise instance, it needs to be converted to a Promise instance
Promise.resolve(p).then(
val= > resolve(val),
err= > reject(err),
)
})
})
}
Copy the code
Implement instanceof detection data type
Left indicates the data to be detected, and right indicates the type. The principle is implemented with the prototype chain, A(instance object)instanceofB(constructor).function instanceof(left, right){
let proto = left._proto_
let prototype = right.prototype
while(true) {if(proto === null) return false
if(proto === prototype) return true
proto = proto._proto_
}
}
Copy the code
Ten, ajax
(1) Ajax of GET request
let xhr = new XMLHttpRequest() // create a connection
xhr.open('GET', url, true) // connect to the server
xhr.onreadystatechange = function () { //4. Receive the request and trigger this function when the state changes
if (xhr.readyState === 4) {
if (xhr.status === 200) {//xhr.responseText is a string that needs to be converted to JSON
success(JSON.parse(xhr.responseText))
} else {
fail(xhr.status)
}
}
}
xhr.send(null) //3
Copy the code
(2) Ajax for POST requests
let xhr = new XMLHttpRequest() // create a connection
const postData = {
userName: 'zhangshan'.passWord: 'xxx'
}
xhr.open('POST', url, true) // connect to the server
xhr.onreadystatechange = function () { //4. Receive the request and trigger this function when the state changes
if (xhr.readyState === 4) {
if (xhr.status === 200) {//xhr.responseText is a string that needs to be converted to JSON
success(JSON.parse(xhr.responseText))
} else {
fail(xhr.status)
}
}
}
xhr.send(JSON.stringify(postData)) //3, send request (need to send string, json to string)
Copy the code
(3) Optimize with Promise
function ajax(url) {
return new Promise((resolve, reject) = > {
let xhr = new XMLHttpRequest() // create a connection
xhr.open('GET', url, true) // connect to the server
xhr.onreadystatechange = function () { //4. Receive the request and trigger this function when the state changes
if (xhr.readyState === 4) {
if (xhr.status === 200) {//xhr.responseText is a string that needs to be converted to JSON
resolve(JSON.parse(xhr.responseText))
}else if(xhr.status === 404){
reject(new Error('404'))
}
}
}
xhr.send(null) //3})}const url = ' '
ajax(url)
.then(res= > console.log(JSON.parse(xhr.responseText)))
.catch(err= > console.log(err))
Copy the code
Closure write a cache tool
function creatCache() {
let data = {} // Hide data, external access is not available
return {
get(key) {
return data[key]
},
set(key, val) {
data[key] = val
}
}
}
var c = creatCache()
c.set('name'.'jin')
console.log(c.get('name'))
Copy the code
Shallow copy, deep copy
The shallow copy copies only the first-layer attributes of an object, while the deep copy copies the attributes recursively.
// Shallow copy (obj1 is the object to be copied)
// Method 1: original version (obj1 is the object to be copied, obj2 is already an object by default)
function shallowCopy(obj1, obj2){
for(let key in obj1){
obj2[key] = obj1[key]
}
}
// Method 1: optimized version (obj is the object to be copied)
function shallowClone(obj){
if(typeofobj ! = ='object' || obj == null) {//obj is null, or is not an object or array
return obj
}
let result
if(obj instanceof Array){
result = []
}else{
result = {}
}
for(let key in obj){// For in an object can enumerate properties, including the properties and methods of its prototype,
if(obj.hasOwnProperty(key)){ // Ensure that key is not an attribute of the stereotype
// recursive call
result[key] = obj[key]
}
}
// Return the result
return result
}
2 / / way
function shallowCopy(obj1, obj2){
obj2 = Object.assign({}, obj1)
}
Copy the code
// Deep copy (obj1 is the object to be copied)
// Method 1: original version (obj1 is the object to be copied)
function deepCopy(obj1, obj2){
for(let key in obj1){Obj1.hasownperporty (key) can be used to determine if the instance has this property
let item = obj1[key]
if(item instanceof Array) {// Cannot use typeof item because you cannot distinguish objects from arrays
obj2[key] = []
deepCopy(item, obj2[key])
}else if(item instanceof Object){
obj2[key] = {}
deepCopy(item, obj2[key])
}else{
obj2[key] = item
}
}
}
// Method 1: optimized version (obj is the object to be copied, obj2 is already an object by default)Ideas:1, check whether it is a value type or a reference type.2Is an array or an object.3, recursion,function deepClone(obj){
if(typeofobj ! = ='object' || obj == null) {//obj is null, or is not an object or array
return obj
}
let result
if(obj instanceof Array){
result = []
}else{
result = {}
}
for(let key in obj){// For in an object can enumerate properties, including the properties and methods of its prototype,
if(obj.hasOwnProperty(key)){ // Ensure that key is not an attribute of the stereotype
// recursive call
result[key] = deepClone(obj[key])
}
}
// Return the result
returnResult} bug: An infinite loop occurs when two objects reference each other.2 / / way
function deepCopy(obj1, obj2){
obj2 = JSON.parse(JSON.stringify(obj1))} Flaw: This method cannot copy function propertiesCopy the code
String hump
Method 1: Split into arrays, capitalize toUpperCase(), subString ()1) is the element after the first elementvar str="border-bottom-color";
function tf(){
var arr=str.split("-");
for(var i=1; i<arr.length; i++){ arr[i]=arr[i].charAt(0).toUpperCase()+arr[i].substring(1);
}
return arr.join(""); }; tf(str); Method two: revar str="border-bottom-color";
function tf(){
var re=/-(\w)/g;
str=str.replace(re,function($0, $1){
return $1.toUpperCase();
});
alert(str)
};
tf(str);
Copy the code
14. Lazy loading of images
You can customize the img tag attribute data-src=’default.png’. Add the SRC attribute only when the image is detected to appear in the window.
function lazyload() {
const imgs = document.getElementsByTagName('img');
const len = imgs.length;
// Height of viewport
const viewHeight = document.documentElement.clientHeight;
// Scroll bar height
const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop;
for (let i = 0; i < len; i++) {
const offsetHeight = imgs[i].offsetTop;
if (offsetHeight < viewHeight + scrollHeight) {
constsrc = imgs[i].dataset.src; imgs[i].src = src; }}}// Use throttling to optimize
window.addEventListener('scroll', lazyload);
Copy the code
15. Scroll loading
The principle is to listen to page scrolling events and analyze the property relations of clientHeight, scrollTop and scrollHeight.
window.addEventListener('scroll'.function() {
const clientHeight = document.documentElement.clientHeight;
const scrollTop = document.documentElement.scrollTop;
const scrollHeight = document.documentElement.scrollHeight;
if (clientHeight + scrollTop >= scrollHeight) {
// Scroll to the bottom of the page detected, proceed with subsequent operations
// ...}},false);
Copy the code