文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

注意: 在维基百科上只一些内容只适合0.12以前的版本. 0.13以后的更多的官方文档, 请看http://docs.nwjs.io

不同的基于webkit的窗口有不同的JavaScript 上下文环境, 比如:每个窗口都有自己的全局对象和一套自己的全局构造函数(例如 数组或对象)。

这是一些在Web浏览器中的常规做法。这是一件好事, 因为:

  • 当某个对象的原型被一些库替换或扩充时 (例如 Prototype) 或一个简单的脚本,类似的其他窗口中的对象不受影响;

  • 当一个程序员犯了错误(例如missing new before a poorly written constructor) 并且这个错误影响了全局作用域, 它仍然不会有太大的影响(一系列的窗口);

  • 恶意应用程序不能访问机密数据结构在其他窗口.

第 1 段(可获 1.74 积分)

在 node-webkit 中 Node 模块运行在它们独自共享的 Node 上下文中(默认是共享的,也可以通过增加参数 'new-instance': true 到 Window.open 中,这样当打开新窗口就会使用一个独立的 Node.js 上下文)。

检测脚本的上下文

如果你使用的是 require() 方法 (Node.js modules API) ,那么所包含的模块也运行在 Node 上下文。(当你调用 require() 函数,或者来自某些引入模块的函数时,JS 引擎进入 Node 上下文然后在函数返回时离开)

如果是在同一个窗口中使用 HTML <script> 元素 (或者 jQuery 的 $.getScript(), 或者其他类似方法) ,那么脚本则运行在窗口的上下文中。

第 2 段(可获 1.44 积分)

如果模块是通过应用的 manifest 文件的  "node-main" 属性进行指定的,那么模块将运行在 Node 上下文,但是之后就可以访问 window 对象. (详情请看文章 “node-main” )

Node 上下文的特性和限制

运行在 Node 上下文的脚本可以使用 __dirname 变量来获取其所在的目录。

Node.js global 对象是在 Node 上下文中的全局对象。任何 WebKit  窗口的 windows 对象并非全局,甚至在 Node 的上下文是隐式的(唯一的例外是 node-main ),例如,当你需要访问 windows 对象时,你必须(显式的)传递 windows 对象给你的模块函数。

第 3 段(可获 1.61 积分)

这也意味着你不能依靠 alert() (它其实是 window.alert()) 来debug.然而,你可以使用console.log(); i它的输出 (和这个输出方法类似的有console.warn() 和 console.error()) 是输出到WebKit的控制台. 你也许在的 “开发者工具” 窗口中可以看到它 (在 “控制台”选项卡).

你不能使用require('nw.gui') (去访问node-webkit的 GUI API) 在Node的上下文环境中, 因为在window对象之外没有GUI.

一些其它的浏览器特性(比如 Worker和 WebSocket接口) 在Node的上下文环境中也是不能访问的.

第 4 段(可获 1.09 积分)

解决其他脚本的相对路径问题

在WebKit中相对路径是根据主HTML文件路径来解析的 (像所有的浏览器一样)。相对路径在node的模块中是根据当前模块的位置来解析的 (像node.js一直是这样做的)。 只要记住你是在哪个上下文环境中。

例如,如果我们有文件 /myApp/main.html:

<html>
  <head>
    <!-- will be resolved according to this html file path -->
    <script src="components/myComponent.js"></script>
  </head>
  <body>
    <script>
      // will be resolved according to this html file path
      var hello = require('./libs/myLib');
      // __dirname is not defined in webkit context, this is only node.js thing
      console.log(__dirname); // undefined
    </script>
  </body>
</html>
第 5 段(可获 0.69 积分)

在文件 /myApp/components/myComponent.js 我们可以这样做:

// we are still in webkit context, so paths are still resolved according to main.html

var something = require('./util.js'); // will look for file /myApp/util.js NOT for /myApp/components/util.js

// __dirname still not defined
console.log(__dirname); // undefined

在文件 /myApp/libs/myLib.js 中我们可以这样做:

// here we are in node.js context, so paths are resolved according to this file

var something = require('./otherUtil.js'); // will look for file /myApp/libs/otherUtil.js

// __dirname is defined
console.log(__dirname); // '/myApp/libs'
第 6 段(可获 0.15 积分)

工作在不同的上下文

使用不同的上下文当然是有好处的,但是有时候也会让你的代码出现一些问题,因此需要一些相应的解决办法。

