JavaScript#
Collection of JS Syntax#
Implementing call function#
Function.prototype.wxcall = function (thisArg, ...arg) {
var fn = this;
thisArg = thisArg ? Object(thisArg) : window;
thisArg.fn = fn;
return thisArg.fn(...arg);
};
console.log(foo.wxcall('abc', 10, 20));
console.log(foo.wxcall({}, 10, 20));
let result = foo.wxcall(null, 10, 20);
console.log(result);
Implementing apply function#
Function.prototype.wxapply = function (thisArg, arrArg) {
var fn = this;
thisArg = thisArg ? Object(thisArg) : window;
thisArg.fn = fn;
arrArg ? thisArg.fn(...arrArg) : thisArg.fn(); // Check if parameters are passed
delete thisArg.fn;
};
function foo(a, b, c) {
console.log(a + b + c);
}
foo.wxapply({}, [10, 20, 30]);
foo.wxapply({}, [10, 20, 30]);
Implementing slice function#
Array.prototype.wxslice = function (start, end) {
var arr = this;
let newArr = [];
for (let i = start; i < end; i++) {
newArr.push(arr[i]);
}
return newArr;
};
console.log([1, 2, 3, 4, 5, 6, 7, 8].wxslice(0, 2));
defineProperty#
const obj = {
name: 'Tom',
age: 18,
};
// Object.defineProperty(obj,'name',{
// get:function(){
// console.log('Detected access to name property');
// },
// set:function(){
// console.log('Detected setting of name property');
// }
// })
// console.log(obj.name); //Detected access to name property
// obj.name = 'Kobe' //Detected setting of name property
// Can get all keys of the object
Object.keys(obj).forEach(key => {
let value = obj[key];
console.log(key);
/* name
age
*/
Object.defineProperty(obj, key, {
get: function () {
console.log('Detected access to property');
return value;
},
set: function (newValue) {
console.log('Detected setting of name property');
},
});
});
console.log(obj.name); //Detected access to name property
obj.name = 'Kobe'; //Detected setting of name property
Proxy#
const obj = {
name: 'Tom',
age: 18,
height: 1.88,
};
// Create a proxy
// Parameter one: proxy the obj object Parameter two: handler (there are 13 types of handlers)
const objProxy = new Proxy(obj, {
// Getter handler (automatically called when getting value)
get: function (target, key) {
// target is the proxied object; key
console.log(target); //{ name: 'Tom', age: 18, height: 1.88 }
console.log(key); // name, the key being accessed
return target[key];
},
// Setter handler (automatically called when setting value)
set: function (target, key, newValue) {
target[key] = newValue;
},
});
// Access
// console.log(objProxy); // { name: 'Tom', age: 18, height: 1.88 }
// console.log(objProxy.name); // Tom
// console.log(objProxy.age); // 18
// Set
// objProxy.name = "Kobe"; // through proxy
console.log(objProxy.name); // Kobe
Reactive Principle#
class Depend {
constructor() {
this.reactiveFns = [];
}
// Collect dependencies
addDepend(reactiveFn) {
this.reactiveFns.push(reactiveFn);
}
// Execute dependencies
notify() {
// Iterate through all dependencies and execute
this.reactiveFns.forEach(fn => {
fn();
});
}
}
const depend = new Depend();
const obj = {
name: 'Tom',
age: 18,
};
// Listen for changes in properties (proxy, object.defineProperty) and execute depend.notify()
const objProxy = new Proxy(obj, {
get: function (target, key, receiver) {
// Return the value
return Reflect.get(target, key, receiver);
},
// Automatically executes this method when setting value to the proxy object
set: function (target, key, newvalue, receiver) {
Reflect.set(target, key, newvalue, receiver);
// Notify depend to execute dependencies when setting value
depend.notify();
},
});
function foo() {
console.log('Executing code');
}
depend.addDepend(foo); // Collect dependencies
objProxy.name = 'Lilei';
Iterator#
// An iterator must meet the following conditions
// 1. It is an object
// 2. The object has a next method
// 3. The method returns an object with done and value properties
// const iterator = {
// next: function () {
// return { done: true, value: "" };
// },
// };
// Array
const arr = ['abc', 'cba', 'nba'];
// Use an iterator to traverse the array
// Create an iterator object to access the array
let index = 0;
const arrIterator = {
next: function () {
if (index < arr.length) {
return { done: false, value: arr[index++] };
} else {
return { done: true, value: undefined };
}
},
};
console.log(index); //0
console.log(arrIterator.next()); //{ done: false, value: 'abc' }
console.log(index); //1
console.log(arrIterator.next()); //{ done: false, value: 'cba' }
console.log(index); //2
console.log(arrIterator.next()); //{ done: false, value: 'nba' }
console.log(index); //3
console.log(arrIterator.next()); //{ done: true, value: undefined }
console.log(index); //3
// Iterator optimization
Iterator Generator Function#
function createArryIterator(arr) {
let index = 0;
return {
next: function () {
if (index < arr.length) {
return { done: false, value: arr[index++] };
} else {
return { done: true, value: undefined };
}
},
};
}
const names = ['Tom', 'Sily', 'Lusy'];
const nameIterator = createArryIterator(names);
console.log(nameIterator.next()); //{ done: false, value: 'Tom' }
console.log(nameIterator.next()); //{ done: false, value: 'Sily' }
console.log(nameIterator.next()); //{ done: false, value: 'Lusy' }
const nums = [1, 2, 3, 4, 5];
const numsIterator = createArryIterator(nums);
console.log(numsIterator.next()); //{ done: false, value: 1 }
console.log(numsIterator.next()); //{ done: false, value: 2 }
console.log(numsIterator.next()); //{ done: false, value: 3 }
console.log(numsIterator.next()); //{ done: false, value: 4 }
// Create an infinite iterator
function createNumberIterator() {
let index = 0;
return {
next: function () {
return {
done: false,
value: index++,
};
},
};
}
const numberInterator = createNumberIterator();
console.log(numberInterator.next()); //{ done: false, value: 0 }
console.log(numberInterator.next()); //{ done: false, value: 1 }
console.log(numberInterator.next()); //{ done: false, value: 2 }
console.log(numberInterator.next()); //{ done: false, value: 3 }
console.log(numberInterator.next()); //{ done: false, value: 4 }
console.log(numberInterator.next()); //{ done: false, value: 5 } ......
Iterable Object#
// This object returns an iterator
const iterabalObj = {
names: ['Tom', 'Sily', 'Lusy'],
// An iterable object needs to have the [Symbol.iterator] property, which corresponds to a function that returns an iterator
[Symbol.iterator]: function () {
let index = 0;
return {
next: () => {
if (index < this.names.length) {
return { done: false, value: this.names[index++] };
} else {
return { done: true, value: undefined };
}
},
};
},
};
console.log(iterabalObj[Symbol.iterator]); //[Function: [Symbol.iterator]]
// Calling this function will generate a new iterator for us
const iterator = iterabalObj[Symbol.iterator]();
console.log(iterator.next()); //{ done: false, value: 'Tom' }
console.log(iterator.next()); //{ done: false, value: 'Sily' }
console.log(iterator.next()); //{ done: false, value: 'Lusy' }
// New iterator
const iterator1 = iterabalObj[Symbol.iterator]();
console.log(iterator1.next()); //{ done: false, value: 'Tom' }
console.log(iterator1.next()); //{ done: false, value: 'Sily' }
console.log(iterator1.next()); //{ done: false, value: 'Lusy' }
for (const item of iterabalObj) {
console.log(item);
// Tom
// Sily
// Lusy
}
Generator#
// Generator function (*)
function* foo() {
console.log(1);
yield;
console.log(2);
// yield can control the function to pause
yield;
console.log(3);
yield;
console.log(4);
}
// Calling the generator function does not execute it directly, but returns a generator object
// console.log(foo()); // Object [Generator] {}
const Generator = foo();
// Start executing the first segment of code (code before yield) by executing next()
Generator.next(); //1
// Start executing the second segment of code
Generator.next(); //2
// Start executing the third segment of code
Generator.next(); //3
// Start executing the fourth segment of code
Generator.next(); //4
Generator Function Execution Flow#
function* foo() {
console.log(1);
yield 'Return value'; // The return value of this segment of code
console.log(2);
yield;
console.log(3);
yield;
console.log(4);
}
const Generator = foo();
// The return value is value, done: false
console.log(Generator.next()); // { value: { name: 'tom', age: 18 }, done: false }
// Generator.next() // 1
// Generator function function*
// yield keyword can pause the code here
// Executing the generator function returns a generator object
// Calling the next method of this generator object will execute the code before yield in order,
// Calling next() again will continue executing the code before the next yield
// If you want to return some values before each yield, you can write the return value after yield
Generator Parameter Issue#
// Parameter issue for each segment of code
function* foo() {
console.log(1);
// The parameter passed to the next function during the second execution will be assigned to the return value of the first yield
const n = yield;
// The second segment of code is executed during the second next execution
console.log(2 * n); // 20
yield 'Return value: console.log(Generator.next()) // Return value ......';
console.log(3);
yield;
console.log(4);
}
const Generator = foo();
Generator.next();
Generator.next(10);
// Execute Generator.next() twice
// During the second execution, a parameter is passed
// This parameter will be used as the return value of the first yield, const n = yield uses n to accept this return value
Macro Tasks & Micro Tasks#
// Code after await belongs to micro tasks
// async function a() {
// console.log('a')
// await b()
// console.log('a end')
// }
// async function b() {
// console.log('b')
// }
// a()
// setTimeout(function () {
// console.log('setTimeout')
// }, 0)
// new Promise(function (resolve, reject) {
// console.log('promise')
// resolve()
// }).then(function () {
// console.log('then')
// })
// console.log('main end')
// //
setTimeout(function () {
console.log('8');
}, 0);
async function async1() {
console.log('1');
const data = await async2();
console.log('6');
return data;
}
async function async2() {
return new Promise(resolve => {
console.log('2');
resolve('Result of async2');
}).then(data => {
console.log('4');
return data;
});
}
async1().then(data => {
console.log('7');
console.log(data);
});
new Promise(function (resolve) {
console.log('3');
resolve();
}).then(function () {
console.log('5');
});
//
console.log('script start');
async function async1() {
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 end');
}
async1();
setTimeout(function () {
console.log('setTimeout');
}, 0);
new Promise(resolve => {
console.log('Promise');
resolve();
})
.then(function () {
console.log('promise1');
})
.then(function () {
console.log('promise2');
});
console.log('script end');
//
// console.log('script start')
// async function async1() {
// await async2()
// console.log('async1 end')
// }
// async function async2() {
// console.log('async2 end')
// return Promise.resolve().then(()=>{
// console.log('async2 end1')
// })
// }
// async1()
// setTimeout(function() {
// console.log('setTimeout')
// }, 0)
// new Promise(resolve => {
// console.log('Promise')
// resolve()
// })
// .then(function() {
// console.log('promise1')
// })
// .then(function() {
// console.log('promise2')
// })
// console.log('script end')
// async function async1(){
// await async2()
// console.log('async1 end')
// }
// async function async2(){}
// async1();
// new Promise(function(resolve){
// resolve();
// }).then(function(){
// console.log('promise2')
// }).then(function() {
// console.log('promise3')
// }).then(function() {
// console.log('promise4')
// })
Debounce#
function debounce(fn, delay) {
let timer = null;
return (...args) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// Whether to execute directly for the first time
function debounce(fn, delay, isImmediately = true) {
let timer = null;
let Immediately = isImmediately;
return (...args) => {
if (timer) clearTimeout(timer);
Immediately
? fn.apply(this, args)
: (timer = setTimeout(() => fn.apply(this, args), delay));
Immediately = !isImmediately;
};
}
Throttle#
function throttle(fn, delay) {
let pre = 0;
return (...args) => {
let now = new Date();
if (now - pre > delay) {
fn.apply(this, args);
}
pre = now;
};
}
Deep Clone#
function isObject(value) {
const valueType = typeof value;
return (value !== null && valueType === 'object') || valueType === 'function';
}
function deepClone(originValue) {
// Check if it is a symbol type, create a new symbol
if (typeof originValue === 'symbol') {
return Symbol(originValue.description);
}
if (typeof originValue === 'function') {
// If it is a function, return it directly
return originValue;
}
if (originValue instanceof Set) {
return new Set([...originValue]);
}
if (originValue instanceof Map) {
return new Map([...originValue]);
}
// Check if the passed origin is an object type
if (!isObject(originValue)) {
return originValue;
}
// Check if the passed object is an array or an object
const newObject = Array.isArray(originValue) ? [] : {};
// Iterate through the original object and clone the property values to the new object
for (const key in originValue) {
newObject[key] = deepClone(originValue[key]); // Recursive call
}
const symbolKeys = Object.getOwnPropertySymbols(originValue);
for (const skey of symbolKeys) {
newObject[skey] = deepClone(originValue[skey]);
}
return newObject;
}
const obj1 = {
name: 'Tom',
friends: {
one: {
name: 'sily',
},
two: {
name: 'kobe',
},
},
};
console.log(deepClone(obj1));
Event Bus#
class EventBus {
constructor() {
this.eventBus = {};
}
on(eventName, eventCallBack, thisArg) {
let handlers = this.eventBus[eventName];
if (!handlers) {
handlers = [];
this.eventBus[eventName] = handlers;
}
handlers.push({
eventCallBack,
thisArg,
});
}
off() {}
emit(eventName, ...payload) {
const handlers = this.eventBus[eventName];
if (!handlers) return;
handlers.forEach(handler => {
handler.eventCallBack.apply(handler.thisArg, payload);
});
}
}
const eventBus = new EventBus();
// main.js
eventBus.on(
'a',
function (payload) {
console.log(payload);
},
this
);
// util.js
eventBus.emit('a', { name: 'Tom' });
Implementing slice function#
Array.prototype.wxslice = function (start, end) {
var arr = this;
let newArr = [];
for (let i = start; i < end; i++) {
newArr.push(arr[i]);
}
return newArr;
}
console.log([1, 2, 3, 4, 5, 6, 7, 8].wxslice(0, 2));
Implementing call function#
// Add a wxapply method to all functions
Function.prototype.wxcall = function (thisArg, ...arg) { // Pass in this and parameters
// Here we need to execute the function that calls this method
// Question: Which function executed call
var fn = this; // Who called it is this
// Convert thisArg to object type to prevent passing in a number or other non-object type
// If null or undefined is passed, bind this to window
thisArg = thisArg ? Object(thisArg) : window;
thisArg.fn = fn; // Execute this function
thisArg.fn(...arg); // Bind this and parameters
delete thisArg.fn;
}
function foo(a, b) {
console.log('Executing');
console.log(this);
console.log(a + b);
}
foo.wxcall({}, 10, 20);
Function.prototype.wxcall = function (thisArg, ...arg) {
var fn = this;
thisArg = thisArg ? Object(thisArg) : window;
thisArg.fn = fn;
return thisArg.fn(...arg);
}
function foo(a, b) {
return a + b;
}
console.log(foo.wxcall('abc', 10, 20));
console.log(foo.wxcall({}, 10, 20));
let result = foo.wxcall(null, 10, 20);
console.log(result);
## Function to create an iterator
```js
function createArryIterator(arr) {
let index = 0;
return {
next: function () {
if (index < arr.length) {
return { done: false, value: arr[index++] };
} else {
return { done: true, value: undefined };
}
}
};
}
const names = ['Tom', 'Sily', 'Lusy'];
const nameIterator = createArryIterator(names);
console.log(nameIterator.next()); //{ done: false, value: 'Tom' }
console.log(nameIterator.next()); //{ done: false, value: 'Sily' }
console.log(nameIterator.next()); //{ done: false, value: 'Lusy' }
const nums = [1, 2, 3, 4, 5];
const numsIterator = createArryIterator(nums);
console.log(numsIterator.next()); //{ done: false, value: 1 }
console.log(numsIterator.next()); //{ done: false, value: 2 }
console.log(numsIterator.next()); //{ done: false, value: 3 }
console.log(numsIterator.next()); //{ done: false, value: 4 }
// Create an infinite iterator
function createNumberIterator() {
let index = 0;
return {
next: function () {
return {
done: false,
value: index++
};
}
};
}
const numberInterator = createNumberIterator();
console.log(numberInterator.next()); //{ done: false, value: 0 }
console.log(numberInterator.next()); //{ done: false, value: 1 }
console.log(numberInterator.next()); //{ done: false, value: 2 }
console.log(numberInterator.next()); //{ done: false, value: 3 }
console.log(numberInterator.next()); //{ done: false, value: 4 }
console.log(numberInterator.next()); //{ done: false, value: 5 } ......
Implementing apply function#
Function.prototype.wxapply = function (thisArg, arrArg) {
var fn = this;
thisArg = thisArg ? Object(thisArg) : window;
thisArg.fn = fn;
arrArg ? thisArg.fn(...arrArg) : thisArg.fn(); // Check if parameters are passed
delete thisArg.fn;
}
function foo(a, b, c) {
console.log(a + b + c);
}
foo.wxapply({}, [10, 20, 30]);
foo.wxapply({}, [10, 20, 30]);
Implementing instanceof#
function isPrototypeOf(obj) {
var proto = this.prototype;
while (obj) {
if (obj === proto) {
return true;
}
obj = Object.getPrototypeOf(obj);
}
return false;
}
Iterable Object#
// This object returns an iterator
const iterabalObj = {
names: ['Tom', 'Sily', 'Lusy'],
// An iterable object needs to have the [Symbol.iterator] property, which corresponds to a function that returns an iterator
[Symbol.iterator]: function () {
let index = 0;
return {
next: () => {
if (index < this.names.length) {
return { done: false, value: this.names[index++] };
} else {
return { done: true, value: undefined };
}
}
};
}
}
console.log(iterabalObj[Symbol.iterator]); //[Function: [Symbol.iterator]]
// Calling this function will generate a new iterator for us
const iterator = iterabalObj[Symbol.iterator]();
console.log(iterator.next()); //{ done: false, value: 'Tom' }
console.log(iterator.next()); //{ done: false, value: 'Sily' }
console.log(iterator.next()); //{ done: false, value: 'Lusy' }
// New iterator
const iterator1 = iterabalObj[Symbol.iterator]();
console.log(iterator1.next()); //{ done: false, value: 'Tom' }
console.log(iterator1.next()); //{ done: false, value: 'Sily' }
console.log(iterator1.next()); //{ done: false, value: 'Lusy' }
for (const item of iterabalObj) {
console.log(item);
// Tom
// Sily
// Lusy
}
Listening to Object Operations#
const obj = {
name: 'Tom',
age: 18
}
// Object.defineProperty(obj,'name',{
// get:function(){
// console.log('Detected access to name property');
// },
// set:function(){
// console.log('Detected setting of name property');
// }
// })
// console.log(obj.name); //Detected access to name property
// obj.name = 'Kobe' //Detected setting of name property
// Can get all keys of the object
Object.keys(obj).forEach(key => {
let value = obj[key];
console.log(key);
/* name
age
*/
Object.defineProperty(obj, key, {
get: function () {
console.log('Detected access to property');
return value;
},
set: function (newValue) {
console.log('Detected setting of name property');
}
});
});
console.log(obj.name); //Detected access to name property
obj.name = 'Kobe'; //Detected setting of name property
Reactive Principle#
class Depend {
constructor() {
this.reactiveFns = [];
}
// Collect dependencies
addDepend(reactiveFn) {
this.reactiveFns.push(reactiveFn);
}
// Execute dependencies
notify() {
// Iterate through all dependencies and execute
this.reactiveFns.forEach(fn => {
fn();
});
}
}
const depend = new Depend();
const obj = {
name: 'Tom',
age: 18,
};
// Listen for changes in properties (proxy, object.defineProperty) and execute depend.notify()
const objProxy = new Proxy(obj, {
get: function (target, key, receiver) {
// Return the value
return Reflect.get(target, key, receiver);
},
// Automatically executes this method when setting value to the proxy object
set: function (target, key, newvalue, receiver) {
Reflect.set(target, key, newvalue, receiver);
// Notify depend to execute dependencies when setting value
depend.notify();
}
});
function foo() {
console.log('Executing code');
}
depend.addDepend(foo); // Collect dependencies
objProxy.name = 'Lilei';
Iterator#
// An iterator must meet the following conditions
// 1. It is an object
// 2. The object has a next method
// 3. The method returns an object with done and value properties
// const iterator = {
// next: function () {
// return { done: true, value: "" };
// },
// };
// Array
const arr = ['abc', 'cba', 'nba'];
// Use an iterator to traverse the array
// Create an iterator object to access the array
let index = 0;
const arrIterator = {
next: function () {
if (index < arr.length) {
return { done: false, value: arr[index++] };
} else {
return { done: true, value: undefined };
}
}
};
console.log(index); //0
console.log(arrIterator.next()); //{ done: false, value: 'abc' }
console.log(index); //1
console.log(arrIterator.next()); //{ done: false, value: 'cba' }
console.log(index); //2
console.log(arrIterator.next()); //{ done: false, value: 'nba' }
console.log(index); //3
console.log(arrIterator.next()); //{ done: true, value: undefined }
console.log(index); //3
// Iterator optimization
Custom Class Iterability#
// Custom iterable class
class Classroom {
constructor(address, name, students) {
this.address = address;
this.name = name;
this.students = students;
}
entry(newStudents) {
this.students.push(newStudents);
}
[Symbol.iterator]() {
// This function returns an iterator
let index = 0;
return {
next: () => {
if (index < this.students.length) {
return { done: false, value: this.students[index++] };
} else {
return { done: true, value: undefined };
}
},
};
}
}
const classroom = new Classroom("Beijing", "Primary School", ["tom", "sily", "kobe"]);
for (item of classroom) {
console.log(item);
// tom
// sily
// kobe
}
Built-in Iterable Object Creation#
const names = ['abc', 'cba', 'nba'];
console.log(names[Symbol.iterator]); //[Function: values]
const iterator = names[Symbol.iterator]() // Executing this function returns an iterator
console.log(iterator.next()); //{ value: 'abc', done: false }
console.log(iterator.next()); //{ value: 'cba', done: false }
console.log(iterator.next()); //{ value: 'nba', done: false }
console.log(iterator.next()); //{ value: undefined, done: true }
Generator Parameter Issue#
// Parameter issue for each segment of code
function* foo() {
console.log(1);
// The parameter passed to the next function during the second execution will be assigned to the return value of the first yield
const n = yield;
// The second segment of code is executed during the second next execution
console.log(2 * n); // 20
yield 'Return value :console.log(Generator.next()) // Return value ......';
console.log(3);
yield;
console.log(4);
}
const Generator = foo();
Generator.next();
Generator.next(10);
Generator Function Execution Flow#
function* foo() {
console.log(1);
yield 'Return value'; // The return value of this segment of code
console.log(2);
yield;
console.log(3);
yield;
console.log(4);
}
const Generator = foo();
// The return value is value, done: false
console.log(Generator.next()); // { value: { name: 'tom', age: 18 }, done: false }
// Generator.next() // 1
Proxy Usage#
const obj = {
name: "Tom",
age: 18,
height: 1.88,
};
// Create a proxy
// Parameter one: proxy the obj object Parameter two: handler (there are 13 types of handlers)
const objProxy = new Proxy(obj, {
// Getter handler (automatically called when getting value)
get: function(target, key) {
console.log(target);
console.log(key);
return target[key];
},
// Setter handler (automatically called when setting value)
set: function(target, key, newValue) {
target[key] = newValue;
},
// Listen for in handler
has: function(target, key) {
console.log('Using in operator');
return key in target;
},
// Listen for delete handler
deleteProperty: function(target, key) {
console.log('Detected property deletion');
delete target[key];
}
});
// console.log(objProxy.name); // Kobe
// console.log('name' in objProxy); // true
delete objProxy.name;
Prototype#
function Fn() {}
Fn.prototype = {
name: 'Tom',
age: 18
}
const p1 = new Fn();
const p2 = new Fn();
console.log(p1.name);
console.log(p2.name);
console.log(p2.toString());
Array Deduplication#
const unique = (arr) => {
if (!Array.isArray(arr)) {
return console.error('Parameter must be an array');
}
const temArr = [];
for (let i = 0; i < arr.length; i++) {
if (temArr.indexOf(arr[i]) === -1) {
temArr.push(arr[i]);
}
// OR
// if (temArr.includes(arr[i])) {
// temArr.push(arr[i]);
// }
}
return temArr.sort();
}
const result = unique([1, 1, 1, 2, 3, 2, 3, 4, 5, 6, 4, 2, 9, 8, 7, 8, 7, 6]);
console.log(result);
console.log(typeof 3..toString());
// 3 ===> 0000 0011 ===> 0000 1100
console.log(3 << 2);