Обновление: Спасибо за вашу поддержку. Недавно я бросил сводку данных для всех, чтобы прочитать систему. В будущем будет больше контента и больше оптимизации.Нажмите здесь, чтобы просмотреть
------ Далее идет текст ------
В предыдущей статье было подробно описано использование этого.Если вы не понимаете этого, проверьте [Дополнительно 3-1].
вызов() и применение()
Метод call() вызывает функцию с указанным
thisзначение и отдельно предоставляемые параметры (список параметров).
call()а такжеapply()Разница в том, чтоcall()метод принимаетсписок из нескольких параметров,а такжеapply()метод принимаетмассив с несколькими параметрами
Например:
var func = function(arg1, arg2) {
...
};
func.call(this, arg1, arg2); // 使用 call,参数列表
func.apply(this, [arg1, arg2]) // 使用 apply,参数数组
сцены, которые будут использоваться
Вот некоторые распространенные варианты использования:
1. Объединить два массива
var vegetables = ['parsnip', 'potato'];
var moreVegs = ['celery', 'beetroot'];
// 将第二个数组融合进第一个数组
// 相当于 vegetables.push('celery', 'beetroot');
Array.prototype.push.apply(vegetables, moreVegs);
// 4
vegetables;
// ['parsnip', 'potato', 'celery', 'beetroot']
Когда второй массив (как в примереmoreVegs) слишком велик, не используйте этот метод для объединения массивов, так какСуществует ограничение на количество аргументов, которые может принимать функция.из.不同的引擎有不同的限制,JS核心限制在 65535,有些引擎会抛出异常,有些不抛出异常但丢失多余参数。
Как это решить? Путь этоРазделите массив параметров на части и выполните цикл в целевом методе.
function concatOfArray(arr1, arr2) {
var QUANTUM = 32768;
for (var i = 0, len = arr2.length; i < len; i += QUANTUM) {
Array.prototype.push.apply(
arr1,
arr2.slice(i, Math.min(i + QUANTUM, len) )
);
}
return arr1;
}
// 验证代码
var arr1 = [-3, -2, -1];
var arr2 = [];
for(var i = 0; i < 1000000; i++) {
arr2.push(i);
}
Array.prototype.push.apply(arr1, arr2);
// Uncaught RangeError: Maximum call stack size exceeded
concatOfArray(arr1, arr2);
// (1000003) [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]
2. Получить максимальное и минимальное значения в массиве
var numbers = [5, 458 , 120 , -215 ];
Math.max.apply(Math, numbers); //458
Math.max.call(Math, 5, 458 , 120 , -215); //458
// ES6
Math.max.call(Math, ...numbers); // 458
Зачем использовать это, потому что массивыnumbersне сам по себеmax метод, ноMath Да, так вот использование call / applyиспользоватьMath.maxметод.
3. Проверьте, является ли это массивом
function isArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]';
}
isArray([1, 2, 3]);
// true
// 直接使用 toString()
[1, 2, 3].toString(); // "1,2,3"
"123".toString(); // "123"
123.toString(); // SyntaxError: Invalid or unexpected token
Number(123).toString(); // "123"
Object(123).toString(); // "123"
в состоянии пройтиtoString()чтобы получить тип каждого объекта, но разные объектыtoString()Существуют разные реализации, поэтомуObject.prototype.toString()для обнаружения необходимоcall() / apply()form для вызова, передавая объект для проверки в качестве первого аргумента.
еще одинПроверьте, является ли это массивомМетоды
var toStr = Function.prototype.call.bind(Object.prototype.toString);
function isArray(obj){
return toStr(obj) === '[object Array]';
}
isArray([1, 2, 3]);
// true
// 使用改造后的 toStr
toStr([1, 2, 3]); // "[object Array]"
toStr("123"); // "[object String]"
toStr(123); // "[object Number]"
toStr(Object(123)); // "[object Number]"
Вышеупомянутый метод используется в первую очередьFunction.prototype.callфункция определяетthisзначение, то.bindВозвращает новую функцию, которая всегдаObject.prototype.toString设置为传入参数。 на самом деле эквивалентноObject.prototype.toString.call().
вот одинпомещениедаtoString()методне распространяется
Object.prototype.toString = function() {
return '';
}
isArray([1, 2, 3]);
// false
4. Массивоподобные объекты используют методы массива
var domNodes = document.getElementsByTagName("*");
domNodes.unshift("h1");
// TypeError: domNodes.unshift is not a function
var domNodeArrays = Array.prototype.slice.call(domNodes);
domNodeArrays.unshift("h1"); // 505 不同环境下数据不同
// (505) ["h1", html.gr__hujiang_com, head, meta, ...]
Объект-массив имеет следующие два свойства
- 1. Имеет: нижний индекс числового индекса, указывающий на элемент объекта и
lengthАтрибуты - 2. Не имеет: таких как
push,shift,forEachтак же какindexOfтакие методы, как объекты массива, имеют
Чтобы уточнить, подобный массиву объектобъект. В JS есть методмассивныйструктура объекта, напримерargumentsобъекты и те, которые возвращаются DOM APINodeListОбъекты относятся к массивам, как объекты, и массивные объекты не могут быть использованыpush/pop/shift/unshiftи другие методы массива, черезArray.prototype.slice.callПреобразованный в реальный массив, вы можете использоватьArrayвсе способы ниже.
Массивоподобный объект в массивдругие методы:
// 上面代码等同于
var arr = [].slice.call(arguments);
ES6:
let arr = Array.from(arguments);
let arr = [...arguments];
Array.from()Два типа объектов могут быть превращены в настоящие массивы:массивныйобъект иПроходной(итерируемые) объекты (включая новые структуры данных ES6 Set и Map).
PS расширение один: зачем проходитьArray.prototype.slice.call()Можете ли вы преобразовать массивоподобный объект в массив?
на самом деле это очень легко,sliceБудуArray-likeОбъект помещается в новыйArrayв.
Следующий код MDN оsliceПолифилл, ссылкаArray.prototype.slice()
Array.prototype.slice = function(begin, end) {
end = (typeof end !== 'undefined') ? end : this.length;
// For array like object we handle it ourselves.
var i, cloned = [],
size, len = this.length;
// Handle negative value for "begin"
var start = begin || 0;
start = (start >= 0) ? start : Math.max(0, len + start);
// Handle negative value for "end"
var upTo = (typeof end == 'number') ? Math.min(end, len) : len;
if (end < 0) {
upTo = len + end;
}
// Actual expected size of the slice
size = upTo - start;
if (size > 0) {
cloned = new Array(size);
if (this.charAt) {
for (i = 0; i < size; i++) {
cloned[i] = this.charAt(start + i);
}
} else {
for (i = 0; i < size; i++) {
cloned[i] = this[start + i];
}
}
}
return cloned;
};
}
PS расширение два:пройти черезArray.prototype.slice.call()Это достаточно? В чем проблема?
существуетНе поддерживается в более ранних версиях IEпройти черезArray.prototype.slice.call(args)Преобразуйте массивоподобный объект в массив, потому что более ранняя версия IE (IE DOMобъектcomРеализован в виде объекта, js объекта иcomОбъект не может быть преобразован.
Совместимое письмо выглядит следующим образом:
function toArray(nodes){
try {
// works in every browser except IE
return Array.prototype.slice.call(nodes);
} catch(err) {
// Fails in IE < 9
var arr = [],
length = nodes.length;
for(var i = 0; i < length; i++){
// arr.push(nodes[i]); // 两种都可以
arr[i] = nodes[i];
}
return arr;
}
}
Расширение PSТретий: зачем нужны массивоподобные объекты? Или почему объект, подобный массиву, кажется, решает проблему?
Типизированные массивы JavaScript похожи на массивы.объекти предоставляет механизм доступа к необработанным двоичным данным.
ArrayСохраненные объекты могут динамически увеличиваться и уменьшаться и могут хранить любое значение JavaScript. Движок JavaScript выполняет некоторые внутренние оптимизации, чтобы операции с массивами могли быть быстрыми. Однако по мере того, как веб-приложения становятся все более и более мощными, особенно с новыми дополнениями, такими как редактирование аудио и видео, доступ к необработанным данным из веб-сокетов и т. д., становится ясно, что иногда, если вы используете код JavaScript, вы можете быстро и легко использовать типизированные массивы. ● Манипуляции с необработанными двоичными данными, которые могут быть очень полезными.
Одним словом, сложные данные можно обрабатывать быстрее.
5. Вызов родительского конструктора для реализации наследования
function SuperType(){
this.color=["red", "green", "blue"];
}
function SubType(){
// 核心代码,继承自SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.color.push("black");
console.log(instance1.color);
// ["red", "green", "blue", "black"]
var instance2 = new SubType();
console.log(instance2.color);
// ["red", "green", "blue"]
В дочернем конструкторе вызов родительского конструктораcallметод реализации наследования, поэтомуSubTypeКаждый экземпляр волиSuperTypeСделайте копию свойств в .
недостаток:
- Может наследовать только от родительского классапримерСвойства и методы, не могут наследовать свойства/методы прототипа
- Невозможно добиться повторного использования, каждый подкласс имеет копию функции экземпляра родительского класса, что влияет на производительность.
Чтобы узнать больше о схемах наследования, ознакомьтесь с моей предыдущей статьей.Восемь распространенных схем наследования в JavaScript
Макетная реализация вызова
Следующая ссылка отУглубленный вызов JavaScript и реализация имитационного моделирования
Давайте рассмотрим следующий простой пример
var value = 1;
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar.call(foo); // 1
Из приведенного выше введения мы знаем, чтоcall()Есть два основных момента
- 1,
call()Изменил суть этого - 2. Функция
barказнен
Первый шаг в симуляции
если звонишьcall()при вводе функцииbar()добавить вfoo()объект, то есть следующим образом
var foo = {
value: 1,
bar: function() {
console.log(this.value);
}
};
foo.bar(); // 1
Это изменение может быть достигнуто: измените указатель this и выполните функциюbar.
Но написание этого имеет побочный эффектfooДобавлен дополнительный атрибут, как решить?
Решение простое, используйтеdeleteПросто удалите его.
Итак, пока реализованы следующие 3 шага, моделирование может быть реализовано.
- 1. Установить функцию как свойство объекта:
foo.fn = bar - 2. Функция исполнения:
foo.fn() - 3. Функция удаления:
delete foo.fn
Код реализован следующим образом:
// 第一版
Function.prototype.call2 = function(context) {
// 首先要获取调用call的函数,用this可以获取
context.fn = this; // foo.fn = bar
context.fn(); // foo.fn()
delete context.fn; // delete foo.fn
}
// 测试一下
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar.call2(foo); // 1
Идеально!
Имитационная реализация второго шага
В первой версии была проблема, и это была функцияbarне может получать параметры, поэтому мы можем начать сargumentsПолучите параметры из , выньте предпоследние параметры и поместите их в массив, почему вы должны отбросить первый параметр, потому что первый параметрthis.
Метод преобразования массивоподобных объектов в массивы был представлен выше, но здесь мы используем решение ES3.
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
Массив параметров готов, осталось выполнить функциюcontext.fn().
context.fn( args.join(',') ); // 这样不行
Вышеупомянутый прямой вызов, конечно, не работает,args.join(',')вернет строку и не будет выполняться.
принято здесьevalметод достижения, записанный в функцию.
eval('context.fn(' + args +')')
в приведенном выше кодеargsбудет вызываться автоматическиargs.toString()метод, потому что'context.fn(' + args +')'По сути конкатенация строк, она будет вызываться автоматическиtoString()Способ, следующий код:
var args = ["a1", "b2", "c3"];
console.log(args);
// ["a1", "b2", "c3"]
console.log(args.toString());
// a1,b2,c3
console.log("" + args);
// a1,b2,c3
Итак реализована вторая версия, код такой:
// 第二版
Function.prototype.call2 = function(context) {
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
eval('context.fn(' + args +')');
delete context.fn;
}
// 测试一下
var foo = {
value: 1
};
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call2(foo, 'kevin', 18);
// kevin
// 18
// 1
Идеально! !
Имитационная реализация третьего шага
Следует отметить еще 2 детали:
- 1. Этот параметр можно передать
nullилиundefined, в это время это указывает на окно - 2. Параметр this может передавать данные базового типа, а собственный вызов будет автоматически преобразован с помощью Object().
- 3. Функции могут возвращать значения
Реализовать вышеперечисленные три пункта очень просто, код выглядит следующим образом
// 第三版
Function.prototype.call2 = function (context) {
context = context ? Object(context) : window; // 实现细节 1 和 2
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args +')');
delete context.fn
return result; // 实现细节 2
}
// 测试一下
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
function foo() {
console.log(this);
}
bar.call2(null); // 2
foo.call2(123); // Number {123, fn: ƒ}
bar.call2(obj, 'kevin', 18);
// 1
// {
// value: 1,
// name: 'kevin',
// age: 18
// }
Идеально! ! !
Сводка вызовов и применений имитационного моделирования
Макетная реализация вызова
ES3:
Function.prototype.call = function (context) {
context = context ? Object(context) : window;
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args +')');
delete context.fn
return result;
}
ЭС6:
Function.prototype.call = function (context) {
context = context ? Object(context) : window;
context.fn = this;
let args = [...arguments].slice(1);
let result = context.fn(...args);
delete context.fn
return result;
}
Макетная реализация применения
ES3:
Function.prototype.apply = function (context, arr) {
context = context ? Object(context) : window;
context.fn = this;
var result;
// 判断是否存在第二个参数
if (!arr) {
result = context.fn();
} else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')');
}
delete context.fn
return result;
}
ЭС6:
Function.prototype.apply = function (context, arr) {
context = context ? Object(context) : window;
context.fn = this;
let result;
if (!arr) {
result = context.fn();
} else {
result = context.fn(...arr);
}
delete context.fn
return result;
}
мыслительные вопросы
callа такжеapplyЕсть ли проблема с фиктивной реализацией? Комментарии приветствуются.
PS: Мыслительные вопросы предыдущего выпуска зарезервированы для объяснения в следующем выпуске, а следующий выпуск будет посвящен введению.bindПринцип и реализация
Ссылаться на
Углубленный вызов JavaScript и реализация имитационного моделирования
Function.prototype.apply() MDN
Array.prototype.slice() из MDN
Восемь распространенных схем наследования в JavaScript
Волшебный JavaScript в простых условиях применить, звонить, связывать