In JavaScript, vars are hoisted, but their initializations are not. functions and their definitions are also hoisted. Your code relies on hoisting, which can be problematic.
Here's a visualization of the hoisting in your original working code:
// 👇 HOISTED DECLARATIONS
var dataObj = undefined;
function dataFunction (){
return dataObj
}
var methodsObj = undefined;
function methodImpl (){
this.title = 'Infinite Jest'
}
// 👆 HOISTED DECLARATIONS
const app = Vue.createApp({
data: dataFunction,
methods: {method1: methodImpl} // ✅ methodImpl declared above
})
// INITIALIZATION
dataObj = {
title: 'Holiday',
author: 'Stanley Middleton',
age: 45
}
// INITIALIZATION
methodsObj = {
method1: methodImpl
}
app.mount('#app')
Now consider that same code, but replace methods: {method1: methodImpl} with methods: methodsObj:
// 👇 HOISTED DECLARATIONS
var dataObj = undefined;
function dataFunction (){
return dataObj
}
var methodsObj = undefined;
function methodImpl (){
this.title = 'Infinite Jest'
}
// 👆 HOISTED DECLARATIONS
const app = Vue.createApp({
data: dataFunction,
methods: methodsObj // ❌ methodsObj undefined above, so method1 would be undefined in the app
})
// INITIALIZATION
dataObj = {
title: 'Holiday',
author: 'Stanley Middleton',
age: 45
}
// INITIALIZATION
methodsObj = {
method1: methodImpl
}
app.mount('#app')
The issue with methodsObj in the example above can be fixed by declaring and initializing it before usage (which is generally a best practice):
// declare and initialize vars here...
var dataObj = {
title: 'Holiday',
author: 'Stanley Middleton',
age: 45
}
function dataFunction (){
return dataObj
}
var methodsObj = {
method1: methodImpl
}
function methodImpl (){
this.title = 'Infinite Jest'
}
// now, use the declarations from above
const app = Vue.createApp({
data: dataFunction,
methods: methodsObj
})
app.mount('#app')
And here's the visualization of the hoisting above:
// 👇 HOISTED DECLARATIONS
var dataObj = undefined;
function dataFunction (){
return dataObj
}
var methodsObj = undefined;
function methodImpl (){
this.title = 'Infinite Jest'
}
// 👆 HOISTED DECLARATIONS
// INITIALIZATION
dataObj = {
title: 'Holiday',
author: 'Stanley Middleton',
age: 45
}
// INITIALIZATION
methodsObj = {
method1: methodImpl
}
const app = Vue.createApp({
data: dataFunction,
methods: methodsObj // ✅ methodsObj declared and initialized above
})
app.mount('#app')
demo
One of the motivations of const and let (introduced in ES2015) was to avoid common bugs from var hoisting, such as you observed above. If you had used const/let instead of var in your original code, the compiler would've thrown an error, which might've signaled to you then that the declaration should be moved above the usage. Consider using a linter that discourages the use of var.
methods: methodsObj. Also it doesn't make sense to define dataObj outside dataFunction, the reason whydatais a function is that this allows to return a new object each time it's called and not share it between component instances