这种问题最常见的原因是 JavaScript 的 instanceof 操作符的行为导致。你在 MDN 应该能看到的 someValue instanceof someConstructor 这样的测试会在其原型链中存在 prototype 属性。但是,如果 someValue 是来自不同 JavaScript 上下文时,这样它就有其祖先对象,这样 someValue instanceof someConstructor 的检查操作就会不可避免的失败。

第 7 段(可获 1.18 积分)

例如, 一个简单的检查某个值是为数组类型(someValue instanceof Array )不能确定一个变量的值是一个数组的如果是通过另一个上下文 (查看 “D以绝对的精度证明一个是否为一个javascrip数组” 获取更多信息)。

直接通过构造函数来判断也会存在同样的问题(例如, 用someValue.constructor === Array 来代替 someValue instanceof Array).

下面的构造函数都是上下文相关的全局对象的子对象, 因此,它们的实例会受到影响:

  • 标准对象 types: ArrayBooleanDateFunctionNumberObjectRegExpString.

  • 数组类型types: ArrayBufferDataViewFloat32ArrayFloat64ArrayInt16Array,Int32ArrayInt8ArrayUint16ArrayUint32ArrayUint8Array.

  • Error types: ErrorEvalErrorRangeErrorReferenceErrorSyntaxErrorTypeError,URIError.

第 8 段(可获 1 积分)

有几种方法可以解决这个问题。

避免使用instanceof

防止上下文相关的问题的最简单方法是避免使用instanceof当一个值可能来自另一个JavaScript上下文的时候. 例如, 你也许会使用 Array.isArray 来检查一个值是否为一个数组, 并且这个方法运行在一个可靠的上下文环境中。

然而, 如果这样一个方便的替代方法不是现成的, 或者当你面对的是别人的又问题的代码的时候(不是自己的),这个时候可能会是一个麻烦, 另一个解决方案是必要的。

使用来自其它上下文的构造函数

第 9 段(可获 1.2 积分)

当你预计需要传递某个值到一些其他的上下文,你可以在指定的上下文中使用一个构造器来构造你的值。这个值可以可以在这个上下文中使用 instanceof 进行检查。

例如,著名的 async 模块使用 (在其 代码 中 2013-05-20; 后来被修复了) 很多的 .constructor 检查 (在代码行 472505545675752) 因此在其他的上下文中遇到数组就会出错。例如,如果你在一个 node-webkit 的窗口上下文中运行如下代码:

require('async').waterfall([
   function(callback){
      console.log('1.');
      callback(null, 'one', 'two');
   },
   function(arg1, arg2, callback){
      console.log('2.');
      callback(null, 'three');
   },
   function(arg1, callback){
      console.log('3.');
      callback(null, 'done');
   }
], function (err, result) {
   console.log('Fin.');
   if( err ) throw err;
   console.log(result);
});
第 10 段(可获 1.06 积分)

它抛出了这些错误 Error: First argument to waterfall must be an array of functions (错误的认为它不是一个数组)。

使用 nwglobal 模块, 你可以访问到Node上下文环境中的 Array的构造函数,然后像下面这样重写它:

require('async').waterfall( require('nwglobal').Array(
   function(callback){
      console.log('1.');
      callback(null, 'one', 'two');
   },
   function(arg1, arg2, callback){
      console.log('2.');
      callback(null, 'three');
   },
   function(arg1, callback){
      console.log('3.');
      callback(null, 'done');
   }
), function (err, result) {
   console.log('Fin.');
   if( err ) throw err;
   console.log(result);
});
第 11 段(可获 0.34 积分)

这让 async 模块很过瘾。

然后在某些情况下你不能使用构造器直接创建值(例如你可能会在 MDN 看到的,使用能够 Function 构造器在效率上比直接声明一个函数要低很多,而且这种方式也没法使用闭包),在这种情况下我们需要其他的解决方案。

替代 __proto__

__proto__ 不是对象的标准属性,但是使用广泛,主要用来修改对象内部的 “[[Prototype]]” 属性(最初包含了构造器的属性)。

第 12 段(可获 1.11 积分)

当你预见需要传递值到其他的上下文,你需要预先替换值的 __proto__ 属性为来自那个上下文的构造器。这个值可以在上下文中轻松的使用 instanceof 进行检查。

例如,著名的 async 模块使用 (in its code dated 2013-05-20;后来被修复了) 了 instanceof Function 检查(on line 428) ,因此当它遇到来自其他上下文的函数时会导致失败,试试在一个 node-webkit 的 windows 上下文中运行下列代码时:

