javascript example - requestAnimationFrame с этим ключевым словом




anime js (6)

Я использую webkitRequestAnimationFrame но у меня возникают проблемы с его использованием внутри объекта. Если я передам this ключевое слово, оно будет использовать window и я не смогу найти способ использовать указанный объект.

Пример:

Display.prototype.draw = function(){
  this.cxt.clearRect(0, 0, this.canvas.width, this.canvas.height);
  //Animation stuff here.

  window.webkitRequestAnimationFrame(this.draw);
};

Я также пробовал это, но безрезультатно:

Display.prototype.draw = function(){
  this.cxt.clearRect(0, 0, this.canvas.width, this.canvas.height);
  //Animation stuff here.

  var draw = this.draw;
  window.webkitRequestAnimationFrame(draw);
};

Answers

Теперь, когда ES6 / 2015 здесь, если вы используете транспилятор, тогда функция стрелки имеет лексическую привязку, поэтому вместо:

window.webkitRequestAnimationFrame(this.draw.bind(this));

ты можешь сделать:

window.webkitRequestAnimationFrame(() => this.draw());

который немного чище.

Я использовал это эффективно с перепиской Typcript на ES5.


А также вы можете использовать прошивку requestAnimationFrame, чтобы она работала во всех браузерах https://github.com/kof/animation-frame


Я не могу гарантировать, что это хорошая идея, и что я прав, но работает .bind на каждом requestAnimationFrame означает создание новой функции на каждой итерации. Мне это просто не подходит.

Вот почему в моем проекте я кэшировал связанную функцию, чтобы избежать анти-шаблона.

Простой пример:

var Game = function () {
    this.counter = 0;
    this.loop = function () {
        console.log(this.counter++); 
        requestAnimationFrame(this.loop);
    }.bind(this);
    this.loop();
}
var gameOne = new Game();

Если у вас более сложный проект с наследованием прототипов, вы все равно можете создать кэшированную функцию с «этой» привязкой в ​​конструкторе объекта

var Game = function () {
    this.counter = 0;
    this.loopBound = this.loop.bind(this);
    this.loopBound();
}
Game.prototype.loop = function () {
    console.log(this.counter++); 
    requestAnimationFrame(this.loopBound);
}
var gameOne = new Game();

Мысли? http://jsfiddle.net/3t9pboe8/ (смотрите в консоли)


Я пытаюсь передать display.draw, который является функцией, в которой находится webkitRequestAnimationFram.

webkitRequestAnimationFrame предположительно вызовет функцию, в которую вы проходите, что-то вроде этого:

function webkitRequestAnimationFrame(callback)
{
    // stuff...
    callback();
    // other stuff...
}

На этом этапе вы отделили (отделили) функцию draw от контекста вызова. Вам необходимо привязать функцию ( draw ) к ее контексту (экземпляр Display ).

Вы можете использовать Function.bind , но для этого требуется поддержка JavaScript 1.8 (или просто используйте рекомендуемый патч).

Display.prototype.draw = function()
{
    // snip...

    window.webkitRequestAnimationFrame(this.draw.bind(this));
};

вы не должны использовать «это». Будь проще.

var game = {
      canvas:null,
      context:null,

    init:function(){
            // init canvas, context, etc
    },      

    update:function(){
        //do something
        game.render();                        
        requestAnimationFrame(game.update, game.canvas);        
    },            
};

Это ключевое слово ведет себя по-разному в JavaScript по сравнению с другим языком. В объектно-ориентированных языках this ключевое слово относится к текущему экземпляру класса. В JavaScript значение this определяется главным образом контекстом вызова функции ( context.function() ) и где он вызывается.

1. При использовании в глобальном контексте

Когда вы используете this в глобальном контексте, он привязан к глобальному объекту ( window в браузере)

document.write(this);  //[object Window]

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

function f1()
{
   return this;
}
document.write(f1());  //[object Window]

Выше f1 делается методом глобального объекта. Таким образом, мы можем также назвать его на объекте window следующим образом:

function f()
{
    return this;
}

document.write(window.f()); //[object Window]

2. При использовании метода внутри объекта

Когда вы используете this ключевое слово внутри метода объекта, this привязывается к «непосредственному» охватывающему объекту.

var obj = {
    name: "obj",
    f: function () {
        return this + ":" + this.name;
    }
};
document.write(obj.f());  //[object Object]:obj

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

var obj = {
    name: "obj1",
    nestedobj: {
        name:"nestedobj",
        f: function () {
            return this + ":" + this.name;
        }
    }            
}

