Pass array.push as an argument in function

Hi, I’m doing some example on high order functions with the following code:

function loop(value, test, update, body) {
    if (test(value)) {
        body(value);
        value = update(value);
        return loop(value, test, update, body)
    }
}

loop(3, n => n > 0, n => n - 1, console.log)

And it works as expected. Next, I tried to put Array.prototype.push() as body argument and I receive an error.

TypeError: Cannot convert undefined or null to object
let loopArray = [];
loop(1, n => n > 0, n => n - 1, loopArray.push);

I changed the code to this:

let loopArray = [];
loop(1, n => n > 0, n => n - 1, (element) => loopArray.push(element))

So it’s okay now.
My question is, what is the difference between console.log and loopAray.push as a parameter?

D.

only thought that strikes to mind is not being able to use the prototype’s functions as parameters without losing context… console.log isnt a prototype, so it may be able to keep its context intact by relying on the global definition…

1 Like

I think you’re right, I just tried with some other functions and it behaves the same.
Thank you @m_hutley for pointing me in the right direction.

Interesting.

‘push’ loses it’s context(this) as soon as it is passed into loop, which makes sense. You are not passing in loopArray.push you are just passing in the right hand method ‘push’

A simple test

var push = Array.prototype.push;
push(1) /* Throws exactly the same Type Error */

or

var obj = {
    person : 'Bob',
    sayName(){
        console.log(this.person)
    }
}

obj.sayName(); // Bob

(function(sayName){

  sayName()  // undefined (window.person)
    
}(obj.sayName))

A test with binding

var arr = [];

function loop(value, test, update, body) {
    if (test(value)) {
        body(value);
        value = update(value);
        return loop(value, test, update, body)
    }
}

loop(3, n => n > 0, n => n - 1, arr.push.bind(arr))

console.log(arr) /* [3,2,1] */

What is odd though is you would have thought that the ‘this’ in ‘push’ would resolve to window and throw some sort of related error.

This one in particular
Uncaught TypeError: Failed to set an indexed property on ‘Window’: Index property setter is not supported.

Looking at the ECMA docs it is failing at the first step


When the push method is called with zero or more arguments the following steps are taken:

  1. Let O be ToObject( this value).

ToObject will throw a TypeError for null or undefined

console is a property of window (In chrome anyway).

you can console.log it
console.log(window.console)

You can also do a similar test to above
var con = console.log; con('hello') // hello

Yes, just to add what has already been said – in JS methods are not automatically bound to their context, they have to be explicitly invoked on a given object. You can however bind() a function to its (or any other) context like so:

const array = ['foo']
const push = array.push.bind(array)

push('bar')
console.log(array) // 'foo', 'bar'

BTW conversely you can “borrow” methods from other objects, such as when calling map() on a node list (or forEach() before it became part of the DOM specs):

const elements = document.querySelectorAll('div')
const ids = [].map.call(elements, element => element.id)
// Or, longer but without creating an intermediary empty array:
// const ids = Array.prototype.map.call(elements, element => element.id)

This actually makes for a very simple polyfill for aforementioned forEach():

NodeList.prototype.forEach = (
  NodeList.prototype.forEach ||
  Array.prototype.forEach
)

And this is why you should always enable strict mode. ;-)

First off sorry Krledev for hijacking your thread.

You have got me there, I have learnt quite a lot in JS over the years, but for some reason not really considered ‘use strict’. I had the idea that it would enforce a coding style similar to Crockford’s JSLint, which I am not a 100% a fan of — not at it’s strictest anyway

Example

let x = 5,
    y = 10,
    z = 'a';

JSLint Errors

Expected ';' and instead saw ','.
let x = 5,
Unexpected ','.
let x = 5,
Use double quotes, not single quotes.
    z = 'a';

I am glad to see ‘no strict’ isn’t so strict and will pick up on useful things like trying to use undeclared variables — that said I don’t think it is a safeguard for terrible coding.

Going back to array.push as an argument, how would ‘no strict’ make a difference in this situation?

'use strict'

const arr = [];

function loop(value, test, update, body) {

    if (test(value)) {
        body(value);
        value = update(value);
        return loop(value, test, update, body)
    }
}

loop(3, n => n > 0, n => n - 1, arr.push)

console.log(arr)

Error Thrown

Uncaught TypeError: Cannot convert undefined or null to object

I am sure I am being dense, but as I say I would have expected this instead.

