获取所有用户定义的窗口属性?

50

有没有一种方法可以在JavaScript中查找所有用户定义的窗口属性和变量(全局变量)?

我尝试了 console.log(window),但列表是无穷无尽的。


@Bergi:这里的哪个答案回答了我的问题,它刚被标记为重复?你是说这个问题是重复的,即使答案不是。关于keys()的答案应该从我的问题中移除吗?还是你在说其中一个是错误的,即使这里的答案看起来过时,其中一个仍然是正确的答案? - hippietrail
@hippietrail:我不明白为什么这里的答案会过时。它们对你来说有什么问题吗?如果我没有误解你的问题,它似乎正是你想要的。 - Bergi
7个回答

113

您还可以将窗口与干净版本的窗口进行比较,而不是尝试在运行时进行快照以进行比较。 我在控制台中运行了此操作,但您可以将其转换为函数。

// make sure it doesn't count my own properties
(function () {
    var results, currentWindow,
    // create an iframe and append to body to load a clean window object
    iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    document.body.appendChild(iframe);
    // get the current list of properties on window
    currentWindow = Object.getOwnPropertyNames(window);
    // filter the list against the properties that exist in the clean window
    results = currentWindow.filter(function(prop) {
        return !iframe.contentWindow.hasOwnProperty(prop);
    });
    // log an array of properties that are different
    console.log(results);
    document.body.removeChild(iframe);
}());

Chrome的控制台在11月18日之前不能正确记录results,所以我不得不把results变成全局变量然后记录它。代码很棒! - Maen

12

这与 @jungy 的回答意思相同,但我们可以用三行代码实现:

document.body.appendChild(document.createElement('div')).innerHTML='<iframe id="temoin" style="display:none"></iframe>';

for (a in window) if (!(a in window.frames[window.frames.length-1])) console.log(a, window[a])

document.body.removeChild($$('#temoin')[0].parentNode);

首先我们添加一个隐藏的iframe;然后我们对iframe中的标准JavaScript API测试现有变量;最后我们移除iframe。

为了更方便地工作,按字母顺序对结果进行排序可能会很有用,并且在三行版本中仍然是可能的:

document.body.appendChild(document.createElement('div')).innerHTML='<iframe id="temoin" style="display:none"></iframe>';

Object.keys(window).filter(a => !(a in window.frames[window.frames.length-1])).sort().forEach((a,i) => console.log(i, a, window[a]));

document.body.removeChild($$('#temoin')[0].parentNode);

它可以被打包成一个书签:

javascript:document.body.appendChild(document.createElement('div')).innerHTML='<iframe%20id="temoin"%20style="display:none"></iframe>';Object.keys(window).filter(a=>!(a%20in%20window.frames[window.frames.length-1])).sort().forEach((a,i)=>console.log(i,a,window[a]));document.body.removeChild(document.querySelectorAll('#temoin')[0].parentNode);throw 'done';

1
“按时间顺序排序可能很有用,而且在三行版本中仍然可以实现:...” 你不是指字母顺序吗?(按时间顺序排序意味着基于时间,而不是按字母/词汇排序) - Venryx
真的,固定的。谢谢。 - Siltaar

11

你需要自己完成这项工作。在尽可能早的时候阅读所有属性。从那时起,您可以将属性列表与您的静态列表进行比较。

var globalProps = [ ];

function readGlobalProps() {
    globalProps = Object.getOwnPropertyNames( window );
}

function findNewEntries() {
    var currentPropList = Object.getOwnPropertyNames( window );

    return currentPropList.filter( findDuplicate );

    function findDuplicate( propName ) {
        return globalProps.indexOf( propName ) === -1;
    }
}

所以现在,我们可以像这样继续前进

// on init
readGlobalProps();  // store current properties on global object

以后

window.foobar = 42;

findNewEntries(); // returns an array of new properties, in this case ['foobar']
当然,这里的警告是,你只能在脚本最早调用全局属性列表的时候“冻结”它。

1
我认为这可能是最接近完美答案的回答。 - Andreas Louv
1
@NULL 好吧,我已经习惯了在过去几个月中提到它,我觉得现在是时候继续前进了 :) - jAndy
1
@jAndy 或许可以将 readGlobalProps 放在一个 IIFE 中,这样一来你定义它的同时就会被调用了。 - GriffLab
@GriffLab 是的。我猜想他们假设人们不用担心旧版浏览器,就像我假设他们将应用逻辑放在闭包的私有作用域中一样 :-) - jAndy
这对于用户脚本(Greasemonkey,Tapermonkey)没有任何用处,因为我不相信用户脚本能够在页面中的js之前保证运行。那么在这种情况下,第二好的方法是什么? - hippietrail
显示剩余4条评论