var getData = function (callback) {
   setTimeout(function(){
       console.log('1.1: got data');
       callback();
   }, 300);
}
var makeFolder = function (callback) {
   setTimeout(function(){
       console.log('1.1: made folder');
       callback();
   }, 200);
}
var writeFile = function(callback) {
   setTimeout(function(){
       console.log('1.1: wrote file');
       callback(null, 'myfile');
   }, 300);
}
var emailFiles = function(callback, results) {
   console.log('1.1: emailed file: '+results.writeFile);
   callback(null, results.writeFile);
}
require('async').auto({
   getData:getData ,
   makeFolder:makeFolder,
   writeFile: ['getData', 'makeFolder',writeFile],
   emailFiles: ['writeFile',emailFiles]
}, function(err, results) {
   console.log('1.1: err: '+ err);
   console.log('1.1: results: '+ results);
});
第 13 段(可获 1.01 积分)

它抛出了一个没有slice函数的错误 (错误地认为给定的值不是一个函数,因此它必须是一个数组, 然后试图调用数组类型上的slice函数).

使用 nwglobal 模块, 你也许可以访问Node的 上下文环境中的 Function 然后用以下代码重写它

var getData = function (callback) {
   setTimeout(function(){
       console.log('1.1: got data');
       callback();
   }, 300);
}
getData.__proto__ = require('nwglobal').Function;
var makeFolder=  function (callback) {
   setTimeout(function(){
       console.log('1.1: made folder');
       callback();
   }, 200);
}
makeFolder.__proto__ = require('nwglobal').Function;
var writeFile= function(callback) {
   setTimeout(function(){
       console.log('1.1: wrote file');
       callback(null, 'myfile');
   }, 300);
}
writeFile.__proto__ = require('nwglobal').Function;
var emailFiles= function(callback, results) {
   console.log('1.1: emailed file: '+results.writeFile);
   callback(null, results.writeFile);
}
emailFiles.__proto__ = require('nwglobal').Function;
require('async').auto({
   getData:getData ,
   makeFolder:makeFolder,
   writeFile: ['getData', 'makeFolder',writeFile],
   emailFiles: ['writeFile',emailFiles]
}, function(err, results) {
   console.log('1.1: err: '+ err);
   console.log('1.1: results: '+ results);
});
第 14 段(可获 0.58 积分)

这让 async 模块变得很好。

避免使用 Node 的 setImmediate

在 WebKit 和 Node 的上下文进行切换需要一点时间。

绝大多数情况下这个延迟不会导致严重问题,但如果你在 WebKit 上下文中,并希望推迟一些函数的执行(在某些相同函数可能被推迟数百次的情况下,例如当你绘制 500 个对象并在每次对象出现时让 WebKit 重绘窗口,而不是强制用户盲目的盯着白屏看),然后使用 Node 的 setImmediate (导出自 Node.js 模块) 实际上会比你期望的更少的及时性。

第 15 段(可获 1.51 积分)

为了解决这个问题通常需要有足够的定义 (在WebKit的上下文中) 和使用David Baron's的 setZeroTimeout 函数来代替Node的setImmediate函数。

// Only add setZeroTimeout to the window object, and hide
// everything else in a closure.
(function() {
   var timeouts = [];
   var messageName = "zero-timeout-message";

   // Like setTimeout, but only takes a function argument.
   // There's no time argument (always 0) and no function's arguments
   // (you have to use a closure if such arguments are necessary).
   function setZeroTimeout(fn) {
      timeouts.push(fn);
      window.postMessage(messageName, "*");
   }

   function handleMessage(event) {
      if (event.source == window && event.data == messageName) {
         event.stopPropagation();
         if (timeouts.length > 0) {
            var fn = timeouts.shift();
            fn();
         }
      }
   }

   window.addEventListener("message", handleMessage, true);

   // Add the one thing we want added to the window object.
   window.setZeroTimeout = setZeroTimeout;
})();
第 16 段(可获 0.31 积分)

使用标准的 window.setTimeout(你的函数, 0)性能非常差 因为HTML5 标准定义了它的最小超时时间是 4毫秒 甚至可以设置为 0 (例如: 即使是你使用 setTimeout250倍, 对于你整个应用来说,已经有额外的延迟了).

第 17 段(可获 0.49 积分)

文章评论

班纳睿
这个跟java无关的,为啥被加上java的标签呢?
coyee
去掉啦
班纳睿
是自动加的标签吗?