How do Javascript functions behave in relation to the scope?

Asked

Viewed 268 times

6

In Javascript, functions are objects and can be passed as parameters to other functions. But what about when a function with dependencies on an object, such as variables and other functions, is passed to another object ? It always runs from the first object ? What if I wanted the function to access properties of the second object ? The code below prints "first object".

$(document).ready(function(){
	objeto = new Obj1();
	objeto.passa();
});

function Obj1(){
	var texto = "Primeiro Objeto";
	var minhaFunc = function(){
		document.write(texto);
	}
	var objeto = new Obj2();

	this.passa = function(){
		objeto.setFunc(minhaFunc);
		objeto.exec();
	}
}

function Obj2(){
	var texto = "Segundo Objeto";
	var minhaOutraFunc;
	
	this.setFunc = function(func){
		minhaOutraFunc = func;
	}
	
	this.exec = function(){
		minhaOutraFunc.call();
	}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>

  • More specifically.. What would you want with this function? A method that prints something straight, like the first function?

  • I imagined that as the function is passed to the second object, it would print the "text" variable of the second object.

  • Um.. I get it.. There are some explanations for this. I will try to put them together in one answer :)

3 answers

9


Interesting question.

Consider the following example:

function a() {
    console.log('a', this);
}

function Obj() {
    var b = function() {
        console.log('b', this);
    }
    this.c = function() {
        console.log('c', this);
    }
    b();
    this.c();
}

function d() {
    function e() {
        console.log('e', this)
    }
    e();
}

a();
var o = new Obj();
o.c.call(window);
d();

This is gonna work:

a Window
b Window
c Obj {}
c Window
e window

