Подробно о методах apply(), call() и bind(), необходимых каждому javascript разработчику

BX.localStorage


Andrey Telegin 13.06.201822:55

Работа с локальным хранилищемДля работы с локальным хранилищем используется расширение ls (core_ls.js)localStorage работает во всех современных браузерах. IE поддерживает его начиная с версии 8. Для IE7 существует методика эмуляции поведения, аналогичного localStorage. Все эти методы объединены в единый интерфейс BX.localStorage.Примечание:, подключение расширения дает возможность использовать дополнительные параметры lsId, lsTimeout и lsForce в BX.ajax.script

BX.localStorage.set("sortKey", "NAME", 111);
  
BX('localz').innerHTML = BX.localStorage.get("sortKey");

php

require($_SERVER."/bitrix/header.php");
$APPLICATION->SetTitle("BX.localStorage");
CJSCore::Init(array('ls')); 


………………………………………..

require($_SERVER."/bitrix/footer.php");

ПримерДокументация для разработчиков

Решение 2: привязать контекст с помощью bind

В современном JavaScript у функций есть встроенный метод bind, который позволяет зафиксировать .

Базовый синтаксис :

Результатом вызова является особый «экзотический объект» (термин взят из спецификации), который вызывается как функция и прозрачно передаёт вызов в , при этом устанавливая .

Другими словами, вызов подобен вызову с фиксированным .

Например, здесь передаёт вызов в , фиксируя :

Здесь – это «связанный вариант» , с фиксированным .

Все аргументы передаются исходному методу как есть, например:

Теперь давайте попробуем с методом объекта:

В строке мы берём метод и привязываем его к . Теперь – это «связанная» функция, которая может быть вызвана отдельно или передана в (контекст всегда будет правильным).

Здесь мы можем увидеть, что исправляет только , а аргументы передаются как есть:

Удобный метод:

Если у объекта много методов и мы планируем их активно передавать, то можно привязать контекст для них всех в цикле:

Некоторые JS-библиотеки предоставляют встроенные функции для удобной массовой привязки контекста, например в lodash.

Borrowing a method

Now let’s make one more minor improvement in the hashing function:

As of now, it works only on two arguments. It would be better if it could glue any number of .

The natural solution would be to use arr.join method:

…Unfortunately, that won’t work. Because we are calling , and object is both iterable and array-like, but not a real array.

So calling on it would fail, as we can see below:

Still, there’s an easy way to use array join:

The trick is called method borrowing.

We take (borrow) a join method from a regular array () and use to run it in the context of .

Why does it work?

That’s because the internal algorithm of the native method is very simple.

Taken from the specification almost “as-is”:

  1. Let be the first argument or, if no arguments, then a comma .
  2. Let be an empty string.
  3. Append to .
  4. Append and .
  5. Append and .
  6. …Do so until items are glued.
  7. Return .

So, technically it takes and joins , …etc together. It’s intentionally written in a way that allows any array-like (not a coincidence, many methods follow this practice). That’s why it also works with .

Примеры

Пример: Обработать клик и двойной клик по параграфу.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>bind demo</title>
  <style>
  p {
    background: yellow;
    font-weight: bold;
    cursor: pointer;
    padding: 5px;
  }
  p.over {
     background: #ccc;
  }
  span {
    color: red;
  }
  </style>
  <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>

<p>Click or double click here.</p>
<span></span>

<script>
$( "p" ).bind( "click", function( event ) {
  var str = "( " + event.pageX + ", " + event.pageY + " )";
  $( "span" ).text( "Click happened! " + str );
});
$( "p" ).bind( "dblclick", function() {
  $( "span" ).text( "Double-click happened in " + this.nodeName );
});
$( "p" ).bind( "mouseenter mouseleave", function( event ) {
  $( this ).toggleClass( "over" );
});
</script>

</body>
</html>

Демо:

Пример: вывести сообщение при клике по любому из параграфов:

$( "p" ).bind( "click", function() {
 alert( $( this ).text() );
});

Пример: Передаём данные в обработчик:

function handler( event ) {
 alert( event.data.foo );
}
$( "p" ).bind( "click", {
 foo: "bar"
}, handler );

Пример: Отмена стандартного действия:

$( "form" ).bind( "submit", function() {
 return false;
});

Пример: Отмена стандартного действия с помощью метода .preventDefault().

