Read the benefits of the source code, needless to say, the first advanced factory necessary, can also improve their ability to learn from the experience of predecessors. Source code is often the best practice left behind, we follow the footsteps of predecessors to learn will let us get twice the result with half the effort.
- Call, apply, bind implementation
- The new implementation
- Class implementation inheritance
- Async/await
- Reduce implementation
- Implement a two-way data binding
- Instanceof implementation
- Array. The isArray implementation
- Object. Create basic implementation principles
- GetOwnPropertyNames implementation
- Promise to realize
- Write an anti – shake/throttling function
- Implementation of currization functions
- Write a deep copy by hand
Call, apply, bind implementation
Call, apply, and bind essentially change the direction of this. Call, apply, calls a function directly, while bind returns a new function. Call differs from apply only in parameters.
The bind to realize
- Arrow function
this
Always point to its scope - Function as a constructor
new
Keywords should not be changed when invokedthis
Point to, becauseThe new binding
Is of higher priority thanAccording to the binding
和Hard binding
Function.prototype.mybind = function(thisArg) {
if (typeof this! = ='function') {
throw TypeError("Bind must be called on a function");
}
// Get the parameters to pass to the caller
const args = Array.prototype.slice.call(arguments.1),
/ / save this
self = this.// Build a clean function that holds the prototype of the original function
nop = function() {},
// The bound function
bound = function() {
// this instanceof nop determines whether to use new to call bound
// If new is called, this points to an example,
// If it is not called new, change this to point to the specified object O
return self.apply(
this instanceof nop ? this : thisArg,
args.concat(Array.prototype.slice.call(arguments))); };// Arrow function has no prototype; arrow function this always points to its scope
if (this.prototype) {
nop.prototype = this.prototype;
}
// Modify the prototype pointing of the binding function
bound.prototype = new nop();
returnbound; }}Copy the code
Test mybind
const bar = function() {
console.log(this.name, arguments);
};
bar.prototype.name = 'bar';
const foo = {
name: 'foo'
};
const bound = bar.mybind(foo, 22.33.44);
new bound(); // bar, [22, 33, 44]
bound(); // foo, [22, 33, 44]
Copy the code
The call to realize
Bind is a method that wraps call and changes the direction of this and returns a new function. So how does call change the direction of this? The principle is simple: in method invocation mode, this always refers to the object on which the method was called. This refers to the location of the method, not the declared location of the method (special for arrow functions). So let’s write a little demo just to make sense of it.
const foo = { name: 'foo' };
foo.fn = function() {
// This refers to foo
// Because foo called fn,
// fn's this refers to foo, the object on which it was called
console.log(this.name); // foo
};
Copy the code
Use the mechanism of this to implement Call
Function.prototype.mycall = function(thisArg) {
// This refers to the object calling call
if (typeof this! = ='function') {
// An error is reported if the call is not a function
throw new TypeError('Error');
}
// Declare a Symbol property to prevent the fn from being occupied
const fn = Symbol('fn')
const args = [...arguments].slice(1);
thisArg = thisArg || window;
// Add the object calling the call function to thisArg's property
thisArg[fn] = this;
// Execute the property
constresult = thisArg[fn](... args);// Delete this attribute
delete thisArg[fn];
// Returns the result of the function execution
return result;
}
Copy the code
Apply to realize
Function.prototype.myapply = function(thisArg) {
if (typeof this! = ='function') {
throw this + ' is not a function';
}
const args = arguments[1];
const fn = Symbol('fn')
thisArg[fn] = this;
constresult = thisArg[fn](... arg);delete thisArg[fn];
return result;
};
Copy the code
Test mycall myapply
const bar = function() {
console.log(this.name, arguments);
};
bar.prototype.name = 'bar';
const foo = {
name: 'foo'
};
bar.mycall(foo, 1.2.3); // foo [1, 2, 3]
bar.myapply(foo, [1.2.3]); // foo [1, 2, 3]
Copy the code
Implementation Principle of Reduce
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
Array.prototype.myreduce = function reduce(callbackfn) {
// Get the array
const O = this,
len = O.length;
/ / values
let k = 0./ / accumulator
accumulator = undefined.// whether the value of k subscript exists
kPresent = false./ / initial value
initialValue = arguments.length > 1 ? arguments[1] : undefined;
if (typeofcallbackfn ! = ='function') {
throw new TypeError(callbackfn + ' is not a function');
}
// The array is empty and has an initial value
if (len === 0 && arguments.length < 2) {
throw new TypeError('Reduce of empty array with no initial value');
}
// If the initial value exists
if (arguments.length > 1) {
// Set the accumulator to its initial value
accumulator = initialValue;
// The initial value does not exist
} else {
accumulator = O[k];
++k;
}
while (k < len) {
// Empty [,,,]
kPresent = O.hasOwnProperty(k);
if (kPresent) {
const kValue = O[k];
/ / call callbackfn
accumulator = callbackfn.apply(undefined, [accumulator, kValue, k, O]);
}
++k;
}
return accumulator;
};
Copy the code
test
const rReduce = ['1'.null.undefined.3.4].reduce((a, b) = > a + b, 3);
const mReduce = ['1'.null.undefined.3.4].myreduce((a, b) = > a + b, 3);
console.log(rReduce, mReduce);
// 31nullundefined34 31nullundefined34
Copy the code
The new implementation
We need to know what did we do when new
- Create a new object;
- Assign the constructor’s scope to the new object (so this refers to the new object)
- Execute the code in the constructor (add attributes to this new object)
- Returns a new object.
Since new cannot be overridden, we use the myNew function to simulate new
function myNew() {
// Create an instance object
var obj = new Object(a);// Get the constructor passed in from the outside
var Constructor = Array.prototype.shift.call(arguments);
// Implement inheritance. Instances can access constructor properties
obj.__proto__ = Constructor.prototype;
// Call the constructor and change its this to refer to the instance
var ret = Constructor.apply(obj, arguments);
// The constructor returns an object if the value is an object, or a new instance if it is not
return typeof ret === 'object'&& ret ! = =null ? ret : obj;
}
Copy the code
Test myNew
// ========= No return value =============
const testNewFun = function(name) {
this.name = name;
};
const newObj = myNew(testNewFun, 'foo');
console.log(newObj); // { name: "foo" }
console.log(newObj instanceof testNewFun); // true
// ========= has the return value =============
const testNewFun = function(name) {
this.name = name;
return {};
};
const newObj = myNew(testNewFun, 'foo');
console.log(newObj); / / {}
console.log(newObj instanceof testNewFun); // false
Copy the code
Class implementation inheritance
Use ES5 and ES6 to see how class inheritance works
Implementation extends B
Use es6 syntax
class B {
constructor(opt) {
this.BName = opt.name; }}class A extends B {
constructor() {
// Pass a parameter to the parent class
super({ name: 'B' });
// This must be used under super()
console.log(this); }}Copy the code
Use es5 syntax
Use parasitic combinatorial inheritance
- Stereotype chain inheritance allows subclasses to invoke methods and properties on their parent class’s stereotype
- Using constructor inheritance, we can pass arguments to the parent class
- Parasitic inheritance, which creates a clean function with no constructor, used to send the parent class’s prototype
// Implement inheritance by inheriting prototype from the parent class
function __extends(child, parent) {
// Modify the object prototype
Object.setPrototypeOf(child, parent);
// Create a clean constructor that inherits the prototype of the parent class
// The advantage of this is that changing the prototype of the subclass does not affect the prototype of the parent class
function __() {
// Fix constructor to point to a subclass
this.constructor = child;
}
// Stereotype inheritance inherits the attributes of the parent stereotype, but cannot pass arguments to the parent constructor
child.prototype =
parent === null
? Object.create(parent)
: ((__.prototype = parent.prototype), new(4)); }var B = (function() {
function B(opt) {
this.name = opt.name;
}
returnB; }) ();var A = (function(_super) {
__extends(A, _super);
function A() {
Use super to pass arguments to the parent class
return(_super ! = =null && _super.apply(this, { name: 'B'})) | |this;
}
return A;
})(B);
Copy the code
The test class
const a = new A();
console.log(a.BName, a.constructor); ƒ A() {}
Copy the code
Async/await
The principle is to split code fragments using generators. Then we use a function that iterates over itself, with each yield wrapped in a promise. The timing of the next step is controlled by the promise
Async /await is the keyword and cannot override its methods, we use functions to emulate
Asynchronous iteration, simulating asynchronous functions
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments;
// Promise the return value
return new Promise(function(resolve, reject) {
// Get an iterator instance
var gen = fn.apply(self, args);
// Go to the next step
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
}
// Throw an exception
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
}
// The first time
_next(undefined);
});
};
}
Copy the code
Perform iteration steps to process the results of the next iteration
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
// The iterator is complete
resolve(value);
} else {
// This line of code is the essence
// Promise all values
For example, yield 1
// const a = promise.resolve (1
// const b = promise. resolve(a) b is a Promise
// We can do unified promise output
// Perform the next step after the promise is completed
// Call next recursively until done == true
Promise.resolve(value).then(_next, _throw); }}Copy the code
Test _asyncToGenerator
const asyncFunc = _asyncToGenerator(function* () {
const e = yield new Promise(resolve= > {
setTimeout((a)= > {
resolve('e');
}, 1000);
});
const a = yield Promise.resolve('a');
const d = yield 'd';
const b = yield Promise.resolve('b');
const c = yield Promise.resolve('c');
return [a, b, c, d, e];
});
asyncFunc().then(res= > {
console.log(res); // ['a', 'b', 'c', 'd', 'e']
});
Copy the code
Implement a bidirectional binding
DefineProperty version
/ / data
const data = {
text: 'default'
};
const input = document.getElementById('input');
const span = document.getElementById('span');
// Data hijacking
Object.defineProperty(data, 'text', {
// Data changes --> Modify viewset(newVal) { input.value = newVal; span.innerHTML = newVal; }});// View changes --> Data changes
input.addEventListener('keyup'.function(e) {
data.text = e.target.value;
});
Copy the code
The proxy version
/ / data
const data = {
text: 'default'
};
const input = document.getElementById('input');
const span = document.getElementById('span');
// Data hijacking
const handler = {
set(target, key, value) {
target[key] = value;
// Data changes --> Modify view
input.value = value;
span.innerHTML = value;
returnvalue; }};const proxy = new Proxy(data, handler);
// View changes --> Data changes
input.addEventListener('keyup'.function(e) {
proxy.text = e.target.value;
});
Copy the code
Object. Create basic implementation principles
if (typeof Object.create ! = ="function") {
Object.create = function (prototype, properties) {
if (typeofprototype ! = ="object") { throw TypeError(a); }function Ctor() {}
Ctor.prototype = prototype;
var o = new Ctor();
if (prototype) { o.constructor = Ctor; }
if(properties ! = =undefined) {
if(properties ! = =Object(properties)) { throw TypeError(a); }Object.defineProperties(o, properties);
}
return o;
};
}
Copy the code
Instanceof implementation
Principle: L __proto__ is not equal to r.prototype, is not equal to l.__proto__.__proto__ until __proto__ is null
// L represents the left expression, R represents the right expression
function instance_of(L, R) {
var O = R.prototype;
L = L.__proto__;
while (true) {
if (L === null) return false;
// Return true if O is strictly L
if (O === L) return true; L = L.__proto__; }}Copy the code
Array. The isArray implementation
Array.myIsArray = function(o) {
return Object.prototype.toString.call(Object(o)) === '[object Array]';
};
console.log(Array.myIsArray([])); // true
Copy the code
GetOwnPropertyNames implementation
Note: Cannot get non-enumerable properties
if (typeof Object.getOwnPropertyNames ! = ='function') {
Object.getOwnPropertyNames = function(o) {
if(o ! = =Object(o)) {
throw TypeError('Object.getOwnPropertyNames called on non-object');
}
var props = [],
p;
for (p in o) {
if (Object.prototype.hasOwnProperty.call(o, p)) { props.push(p); }}return props;
};
}
Copy the code
Promise to realize
How it works: It’s basically a publishing subscriber model
- The constructor accepts one
executor
Delta function, and will be innew Promise()
Is executed immediately then
To collect the callback functionSuccess/failure queue
executor
Call from a functionresolve/reject
functionresolve/reject
A callback in the queue is triggered when a function is called
Let’s take a look at the overall code to get an idea
The complete code
const isFunction = variable= > typeof variable === 'function';
// Define the three state constants for Promise
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// the constructor is triggered when new
constructor(handle: Function) {
try {
handle(this._resolve, this._reject);
} catch (err) {
this._reject(err); }}// Status pending depressing Rejected
private _status: string = PENDING;
// Store value for then return
private _value: string | undefined = undefined;
// The failure queue is injected at then and triggered at resolve
private _rejectedQueues: any = [];
// The success queue is injected at then and triggered at resolve
private _fulfilledQueues: any = [];
// The function executed when resovle
private _resolve = val= > {
const run = (a)= > {
if (this._status ! == PENDING)return;
this._status = FULFILLED;
// Execute the functions in the success queue in turn and empty the queue
const runFulfilled = value= > {
let cb;
while ((cb = this._fulfilledQueues.shift())) { cb(value); }};// Execute the functions in the failure queue in turn and empty the queue
const runRejected = error= > {
let cb;
while ((cb = this._rejectedQueues.shift())) { cb(error); }};/* * If the resolve argument is a Promise object, * the current Promsie state must wait for the Promise state to change * and the state depends on the Promsie state */
if (val instanceof MyPromise) {
val.then(
value= > {
this._value = value;
runFulfilled(value);
},
err => {
this._value = err; runRejected(err); }); }else {
this._value = val; runFulfilled(val); }};// Asynchronous invocation
setTimeout(run);
};
// reject
private _reject = err= > {
if (this._status ! == PENDING)return;
// Execute the functions in the failure queue in turn and empty the queue
const run = (a)= > {
this._status = REJECTED;
this._value = err;
let cb;
while ((cb = this._rejectedQueues.shift())) { cb(err); }};// To support synchronous promises, asynchronous calls are used
setTimeout(run);
};
/ / then methodthen(onFulfilled? , onRejected?) {const { _value, _status } = this;
// Return a new Promise object
return new MyPromise((onFulfilledNext, onRejectedNext) = > {
// Encapsulates a function to execute on success
const fulfilled = value= > {
try {
if(! isFunction(onFulfilled)) { onFulfilledNext(value); }else {
const res = onFulfilled(value);
if (res instanceof MyPromise) {
If the current callback returns a MyPromise object, you must wait for its state to change before executing the next callback
res.then(onFulfilledNext, onRejectedNext);
} else {
Otherwise, the result is passed directly as an argument to the callback function of the next THEN, and the callback function of the next THEN is executed immediatelyonFulfilledNext(res); }}}catch (err) {
// If the function fails, the state of the new Promise object is failedonRejectedNext(err); }};// Encapsulates a function to execute on failure
const rejected = error= > {
try {
if(! isFunction(onRejected)) { onRejectedNext(error); }else {
const res = onRejected(error);
if (res instanceof MyPromise) {
If the current callback returns a MyPromise object, you must wait for its state to change before executing the next callback
res.then(onFulfilledNext, onRejectedNext);
} else {
Otherwise, the result is passed directly as an argument to the callback function of the next THEN, and the callback function of the next THEN is executed immediatelyonFulfilledNext(res); }}}catch (err) {
// If the function fails, the state of the new Promise object is failedonRejectedNext(err); }};switch (_status) {
// When the state is pending, the then method callback is queued for execution
case PENDING:
this._fulfilledQueues.push(fulfilled);
this._rejectedQueues.push(rejected);
break;
// When the state has changed, the corresponding callback function is executed immediately
case FULFILLED:
fulfilled(_value);
break;
case REJECTED:
rejected(_value);
break; }}); }/ / catch method
catch(onRejected) {
return this.then(undefined, onRejected);
}
/ / finally
finally(cb) {
return this.then(
value= > MyPromise.resolve(cb()).then((a)= > value),
reason =>
MyPromise.resolve(cb()).then((a)= > {
throwreason; })); }// Static resolve method
static resolve(value) {
// If the argument is MyPromise instance, return that instance directly
if (value instanceof MyPromise) return value;
return new MyPromise(resolve= > resolve(value));
}
// Static reject
static reject(value) {
return new MyPromise((resolve, reject) = > reject(value));
}
// Static all method
static all(list) {
return new MyPromise((resolve, reject) = > {
// A collection of returned values
let values = [];
let count = 0;
for (let [i, p] of list.entries()) {
// If the array argument is not an instance of MyPromise, call myPromise.resolve first
this.resolve(p).then(
res= > {
values[i] = res;
count++;
// This is a big pity. The MyPromise state will become a big pity
if (count === list.length) resolve(values);
},
err => {
// MyPromise becomes rejected when there is a rejected statereject(err); }); }}); }// Add static race methods
static race(list) {
return new MyPromise((resolve, reject) = > {
for (let p of list) {
// The state of the new MyPromise changes as soon as one instance changes the state first
this.resolve(p).then(
res= >{ resolve(res); }, err => { reject(err); }); }}); }}Copy the code
Anti-shake/throttling
The anti-vibration function onScroll will trigger once at the end of the function to delay execution
function debounce(func, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
timeout = setTimeout((a)= > {
func.apply(context, args);
}, wait);
};
}
/ / use
window.onscroll = debounce(function() {
console.log('debounce');
}, 1000);
Copy the code
The throttle function onscroll is triggered every once in a while, like a water drop
function throttle(fn, delay) {
var prevTime = Date.now();
return function() {
var curTime = Date.now();
if (curTime - prevTime > delay) {
fn.apply(this.arguments); prevTime = curTime; }}; }/ / use
var throtteScroll = throttle(function() {
console.log('throtte');
}, 1000);
window.onscroll = throtteScroll;
Copy the code
Currization of the function
In fact, we use The Coriolization function all the time, we just don’t summarize it. Its essence is to decompose a function with many parameters into multiple functions with a single parameter.
In practical application:
- Deferred calculation (use closures to store the parameters passed in and start executing the function when the number of parameters passed in is enough)
- Dynamically create a function (returns a function that takes the remaining arguments if it runs out of arguments)
- Parameter reuse (each parameter can be reused multiple times)
const curry = fn= >
(judge = (. args) = >args.length >= fn.length ? fn(... args) :(. arg) = >judge(... args, ... arg));const sum = (a, b, c, d) = > a + b + c + d;
const currySum = curry(sum);
currySum(1) (2) (3) (4); / / 10
currySum(1.2) (3) (4); / / 10
currySum(1) (2.3) (4); / / 10
Copy the code
Write a deep copy by hand
The shallow copy only copies the address value and actually points to the data in the same heap. The deep copy recreates the same data and points to different address values in the heap. Modifying the variable before the assignment does not affect the variable after the assignment.
It’s too complicated to make a perfect god copy, so here’s a brief introduction that can be used in most scenarios
Judge type function
function getType(obj) {
const str = Object.prototype.toString.call(obj);
const map = {
'[object Boolean]': 'boolean'.'[object Number]': 'number'.'[object String]': 'string'.'[object Function]': 'function'.'[object Array]': 'array'.'[object Date]': 'date'.'[object RegExp]': 'regExp'.'[object Undefined]': 'undefined'.'[object Null]': 'null'.'[object Object]': 'object'
};
if (obj instanceof Element) {
// Determine whether it is a DOM element, such as div, etc
return 'element';
}
return map[str];
}
Copy the code
Array Object function (array Object function) The main thing is to get people thinking
function deepCopy(ori) {
const type = getType(ori);
let copy;
switch (type) {
case 'array':
return copyArray(ori, type, copy);
case 'object':
return copyObject(ori, type, copy);
case 'function':
return copyFunction(ori, type, copy);
default:
returnori; }}function copyArray(ori, type, copy = []) {
for (const [index, value] of ori.entries()) {
copy[index] = deepCopy(value);
}
return copy;
}
function copyObject(ori, type, copy = {}) {
for (const [key, value] of Object.entries(ori)) {
copy[key] = deepCopy(value);
}
return copy;
}
function copyFunction(ori, type, copy = () = >{{})const fun = eval(ori.toString());
fun.prototype = ori.prototype
return fun
}
Copy the code
There are a few small things at the end
- Want to join the group of advanced learning front-end plus my wechat
luoxue2479
Reply to add group - If there is a mistake, welcome to point out in the message area, discuss together, you can also add my wechat
- There will be a panel discussion in the group every day github.com/luoxue-vict…
- My public number [front-end technology artisan], let’s learn.
Refer to the article
Cloud.tencent.com/developer/a…
www.jianshu.com/p/b4f0425b2…
Blog.csdn.net/LL187811327…