为什么Javascript要求事件监听器是返回函数的函数?

为什么Javascript要求事件监听器是返回函数的函数?,javascript,callback,scope,Javascript,Callback,Scope,我正在尝试生成一个网格来响应点击和鼠标悬停事件。我有一个tile manager对象,它保存tile对象,每个对象都有自己的事件侦听器。目前,实际调用的方法在manager对象中,尽管我更希望它们存在于tile对象中 我想找到一个比我目前知道的解决方案更优雅的解决方案,或者至少理解为什么这个解决方案在其他解决方案不起作用时仍然有效: 为了便于参考和澄清,cell指的是JavaScript对象,cell\u节点指的是单元格中的DOM对象,另外\u handleClick和click\u callb

我正在尝试生成一个网格来响应点击和鼠标悬停事件。我有一个tile manager对象,它保存tile对象,每个对象都有自己的事件侦听器。目前,实际调用的方法在manager对象中,尽管我更希望它们存在于tile对象中

我想找到一个比我目前知道的解决方案更优雅的解决方案,或者至少理解为什么这个解决方案在其他解决方案不起作用时仍然有效:

为了便于参考和澄清,
cell
指的是JavaScript对象,
cell\u节点
指的是单元格中的DOM对象,另外
\u handleClick
click\u callback
是相同的函数

目前,为了查看是否发生回调,我将回调定义为:

    _handleClick(el,r,c,i){
        console.log("element:" + el)
        console.log("row:" + r)
        console.log("col:" + c)
        console.log("index:" + i)
    }
其中
\u handleClick(el,r,c,i)
位于管理器对象中,而不是磁贴中

addEventListener
方法如下所示:

cell_node.addEventListener('click',
                (function(el,r,c,i){
                    return function(){
                        click_callback(el,r,c,i);
                    }
                })(cell,r,c,i),false);
我甚至无法开始理解为什么这是必要的,或者为什么它在以下尝试中起作用:

cell_node.addEventListener('click', cell.some_clicked_function_with_no_arguments)
不工作。如果我定义了
单元格。一些用
this
关键字单击了带有参数的函数,所有内容都显示为未定义,这让我更加困惑

这两种方法全文如下

class gridTile{
    constructor(row, col, tile_id){
        this.text= row + ", " + col;
        this.child_text_div = this._make_child_text_div(this.text);
        this.element = this._make_root_element()
        this.row = row;
        this.col = col;
        this.id = tile_id;
    }
    _get_root_style(){
        var randomColor = '#'+(Math.random() * 0xFFFFFF << 0).toString(16).padStart(6, '0');
        return `
            width: 100%;
            padding-top: 100%;
            //height: ${this.height}%;
            background-color: ${randomColor};
            position: relative;
            `;
    }
    _make_child_text_div(text){
        var cssobject =`
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        position: absolute;
        `;
        var obj = document.createElement("div");
        obj.setAttribute("style", cssobject);
        obj.innerHTML = text;
        return obj;
    }
    _make_root_element(){
        var root_grid_div = document.createElement("div");
        root_grid_div.setAttribute("style", this._get_root_style());
        root_grid_div.appendChild(this.child_text_div);
        return root_grid_div;
    }
    get_element(){return this.element;}

    clicked_callback(cell){
        return function(cell){
            console.log("element:" + cell.element);
            console.log("row:" + cell.row);
            console.log("col:" + cell.col);
            console.log("index:" + cell.tile_id);
        }

    };

}

class Grid{
    constructor(rows, columns){
        this.grid_div = this._generate_grid_div(rows, columns, this._handleClick);
    }

    _generate_grid_div(rows, cols, click_callback){
        // generate styling row and columns
        var row_percent = String(1/rows * 100) + "% ";
        var col_percent = String(1/cols * 100) + "% ";
        console.log(row_percent + ", " + col_percent);

        var rowstyle = "grid-template-rows: ";
        for (var i=0; i<rows; i++){
            rowstyle += row_percent
        }
        var colstyle = "grid-template-columns: ";
        for (var i=0; i<cols; i++){
            colstyle += col_percent
        }
        var style = `
        display: grid;
        ${rowstyle};
        ${colstyle};
        `
        var grid = document.createElement('div');
        grid.className = 'grid';
        grid.setAttribute("style", style)

        var i=0;
        for (var r=0;r<rows;++r){
            for (var c=0;c<cols;++c){

                var cell = new gridTile(r, c, i);
                var cell_node = grid.appendChild(cell.get_element())

                cell_node.addEventListener('click',
                (function(el,r,c,i){
                    return function(){
                        click_callback(el,r,c,i);
                    }
                })(cell,r,c,i),false);

                cell_node.addEventListener('mouseenter', click_callback(cell,r,c,i));

                ++i;
            }
        }
        return grid;
    }

    _handleClick(el,r,c,i){
        console.log("element:" + el)
        console.log("row:" + r)
        console.log("col:" + c)
        console.log("index:" + i)
    }

    get_grid_element(){
        return this.grid_div;
    }

}

类网格块{
构造函数(行、列、块id){
this.text=行+“,”+列;
this.child\u text\u div=this.\u make\u child\u text\u div(this.text);
this.element=this.\u make\u root\u element()
this.row=行;
this.col=col;
this.id=tile\u id;
}
_获取根目录样式(){

var randomColor='#'+(Math.random()*0xFFFFFF将
addEventListener()
调用包装到IIFE中可能会使您更容易阅读。正如上面的评论中所提到的,没有足够的证据表明是否可以对其进行重构以删除IIFE

以下操作将完全相同:

(function(el, r, c, i) {
  cell_node.addEventListener('click', function() {
    click_callback(el, r, c, i);
  }, false);
})(cell, r, c, i)

通过在单独的方法中添加箭头函数,最终解决了此问题:

    _make_root_element(){
        var root_grid_div = document.createElement("div");
        root_grid_div.setAttribute("style", this._get_root_style());
        root_grid_div.appendChild(this.child_text_div);
        this._add_listeners_to_root_element(root_grid_div);
        return root_grid_div;
    }

    get_element(){return this.element;}

    _add_listeners_to_root_element(el){
        el.addEventListener('click', () => this._clicked_callback());
    }

    _clicked_callback(){
        console.log("element:" + this.element);
        console.log("row:" + this.row);
        console.log("col:" + this.col);
        console.log("index:" + this.id);
    };

这一次,
This
不是调用事件的元素,但现在在定义箭头函数的对象范围内。这在很大程度上帮助我理解了发生的事情,而其他帖子没有这样做。这更多地反映了我缺乏理解能力,而不是其他人努力解释对我来说是这样。

可能与相关,但你需要发布更多的代码。还要确保了解iLife是如何工作的(并且你不一定要将它放在
addEventListener
参数列表中)最好的解决方案是使用
const
let
而不是
var
,然后您只需要执行
。addEventListener('mouseenter',()=>单击回调(单元格,r,c,i))
我已经添加了完整的代码,很抱歉!此外,在@Bergi评论中的链接中,我注意到下面的示例使用了一个我认为有效的匿名函数。为什么一个匿名函数或箭头函数在指向另一个对象函数时无效?这真的是一个范围问题吗?