document.write(obj.nestedobj.f()); //[object Object]:nestedobj

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

var obj1 = {
    name: "obj1",
}

function returnName() {
    return this + ":" + this.name;
}

obj1.f = returnName; //add method to object
document.write(obj1.f()); //[object Object]:obj1

3. При вызове функции контекста

Когда вы используете this внутреннюю функцию, которая вызывается без какого-либо контекста (т. Е. Не на каком-либо объекте), она привязана к глобальному объекту ( window в браузере) (даже если функция определена внутри объекта).

var context = "global";

var obj = {  
    context: "object",
    method: function () {                  
        function f() {
            var context = "function";
            return this + ":" +this.context; 
        };
        return f(); //invoked without context
    }
};

document.write(obj.method()); //[object Window]:global 

Попытка все это с помощью функций

Мы также можем попробовать выше точки с функциями. Однако есть некоторые отличия.

  • Выше мы добавили элементы в объекты, используя объектную литературу. Мы можем добавить членов к функциям, используя this . чтобы указать их.
  • Обозначение литерала объекта создает экземпляр объекта, который мы можем использовать немедленно. С помощью функции нам может понадобиться сначала создать экземпляр, используя new оператор.
  • Также в объектном литеральном подходе мы можем явно добавлять членов к уже определенному объекту с помощью оператора точки. Это добавляется только к конкретному экземпляру. Однако я добавил переменную в прототип функции, чтобы она отражалась во всех экземплярах функции.

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

/********************************************************************* 
  1. When you add variable to the function using this keyword, it 
     gets added to the function prototype, thus allowing all function 
     instances to have their own copy of the variables added.
*********************************************************************/
function functionDef()
{
    this.name = "ObjDefinition";
    this.getName = function(){                
        return this+":"+this.name;
    }
}        

obj1 = new functionDef();
document.write(obj1.getName() + "<br />"); //[object Object]:ObjDefinition   

/********************************************************************* 
   2. Members explicitly added to the function protorype also behave 
      as above: all function instances have their own copy of the 
      variable added.
*********************************************************************/
functionDef.prototype.version = 1;
functionDef.prototype.getVersion = function(){
    return "v"+this.version; //see how this.version refers to the
                             //version variable added through 
                             //prototype
}
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   3. Illustrating that the function variables added by both above 
      ways have their own copies across function instances
*********************************************************************/
functionDef.prototype.incrementVersion = function(){
    this.version = this.version + 1;
}
var obj2 = new functionDef();
document.write(obj2.getVersion() + "<br />"); //v1

obj2.incrementVersion();      //incrementing version in obj2
                              //does not affect obj1 version

document.write(obj2.getVersion() + "<br />"); //v2
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   4. `this` keyword refers to the immediate parent object. If you 
       nest the object through function prototype, then `this` inside 
       object refers to the nested object not the function instance
*********************************************************************/
functionDef.prototype.nestedObj = { name: 'nestedObj', 
                                    getName1 : function(){
                                        return this+":"+this.name;
                                    }                            
                                  };

document.write(obj2.nestedObj.getName1() + "<br />"); //[object Object]:nestedObj

/********************************************************************* 
   5. If the method is on an object's prototype chain, `this` refers 
      to the object the method was called on, as if the method was on 
      the object.
*********************************************************************/
var ProtoObj = { fun: function () { return this.a } };
var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj
                                    //as its prototype
obj3.a = 999;                       //adding instance member to obj3
document.write(obj3.fun()+"<br />");//999
                                    //calling obj3.fun() makes 
                                    //ProtoObj.fun() to access obj3.a as 
                                    //if fun() is defined on obj3

4. При использовании функции внутреннего конструктора .

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

var myname = "global context";
function SimpleFun()
{
    this.myname = "simple function";
}

var obj1 = new SimpleFun(); //adds myname to obj1
//1. `new` causes `this` inside the SimpleFun() to point to the
//   object being constructed thus adding any member
//   created inside SimipleFun() using this.membername to the
//   object being constructed
//2. And by default `new` makes function to return newly 
//   constructed object if no explicit return value is specified

document.write(obj1.myname); //simple function

5. При использовании внутренней функции, определенной в цепочке прототипов

Если метод находится в цепочке прототипов объекта, this внутри такого метода относится к объекту, на который был вызван метод, как если бы метод был определен на объекте.

var ProtoObj = {
    fun: function () {
        return this.a;
    }
};
//Object.create() creates object with ProtoObj as its
//prototype and assigns it to obj3, thus making fun() 
//to be the method on its prototype chain

