文档结构  
可译网翻译有奖活动正在进行中,查看详情 现在前往 注册?
原作者:nwjs    来源:Github [英文]
miniwa    计算机    2016-09-30    2评/521阅
翻译进度:59%   参与翻译: zhongzhong (10)

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

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

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

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

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

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

第 1 段(可获 1.74 积分)

Node modules in node-webkit run in their own shared Node context. (Shared by default; however, you may explicitly add 'new-instance': true to the options of Window.open if you need your new window to have a separate Node.js context.)

Determining the context of a script

If the require() method (of Node.js modules API) is used, then the required module runs in the Node's context. (When you call the require() function or a function from some required module, the JS engine enters the Node's context and leaves it after the function returns.)

If HTML <script> element (or jQuery's $.getScript(), or any other similar method) is used in some window, then the script runs in the context of that window.

第 2 段(可获 1.44 积分)

If the module is given as the value of the "node-main" property of the application's manifest file, then the module runs in the Node's context but later has access to the window object. (See the “node-main” article for details.)

Features and limitations of the Node's context

Scripts running in the Node's context may use __dirname variable to read the path of their file's directory.

The Node.js global object is the global object in the Node's context. Any WebKit window'swindow object is not the global object and even is not implicitly available in the Node's context (the special case of node-main is the only exception), i.e. you have to (explicitly) pass the windowobject to your module's function if you need to access it.

第 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 积分)

Working around differences of contexts

While differences of contexts are generally beneficial, sometimes they may constitute a problem in your (or some other person's) code, and a need for a workaround arises.

The most common cause for such problems is the behaviour of the instanceof operator in JavaScript. As you may see in MDN, the operation someValue instanceof someConstructor tests whether an object has in its prototype chain the prototype property of the given constructor. However, if someValue is passed from a different JavaScript context, then it has its own line of ancestor objects, and the someValue instanceof someConstructor check fails inevitably.

第 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 积分)

When you foresee passing a value to some other context, you may providently use a constructor from that context in order to construct your value. The value then would easily pass anyinstanceof checks in that context.

For example, the well-known async module used (in its code dated 2013-05-20; it was fixed later) numerous .constructor checks (in lines 472505545675752) and thus failed whenever it encountered an array from another context. For example, if you run the following code from a node-webkit's window context,

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 积分)

It makes the async module happy.

However, in some cases you cannot (or won't) use the constructor directly to create your value. (For example, as you may see in MDN, using the Function constructor is less efficient than declaring a function, and it also does not create a closure.) In such cases another workaround is necessary.

Replacing __proto__

The non-standard (but widely implemented) __proto__ property of an object can be used (as you may see in MDN) to change the object's internal “[[Prototype]]” property (initially containing the prototype of its constructor).

第 12 段(可获 1.11 积分)

When you foresee passing a value to some other context, you may providently replace the value's__proto__ property with a constructor from that context. The value then would easily pass anyinstanceof checks in that context.

For example, the well-known async module used (in its code dated 2013-05-20; it was fixed later) an instanceof Function check (on line 428) and thus it failed whenever it encountered a function from another context. For example, if you run the following code from a node-webkit's window context,

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 积分)

It makes the async module happy.

Avoiding Node's setImmediate

Switching between WebKit's and Node's contexts takes some time.

In most cases this delay does not constitute a serious problem, but if you are in a WebKit's context and you are simply deferring some function's execution (in some hot spot where the same function is likely to be deferred hundreds of times; for example, when you draw some 500 objects giving the WebKit its chance to redraw the window after each object's appearance rather than forcing the user to stare on a blank screen mindlessly), then using Node's setImmediate (exported from some Node.js module) can actually become less “immediate” than you would be happy to experience.

第 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的标签呢?
班纳睿
是自动加的标签吗?