未定义的值,可变范围问题

时间:2017-10-11 20:54:30

标签: javascript variables scope

我不确定为什么我在控制台中收到undefined。我读到了范围变量,并了解到在范围之外定义变量应该使该变量可以在特定函数范围之外访问。 不确定我在这里做错了什么:

const history = createHistory();
const location = history.location;
let loc;
const listen = history.listen((location) => {
  loc = `${location.search}${location.hash}`;
  return loc;
})
console.log(loc);

我的控制台正在记录undefined

4 个答案:

答案 0 :(得分:1)

这是因为您在为loc分配值之前正在记录undefined。它仅被初始化,因此其值为const history = createHistory(); const location = history.location; let loc; const listen = history.listen((location) => { loc = `${location.search}${location.hash}`; console.log(loc); }) 。当调用传递给listen的调用时,它将被赋值。要记录正确的值,您需要将代码更改为:

listen

我认为listen是异步的,因此在这种情况下,您无法真正返回它或使用您在问题中描述的方式。但是一旦你进入jQuery.param = function( a, traditional ) { var prefix, s = [], add = function( key, value ) { // If value is a function, invoke it and return its value value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value ); s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); }; [...] 回调,你就可以将该值传递给其他一些回调。有关更多说明和示例,请参阅How do I return the response from an asynchronous call?

答案 1 :(得分:0)

(location) => { loc = `${location.search}${location.hash}`; } 该变量未定义时。

作为此功能

// Get JSON data

var treeData = {
  "name": "rootAlert",
  "alert": "true",
  "children": [{
    "name": "Child1",
    "alert": "true",
    "children": [{
      "name": "Child1-1",
      "alert": "false"
    }, {
      "name": "Child1-2",
      "alert": "false"
    }, {
      "name": "Child1-3",
      "alert": "true"
    }]
  }, {
    "name": "Child2",
    "alert": "false",
    "children": [{
      "name": "Child2-1",
      "alert": "false"
    }, {
      "name": "Child2-2",
      "alert": "false"
    }, {
      "name": "Child2-3",
      "alert": "false"
    }]
  }, {
    "name": "Child3",
    "alert": "false"
  }]
}






// Calculate total nodes, max label length
var totalNodes = 0;
var maxLabelLength = 0;
// variables for drag/drop
var selectedNode = null;
var draggingNode = null;
// panning variables
var panSpeed = 200;
var panBoundary = 20; // Within 20px from edges will pan when dragging.
// Misc. variables
var i = 0;
var duration = 750;
var root;

// size of the diagram
var viewerWidth = $(document).width();
var viewerHeight = $(document).height();

var tree = d3.layout.tree()
  .size([viewerHeight, viewerWidth]);

// define a d3 diagonal projection for use by the node paths later on.
var diagonal = d3.svg.diagonal()
  .projection(function(d) {
    return [d.y, d.x];
  });

// A recursive helper function for performing some setup by walking through all nodes

function visit(parent, visitFn, childrenFn) {
  if (!parent) return;

  visitFn(parent);

  var children = childrenFn(parent);
  if (children) {
    var count = children.length;
    for (var i = 0; i < count; i++) {
      visit(children[i], visitFn, childrenFn);
    }
  }
}

function visit2(parent, visitFn, childrenFn) {
  if (!parent) return;

  visitFn(parent);

  var children = childrenFn(parent);
  if (children) {
    var count = children.length;
    for (var i = 0; i < count; i++) {
      visit(children[i], visitFn, childrenFn);
    }
  }
}

// Call visit function to establish maxLabelLength
visit(treeData, function(d) {
  totalNodes++;
  maxLabelLength = Math.max(d.name.length, maxLabelLength);

}, function(d) {
  return d.children && d.children.length > 0 ? d.children : null;
});

// TODO: Pan function, can be better implemented.

function pan(domNode, direction) {
  var speed = panSpeed;
  if (panTimer) {
    clearTimeout(panTimer);
    translateCoords = d3.transform(svgGroup.attr("transform"));
    if (direction == 'left' || direction == 'right') {
      translateX = direction == 'left' ? translateCoords.translate[0] + speed : translateCoords.translate[0] - speed;
      translateY = translateCoords.translate[1];
    } else if (direction == 'up' || direction == 'down') {
      translateX = translateCoords.translate[0];
      translateY = direction == 'up' ? translateCoords.translate[1] + speed : translateCoords.translate[1] - speed;
    }
    scaleX = translateCoords.scale[0];
    scaleY = translateCoords.scale[1];
    scale = zoomListener.scale();
    svgGroup.transition().attr("transform", "translate(" + translateX + "," + translateY + ")scale(" + scale + ")");
    d3.select(domNode).select('g.node').attr("transform", "translate(" + translateX + "," + translateY + ")");
    zoomListener.scale(zoomListener.scale());
    zoomListener.translate([translateX, translateY]);
    panTimer = setTimeout(function() {
      pan(domNode, speed, direction);
    }, 50);
  }
}

// Define the zoom function for the zoomable tree

function zoom() {
  svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}


// define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents
var zoomListener = d3.behavior.zoom().scaleExtent([0.1, 3]).on("zoom", zoom);

// define the baseSvg, attaching a class for styling and the zoomListener
var baseSvg = d3.select("#tree-container").append("svg")
  .attr("width", viewerWidth)
  .attr("height", viewerHeight)
  .attr("class", "overlay")
  .call(zoomListener);


// Function to center node when clicked/dropped so node doesn't get lost when collapsing/moving with large amount of children.

function centerNode(source) {
  scale = zoomListener.scale();
  x = -source.y0;
  y = -source.x0;
  x = x * scale + viewerWidth / 2;
  y = y * scale + viewerHeight / 2;
  d3.select('g').transition()
    .duration(duration)
    .attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")");
  zoomListener.scale(scale);
  zoomListener.translate([x, y]);
}

function leftAlignNode(source) {
  scale = zoomListener.scale();
  x = -source.y0;
  y = -source.x0;
  x = (x * scale) + 100;
  y = y * scale + viewerHeight / 2;
  d3.select('g').transition()
    .duration(duration)
    .attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")");
  zoomListener.scale(scale);
  zoomListener.translate([x, y]);
}