var obj3 = Object.create(ProtoObj);
obj3.a = 999;
document.write(obj3.fun()); //999

//Notice that fun() is defined on obj3's prototype but 
//`this.a` inside fun() retrieves obj3.a   

6. Функции внутреннего вызова (), apply () и bind ()

  • Все эти методы определены в Function.prototype .
  • Эти методы позволяют написать функцию один раз и вызвать ее в другом контексте. Другими словами, они позволяют указать значение this которое будет использоваться во время выполнения функции. Они также принимают любые параметры, которые должны быть переданы исходной функции при ее вызове.
  • fun.apply(obj1 [, argsArray]) Устанавливает obj1 как значение this внутри fun() и вызывает fun() передавая элементы argsArray качестве своих аргументов.
  • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - Устанавливает obj1 как значение this внутри fun() и вызывает fun() проходящий через arg1, arg2, arg3, ... как его аргументы.
  • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - возвращает ссылку на функцию fun с this внутри fun, связанным с obj1 и параметрами fun связанными с указанными параметрами arg1, arg2, arg3,...
  • К настоящему времени разница между apply , call и bind должна стать очевидной. apply позволяет указывать аргументы для функции как объект типа массива, т. е. объект с свойством числовой length и соответствующие неотрицательные целочисленные свойства. В то время как call позволяет напрямую указывать аргументы функции. Оба apply и call немедленно вызывает функцию в указанном контексте и с указанными аргументами. С другой стороны, bind просто возвращает функцию, связанную с указанным значением и аргументами. Мы можем зафиксировать ссылку на эту возвращаемую функцию, присвоив ее переменной, и позже мы можем ее вызвать в любое время.
function add(inc1, inc2)
{
    return this.a + inc1 + inc2;
}

var o = { a : 4 };
document.write(add.call(o, 5, 6)+"<br />"); //15
      //above add.call(o,5,6) sets `this` inside
      //add() to `o` and calls add() resulting:
      // this.a + inc1 + inc2 = 
      // `o.a` i.e. 4 + 5 + 6 = 15
document.write(add.apply(o, [5, 6]) + "<br />"); //15
      // `o.a` i.e. 4 + 5 + 6 = 15

var g = add.bind(o, 5, 6);       //g: `o.a` i.e. 4 + 5 + 6
document.write(g()+"<br />");    //15

var h = add.bind(o, 5);          //h: `o.a` i.e. 4 + 5 + ?
document.write(h(6) + "<br />"); //15
      // 4 + 5 + 6 = 15
document.write(h() + "<br />");  //NaN
      //no parameter is passed to h()
      //thus inc2 inside add() is `undefined`
      //4 + 5 + undefined = NaN</code>

7. this внутренние обработчики событий

  • Когда вы назначаете функцию непосредственно обработчикам событий элемента, использование this непосредственно внутри функции обработки событий относится к соответствующему элементу. Такое прямое назначение функций может быть выполнено с помощью метода addeventListener или с помощью традиционных методов регистрации событий, таких как onclick .
  • Аналогично, когда вы используете this непосредственно внутри свойства события (например, <button onclick="...this..." > ) элемента, он ссылается на элемент.
  • Однако использование this косвенно через другую функцию, вызванную внутри функции обработки событий или свойства события, разрешается в глобальное window объекта.
  • Такое же поведение выше, когда мы присоединяем функцию к обработчику событий с использованием метода регистрации событий Microsoft attachEvent . Вместо того, чтобы назначать функцию обработчику события (и тем самым создавать функциональный метод элемента), он вызывает функцию в событии (фактически вызывая его в глобальном контексте).

Я рекомендую лучше попробовать это в JSFiddle .

<script> 
    function clickedMe() {
       alert(this + " : " + this.tagName + " : " + this.id);
    } 
    document.getElementById("button1").addEventListener("click", clickedMe, false);
    document.getElementById("button2").onclick = clickedMe;
    document.getElementById("button5").attachEvent('onclick', clickedMe);   
</script>

<h3>Using `this` "directly" inside event handler or event property</h3>
<button id="button1">click() "assigned" using addEventListner() </button><br />
<button id="button2">click() "assigned" using click() </button><br />
<button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used `this` directly in click event property</button>

<h3>Using `this` "indirectly" inside event handler or event property</h3>
<button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">`this` used indirectly, inside function <br /> defined & called inside event property</button><br />

<button id="button4" onclick="clickedMe()">`this` used indirectly, inside function <br /> called inside event property</button> <br />

IE only: <button id="button5">click() "attached" using attachEvent() </button>




javascript object animation this