$( "form" ).bind( "submit", function( event ) {
 event.preventDefault();
});

Пример: Отмена стандартного действия с помощью метода .stopPropagation().

$( "form" ).bind( "submit", function( event ) {
 event.stopPropagation();
});

Пример: использование собственного события.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>bind demo</title>
  <style>
  p {
    color: red;
  }
  span {
    color: blue;
  }
  	</style>
  <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>

<p>Has an attached custom event.</p>
<button>Trigger custom event</button>
<span style="display: none;"></span>

<script>
$( "p" ).bind( "myCustomEvent", function( e, myName, myValue ) {
  $( this ).text( myName + ", hi there!" );
  $( "span" )
    .stop()
    .css( "opacity", 1 )
    .text( "myName = " + myName )
    .fadeIn( 30 )
    .fadeOut( 1000 );
  });
$( "button" ).click(function() {
  $( "p" ).trigger( "myCustomEvent",  );
});
</script>

</body>
</html>

Демо:

Пример: Обработка нескольких событий.

$( "div.test" ).bind({
 click: function() {
   $( this ).addClass( "active" );
 },
 mouseenter: function() {
   $( this ).addClass( "inside" );
 },
 mouseleave: function() {
   $( this ).removeClass( "inside" );
 }
});

JS Tutorial

JS HOMEJS IntroductionJS Where ToJS OutputJS StatementsJS SyntaxJS CommentsJS VariablesJS OperatorsJS ArithmeticJS AssignmentJS Data TypesJS FunctionsJS ObjectsJS EventsJS StringsJS String MethodsJS NumbersJS Number MethodsJS ArraysJS Array MethodsJS Array SortJS Array IterationJS DatesJS Date FormatsJS Date Get MethodsJS Date Set MethodsJS MathJS RandomJS BooleansJS ComparisonsJS ConditionsJS SwitchJS Loop ForJS Loop WhileJS BreakJS Type ConversionJS BitwiseJS RegExpJS ErrorsJS ScopeJS HoistingJS Strict ModeJS this KeywordJS LetJS ConstJS Arrow FunctionJS DebuggingJS Style GuideJS Best PracticesJS MistakesJS PerformanceJS Reserved WordsJS VersionsJS Version ES5JS Version ES6JS JSON

Потеря «this»

Мы уже видели примеры потери . Как только метод передаётся отдельно от объекта – теряется.

Вот как это может произойти в случае с :

При запуске этого кода мы видим, что вызов возвращает не «Вася», а !

Это произошло потому, что получил функцию отдельно от объекта (именно здесь функция и потеряла контекст). То есть последняя строка может быть переписана как:


Метод в браузере имеет особенность: он устанавливает для вызова функции (в Node.js становится объектом таймера, но здесь это не имеет значения). Таким образом, для он пытается получить , которого не существует. В других подобных случаях обычно просто становится .

Задача довольно типичная – мы хотим передать метод объекта куда-то ещё (в этом конкретном случае – в планировщик), где он будет вызван. Как бы сделать так, чтобы он вызывался в правильном контексте?

Публичная оконная библиотека PopUp

Andrey Telegin 11.06.201820:53

Используется расширение popup (core_popup.js). Данный вид окон используется для работы публичный интерфейсов. Для административных интерфейсов (в т.ч. и для административных интерфейсов в публичной части) следует использовать расширение window.Html code

<div id="hideBlock" style="display:none; ">
 <h1>Заголовок popur</h1>
 <div class="popup-bg">
  <div class="popup-info"  id="popup-info" >
   Текст popur
  </div>
 </div>
</div>
<div class="css_popup">  Открыть окошко с привязкой к классу </div>
 <br>
 
<div class="css_bind" id="mouse">
  Открыть окошко с привязкой к id<br>
 <br>
</div>

script

   window.BXDEBUG = true;