// Toggle children function

function toggleChildren(d) {
  if (d.children) {
    d._children = d.children;
    d.children = null;
  } else if (d._children) {
    d.children = d._children;
    d._children = null;
  }
  return d;
}

function toggle(d) {
  if (d.children) {
    d._children = d.children;
    d.children = null;
  } else {
    d.children = d._children;
    d._children = null;
  }
}

// Toggle children on click.

function click(d) {
  if (d3.event.defaultPrevented) return; // click suppressed

  if (d._children != null) {
    var isCollapsed = true
  } else {
    var isCollapsed = false;
  }

  d = toggleChildren(d);
  update(d);

  if (isCollapsed) {
    leftAlignNode(d);
  } else {
    centerNode(d);
  }

}

function update(source) {
  // Compute the new height, function counts total children of root node and sets tree height accordingly.
  // This prevents the layout looking squashed when new nodes are made visible or looking sparse when nodes are removed
  // This makes the layout more consistent.
  var levelWidth = [1];
  var childCount = function(level, n) {

    if (n.children && n.children.length > 0) {
      if (levelWidth.length <= level + 1) levelWidth.push(0);

      levelWidth[level + 1] += n.children.length;
      n.children.forEach(function(d) {
        childCount(level + 1, d);
      });
    }
  };
  childCount(0, root);
  var newHeight = d3.max(levelWidth) * 25; // 25 pixels per line
  tree = tree.size([newHeight, viewerWidth]);

  // Compute the new tree layout.
  var nodes = tree.nodes(root).reverse(),
    links = tree.links(nodes);

  // Set widths between levels based on maxLabelLength.
  nodes.forEach(function(d) {
    d.y = (d.depth * (maxLabelLength * 5)); //maxLabelLength * 10px
    // alternatively to keep a fixed scale one can set a fixed depth per level
    // Normalize for fixed-depth by commenting out below line
    // d.y = (d.depth * 500); //500px per level.
  });

  // Update the nodes…
  node = svgGroup.selectAll("g.node")
    .data(nodes, function(d) {
      return d.id || (d.id = ++i);
    });

  // Enter any new nodes at the parent's previous position.
  var nodeEnter = node.enter().append("g")
    //.call(dragListener)
    .attr("class", "node")
    .attr("transform", function(d) {
      return "translate(" + source.y0 + "," + source.x0 + ")";
    })
    .on('click', click);

  nodeEnter.append("circle")
    .attr('class', 'nodeCircle')
    .attr("r", 0)
    .style("fill", function(d) {
      return d._children ? "lightsteelblue" : "#fff";
    });

  nodeEnter.append("text")
    .attr("x", function(d) {
      return d.children || d._children ? -10 : 10;
    })
    .attr("dy", ".35em")
    .attr('class', 'nodeText')
    .attr("text-anchor", function(d) {
      return d.children || d._children ? "end" : "start";
    })
    .text(function(d) {
      return d.name;
    })
    .style("fill-opacity", 0);


  // Update the text to reflect whether node has children or not.
  node.select('text')
    .attr("x", function(d) {
      return d.children || d._children ? -10 : 10;
    })
    .attr("text-anchor", function(d) {
      return d.children || d._children ? "end" : "start";
    })
    .text(function(d) {
      return d.name;
    });

  // Change the circle fill depending on whether it has children and is collapsed
  node.select("circle.nodeCircle")
    .attr("r", 4.5)
    .style("fill", function(d) {
      return d._children ? "lightsteelblue" : "#fff";
    });

  // Transition nodes to their new position.
  var nodeUpdate = node.transition()
    .duration(duration)
    .attr("transform", function(d) {
      return "translate(" + d.y + "," + d.x + ")";
    });
  nodeUpdate.select("circle")
    .attr("r", 4.5)
    .style("fill", function(d) { // alert(d.alert);
      //console.log(d.name + ' is ' + d.alert)
      if (d.alert == 'true') //if alert == true
        return "red";
      else return d._children ? "green" : "green";
    });

  // Fade the text in
  nodeUpdate.select("text")
    .style("fill-opacity", 1);

  // Transition exiting nodes to the parent's new position.
  var nodeExit = node.exit().transition()
    .duration(duration)
    .attr("transform", function(d) {
      return "translate(" + source.y + "," + source.x + ")";
    })
    .remove();

  nodeExit.select("circle")
    .attr("r", 0);

  nodeExit.select("text")
    .style("fill-opacity", 0);

  // Update the links…
  var link = svgGroup.selectAll("path.link")
    .data(links, function(d) {
      return d.target.id;
    });

  // Enter any new links at the parent's previous position.
  link.enter().insert("path", "g")
    .attr("class", "link")
    .attr("d", function(d) {
      var o = {
        x: source.x0,
        y: source.y0
      };
      return diagonal({
        source: o,
        target: o
      });
    });

  // Transition links to their new position.
  link.transition()
    .duration(duration)
    .attr("d", diagonal);

  // Transition exiting nodes to the parent's new position.
  link.exit().transition()
    .duration(duration)
    .attr("d", function(d) {
      var o = {
        x: source.x,
        y: source.y
      };
      return diagonal({
        source: o,
        target: o
      });
    })
    .remove();

  // Stash the old positions for transition.
  nodes.forEach(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
}

// Append a group which holds all nodes and which the zoom Listener can act upon.
var svgGroup = baseSvg.append("g");

// Define the root
root = treeData;
root.x0 = viewerHeight / 2;
root.y0 = 0;

// Layout the tree initially and center on the root node.
tree.nodes(root).forEach(function(n) {
  toggle(n);
});
update(root);
leftAlignNode(root);


setInterval(function() {
  //update the color of each node
}, 2000);

将在它之后运行。

答案 2 :(得分:0)

问题是只有在位置发生变化时才会调用history.listen方法。 history.listen(...)调用声明了历史记录更改时应该调用的代码,但该代码在此时尚未运行。

在伪代码中,这是你所指示的:

  • 声明一些常量和变量
  • 注册一些代码,以便在历史记录更改时运行
  • 将(仍然未初始化的)变量的值打印到控制台

这可能更接近你的意思了:

const history = createHistory();
const location = history.location;
let loc;

const listen = history.listen((location) => {
  loc = `${location.search}${location.hash}`;
  console.log(loc);
})

答案 3 :(得分:0)

你可以,但正如其他人所说,loc变量不会被分配一个值,直到你改变位置,因此执行listen方法。在此之前,它的值将为undefined。另外,为什么你想拥有listen变量,当你已经在代码之上声明它时,它会获得loc的返回值?