3
我在ChromeDev工具的控制台中运行了这个命令,并复制了所有用户定义的属性。
const getUserDefinedKeys = () => {
  const globalKeys = [ 'postMessage','blur','focus','close','parent','opener','top','length','frames','closed','location','self','window','document','name','customElements','history','locationbar','menubar','personalbar','scrollbars','statusbar','toolbar','status','frameElement','navigator','origin','external','screen','innerWidth','innerHeight','scrollX','pageXOffset','scrollY','pageYOffset','visualViewport','screenX','screenY','outerWidth','outerHeight','devicePixelRatio','clientInformation','screenLeft','screenTop','defaultStatus','defaultstatus','styleMedia','onanimationend','onanimationiteration','onanimationstart','onsearch','ontransitionend','onwebkitanimationend','onwebkitanimationiteration','onwebkitanimationstart','onwebkittransitionend','isSecureContext','onabort','onblur','oncancel','oncanplay','oncanplaythrough','onchange','onclick','onclose','oncontextmenu','oncuechange','ondblclick','ondrag','ondragend','ondragenter','ondragleave','ondragover','ondragstart','ondrop','ondurationchange','onemptied','onended','onerror','onfocus','oninput','oninvalid','onkeydown','onkeypress','onkeyup','onload','onloadeddata','onloadedmetadata','onloadstart','onmousedown','onmouseenter','onmouseleave','onmousemove','onmouseout','onmouseover','onmouseup','onmousewheel','onpause','onplay','onplaying','onprogress','onratechange','onreset','onresize','onscroll','onseeked','onseeking','onselect','onstalled','onsubmit','onsuspend','ontimeupdate','ontoggle','onvolumechange','onwaiting','onwheel','onauxclick','ongotpointercapture','onlostpointercapture','onpointerdown','onpointermove','onpointerup','onpointercancel','onpointerover','onpointerout','onpointerenter','onpointerleave','onselectstart','onselectionchange','onafterprint','onbeforeprint','onbeforeunload','onhashchange','onlanguagechange','onmessage','onmessageerror','onoffline','ononline','onpagehide','onpageshow','onpopstate','onrejectionhandled','onstorage','onunhandledrejection','onunload','performance','stop','open','alert','confirm','prompt','print','queueMicrotask','requestAnimationFrame','cancelAnimationFrame','captureEvents','releaseEvents','requestIdleCallback','cancelIdleCallback','getComputedStyle','matchMedia','moveTo','moveBy','resizeTo','resizeBy','scroll','scrollTo','scrollBy','getSelection','find','webkitRequestAnimationFrame','webkitCancelAnimationFrame','fetch','btoa','atob','setTimeout','clearTimeout','setInterval','clearInterval','createImageBitmap','onappinstalled','onbeforeinstallprompt','crypto','indexedDB','webkitStorageInfo','sessionStorage','localStorage','chrome','onformdata','onpointerrawupdate','speechSynthesis','webkitRequestFileSystem','webkitResolveLocalFileSystemURL','openDatabase','applicationCache','caches','ondevicemotion','ondeviceorientation','ondeviceorientationabsolute','WebUIListener','cr','assert','assertNotReached','assertInstanceof','$','getSVGElement','getDeepActiveElement','findAncestorByClass','findAncestor','disableTextSelectAndDrag','isRTL','getRequiredElement','queryRequiredElement','appendParam','createElementWithClassName','ensureTransitionEndEvent','scrollTopForDocument','setScrollTopForDocument','scrollLeftForDocument','setScrollLeftForDocument','HTMLEscape','elide','quoteString','listenOnce','hasKeyModifiers','isTextInputElement' ];
  return Object.fromEntries(Object.entries(window).filter(([ key ]) => !globalKeys.includes(key)));
};

const getCircularReplacer = () => {
  const seen = new WeakSet();
  return (key, value) => {
    if (typeof value === 'object' && value !== null) {
      if (seen.has(value)) {
        return;
      }
      seen.add(value);
    }
    return value;
  };
};

copy(JSON.stringify(getUserDefinedKeys(), getCircularReplacer()));

如果你的页面有iframe并且出现以下错误,请尝试使用 --disable-web-security 参数启动Chrome: "Blocked a frame with origin "" from accessing a cross-origin frame." - Trent

0

窗口对象的属性是按时间顺序排列的。因此,在您网页中包含的第一个脚本的开头创建一些具有唯一名称的变量,并获取该属性的索引:

var abcdefghijklmnopqrstuvwxyz = true;
var firstOwnPropertyFound = Object.keys(window).indexOf('abcdefghijklmnopqrstuvwxyz');

然后,无论您想在哪里获取所有用户定义属性的数组,请使用:

let myProp = Object.keys(window).slice(firstOwnPropertyFound);

或者如果你想跳过前两个变量:

let myProp = Object.keys(window).slice(firstOwnPropertyFound + 2);

myProp变量是包含您创建的所有属性名称的数组。例如,在我的测试网页中:

Array(7) [
  0: "abcdefghijklmnopqrstuvwxyz"
  1: "firstOwnPropertyFound"
  2: "st"
  3: "clog"
  4: "tstfnc"
  5: "tstFNC1"
  6: "obj"
  length: 7
]

接着访问所有变量:

myProp.forEach(item => {
  console.log(window[item]);
}

我使用这个,它可以正常工作。(对不起我的英语不好)


0
我没有创建这段代码,这个答案是对这个原始答案的改进版本。
改进之处包括:
  1. 将代码封装在一个函数中,以便需要时使用
  2. 显示属性的数据,而不仅仅是属性的名称

(function showAllNonNativeWindowProperties(){

     var results, currentWindow,
  
    iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    document.body.appendChild(iframe);
  
    currentWindow = Object.getOwnPropertyNames(window);
 
    results = currentWindow.filter(function(prop) {
        return !iframe.contentWindow.hasOwnProperty(prop);
    });

    for (var each of results.sort()){
        console.log(`${each}: `, window[`${each}`]);
    }
    
    document.body.removeChild(iframe);
})()

-6
也许是这样的吧:
for (var property in window)
{
    if (window.hasOwnProperty(property))
        console.log(property)
}

7
它还将提供由浏览器定义的属性。 - bugwheels94

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接