BX.ready(function(){
   var oPopup = new BX.PopupWindow('vspl_feedback', window.body, {
      autoHide : true,
      offsetTop : 1,
      offsetLeft : 0,
      lightShadow : true,
      closeIcon : true,
      closeByEsc : true,
      overlay: {
         backgroundColor: 'red', opacity: '80'
      },
    buttons: [
      new BX.PopupWindowButton({
          text: "Старт",
          className: "popup-window-button-accept",
          events: {click: function(){
           location.href="/";
          }}
      }),
      new BX.PopupWindowButton({
          text: "Закрыть окно",
          className: "webform-button-link-cancel",
          events: {click: function(){
           this.popupWindow.close(); // закрытие окна
          }}
      })
     ]
   
   });
   oPopup.setContent(BX('hideBlock'));
   BX.bindDelegate(
      document.body, 'click', {className: 'css_popup' },
         BX.proxy(function(e){
            if(!e)
               e = window.event;
            oPopup.show();
               return BX.PreventDefault(e);
         }, oPopup)
   );
    BX.bind(BX('mouse'), 'click', function() 
 {
  oPopup.show();
   
 });  

});

css

.css_popup, .css_bind{
 cursor:pointer;
 color:#000;
 font-size: 21px;
}
.popup-window{
    max-width:80%;
 border-radius:5px;
}  
        
}.popup-bg{
    width:100%;
    min-height:100%;
    background-color: #aaa;
    overflow:hidden;
        
}
.popup-bg .popup-info{
 margin:12px;
   /* margin:20px auto 20px auto;
    min-width:100px;*/
    min-height: 40px;
    padding:12px;
    background-color: #c5c5c5;
    border-radius:5px;
    box-shadow: 0px 0px 10px #333;
}

php

require($_SERVER."/bitrix/header.php");
$APPLICATION->SetTitle(" PopUp");
   CJSCore::Init(array("popup"));
  ...............

require($_SERVER."/bitrix/footer.php");

Пример popur

Выставляем значение this с помощью Apply или Call

Как и в примере с , мы также можем указывать значение для this, когда вызываем функцию используя методы и . Первый параметр в и выставляет this значение объекту на котором вызвана функция.

Вот очень быстрый и показательный пример для начинающих, перед тем, как мы окунёмся в использование Apply и Call:

//  Глобальная переменная для демонстрации        var avgScore = "global avgScore";// Функция        function avg (arrayOfScores) {//  Складываем все показатели            var sumOfScores = arrayOfScores.reduce (function (prev, cur, index, array) {                return prev + cur;            });//  В этом случае this будет привязан к глобальному объекту, пока мы не выставим его с call() или apply()            this.avgScore = sumOfScores / arrayOfScores.length;        }        var gameController = {            scores: ,            avgScore:null        }// Если мы выполним функцию avg, то this внутри функции будет привязана к глобальному объекту:        avg (gameController.scores);// Вот, что получаем:        console.log (window.avgScore); // 46.4        console.log (gameController.avgScore); // null//  Сбрасываем avgScore        avgScore = "global avgScore";// Чтобы указать, что значение this привязано к gameController,        // Мы вызываем call() метод:        avg.call (gameController, gameController.scores);        console.log (window.avgScore); //global avgScore        console.log (gameController.avgScore); // 46.4

А сейчас очень внимательно! Обратите внимание, что первый аргумент в выставляет значение. В предыдущем примере, оно выставляется на объект

А другие аргументы, после первого, передаются как параметры функции .

Методы и практически идентичны при работе с выставлением значения , за исключением того, что вы передаёте параметры функции в как массив, в то время, как в , параметры передаются в индивидуальном порядке. Но дальше ещё интереснее. У есть ещё одна фича, которой нет у и очень скоро вы о ней узнаете.

Используем call() и apply(), чтобы выставлять this в Callback функциях

//  Создаём объект со свойствами и методами//  Далее мы передадим метод, как колбэк другой функции    var clientData = {      id: 094545,      fullName: "Not Set",//  Метод на объекте clientData    setUserName: function (firstName, lastName)  {//  тут мы выставляем fullName свойство в данном объекте    this.fullName = firstName + " " + lastName;    }    }

Сама функция, тут очень внимательно:

