2015-09-06 5 views
0

Я читал кучу веб-страниц, которые рассказывают о вопросах интервью с JavaScript, и я заметил, что многие из них говорят, что важно знать разницу между .call() и .apply().Почему знание разницы между .call() и .apply() так важно?

Например:

http://www.sitepoint.com/5-typical-javascript-interview-exercises/

https://github.com/h5bp/Front-end-Developer-Interview-Questions

Для жизни меня, я не могу понять, почему это считается столь важным. Кто-нибудь знает? Для меня это, вероятно, так же важно, как знать разницу между, скажем, длинной датой js и короткой датой js (то есть для меня это не очень важно). Мой инстинкт кишки говорит мне, что кто-то влиятельный в какой-то момент сказал, что важно знать разницу между .call() и .apply(), и все просто скопировали то, что сказал этот человек. Но, может быть, я ничего не понимаю о .call() и .apply()? Я не уверен.

+2

Что вы толпите. Они разные. Различные способы обработки параметров. http://stackoverflow.com/questions/1986896/what-is-the-difference-between-call-and-apply – Daniel

ответ

5

Проще говоря, более сложные типы Javascript будет иногда нужно использовать либо .call() или .apply(), в частности, код, который пытается прокси-функции и передавать аргументы или контролировать значение this.

Если вы делаете такие вещи, вам нужно знать, как работают .call() и .apply(), поэтому вы можете использовать правильный метод (так как они не работают одинаково). Если вы не делаете такие типы продвинутых вещей, тогда много Javascript может быть написано без их использования.

Как вопрос интервью, я думаю, что это разумный тест на то, понимаете ли вы некоторые из более продвинутых нюансов передачи аргументов, методы вызова и обработку this в Javascript. Если вы хотите преуспеть в этом вопросе, вам нужно будет понять как .call(), так и .apply(), теперь, как объяснить, что они делают и как и когда их использовать, и когда вы будете использовать один против другого.

Вся концепция this и то, как она контролируется или установлена ​​в Javascript, часто не понимается хорошо опытными программистами Javascript (черт возьми, я даже видел опытных разработчиков, которые не понимали этого хорошо), поэтому опросив кандидата по вещам, связанным с этим, является разумным тестом, и .call() и .apply() являются для него несколько центральными.

Если вы разрабатывали библиотечный или рамочный код, вы, скорее всего, будете использовать .call() или .apply() в своей работе.


Вот основной резюме:

Каждая функция в JavaScript, является объектом, который имеет некоторые свойства. Таким образом, помимо возможности вызвать функцию, например, func(), вы также можете ссылаться на свойства на ней, как func.length.

Два свойства, которые имеют каждая функция: .call() и .apply(). Оба они позволяют вам вызвать функцию несколькими способами, чем просто называть ее func().

.call()

Мы все знаем, конечно, что если вы просто хотите вызвать функцию с фиксированным числом аргументов, вы можете просто выполнить func(arg1, arg2), и эти аргументы будут переданы функции, но что, если вы хотите контролировать значение this, когда вы передадите эти фиксированные аргументы? Вызов func как func(arg1, arg2) вызовет значение this внутри этой функции, которое должно быть установлено либо глобальному объекту, либо window в браузере, либо если он работает в строгом режиме до undefined. Каждый вызов функции в Javascript, такой как func(arg1, arg2), сбрасывает указатель this таким образом. Это где .call() приходит Вы можете выполнить:.

func.call(someThisValue, arg1, arg2) 

И он будет делать то же самое, за исключением того, func(arg1, arg2) это приведет this указатель должен быть установлен на someThisValue.

Примечание: вы также можете использовать .call() только с одним аргументом, и он просто установит указатель this и не будет передавать никаких аргументов.

func.call(someThisValue) 

.Не()

Но что, если ваш список аргументов не фиксирован и ваши аргументы в переменном массиве длиной или массив типа объекта. Вы не можете использовать .call(), потому что вы не можете ввести правильный оператор .call() для каждого возможного списка аргументов. Здесь вводится .apply(). Каноническое использование для .apply() - это когда вы просто пытаетесь передать аргументы из другого вызова функции. Это обычное явление, когда вы проксируете другие вызовы функций, чтобы немного изменить их поведение, но все же вызывать оригинал, а оригинал имеет различные формы (так что вы точно не знаете переданные аргументы) или множество различных типов функций все вызовы проходят через этот прокси. В этом случае вы можете использовать .apply(), часто с объектом arguments. Вы можете увидеть пример этого в MDN polyfill for .bind().

Предположим, что я хочу связать какую-то существующую функцию с именем doIt() для ведения журнала, а doIt() имеет несколько различных способов ее вызова. Используя .apply(), я мог бы сделать это следующим образом:

// regular function already defined in your program 
function doIt(arg1, arg2) { 
    // do something 
} 

// then actual usage elsewhere in the program would be just this: 
doIt("foo", "bar"); 

// now install a proxy that can intercept all calls to doIt() and 
// add some behavior before and after 
(function(origDoIt) { 
    // replace doIt function with my own proxy 
    doIt = function() { 
     console.log("before doIt()"); 
     // call the original function with all the arguments and this pointer 
     // that were passed 
     var retVal = origDoIt.apply(this, arguments); 
     console.log("after doIt()"); 
     return retVal; 
    } 
})(doIt); 

FYI, .apply() может также использоваться, чтобы просто установить this указатель, как в:

func.apply(someThisValue) 

В этом конкретном случае (и только в этом случае), он работает одинаково с .call().


Это одно из моих любимых приложений .apply(). Метод Math.max() принимает переменное количество аргументов и возвращает наибольшее количество из этих аргументов. Таким образом:

Math.max(1,2,3,4) 

вернет 4.

Но, используя .apply(), мы можем найти максимальное число в любом массиве.

var list = [999,888,777,666,555,444,333,1000]; 
var m = Math.max.apply(Math, list); 
console.log(m); // 1000 

Мы используем .apply(), чтобы отправить весь list массив в качестве аргументов Math.max() поэтому он будет работать на весь массив.

Обратите внимание, когда ES6 полностью реализована повсеместно или в вашем конкретном исполнении envirionment, вы можете также использовать новый оператор распространения, чтобы сделать что-то подобное:

var list = [999,888,777,666,555,444,333,1000]; 
var m = Math.max(...list); 
console.log(m); // 1000 

, который становится своего рода стенография для того, что мы делаем с .apply()

+0

Я думаю, что я получил разницу в 'call' и' apply'. Но мне все еще трудно понять те сразу вызываемые функции. Откуда возникает переменная 'arguments'? Это из области вокруг этой анонимной функции? – wullxz

+0

@wullxz - 'arguments' присутствует внутри каждой функции scope. Это массивный объект, содержащий все аргументы, переданные функции, и содержит свойство '.length', которое сообщает вам, сколько их было. 'arguments [0]' - первый аргумент, переданный функции, 'arguments [1]', второй и т. д. Дополнительную информацию см. В [здесь, в MDN] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments). В этом случае мне не пришлось использовать IIFE, но он сохранил мою глобальную переменную, чтобы сохранить ссылку на функцию «origDoIt». – jfriend00

+0

@wullxz - «аргументы», которые я использую в этом коде, не от внешней функции IIFE, это от того, когда вызывается функция замены doIt(). – jfriend00