Okay, some more stuff.
First, what’s the idea behind the two-dimensional array? I guess if one dimension is waves and the other is their enemies, that’s fine. But if it’s some sort of 80 x 80 monster, you’re hosed! 
A quick note. This
for j=1, #gameData.enemysArr[1] do if (gameData.enemysArr[i][j] ~= nil) then
is not safe, unless the very first “wave” has no nil members and is some maximum length or something. (That sounds kind of odd, so I’m guessing it’s not the case.) The length operator, #, will not behave well in the presence of nil elements. If you need gaps, false is your friend here.
(Also, you don’t need to declare locals i and j. The loops do it for you.)
Do you actually need the existence checks inside hasCollidedCircle ()? Can your enemy exist but not its container? It seems like you could do the check for the bullet’s Mc once at the beginning and be done with it. For that matter, I assume you could just return after destroying your bullet. Or can it take out multiple enemies?
Actually, you might be able to get some improvement if destroy () doesn’t necessarily remove the display object, but just turns it off, letting it be awoken later for a new enemy. But this might require some work if the enemy types vary a lot.
Also, with stuff like gameData.enemysArr[i][j], you’re doing a bunch of accesses in a tight loop, so you’ll gain a little bit just by stashing the intermediate parts into local variables.
Now, the bigger gains would come from restructuring your data. If you refer to my earlier long post, I’m going to assume those sorts of conditions. So, say, you would have your game screen, and it could be broken up a bit:

(Lovingly crafted in 45 seconds.)
As we all know, something in the upper right is not all that close to stuff in the lower left, and likewise for things on the top versus the bottom, etc. In general, something in one of those “boxes” probably doesn’t care much about a “box” much further away.
Now, we have our bullet zinging around:

And it will overlap one or more boxes. Furthermore, it can reason out which cells it’s covering. Assuming x = 0 on the left and y = 0 at the top, for example, you could visit the bullet’s neighborhood:
local floor = math.floor local max = math.max local min = math.min local Cells = {} for row = 1, CellRows do local line = {} for col = 1, CellColumns do line[col] = {} end Cells[row] = line end -- stuff local CellWidth = display.contentWidth / CellColumns local CellHeight = display.contentHeight / CellRows function VisitCells (object, radius, action) local top = max(1, floor((object.y - radius) / CellHeight) + 1) local bottom = min(CellRows, floor((object.y + radius) / CellHeight) + 1) local left = max(1, floor((object.x - radius) / CellWidth) + 1) local right = min(CellColumns, floor((object.x + radius) / CellWidth) + 1) for row = top, bottom do local line = Cells[row] for col = left, right do local cell = line[col] action(cell, object) end end end
Then, for instance, you could indicate that the bullet is in all those cells:
local function AddBulletToCell (cell, bullet) cell[bullet] = true end -- stuff VisitCells(bullet, bullet.radius, AddBulletToCell)
Or remove it from them (when it’s destroyed, before moving, etc.):
local function RemoveBulletFromCell (cell, bullet) cell[bullet] = nil end -- stuff VisitCells(bullet, bullet.radius, RemoveBulletFromCell )
We also have enemies prowling around! Probably bigger than bullets:

But you could add and remove them from cells the same way.
These cells are generally going to be pretty sparse. The bullets / enemies are all over the place, so there probably will only be a few in a given cell. So a given bullet could actually just enumerate enemies it might touch:
local pairs = pairs -- stuff local function GatherEnemies (cell, bullet) for thing in pairs(cell) do if IsEnemy(thing) then bullet.n = bullet.n + 1 bullet.enemy\_list[bullet.n] = thing end end end function BulletCollide (bullet) bullet.n = 0 -- reset the enemy list VisitCells(bullet, bullet.radius, GatherEnemies) local list = bullet.enemy\_list for i = 1, bullet.n do if bullet:hasCollidedCircle(bullet, list[i]) then bullet:destroy() list[i]:destroy() return end end end
Likewise for the player.
Now, an enemy might sprawl across several cells, so we could encounter it several times during the gather process. We could set a flag the first time we encounter it, and ignore it after that. But then we’d have to clean up all of our flags. In the above code this might be okay (we could clear it during the loop), but it complicates the early return on a collision. Another method is to just keep a “current” ID and see if it’s been assigned that; if so, ignore it. Then the above becomes:
local pairs = pairs -- stuff local function GatherEnemies (cell, bullet) local id = bullet.id for thing in pairs(cell) do if IsEnemy(thing) and thing.id ~= id then bullet.n = bullet.n + 1 bullet.enemy\_list[bullet.n] = thing thing.id = id end end end function BulletCollide (bullet) bullet.id = bullet.id + 1 -- initialize to 0 or whatever on bullet creation bullet.n = 0 -- reset the enemy list VisitCells(bullet, bullet.radius, GatherEnemies) local list = bullet.enemy\_list for i = 1, bullet.n do if bullet:hasCollidedCircle(bullet, list[i]) then bullet:destroy() -- do normal stuff, plus remove from cell, etc. list[i]:destroy() -- ditto return end end end
Anyhow, this is all pseudo-code, but the ideas might be worth a shot. The main idea is that it breaks a big problem down into smaller, more manageable ones. I could elaborate if you have questions.
EDITS : A couple code typos and wonky text bits.