Sets up a duplex object stream over window.postMessage 在window.postMessage上设置一个双工对象流javascript
因此咱们先学习一下window.postMessage:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessagehtml
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow:其余窗口的一个引用 message:要发送的数据信息 targetOrigin:该信息要发送给的目标窗口,"*" 则表示无限制。若是你明确的知道消息应该发送到哪一个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将致使数据泄露到任何对数据感兴趣的恶意站点 transfer(可选):是一串和message 同时传递的 Transferable 对象. 这些对象的全部权将被转移给消息的接收方,而发送一方将再也不保有全部权。
而后其余窗口就经过监听来接受别的窗口发来的数据:java
window.addEventListener("message", receiveMessage, false); function receiveMessage(event) { // For Chrome, the origin property is in the event.originalEvent // object. // 这里不许确,chrome没有这个属性 // var origin = event.origin || event.originalEvent.origin; var origin = event.origin if (origin !== "http://example.org:8080")//若是你确实但愿从其余网站接收message,请始终使用origin和source属性验证发件人的身份 return; // ... }
message 的属性,即receiveMessage中的event:node
data 从其余 window 中传递过来的对象。 origin 调用 postMessage 时消息发送方窗口的 origin . 这个字符串由 协议、“://“、域名、“ : 端口号”拼接而成。例如 “https://example.org (隐含端口 443)”、“http://example.net (隐含端口 80)”、“http://example.com:8080”。请注意,这个origin不能保证是该窗口的当前或将来origin,由于postMessage被调用后可能被导航到不一样的位置。 source 对发送消息的窗口对象的引用; 您可使用此来在具备不一样origin的两个窗口之间创建双向通讯。
/* * A窗口的域名是<http://example.com:8080>,如下是A窗口的script标签下的代码: */ var popup = window.open(...popup details...); // 若是弹出框没有被阻止且加载完成 // 这行语句没有发送信息出去,即便假设当前页面没有改变location(由于targetOrigin设置不对) popup.postMessage("The user is 'bob' and the password is 'secret'", "https://secure.example.net"); // 假设当前页面没有改变location,这条语句会成功添加message到发送队列中去(targetOrigin设置对了) popup.postMessage("hello there!", "http://example.org"); function receiveMessage(event) { // 咱们能相信信息的发送者吗? (也许这个发送者和咱们最初打开的不是同一个页面). if (event.origin !== "http://example.org") return; // event.source 是咱们经过window.open打开的弹出页面 popup // event.data 是 popup发送给当前页面的消息 "hi there yourself! the secret response is: rheeeeet!" } window.addEventListener("message", receiveMessage, false);
/* * 弹出页 popup 域名是<http://example.org>,如下是script标签中的代码: */ //当A页面postMessage被调用后,这个function被addEventListenner调用 function receiveMessage(event) { // 咱们能信任信息来源吗? if (event.origin !== "http://example.com:8080") return; // event.source 就当前弹出页的来源页面 // event.data 是 "hello there!" // 假设你已经验证了所受到信息的origin (任什么时候候你都应该这样作), 一个很方便的方式就是把enent.source // 做为回信的对象,而且把event.origin做为targetOrigin event.source.postMessage("hi there yourself! the secret response " + "is: rheeeeet!", event.origin); } window.addEventListener("message", receiveMessage, false);
继续post-message-stream的学习git
var streamA = new PostMessageStream({ name: 'thing one', target: 'thing two', }) var streamB = new PostMessageStream({ name: 'thing two', target: 'thing one', }) streamB.on('data', (data) => console.log(data)) streamA.write(chunk)
var messageStream = new PostMessageStream({ // required // name of stream, used to differentiate // when multiple streams are on the same window name: 'source', // name of target stream target: 'sink', // optional // window to send the message to // default is `window` window: iframe.contentWindow, })
其源代码:github
const DuplexStream = require('readable-stream').Duplex const inherits = require('util').inherits module.exports = PostMessageStream inherits(PostMessageStream, DuplexStream) function PostMessageStream (opts) { DuplexStream.call(this, { objectMode: true,//若是想建立一个的能够压入任意形式数据的可读流,只要在建立流的时候设置参数为便可,例如:。 }) this._name = opts.name this._target = opts.target this._targetWindow = opts.targetWindow || window this._origin = (opts.targetWindow ? '*' : location.origin) // initialization flags this._init = false this._haveSyn = false window.addEventListener('message', this._onMessage.bind(this), false)//监听消息 // send syncorization message this._write('SYN', null, noop)//先将要进行第一次握手发送的SYN或收到第一次握手后发送第二次握手的syn准备好 this.cork()/强制把全部写入的数据都缓冲到内存中 } // private PostMessageStream.prototype._onMessage = function (event) { var msg = event.data // validate message if (this._origin !== '*' && event.origin !== this._origin) return if (event.source !== this._targetWindow) return if (typeof msg !== 'object') return if (msg.target !== this._name) return if (!msg.data) return if (!this._init) {//若是该流都尚未初始化,那就说明如今首先要进行三次握手来链接 // listen for handshake if (msg.data === 'SYN') {//收到syn说明收到的是第一次握手或第二次 this._haveSyn = true this._write('ACK', null, noop)//而后写ACK生成第二次握手或第三次发送,此时已经成功同步 } else if (msg.data === 'ACK') {//收到ACK说明收到的是第二次或第三次握手 this._init = true //说明初始化链接已经成功 if (!this._haveSyn) {//若是_haveSyn为false,那就说明收到的是第二次握手 this._write('ACK', null, noop) //因此还须要再发送一次ACK进行第三次握手 } this.uncork()//输出被缓冲的数据 } } else {//不然就是已经链接上了 // forward message try { this.push(msg.data)//将post来的数据push到流中,将调用下面的_write函数 } catch (err) { this.emit('error', err)//出错则触发error事件 } } } // stream plumbing PostMessageStream.prototype._read = noop PostMessageStream.prototype._write = function (data, encoding, cb) { var message = { target: this._target, data: data, } this._targetWindow.postMessage(message, this._origin) cb() } // util function noop () {}objectModetrueReadable({ objectMode: true })
扩展知识:chrome
writable.cork()
writable.cork() 方法会强制把全部写入的数据都缓冲到内存中。 当调用 stream.uncork() 或 stream.end() 方法时,被缓冲的数据才会被输出。
当写入大量小块数据到流时(由于缓存是有大小的,若都是小块数据占据了大内存,剩下的又不能装入一个数据,这样就会浪费内存),内部缓冲可能失效,从而致使性能降低,writable.cork() 主要用于避免这种状况。
writable.uncork()
writable.uncork() 方法会输出 stream.cork() 方法被调用后缓冲的所有数据。
当使用 writable.cork() 和 writable.uncork() 来管理流写入缓存,建议使用 process.nextTick() 来延迟调用 writable.uncork()。 经过这种方式,能够对单个 Node.js 事件循环中调用的全部 writable.write() 方法进行批处理。api
stream.cork(); stream.write('一些 '); stream.write('数据 '); process.nextTick(() => stream.uncork());
若是一个流上屡次调用 writable.cork() 方法,则必须调用一样次数的 writable.uncork() 方法才能输出缓冲的数据。缓存
stream.cork(); stream.write('一些 '); stream.cork(); stream.write('数据 '); process.nextTick(() => { stream.uncork(); // 数据不会被输出,直到第二次调用 uncork()。 stream.uncork(); });
util.inherits(constructor, superConstructor)是一个实现对象间原型继承的函数app
举例说明:
var util = require('util'); function father() { this.name = 'John'; //这为三个在构造函数中定义的属性 this.age = 1979; this.showProperty = function() { console.log('got five house'); }; } father.prototype.showName = function() { //这个是在原型中定义的函数 console.log(this.name); }; function son() { this.name = 'bob'; this.age = 1997; } util.inherits(son, father); //使用util.inherits,因此son仅仅只能继承father在原型中定义的showName函数 var dad = new father(); dad.showName(); dad.showProperty(); console.log(dad); var child = new son(); child.showName(); //成功 // child.showProperty(); //失败 console.log(child);
返回:
userdeMacBook-Pro:stream-learning user$ node test.js John got five house father { name: 'John', age: 1979, showProperty: [Function] } bob
调用child.showProperty();会失败:
/Users/user/stream-learning/test.js:23 child.showProperty(); ^ TypeError: child.showProperty is not a function
(3)对JavaScript prototype 使用介绍想了解的能够去这个博客看看,写的很好
(4)javascript:apply方法 以及和call的区别 (转载)
https://github.com/nodejs/readable-stream
https://nodejs.org/dist/v10.11.0/docs/api/stream.html
You can swap your require('stream')
with require('readable-stream')
without any changes, if you are just using one of the main classes and functions.
const { Readable, Writable, Transform, Duplex, pipeline, finished } = require('readable-stream')
Note that require('stream')
will return Stream
, while require('readable-stream')
will return Readable
. We discourage using whatever is exported directly, but rather use one of the properties as shown in the example above.