(https://jsfiddle.net/m6faf374/)

Whenever you declare a function, the execution context depends on some factors. If the function is not property of an object it will run with global context (cases a, b, and). When a function is a property of an object then the context of the function is that object. Functions without defined context (ie that are not property of any object) run global context.

Scope and context are different things. The scope makes b is a private method of the object, ie not available outside of it. The context has to do with the this within that function and what it is in a given context/moment.

To change the context of a function some methods can be used:

  • .bind(novoContexto) that does not invoke the function, changes only the context
  • .call(novoContexto, arg1, arg2, etc) this method invokes the function
  • .apply(novoContexto, [array com argumentos]) also invokes the function

Returning to your example imagine that the scenario was this (https://jsfiddle.net/zL9mroqm/), i.e., all functions and text are linked to the execution context.

function Obj1() {
    this.texto = "Primeiro Objeto";
    this.minhaFunc = function() {
        console.log(this.texto);
    }
    var objeto = new Obj2();
    this.passa = function() {
        objeto.setFunc(this.minhaFunc);
        objeto.exec();
    }
}

function Obj2() {
    this.texto = "Segundo Objeto";
    this.setFunc = function(func) {
        this.minhaOutraFunc = func;
    }
    this.exec = function() {
        this.minhaOutraFunc();
    }
}

In this case prints Segundo Objeto. If you change this.minhaOutraFunc(); for this.minhaOutraFunc.call(); will give undefined because you are calling/calling the function without context. If you use this.minhaOutraFunc.call(objeto); will give Primeiro Objeto because it argues that the context is the instance of object 1 that you created within the .ready().

  • 1

    Just for the record, I love your answers. How can I become a Javascript master like you, @Sergio?

  • 3

    @Eduardoalmeida thanks! But I am not a master. I learned a lot here, at Soen and with the Mootools community where I participate actively. In addition, I’m looking at new articles on Youtube and Google that I’m reading. Sometimes the questions here force me to read and learn to answer better. I think the trick is to always be curious.

  • I understood, but only one question: Why does b not belong to the object ? I thought the difference between using "var" or "this" was that, in the first case, the variable is private and, in the second, the variable is public.

  • @Carlos this difference is between scope and context. The scope makes it b is a private method of the object, ie not available outside of it. The context has to do with the this and what it is in a given context.

4

This is purely a matter of scope and overwriting (overwriting), since, in Javascript, functions and objects can be easily manipulated as variables.

To access the method or property you only need to inform objeto.propriedade or objeto["propriedade"].

In your example you created the object objeto when creating a variable you receive Obj2(). To access the text of Obj2() in the object Obj1(), you would only need one objeto.texto.

Inside an object you have to put the keyword this when declaring fields: this.texto; and when referring to the field outside the object: objeto.texto

If you want to work with inheritance, you can take a look at the methods .call() and prototype.

Your code accessing the text of Obj2():

$(document).ready(function() {
  objeto = new Obj1();
  objeto.passa();
});

function Obj1() {
  var objeto = new Obj2();
  this.texto = "Primeiro Objeto";
  var minhaFunc = function() {
    document.write(objeto.texto);
  }
  this.passa = function() {
    objeto.setFunc(minhaFunc);
    objeto.exec();
  }
}

function Obj2() {
  this.texto = "Segundo Objeto";
  var minhaOutraFunc;
  this.setFunc = function(func) {
    minhaOutraFunc = func;
  }

  this.exec = function() {
    minhaOutraFunc.call();
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

3

Going by parts:

First, your variable is objeto present in the $(document).ready(... does not possess the var logo it declared in the global scope (reusable in all functions within the script), this variable will receive the new Obj1(), which is a function in the scope.

Within the Obj1() exists:

var minhaFunc = function(){
    document.write(texto);
}

Let’s keep her quiet, we’ll come back to her later.

At another time the function code Obj1(), there is a new variable declaration objeto, but this time with the use of var, that is, is restricted to that function, any call to it within that function will receive, instead of Obj1() the Obj2().

Right after we have:

this.passa = function(){
    objeto.setFunc(minhaFunc);
    objeto.exec();
}

The function passa(), flame to var objeto, which at present refers to the function Obj2(), function which has another "subfunction" to setFunc():

this.setFunc = function(func){
    minhaOutraFunc = func;
}

This function takes as a parameter/argument another Function which will be stored in the var minhaOutraFunc, knowing that it belongs to Obj2(). But in the Obj1() we passed to her the minhaFunc():

    objeto.setFunc(minhaFunc);

That is, the var minhaOutraFunc in the Obj2() now owns the minhaFunc(), previously said ("we will return to it later"). This will print on the screen "First Object".

But within the function passar(), there is also:

objeto.exec();

The exec() in the Obj2() is a function that executes the var minhaOutraFunc, recalls that this possesses the minhaFunc() that belongs to the Obj1() and who writes this text on the screen? Yes. Look:

this.exec = function(){
    minhaOutraFunc.call();
}

The big problem was because the var texto within each Obj, is declared within each function restrictive, due to the use of var.

So when performing a function within the scope of each function, the text variable will match the function in question.

Exactly for this reason, the text of the function is printed on the screen Obj1().

But what if I wanted, in this case, to write the text of Obj2()

Simply declare, the variable texto, in the overall scope, i.e., without the use of var. Look:

var objeto;
$(document).ready(function(){
	objeto = new Obj1();
	objeto.passa();
});

var texto;

function Obj1(){
	texto = "Primeiro Objeto";
	var minhaFunc = function(){
		document.write(texto);
	}
	var objeto = new Obj2();

	this.passa = function(){
		objeto.setFunc(minhaFunc);
		objeto.exec();
	}
}

function Obj2(){
	texto = "Segundo Objeto";
	var minhaOutraFunc;
	
	this.setFunc = function(func){
		minhaOutraFunc = func;
	}
	
	this.exec = function(){
		minhaOutraFunc.call();
	}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

This happens because inside the $(document).ready(... the last execution is that of the function passa() of Obj1(). And the last execution within that function, is the call of the writing on the screen, as what will be written is the variable texto and her last statement within the overall scope (since this time it was declared without the var) was made in the Obj2(), what is in that function will be printed.

And another thing, a variable however much your call/use is not with var, to be considered global, must be declared yes with the var, however in the scope where you want to reuse, in the global, or in a function. This also applies to the variable objeto at the beginning of your code.

And why it worked in the previous situation?

Javascript will try to give you as few errors as possible, especially when we talk about reusing variables. That’s why we don’t need to determine your type, why we can do things like this:

"5" * 8; // 40

Relevant information within all this text: when executing codes involving variables, their execution will take into account the last declaration of these variables.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.