function getUserInput (firstName, lastName, callback, callbackObj) {//  Использование метода apply ниже, выставит this для callbackObj            callback.apply (callbackObj, );        }

Метод выставляет значение для . Так мы можем выполнить колбэк с указанным значением , так что параметры переданные колбэку, будут выставлены на объекте :

// Объект clientData будет использоваться методом Apply, чтобы выставить значение this.    getUserInput ("Barack", "Obama", clientData.setUserName, clientData);// Получаем в консоль    console.log (clientData.fullName); // Barack Obama

Методы , и всегда используются для того, чтобы выставлять значение , при вызове метода, но каждый слегка по-своему и это отображается на том, как мы можем контролировать и поддерживать наш код

Значение в JavaScript также важно, как и любая другая часть языка. Помните, что у нас есть 3 вышеупомянутых метода, которые просто необходимы для эффективной и правильной работы с этим самым

Всегда старайтесь перепроверять то, что предполагается на месте .

Заимствование функций с помощью Apply и Call (Важно знать)

Вообще и в JavaScript используют в основном, чтобы заимствовать функции. С этими методами, мы можем выполнять заимствования также, как и с методом , но при этом быть более гибкими. Давайте рассмотрим следующие примеры:

Переходим к нескольким аргументам с «func.apply»

Теперь давайте сделаем ещё более универсальным. До сих пор он работал только с функциями с одним аргументом.

Как же кешировать метод с несколькими аргументами ?

Здесь у нас есть две задачи для решения.

Во-первых, как использовать оба аргумента и для ключа в коллекции ? Ранее для одного аргумента мы могли просто сохранить результат и вызвать , чтобы получить его позже. Но теперь нам нужно запомнить результат для комбинации аргументов . Встроенный принимает только одно значение как ключ.

Есть много возможных решений:

  1. Реализовать новую (или использовать стороннюю) структуру данных для коллекции, которая более универсальна, чем встроенный , и поддерживает множественные ключи.
  2. Использовать вложенные коллекции: будет , которая хранит пару . Тогда получить мы сможем, вызвав .
  3. Соединить два значения в одно. В нашем конкретном случае мы можем просто использовать строку как ключ к . Для гибкости, мы можем позволить передавать хеширующую функцию в декоратор, которая знает, как сделать одно значение из многих.

Для многих практических применений третий вариант достаточно хорош, поэтому мы будем придерживаться его.

Также нам понадобится заменить на , чтобы передавать все аргументы обёрнутой функции, а не только первый.

Вот более мощный :

Теперь он работает с любым количеством аргументов.

Есть два изменения:

  • В строке вызываем для создания одного ключа из . Здесь мы используем простую функцию «объединения», которая превращает аргументы в ключ . В более сложных случаях могут потребоваться другие функции хеширования.
  • Затем в строке используем для передачи как контекста, так и всех аргументов, полученных обёрткой (независимо от их количества), в исходную функцию.

Вместо мы могли бы написать .

Синтаксис встроенного метода func.apply:

Он выполняет , устанавливая и принимая в качестве списка аргументов псевдомассив .

Единственная разница в синтаксисе между и состоит в том, что ожидает список аргументов, в то время как принимает псевдомассив.

Эти два вызова почти эквивалентны:

Есть только одна небольшая разница:

  • Оператор расширения позволяет передавать перебираемый объект в виде списка в .
  • А принимает только псевдомассив .

Так что эти вызовы дополняют друг друга. Для перебираемых объектов сработает , а где мы ожидаем псевдомассив – .

А если у нас объект, который и то, и другое, например, реальный массив, то технически мы могли бы использовать любой метод, но , вероятно, будет быстрее, потому что большинство движков JavaScript внутренне оптимизируют его лучше.

Передача всех аргументов вместе с контекстом другой функции называется «перенаправлением вызова» (call forwarding).

Простейший вид такого перенаправления:

При вызове из внешнего кода его не отличить от вызова исходной функции.

API

There are two modes in the API:

  • : use a model and a mapper to generate the mapping
  • : use a mapping directly

Providing a model allows for a more principled approach, the direct mapping is the quick and dirty version. When it is important (isn’t always?) to write modular and readable code, you should prefer the first option.

If the element has the attribute, this instructs the library to keep a copy of itself and to reset itself to the intial state each time then element is bound. It is a very useful option when using the library on GUIs.

Bind a DOM element to a , via a function. When the mapper function is applied to the model, it yields a mapping — i.e. . The function should a return an object where keys are extended CSS selectors and the values are derived from the .

  • : DOM element to be bound (effectively the view)
  • : plain JavaScript object or array (the model)
  • : function bridging the model and view (mapping between CSS selectors and values)

The element now modified with the model value via the mapping.

<h1>Hello <span>NAME</span>!</h1>
bind(document.body,{ name"John Smith"},function(model){return{"h1 span"model.name};});

Bind a DOM element to values expressed in a object. The mapping is a plain object where keys are extended CSS selectors that map to the values to be used.

  • : DOM element to be bound (effectively the view)
  • : map between CSS selectors and values

The element now modified with the values in the mapping.

<h1>Hello <span>NAME</span>!</h1>
bind(document.body,{"h1 span""John Smith"});

JS Учебник

JS ГлавнаяJS ВведениеJS УстановкаJS ВыводJS СинтаксисJS ЗаявленияJS КомментарииJS ПеременныеJS ОператорыJS АрифметикаJS ПрисваиванияJS Типы данныхJS ФункцииJS ОбъектыJS ОбластьJS СобытияJS СтрокиJS Методы строкJS ЧислаJS Методы чиселJS МассивыJS Методы массиваJS Сортировка массиваJS Итерация массиваJS ДатыJS Формат датыJS Метод получения датJS Методы набора…JS Математические…JS Случайные числаJS БулевыJS Сравнение…JS Заявления if…elseJS Заявление switchJS Цикл forJS Цикл whileJS Заявление break…JS Преобразование…JS Битовые…JS Регулярные выраженияJS ОшибкиJS ОтладчикJS ПодъемныйJS СтрогийJS Ключевое слово thisJS Руководство стиляJS ПрактикаJS Распространенные ошибкиJS ЭффективностьJS Зарезервированные словаJS ВерсииJS Версия ES5JS Версия ES6JS JSON

Extended Rationale

Since I am presenting a new JavaScript Template Engine in a crowded field, let me give a rationale, which hopefully will convince you (in conjuntion with the examples) of the merits of the current approach. The current templating libraries usually fall in one of three main camps:

In my opinion the first two are fairly convoluted solutions. The first one (ERB style) fills HTML with extraneous tags and it does not make the model explicit. These seem to be solutions transplanted from the server days. The syntax becomes specially nasty once you have to do iteration or conditionals.

As for the special attributes, they do make the model explicit, but they force you to learn a new language which is full of special cases to account for the many possibilities (see rationale for example).

Finally the third option is my favorite, but it is also the least popular as far as I can tell. The options out there also have special cases for iteration and they are not as elegant as they could be if they took the model to heart. The elegance in this approach is that iteration, conditionals, formatting, they are all driven by the data or the mapping function.

The way that Bind attempts to fulfill its goals:

  • Elegance: in the eye of the beholder, but hopefully it flows naturally from HTML/Javascript/CSS
  • KISS: library is less than 50 lines of code, concepts are well known (plain objects, maps, CSS selectors, XSLT)
  • Pure HTML Markup: there are no extraneous attributes polluting the templates, no need to learn new attributes
  • CSS Selectors: the bridging of the model and view is done via a powerful and expressive language
  • Speed: uses native browser implementations like and DOM manipulation with caching

As the library progresses the merits of a virtual DOM (like React’s) can be explored. There are things lost like native CSS matching with this approach.

this in JS:


The value of in JS is 100% determined by how a function is called, and not how it is defined. We can relatively easy find the value of by the ‘left of the dot rule’:

  1. When function are created using the function keyword the value of is the object left of the dot of the function which is called
  2. If there is no object left of the dot then the value of inside a function is often the global object ( in node, in browser). I wouldn’t recommend using the keyword here because it is less explicit than using something like !
  3. There exists certain constructs like arrow functions and functions created using the function which can fix the value of . These are exceptions of the rule but are really helpful to fix the value of .

What is Bind

A very small and lean binding/rendering library. Separates presentation from data very cleanly and very fast. Implements two way binding optionally without kludges. Allows for a hooking up a statically web app to render dynamic content in a principled and economic way.

The main ideas are:

  1. Pure HTML (no attributes) constitutes a (potential) template
  2. Data (JSON/Javascript) data is bound through CSS selectors (i.e. put in element)
  3. Observe data and if it changes re-render bound template
  4. Observe forms and it they change, update (simply mapped) object attributes
  5. Spice CSS selectors with some syntactic sugar to make them more palatable

Using “func.call” for the context

The caching decorator mentioned above is not suited to work with object methods.

For instance, in the code below stops working after the decoration:

The error occurs in the line that tries to access and fails. Can you see why?

The reason is that the wrapper calls the original function as in the line . And, when called like that, the function gets .

We would observe a similar symptom if we tried to run:

So, the wrapper passes the call to the original method, but without the context . Hence the error.

Let’s fix it.

There’s a special built-in function method func.call(context, …args) that allows to call a function explicitly setting .

The syntax is:

It runs providing the first argument as , and the next as the arguments.

To put it simply, these two calls do almost the same:

They both call with arguments , and . The only difference is that also sets to .

As an example, in the code below we call in the context of different objects: runs providing , and the next line sets :

And here we use to call with the given context and phrase:

In our case, we can use in the wrapper to pass the context to the original function:

Now everything is fine.

To make it all clear, let’s see more deeply how is passed along:

  1. After the decoration is now the wrapper .
  2. So when is executed, the wrapper gets as an argument and (it’s the object before dot).
  3. Inside the wrapper, assuming the result is not yet cached, passes the current () and the current argument () to the original method.

Hello World Example

HTML:

<h1>Hello <span>NAME</span>!</h1><p>My favorite fruits are:</p><ul><li>FRUIT</li></ul><p>My favorite color is: <iid="color"style="STYLE">COLOR</i></p><p>Today's date is <bid="date">DATE</b></p>

Javascript:

bind(document.body,{"h1 span""John Smith","li""Orange","Pear","Apple","#color"{"""green","@style""color: green"},"b"newDate()});

Result:

<h1>Hello <span>John Smith</span>!</h1><p>My favorite fruits are:</p><ul><li>Orange</li><li>Pear</li><li>Apple</li></ul><p>My favorite color is: <iid="color"style="color: green;">green</i></p><p>Today's date is <bid="date">Wed Jan 14 2015 22:40:57 GMT-0600 (CST)</b></p>

Mappings can be nested (see selector), so if the value of a CSS selector is another mapping, the context node becomes the one(s) matched by the selector.

The ALL CAPS text in the HTML is where the CSS selectors should match: it makes the template more readable.

Прозрачное кеширование

Представим, что у нас есть функция , выполняющая ресурсоёмкие вычисления, но возвращающая стабильные результаты. Другими словами, для одного и того же она всегда возвращает один и тот же результат.

Если функция вызывается часто, то, вероятно, мы захотим кешировать (запоминать) возвращаемые ею результаты, чтобы сэкономить время на повторных вычислениях.

Вместо того, чтобы усложнять дополнительной функциональностью, мы заключим её в функцию-обёртку – «wrapper» (от англ. «wrap» – обёртывать), которая добавит кеширование. Далее мы увидим, что в таком подходе масса преимуществ.

Вот код с объяснениями:

В коде выше – это декоратор, специальная функция, которая принимает другую функцию и изменяет её поведение.

Идея состоит в том, что мы можем вызвать с любой функцией, в результате чего мы получим кеширующую обёртку. Это здорово, т.к. у нас может быть множество функций, использующих такую функциональность, и всё, что нам нужно сделать – это применить к ним .

Отделяя кеширующий код от основного кода, мы также сохраняем чистоту и простоту последнего.

Результат вызова является «обёрткой», т.е. «оборачивает» вызов в кеширующую логику:

С точки зрения внешнего кода, обёрнутая функция по-прежнему делает то же самое. Обёртка всего лишь добавляет к её поведению аспект кеширования.

Подводя итог, можно выделить несколько преимуществ использования отдельной вместо изменения кода самой :

  • Функцию можно использовать повторно. Мы можем применить её к другой функции.
  • Логика кеширования является отдельной, она не увеличивает сложность самой (если таковая была).
  • При необходимости мы можем объединить несколько декораторов (речь об этом пойдёт позже).

Binding Expressions

The text we put inside mustache tags are called binding expressions. In Vue.js, a binding expression consists of a single JavaScript expression optionally followed by one or more filters.

JavaScript Expressions

So far we’ve only been binding to simple property keys in our templates. But Vue.js actually supports the full power of JavaScript expressions inside data bindings:

{{ number + 1 }}{{ ok ? 'YES' : 'NO' }}{{ message.split('').reverse().join('') }}

These expressions will be evaluated in the data scope of the owner Vue instance. One restriction is that each binding can only contain one single expression, so the following will NOT work:

{{ var a = 1 }}{{ if (ok) { return message } }}

Filters

Vue.js allows you to append optional “filters” to the end of an expression, denoted by the “pipe” symbol:

{{ message | capitalize }}

Here we are “piping” the value of the expression through the built-in filter, which is in fact just a JavaScript function that returns the capitalized value. Vue.js provides a number of built-in filters, and we will talk about how to write your own filters later.

Note that the pipe syntax is not part of JavaScript syntax, therefore you cannot mix filters inside expressions; you can only append them at the end of an expression.


Filters can be chained:

{{ message | filterA | filterB }}

Filters can also take arguments:

{{ message | filterA 'arg1' arg2 }}

The filter function always receives the expression’s value as the first argument. Quoted arguments are interpreted as plain string, while un-quoted ones will be evaluated as expressions. Here, the plain string will be passed into the filter as the second argument, and the value of expression will be evaluated and passed in as the third argument.

func.apply

Instead of we could use .

The syntax of built-in method func.apply is:

It runs the setting and using an array-like object as the list of arguments.

The only syntax difference between and is that expects a list of arguments, while takes an array-like object with them.

So these two calls are almost equivalent:

There’s only a subtle difference:

  • The spread syntax allows to pass iterable as the list to .
  • The accepts only array-like .

So, where we expect an iterable, works, and where we expect an array-like, works.

And for objects that are both iterable and array-like, like a real array, we can use any of them, but will probably be faster, because most JavaScript engines internally optimize it better.

Passing all arguments along with the context to another function is called call forwarding.

That’s the simplest form of it:

When an external code calls such , it is indistinguishable from the call of the original function .

Interpolations

Text

The most basic form of data binding is text interpolation using the “Mustache” syntax (double curly braces):

<span>Message: {{ msg }}</span>

The mustache tag will be replaced with the value of the property on the corresponding data object. It will also be updated whenever the data object’s property changes.

You can also perform one-time interpolations that do not update on data change:

<span>This will never change: {{* msg }}</span>

Raw HTML

The double mustaches interprets the data as plain text, not HTML. In order to output real HTML, you will need to use triple mustaches:

<div>{{{ raw_html }}}</div>

The contents are inserted as plain HTML — data bindings are ignored. If you need to reuse template pieces, you should use .

Dynamically rendering arbitrary HTML on your website can be very dangerous because it can easily lead to XSS attacks. Only use HTML interpolation on trusted content and never on user-provided content.

Attributes

Mustaches can also be used inside HTML attributes:

<div id="item-{{ id }}"></div>

Note that attribute interpolations are disallowed in Vue.js directives and special attributes. Don’t worry, Vue.js will raise warnings for you when mustaches are used in wrong places.

Применение «func.call» для передачи контекста.

Упомянутый выше кеширующий декоратор не подходит для работы с методами объектов.

Например, в приведённом ниже коде перестаёт работать после применения декоратора:

Ошибка возникает в строке . Функция пытается получить доступ к и завершается с ошибкой. Видите почему?

Причина в том, что в строке декоратор вызывает оригинальную функцию как , и она в данном случае получает .

Мы бы наблюдали похожую ситуацию, если бы попытались запустить:

Т.е. декоратор передаёт вызов оригинальному методу, но без контекста. Следовательно – ошибка.

Давайте это исправим.

Существует специальный встроенный метод функции func.call(context, …args), который позволяет вызывать функцию, явно устанавливая .

Синтаксис:

Он запускает функцию , используя первый аргумент как её контекст , а последующие – как её аргументы.

Проще говоря, эти два вызова делают почти то же самое:

Они оба вызывают с аргументами , и . Единственное отличие состоит в том, что ещё и устанавливает равным .

Например, в приведённом ниже коде мы вызываем в контексте различных объектов: запускает , передавая , а следующая строка устанавливает :

Здесь мы используем для вызова с заданным контекстом и фразой:

В нашем случае мы можем использовать в обёртке для передачи контекста в исходную функцию:

Теперь всё в порядке.

Чтобы всё было понятно, давайте посмотрим глубже, как передаётся :

  1. После декорации становится обёрткой .
  2. Так что при выполнении обёртка получает в качестве аргумента и (так как это объект перед точкой).
  3. Внутри обёртки, если результат ещё не кеширован, передаёт текущий () и текущий аргумент () в оригинальную функцию.

С этим читают