21.for in、Object. keys 和 Object. getOwnPropertyNames 对属性遍历有什么区别
- for in 会遍历自身及原型链上的可枚举属性
- Object. keys 会将对象自身的可枚举属性的 key 输出
- Object. getOwnPropertyNames会将自身所有的属性的 key 输出
解析:
ECMAScript 将对象的属性分为两种:数据属性和访问器属性。
var parent = Object.create(Object.prototype, {
a: {
value: 123,
writable: true,
enumerable: true,
configurable: true
}
});
// parent继承自Object.prototype,有一个可枚举的属性a(enumerable:true)。
var child = Object.create(parent, {
b: {
value: 2,
writable: true,
enumerable: true,
configurable: true
},
c: {
value: 3,
writable: true,
enumerable: false,
configurable: true
}
});
//child 继承自 parent ,b可枚举,c不可枚举
for in
for (var key in child) {
console.log(key);
}
// b
// a
// for in 会遍历自身(b)及原型链上的可枚举属性(a),自身属性c不能枚举
Object.keys
console.log(Object.keys(child));
// ["b"]
// Object.keys 会将对象自身的可枚举属性的key输出
Object. getOwnPropertyNames
console.log(Object.getOwnPropertyNames(child));
// ["b","c"]
// 会将自身所有的属性的key输出
补充:
什么是可枚举属性?
可枚举属性是指那些内部 “可枚举” 标志设置为 true 的属性。对于通过直接的赋值和属性初始化的属性,该标识值默认为即为 true。但是对于通过 Object.defineProperty 等定义的属性,该标识值默认为 false。
其中js中基本包装类型的原型属性是不可枚举的,如Object, Array, Number等。
可枚举的属性可以通过for…in循环进行遍历(除非该属性名是一个Symbol),或者通过Object.keys()方法返回一个可枚举属性的数组
补充:
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
语法:Object.create(proto, [propertiesObject])
- proto:新创建对象的原型对象。
- propertiesObject:可选
- 若没有指定为 undefined,则是要添加到新创建对象的可枚举属性
- 即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。
- 这些属性对应Object.defineProperties()的第二个参数
- 返回值:一个新对象,带着指定的原型对象和属性。
22. iframe 跨域通信和不跨域通信
不跨域通信
主页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<iframe name="myIframe" id="iframe" class="" src="flexible.html" width="500px" height="500px">
</iframe>
</body>
<script type="text/javascript" charset="utf-8">
function fullscreen() {
alert(1111);
}
</script>
</html>
子页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
我是子页面
</body>
<script type="text/javascript" charset="utf-8">
// window.parent.fullScreens()
function showalert() {
alert(222);
}
</script>
</html>
1、主页面要是想要调取子页面的 showalert 方法
myIframe.window.showalert();
- 子页面要调主页面的 fullscreen 方法
window.parent.fullScreens();
- js 在 iframe 子页面获取父页面元素:
window.parent.document.getElementById("元素id");
- js 在父页面获取 iframe 子页面元素代码如下:
window.frames["iframe_ID"].document.getElementById("元素id");
跨域通信
使用postMessage(官方用法)
子页面
window.parent.postMessage("hello", "http://127.0.0.1:8089");
父页面接收
window.addEventListener("message", function(event) {
alert(123);
});
为什么会跨域??
为了保证用户信息的安全,95年的时候Netscape公司引进了同源策略,里面的同源指的是三个相同:协议、域名、端口。
违反了同源策略就会出现跨域问题,主要表现为以下三方面:
- 无法读取cookie、localStorage、indexDB
- DOM无法获得
- ajax请求无法发送
场景
最近在做一个需求,需要用iframe引入一个别人封装好的类似视频播放器的东西。iframe里面有一个全屏的按钮,点击后需要页面让iframe全屏,由于受到同源策略的限制,iframe无法告诉页面全屏。
解决办法
- 设置domain
document.domain作用是获取/设置当前文档的原始域部分,同源策略会判断两个文档的原始域是否相同来判断是否跨域。这意味着只要把这个值设置成一样就可以解决跨域问题了。
在此我将domain设置为一级域名的值,a页面url为a.demo.com,a页面中iframe引用的b页面url为b.demo.com,具体设置为
document.domain = 'demo.com'
设置完之后,在a页面的window上挂载使iframe全屏的方法
// a页面
window.toggleFullScreen = () => {
// do something
}
在b页面上可以直接获取到a页面的window对象并直接调用
// b页面
window.parent.toggleFullScreen()
但是这个值的设置也有一定限制,只能设置为当前文档的上一级域或者是跟该文档的URL的domain一致的值。如url为a.demo.com,那domain就只能设置为demo.com或者a.demo.com。因此,设置domain的方法只能用于解决主域相同而子域不同的情况。
- 使用中间页面
可以使用一个与a页面同域名但不同路由的c页面作为中间页面,b页面加载c页面,c页面调用a页面的方法,从而实现b页面调用a页面的方法。具体操作如下:
在a页面的node层新开一个路由,此路由加载一个c页面作为中间页面,c页面的url为a.demo.com/c。c页面只是一个简单的html页面,在window的onload事件上调用了a页面的方法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script>
window.onload = function () {
parent.parent.toggleFullScreen();
}
</script>
</body>
</html>
由于c页面和a页面是符合同源策略的,所以可以避开跨域问题,执行全屏的方法
路由:抽象点说就是:为 http request 中的 path_uri 与执行脚本做绑定(映射)!所谓的执行脚本,就是一段处理业务逻辑的代码,执行脚本可以是 php,java,js 等!举个例子,在 MVC 结构中,route 的会将 uri 映射到 controller,具体业务逻辑在 controller 里面进行。
- window.postMessage() 方法
window.postMessage方法可以安全地实现跨源通信,写明目标窗口的协议、主机地址或端口就可以发信息给它。
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage
可以安全地实现跨源通信。通常,对于两个不同页面的脚本,只有当**执行它们的页面位于具有相同的协议(通常为https),端口号(443为https的默认值),以及主机 (两个页面的模数 Document.domain设置为相同的值) **时,这两个脚本才能相互通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。
// b页面
parent.postMessage(
value,
"http://a.demo.com"
);
// a页面
window.addEventListener("message", function( event ) {
if (event.origin !== 'http://b.demo.com') return;
toggleFullScreen()
});
为了安全,收到信息后要检测下event.origin判断是否要收信息的窗口发过来的。
23.H5与Native如何交互
Hybrid架构的核心就是JSBridge交互,而实现这个交互的前提是弄清楚H5和Native端的交互
什么是JSBridge?
主要是给 JavaScript 提供调用 Native 功能的接口,让混合开发中的前端部分可以方便地使用 Native 的功能(例如:地址位置、摄像头)。
而且 JSBridge 的功能不止调用 Native 功能这么简单宽泛。实际上,JSBridge 就像其名称中的Bridge的意义一样,是 Native 和非 Native 之间的桥梁,它的核心是构建 Native 和非 Native 间消息通信的通道,而且这个通信的通道是双向的。
https://blog.csdn.net/yuzhengfei7/article/details/93468914
本文主要介绍Native端(Android/iOS)和H5端(泛指前端)的交互原理
https://www.jianshu.com/p/477ea20b1ece?from=singlemessage