Uncaught TypeError: Failed to set an indexed property on ‘Window’: Index property setter is not supported.

Not going to lose any sleep over it though;)

1 Like

Because now it tells you that this refers to nothing, rather than implicitly to the global object.

[quote=“rpg_digital, post:7, topic:351241, full:true”]
JSLint Errors

Expected ';' and instead saw ','.
let x = 5,
Unexpected ','.
let x = 5,
Use double quotes, not single quotes.
    z = 'a';

Using separate lines for variable declarations has the benefit of making it much easier to refactor and work with the code. And with single/double quotes, when either achieve the same thing, arguments are reduced by enforcing only one type of them.

There really are good reasons behind each decision there.

let x = 5;
let y = 10;
let z = "a";

You may not like it though, so you can come up with your own rigorous set of standards to use throughout your own shop.

I feel like I am having my wrists slapped. I believe I adopted that style of declaration years ago from Stoyan Stefanov’s Design Patterns book.

I know you are an advocate of JSLint, you have been for many years now.

You had an idea on here, possibly a decade ago, of users submitting projects they wanted to work on, so that we could work through it together.

I submitted a top-down selector engine, in the vein of ‘sizzle’, which would handle pseudo selectors and the like — pre querySelector.

You were really helpful with your time and feedback, and it’s not forgotten. :+1:

ps. I think JSLint was part of your recommendation at the time:)

I’m sorry that it felt that way. I have no intention of telling anyone off about style. Instead, it might help if I share some of my experiences when I used to code in that style with comma separated variables.

Long ago when I used comma separate variables, there were several areas of pain. One of those painful areas was that when variables are added to the start or the end of the list of variables, multiple lines need to be edited. That also means that it’s more difficult to reorder the list of variables too, because you’re sometimes having to change three lines just to move something elsewhere.

// before
var temp,
    num1,
    num2;

// after
var num1, // adding var to this line
    num2, // changing semicolon to a comma
    temp; // removing var, and changing comma to semicolon

I didn’t know that they were painful at the time, just thinking that this is how it always is, until someone pointed it out to me. All of those changes make it in to github too, adding unwanted complexity to simple changes. Using a different coding standard made those changes much easier to achieve.

// before
var temp;
var num1;
var num2;

// after
var num1; // no change needed
var num2; // no change needed
var temp; // no change needed

None of that is major, but on adopting a beginner style with separate variable declarations, all of that pain and annoyance just vanished. It was like coming full circle after having tried different things. The difference was amazing.

My main reason for that hasn’t been all that clear. Some of it is because it’s easier to receive criticism from a computer than it is to receive it from a person. Other aspects are that its recommendations seem to make sense. And at first when some didn’t seem to make sense, such as always enforcing spaces over tabs, that helped me to investigate and learn that it 's about reducing conflict between different people working on the same code. Little to no benefit occurs when someone rewrites all of the code to match their own personal coding style before working on the problem with the code. And that was difficult to accept. That meant removing my personality from the code. But when it results in code that is easier to work with and is easier to reason about? It still hurts, like losing a loved one. But now that I’ve aligned myself with a rigorous standard, that helps to free me up to deal with other things. When questions come in about why this or why that, we don’t have to get into religious arguments about tabs vs spaces, comma-separated vs semicolon, or this-keyword vs named variables. Instead we can just point to the standard and say that’s how it’s done. On reflection it’s like trading several different religious grounds for one religious ground, but it does result things being much easier to explain, and justify.

On reflection the main reason is that aligning with a coding standard gives me a Better Answer ™ when questions arise about different style issues. There are several different types of style guide standards to choose from too. I don’t think that it’s suitable to pick and choose different parts from among them, but there being several types of standards should make it easier to find one that you agree with more than others.

And apologies for the wall of text. It’s been a long lockdown.

1 Like

No need for apologies, it was an interesting read Paul.

Clean and legible code is important to me, but given I haven’t worked in a web-dev team environment, I haven’t had to address the issue of adopting a set standard — web development and scripting is presently more of a sideline for me.

What you say makes sense, and is definitely food for thought, albeit as you say a bit of a hard pill to swallow.

Thanks for posting the style guide standards link, that is really useful and is something I have been looking for. I take on-board your comment about consistency.

It has been a long lockdown you’re right! Personally the positive for me has been finally being able to knuckle down with a few courses.

Thanks

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.