history.state 总是和 popstate event.state 一样吗?

Is history.state always the same as popstate event.state?

history.state 是否总是与 popstate event.state 相同?

window.addEventListener("popstate", function (event){
    console.log(history.state === event.state); // ALWAYS TRUE
    // IT SEEMS
}, false);

如果是,为什么两次拥有同样的东西。

不,它们并不总是相同的。

来自MDN

The popstate event is fired when the active history entry changes. If the history entry being activated was created by a call to history.pushState() or was affected by a call to history.replaceState(), the popstate event's state property contains a copy of the history entry's state object.

Note that just calling history.pushState() or history.replaceState() won't trigger a popstate event. The popstate event will be triggered by doing a browser action such as a click on the back or forward button (or calling history.back() or history.forward() in JavaScript).

以下代码示例表明它们可以不同 (Google Chrome - Version 63.0.3239.108):

请注意: FireFox 存在一些问题。

window.onpopstate = function(event) {
  console.log(event.state === history.state);
};

history.pushState({page: 1}, "title 1", "?page=1");
history.pushState({page: 2}, "title 2", "?page=2");
history.replaceState({page: 3}, "title 3", "?page=3");
history.back(); 
history.back(); 
history.go(2);

是的,它们应该始终代表相同的状态。

根据 the specs 当请求浏览器执行历史遍历时,

  1. Set history.state to state.

其中 state16.1

中的相同
  1. If state changed is true, then fire an event named popstate at the Document object's Window object, using PopStateEvent, with the bubbles attribute initialized to true and the state attribute initialized state.

强调我的

另一个答案的代码证明 Google Chrome 不是 return 同一个对象,而是一个副本。
然而,这两个状态是相同的,而且 UA 似乎不必尊重这个状态对象上的 [SameObject] 策略。

所以为了检查相等性,应该检查状态对象的内容,而不是使用直接相等性检查。

window.onpopstate = function(event) {
  console.log('same state',
    JSON.stringify(event.state) === JSON.stringify(history.state) // always true
  );
};

const obj1 = {page: 1};
history.pushState(obj1, "title 1", "?page=1");

console.log('[SameObject]', history.state === obj1); // false
console.log(history.state); // {page: 1}

history.pushState({page: 2}, "title 2", "?page=2");
history.replaceState({page: 3}, "title 3", "?page=3");
history.back();
history.back(); 
history.go(2);

至于为什么把它设置在两个不同的地方,我不得不承认我没有一个可靠的答案...