Излишне говорить, что все знают о пользе чтения исходного кода.Сначала необходимо войти в большую фабрику, и это также может улучшить ваши способности и перенять опыт предшественников. Исходный код часто является лучшим опытом, оставленным предшественниками, и мы узнаем больше с меньшими усилиями, если будем следовать по стопам наших предшественников.
- позвонить, подать заявку, привязать реализацию
- новая реализация
- класс реализует наследование
- асинхронная/ожидающая реализация
- сократить реализацию
- Реализация двусторонней привязки данных
- экземпляр реализации
- Реализация Array.isArray
- Основной принцип реализации Object.create
- реализация getOwnPropertyNames
- выполнение обещания
- Напишите функцию защиты от сотрясения/дросселя вручную
- Реализация каррирующих функций
- почерк глубокая копия
позвонить, подать заявку, привязать реализацию
call、apply、bind
Суть в измененииthis
направление, разныеcall、apply
это вызов функции напрямую,bind
заключается в возврате новой функции.call
а такжеapply
Только параметры разные.
привязать реализацию
- стрелочная функция
this
всегда указывайте на его масштаб - работать как конструктор
new
ключевое слово не должно менять своеthis
точка, потому чтоnew绑定
приоритет выше, чем显示绑定
а также硬绑定
Function.prototype.mybind = function(thisArg) {
if (typeof this !== 'function') {
throw TypeError("Bind must be called on a function");
}
// 拿到参数,为了传给调用者
const args = Array.prototype.slice.call(arguments, 1),
// 保存 this
self = this,
// 构建一个干净的函数,用于保存原函数的原型
nop = function() {},
// 绑定的函数
bound = function() {
// this instanceof nop, 判断是否使用 new 来调用 bound
// 如果是 new 来调用的话,this的指向就是其实例,
// 如果不是 new 调用的话,就改变 this 指向到指定的对象 o
return self.apply(
this instanceof nop ? this : thisArg,
args.concat(Array.prototype.slice.call(arguments))
);
};
// 箭头函数没有 prototype,箭头函数this永远指向它所在的作用域
if (this.prototype) {
nop.prototype = this.prototype;
}
// 修改绑定函数的原型指向
bound.prototype = new nop();
return bound;
}
}
测试 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]
реализация вызова
bind
упакованcall
метод измененthis
указывает и возвращает новую функцию, затемcall
как внести измененияthis
указывая на это? Принцип очень простой, в режиме вызова метода,this
всегда указывает на объект, для которого вызывается метод, для которого он вызывается,this
Указатель связан с позицией вызова метода, в котором он находится, и не имеет ничего общего с позицией объявления метода (функции со стрелками являются специальными). написать небольшойdemo
чтобы понять.
const foo = { name: 'foo' };
foo.fn = function() {
// 这里的 this 指向了 foo
// 因为 foo 调用了 fn,
// fn 的 this 就指向了调用它所在方法的对象 foo 上
console.log(this.name); // foo
};
использоватьthis
механизм достиженияcall
Function.prototype.mycall = function(thisArg) {
// this指向调用call的对象
if (typeof this !== 'function') {
// 调用call的若不是函数则报错
throw new TypeError('Error');
}
// 声明一个 Symbol 属性,防止 fn 被占用
const fn = Symbol('fn')
const args = [...arguments].slice(1);
thisArg = thisArg || window;
// 将调用call函数的对象添加到thisArg的属性中
thisArg[fn] = this;
// 执行该属性
const result = thisArg[fn](...args);
// 删除该属性
delete thisArg[fn];
// 返回函数执行结果
return result;
}
применить реализацию
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;
const result = thisArg[fn](...arg);
delete thisArg[fn];
return result;
};
测试 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]
Уменьшить принцип реализации
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
Array.prototype.myreduce = function reduce(callbackfn) {
// 拿到数组
const O = this,
len = O.length;
// 下标值
let k = 0,
// 累加器
accumulator = undefined,
// k下标对应的值是否存在
kPresent = false,
// 初始值
initialValue = arguments.length > 1 ? arguments[1] : undefined;
if (typeof callbackfn !== 'function') {
throw new TypeError(callbackfn + ' is not a function');
}
// 数组为空,并且有初始值,报错
if (len === 0 && arguments.length < 2) {
throw new TypeError('Reduce of empty array with no initial value');
}
// 如果初始值存在
if (arguments.length > 1) {
// 设置累加器为初始值
accumulator = initialValue;
// 初始值不存在
} else {
accumulator = O[k];
++k;
}
while (k < len) {
// 判断是否为 empty [,,,]
kPresent = O.hasOwnProperty(k);
if (kPresent) {
const kValue = O[k];
// 调用 callbackfn
accumulator = callbackfn.apply(undefined, [accumulator, kValue, k, O]);
}
++k;
}
return accumulator;
};
测试
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
новая реализация
нам нужно знать, когда
new
что ты делал, когда
- создать новый объект;
- назначьте область конструктора новому объекту (так что это указывает на новый объект)
- Выполните код в конструкторе (добавьте свойства к этому новому объекту)
- Вернуть новый объект.
Поскольку new нельзя переопределить, мы используемmyNew
функция для имитацииnew
function myNew() {
// 创建一个实例对象
var obj = new Object();
// 取得外部传入的构造器
var Constructor = Array.prototype.shift.call(arguments);
// 实现继承,实例可以访问构造器的属性
obj.__proto__ = Constructor.prototype;
// 调用构造器,并改变其 this 指向到实例
var ret = Constructor.apply(obj, arguments);
// 如果构造函数返回值是对象则返回这个对象,如果不是对象则返回新的实例对象
return typeof ret === 'object' && ret !== null ? ret : obj;
}
测试 myNew
// ========= 无返回值 =============
const testNewFun = function(name) {
this.name = name;
};
const newObj = myNew(testNewFun, 'foo');
console.log(newObj); // { name: "foo" }
console.log(newObj instanceof testNewFun); // true
// ========= 有返回值 =============
const testNewFun = function(name) {
this.name = name;
return {};
};
const newObj = myNew(testNewFun, 'foo');
console.log(newObj); // {}
console.log(newObj instanceof testNewFun); // false
класс реализует наследование
В основном используется
es5
а такжеes6
Сравниватьclass
Принципы наследования
реализовать наследованиеA extends B
использоватьes6
грамматика
class B {
constructor(opt) {
this.BName = opt.name;
}
}
class A extends B {
constructor() {
// 向父类传参
super({ name: 'B' });
// this 必须在 super() 下面使用
console.log(this);
}
}
использоватьes5
грамматика
Использование паразитного наследования композиции
- Защищенное цепное наследование, заставьте подкласс вызывать методы и свойства прототипа родительского класса.
- Используя наследование конструктора, вы можете передавать параметры родительскому классу.
- Паразитическое наследование, создание чистых функций без конструкторов, используемых для паразитирования на прототипе родительского класса.
// 实现继承,通过继承父类 prototype
function __extends(child, parent) {
// 修改对象原型
Object.setPrototypeOf(child, parent);
// 寄生继承,创建一个干净的构造函数,用于继承父类的 prototype
// 这样做的好处是,修改子类的 prototype 不会影响父类的 prototype
function __() {
// 修正 constructor 指向子类
this.constructor = child;
}
// 原型继承,继承父类原型属性,但是无法向父类构造函数传参
child.prototype =
parent === null
? Object.create(parent)
: ((__.prototype = parent.prototype), new __());
}
var B = (function() {
function B(opt) {
this.name = opt.name;
}
return B;
})();
var A = (function(_super) {
__extends(A, _super);
function A() {
// 借用继承,可以实现向父类传参, 使用 super 可以向父类传参
return (_super !== null && _super.apply(this, { name: 'B' })) || this;
}
return A;
})(B);
测试 class
const a = new A();
console.log(a.BName, a.constructor); // B ,ƒ A() {}
асинхронная/ожидающая реализация
Принцип заключается в использовании
generator
(Генератор) Разделить фрагменты кода. Затем мы используем функцию, позволяющую ей повторять себя, каждый разyield
использоватьpromise
Заверните. Время выполнения следующего шага задаетсяpromise
контролировать
async/await
является ключевым словом, его метод нельзя переопределить, мы используем функцию для имитации
Асинхронная итерация, имитирующая асинхронные функции
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments;
// 将返回值promise化
return new Promise(function(resolve, reject) {
// 获取迭代器实例
var gen = fn.apply(self, args);
// 执行下一步
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
}
// 抛出异常
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
}
// 第一次触发
_next(undefined);
});
};
}
Выполнение итерационных шагов и обработка результатов следующей итерации
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) {
// 迭代器完成
resolve(value);
} else {
// -- 这行代码就是精髓 --
// 将所有值promise化
// 比如 yield 1
// const a = Promise.resolve(1) a 是一个 promise
// const b = Promise.resolve(a) b 是一个 promise
// 可以做到统一 promise 输出
// 当 promise 执行完之后再执行下一步
// 递归调用 next 函数,直到 done == true
Promise.resolve(value).then(_next, _throw);
}
}
测试 _asyncToGenerator
const asyncFunc = _asyncToGenerator(function*() {
const e = yield new Promise(resolve => {
setTimeout(() => {
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']
});
Реализовать двустороннюю привязку
defineProperty
Версия
// 数据
const data = {
text: 'default'
};
const input = document.getElementById('input');
const span = document.getElementById('span');
// 数据劫持
Object.defineProperty(data, 'text', {
// 数据变化 --> 修改视图
set(newVal) {
input.value = newVal;
span.innerHTML = newVal;
}
});
// 视图更改 --> 数据变化
input.addEventListener('keyup', function(e) {
data.text = e.target.value;
});
proxy
Версия
// 数据
const data = {
text: 'default'
};
const input = document.getElementById('input');
const span = document.getElementById('span');
// 数据劫持
const handler = {
set(target, key, value) {
target[key] = value;
// 数据变化 --> 修改视图
input.value = value;
span.innerHTML = value;
return value;
}
};
const proxy = new Proxy(data, handler);
// 视图更改 --> 数据变化
input.addEventListener('keyup', function(e) {
proxy.text = e.target.value;
});
Основной принцип реализации Object.create
if (typeof Object.create !== "function") {
Object.create = function (prototype, properties) {
if (typeof prototype !== "object") { throw TypeError(); }
function Ctor() {}
Ctor.prototype = prototype;
var o = new Ctor();
if (prototype) { o.constructor = Ctor; }
if (properties !== undefined) {
if (properties !== Object(properties)) { throw TypeError(); }
Object.defineProperties(o, properties);
}
return o;
};
}
экземпляр реализации
принцип:
L
из__proto__
Равен ли онR.prototype
, не значит искатьL.__proto__.__proto__
до__proto__
дляnull
// L 表示左表达式,R 表示右表达式
function instance_of(L, R) {
var O = R.prototype;
L = L.__proto__;
while (true) {
if (L === null) return false;
// 这里重点:当 O 严格等于 L 时,返回 true
if (O === L) return true;
L = L.__proto__;
}
}
Реализация Array.isArray
Array.myIsArray = function(o) {
return Object.prototype.toString.call(Object(o)) === '[object Array]';
};
console.log(Array.myIsArray([])); // true
реализация getOwnPropertyNames
Примечание. Невозможно получить неперечислимые свойства.
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;
};
}
Обещание реализации
Принцип реализации: Фактически это модель публикации-подписки.
- Конструктор получает
executor
функционировать и будетnew Promise()
немедленно выполнить функцию -
then
При сборе зависимостей собирайте функции обратного вызова для成功/失败队列
-
executor
вызов функцииresolve/reject
функция -
resolve/reject
Обратные вызовы в очереди триггера уведомляются при вызове функции
Сначала посмотрите на общий код, есть общая концепция
полный код
const isFunction = variable => typeof variable === 'function';
// 定义Promise的三种状态常量
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// 构造函数,new 时触发
constructor(handle: Function) {
try {
handle(this._resolve, this._reject);
} catch (err) {
this._reject(err);
}
}
// 状态 pending fulfilled rejected
private _status: string = PENDING;
// 储存 value,用于 then 返回
private _value: string | undefined = undefined;
// 失败队列,在 then 时注入,resolve 时触发
private _rejectedQueues: any = [];
// 成功队列,在 then 时注入,resolve 时触发
private _fulfilledQueues: any = [];
// resovle 时执行的函数
private _resolve = val => {
const run = () => {
if (this._status !== PENDING) return;
this._status = FULFILLED;
// 依次执行成功队列中的函数,并清空队列
const runFulfilled = value => {
let cb;
while ((cb = this._fulfilledQueues.shift())) {
cb(value);
}
};
// 依次执行失败队列中的函数,并清空队列
const runRejected = error => {
let cb;
while ((cb = this._rejectedQueues.shift())) {
cb(error);
}
};
/*
* 如果resolve的参数为Promise对象,
* 则必须等待该Promise对象状态改变后当前Promsie的状态才会改变
* 且状态取决于参数Promsie对象的状态
*/
if (val instanceof MyPromise) {
val.then(
value => {
this._value = value;
runFulfilled(value);
},
err => {
this._value = err;
runRejected(err);
}
);
} else {
this._value = val;
runFulfilled(val);
}
};
// 异步调用
setTimeout(run);
};
// reject 时执行的函数
private _reject = err => {
if (this._status !== PENDING) return;
// 依次执行失败队列中的函数,并清空队列
const run = () => {
this._status = REJECTED;
this._value = err;
let cb;
while ((cb = this._rejectedQueues.shift())) {
cb(err);
}
};
// 为了支持同步的Promise,这里采用异步调用
setTimeout(run);
};
// then 方法
then(onFulfilled?, onRejected?) {
const { _value, _status } = this;
// 返回一个新的Promise对象
return new MyPromise((onFulfilledNext, onRejectedNext) => {
// 封装一个成功时执行的函数
const fulfilled = value => {
try {
if (!isFunction(onFulfilled)) {
onFulfilledNext(value);
} else {
const res = onFulfilled(value);
if (res instanceof MyPromise) {
// 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
res.then(onFulfilledNext, onRejectedNext);
} else {
//否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
onFulfilledNext(res);
}
}
} catch (err) {
// 如果函数执行出错,新的Promise对象的状态为失败
onRejectedNext(err);
}
};
// 封装一个失败时执行的函数
const rejected = error => {
try {
if (!isFunction(onRejected)) {
onRejectedNext(error);
} else {
const res = onRejected(error);
if (res instanceof MyPromise) {
// 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
res.then(onFulfilledNext, onRejectedNext);
} else {
//否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
onFulfilledNext(res);
}
}
} catch (err) {
// 如果函数执行出错,新的Promise对象的状态为失败
onRejectedNext(err);
}
};
switch (_status) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case PENDING:
this._fulfilledQueues.push(fulfilled);
this._rejectedQueues.push(rejected);
break;
// 当状态已经改变时,立即执行对应的回调函数
case FULFILLED:
fulfilled(_value);
break;
case REJECTED:
rejected(_value);
break;
}
});
}
// catch 方法
catch(onRejected) {
return this.then(undefined, onRejected);
}
// finally 方法
finally(cb) {
return this.then(
value => MyPromise.resolve(cb()).then(() => value),
reason =>
MyPromise.resolve(cb()).then(() => {
throw reason;
})
);
}
// 静态 resolve 方法
static resolve(value) {
// 如果参数是MyPromise实例,直接返回这个实例
if (value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value));
}
// 静态 reject 方法
static reject(value) {
return new MyPromise((resolve, reject) => reject(value));
}
// 静态 all 方法
static all(list) {
return new MyPromise((resolve, reject) => {
// 返回值的集合
let values = [];
let count = 0;
for (let [i, p] of list.entries()) {
// 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
this.resolve(p).then(
res => {
values[i] = res;
count++;
// 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
if (count === list.length) resolve(values);
},
err => {
// 有一个被rejected时返回的MyPromise状态就变成rejected
reject(err);
}
);
}
});
}
// 添加静态race方法
static race(list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
this.resolve(p).then(
res => {
resolve(res);
},
err => {
reject(err);
}
);
}
});
}
}
Стабилизатор/дросселирование
防抖函数
Запускается один раз в конце onscroll, отложенное выполнение
function debounce(func, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args);
}, wait);
};
}
// 使用
window.onscroll = debounce(function() {
console.log('debounce');
}, 1000);
节流函数
При прокрутке время от времени срабатывает, как капля воды.
function throttle(fn, delay) {
var prevTime = Date.now();
return function() {
var curTime = Date.now();
if (curTime - prevTime > delay) {
fn.apply(this, arguments);
prevTime = curTime;
}
};
}
// 使用
var throtteScroll = throttle(function() {
console.log('throtte');
}, 1000);
window.onscroll = throtteScroll;
Реализация каррирования функций
На самом деле, мы постоянно используем каррирующие функции, просто не суммируем их. Суть его в том, чтобы разложить функцию с множеством параметров на несколько функций с одним параметром.
Практическое применение:
- Отложенный расчет (используйте замыкание для сохранения входящих параметров и запускайте выполнение функции, когда количество входящих параметров достаточно для выполнения функции)
- Создать функцию динамически (если параметров недостаточно, она вернет функцию, которая принимает оставшиеся параметры)
- Повторное использование параметра (каждый параметр можно использовать несколько раз)
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
почерк глубокая копия
Неглубокая копия копирует только значение адреса, которое фактически указывает на данные в той же памяти кучи, в то время как глубокая копия воссоздает те же данные, а значение адреса кучи памяти, на которое они указывают, отличается. В настоящее время изменение данных переменной перед назначением не повлияет на переменную после назначения.
Это слишком сложно, чтобы реализовать совершенную копию Бога. Позвольте мне кратко представить это здесь. Это может быть применено к большинству сценариев.
Тип решения Функция
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) {
// 判断是否是dom元素,如div等
return 'element';
}
return map[str];
}
Простая версия глубокого копирования с тремя примерамиarray
object
function
, который может быть расширен сам по себе. В основном, чтобы заставить людей думать
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:
return ori;
}
}
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
}
Несколько последних мелочей
- Если вы хотите присоединиться к группе, чтобы изучить расширенный интерфейс, добавьте меня в WeChat.
luoxue2479
Ответьте, чтобы присоединиться к группе - Если есть ошибки, укажите на них в области сообщений, обсудите вместе или добавьте меня в WeChat.
- Обсуждения в группе будут каждый деньGitHub.com/falling snow-Вик Т…
- Мой официальный аккаунт [Front-end Technician], давайте учиться вместе.
Справочная статья
cloud.Tencent.com/developer/ ах…
woo woo Краткое описание.com/fear/no 4 отправить 0425 нет 2…