Pull to refresh

Новый взгляд на JavaScript инъекции

Reading time2 min
Views1.9K
Недавно столкнулся со следующей задачей — существует JavaScript объект actions с кучей методов, в каждом из которых должны быть определены следующие переменные:

var persik    = this,     
      actions  = persik.actions,      
      next     = ( persik.dom.возможноЕщёЧтоТо.имяЭтогоМетода.next)? 
          persik.dom.возможноЕщёЧтоТо.имяЭтогоМетода.next : function(){return false}; 


Но определять их очень не хотелось, так как во-первых от этого заметно утяжелится код, а во-вторых объект actions создавался для будущих пользователей моего фреймворка Persik, и как предполагается, каждый сможет не только применять готовые методы actions, но и определять свои. Можно конечно написать в документации «ребята, пожалуйста, в начале каждого создаваемого вами метода вставьте вот эти строчки», либо не создавать ни каких переменных вообще и вместо persik писать this (хотя с this в некоторых случаях могут быть проблемы), вместо actions писать this.actions, а вместо вызова next() ляпнуть что-нибудь вроде:
if (this.dom.возможноЕщёЧтоТо.имяЭтогоМетода.next) this.dom.возможноЕщёЧтоТо.имяЭтогоМетода.next();
Но всё это ужасно не красиво и не правильно, и я решил пойти по другому пути. Идея проста — при инициализации фреймворка пройтись по всем методам объекта actions, с помощью toString() получить каждый метод как строку, нагло вставить туда нужные строчки определения переменных, сделать из строки снова функцию с помощью eval() и заменить существующий метод модифицированным. Не буду приводить исходный код поиска методов внутри actions в моём фреймворке дабы не утомлять публику, а вот модификация методов выглядит примерно следующим образом:

_rewriteFunc : function(func, funcName, variablesStr){     
      var funcArr   = func.toString().split('{');     
      variablesStr  = variablesStr.replace(/\[funcName\]/g, funcName);     
      funcArr[1]    = variablesStr  + funcArr[1];               
      var resultFunc = funcArr.join('{');     
      eval ('var a = ' + resultFunc);         
      return a; 
},


Здесь func — это собственно модифицируемый метод, в funcName передаётся имя этого метода, причём учитывая всех родителей, т.е. то самое this.dom.возможноЕщёЧтоТо.имяЭтогоМетода, а в variablesStr содержится строка с переменными, которые мы определяем. В моём случае это 'var persik = this; var actions = persik.actions; var next = ([funcName].next)? [funcName].next: function(){return false}; '. Описанная выше функция заменяет в variablesStr все '[funcName]' на funcName, вставляет variablesStr в resultFunc после первого символа '{', собирает функцию из строки снова и отдаёт обратно. Объект actions, над которым производятся все вышеперечисленные извращения, можно посмотреть здесь: nikitaeremin.com/js/action.js

Данная техника имеет ряд минусов:
— переменные persik, actions и next создаются во всех методах объекта actions, хотя в некоторых методах они могут остаться не востребованными;
— меняется исходный код JavaScript и при возникновении ошибок периода исполнения FireBug будет ругаться совсем не на те строки, которые в действительности вызвали ошибку, хотя при этом причину ошибки говорит правильно;
— остальные минусы мне сейчас хабраобщественность скажет :)

Не думаю, что описанный выше трюк применим к широкому кругу задач, эта статья скорее для общего развития, но если кто-нибудь захочет поделиться идеями по поводу других областей применения подобных инъекций, пишите на мыло: nikitaeremin[at]gmail.com
Tags:
Hubs:
Total votes 12: ↑5 and ↓7-2
Comments8

Articles