让你的Javascript计算性能提升70%

  现在的JavaScript代码要进行性能优化,通常使用一些常规手段,如:延迟执行、预处理、setTimeout等异步方式避免处理主线程,高大上一点的会使用WebWorker。即使对于WebWorker也仅仅是解决了阻塞主线程的问题,但是对于JavaScript计算性能慢的问题并没有解决。这里对一些需要密集计算的场景我给大家推荐一个神器——WebAssembly。在目前阶段,WebAssembly 适合大量密集计算、并且无需频繁与 JavaScript 及 DOM 进行数据通讯的场景。比如游戏渲染引擎、物理引擎、图像音频视频处理编辑、加密算法等

WebAssembly是什么?

  WebAssembly是一种运行在现代网络浏览器中的新型代码并且提供新的性能特性和效果。它设计的目的不是为了手写代码而是为诸如C、C++和Rust等低级源语言提供一个高效的编译目标。WebAssembly的模块可以被导入的到一个网络app(或Node.js)中,并且暴露出供JavaScript使用的WebAssembly函数。JavaScript框架不但可以使用WebAssembly获得巨大性能优势和新特性,而且还能使得各种功能保持对网络开发者的易用性。这是来自MDN的介绍。但你是不是看了官方介绍也不知道WebAssembly到底是个什么东西呢,没关系开始我也这么觉得。简单来说WebAssembly就是浏览器提供的一项直接运行二进制机器代码的能力。这些机器代码怎么来呢,是通过C、C++或Rust等语言编译来的。

  那么如何编译呢,首先你得学会写C语言代码,然后你得用一系列工具把它编译成二进制代码。这个过程绝对不会是一帆风顺的,因为根据我摸爬滚打的经验来说,这玩意儿从头到尾都是坑。WebAssembly的编译过程需要用到以下工具:

  哦对了,还要装Visual Studio2015,千万别看这vs17新就装了个17,因为Emscripten目前跟vs15的结合性最好。

安装工具链

  在所有工具链之前,需要安装下面几个工具:

  然后下载编译Emscripten,这玩意儿需要FQ,然后特别大,得慢慢等个半小时差不多,然后千万别按照它官网的介绍来安装,要按照MDN上的方式来安装,这样安装下来直接带有Binaryen了(我只会Windows平台的配置):

git clone https://github.com/juj/emsdk.git
cd emsdk

# on Linux or Mac OS X
./emsdk install --build=Release sdk-incoming-64bit binaryen-master-64bit
./emsdk activate --global --build=Release sdk-incoming-64bit binaryen-master-64bit

# on Windows
emsdk install --build=Release sdk-incoming-64bit binaryen-master-64bit
emsdk activate --global --build=Release sdk-incoming-64bit binaryen-master-64bit

  先克隆,克隆之后打开文件夹,运行里面的emcmdprompt.bat,打开的命令行里面可以运行install和active命令:

emsdk install --build=Release sdk-incoming-64bit binaryen-master-64bit
emsdk activate --global --build=Release sdk-incoming-64bit binaryen-master-64bit

  然后添加几个环境变量:

D:\emsdk-portable\binaryen\master_vs2015_64bit_binaryen\bin;d:\emsdk-portable;d:\emsdk-portable\clang\fastcomp\build_incoming_vs2015_64\Release\bin;d:\emsdk-portable\node\4.1.1_64bit\bin;d:\emsdk-portable\python\2.7.5.3_64bit;d:\emsdk-portable\java\7.45_64bit\bin;d:\emsdk-portable\emscripten\incoming;d:\emsdk-portable\binaryen\master;

  在实际的运行中你可能遇到这个错误:

CMake does not find Visual C++ compiler

  那么你需要新建一个Vs15的c++工程,按照这里说的运行一下:Stack Overflow

  I have found the solution. While Visual Studio IDE installed successfully it did not install any build tools and therefore did not install the C++ compiler. By attempting to manually create a C++ project in the Visual Studio 2015 GUI I was able to prompt it to download the C++ packages. Cmake was then able to find the compiler without any difficulty.

  这样一些跟WebAssembly相关的常见命令就可以运行了,本文的不详细解释WebAssembly,只说一些踩过的坑,具体比较详细的我认为这篇文章很不错——WebAssembly 实践:如何写代码。里面的代码在这里——wasm-examples.我们来看一些性能比较,大家同时运行斐波那契数列:

// JavaScript版本
function () {

  function fib (n) {
    if (n < 2) {
      return 1
    }
    return fib(n - 2) + fib(n - 1)
  }

  return { fib }
}
// C版本
int fibonacci (int n, int a, int b) {
    if (n <= 2) {
        return b;
    }
    return fibonacci(n - 1, b, a + b);
}

int fib (int n) {
  return fibonacci(n, 1, 1);
}

  那么它们的性能对比如下:

  一般来讲斐波那契数列计算到40已经是很大的运算量了,可以看出由C直接转化成wasm二进制机器码的计算性能比纯原生js快了接近70%。有的同学可能会觉得这里应该使用尾递归优化,我做过实验,尾递归优化的js在计算40时,差不多是几毫秒,但是同样尾递归优化的c编译成wasm几乎是0。

WebAssembly实际应用

  通常来讲在普通的前端业务中根本不需要使用WebAssembly,但是在一些需要极大的计算能力的场景,比如Web地图、WebAR、Web图像识别中传统方案下js的计算性能无法达到要求,那么这时候就是WebAssembly展现应用能力的时候了。对我们来说在实际中最有用的方式不是简单计算一个数值,而是希望用c来处理一批数据,然后在JavaScript侧能够通过ArrayBuffer形式使用。对此百度地图团队有一篇文章——地图引擎使用WebAssembly的经验分享(WebGL地图引擎技术番外篇之二)专门介绍了它们的一个使用场景。

  这里呢它们提供了一种实践路线:

方案三:C/C++编译

目前主流方案是使用 Emscripten 将 c/c++ 代码编译成 asm.js 或 wasm。一开始没有使用 emscripten 主要是调研期间遇到了两个问题:

ONLY_MY_CODE 模式编译出的 asm.js 代码不是合法的 asm.js 代码
emscripten 默认的编译模式会将一些依赖库代码以及加载 asm.js 的代码一起编译出来,这对于我们这种只需要一个 asm.js 模块的需求来说是不必要的。emscripten 有ONLY_MY_CODE模式可以选择,顾名思义,这个模式就是只会编译出模块代码而不会增加其他多余代码。但是在调研过程中发现这个模式编译出的 asm.js 代码的类型标注有问题,导致代码不合法。
解决方案:官方 github 给出的解答是 ONLY_MY_CODE 模式没有经过严格的测试,可以使用 SIDE_MODULE 选项来达到类似的效果。经过测试这个模式虽然也会增加少量额外代码但是可以解决问题。
emscripten 直接编译 wasm 要求分配内存大于 16MB
emacripten 加上-s WASM=1可以支持直接编译 wasm,但是强制要求分配的内存大于16MB,这对于一些不需要这么大内存的模块来说会造成浪费。
解决方案:放弃使用 emscripten 编译 wasm,现在采用的编译步骤是:
使用 emscripten 将 c++ 代码编译成 asm.js
使用 binaryen 将 asm.js 编译成与 wasm 等价的文本格式 wat 代码
使用 wabt 将 wat 代码编译成二进制 wasm
解决了这两个问题之后,采用此方案就可以达到写一次代码编译同时得到 asm.js 和 wasm 的目的了。

  然而很不幸,这条路在我实践过程中走不通,无论怎样Emscripten都无法产出纯净的asm代码,总是会带有一些胶水代码。比如C代码:

// 分配内存,此数组占0x1000*4=16384byte
static int s_array[0x1000];
static int s_current_index = 0;
int* get_start();
int* get_end();
void generate_array(int);
// 暴露给JS使用,得到数组的开始偏移量
int* get_start() {
    return s_array;
}
// 暴露给JS使用,得到数组的结束偏移量
int* get_end() {
    return &s_array[s_current_index];
}
// 将生成的数组放进内存中
void generate_array(int count) {
    for (int i = 0; i < count; ++i) {
        s_array[i] = i;
    }
    s_current_index = count;
}

  最终经过Emscripten编译成的Asm.js是如下代码:

// Capture the output of this into a variable, if you want
(function(fb, parentModule) {
  var Module = {};
  var args = [];
  Module.arguments = [];
  Module.print = parentModule.print;
  Module.printErr = parentModule.printErr;

  Module.cleanups = [];

  var gb = 0;
  // Each module has its own stack
  var STACKTOP = getMemory(TOTAL_STACK);
  assert(STACKTOP % 8 == 0);
  var STACK_MAX = STACKTOP + TOTAL_STACK;
  Module.cleanups.push(function() {
    parentModule[‘_free‘](STACKTOP); // XXX ensure exported
    parentModule[‘_free‘](gb);
  });

// === Auto-generated preamble library stuff ===

//========================================
// Runtime essentials
//========================================

// === Body ===

var ASM_CONSTS = [];

gb = Runtime.alignMemory(getMemory(16400, 4 || 1));

// STATICTOP = STATIC_BASE + 16400;
/* global initializers */  __ATINIT__.push();

/* memory initializer */ allocate([], "i8", ALLOC_NONE, gb);

/* no memory initializer */
// {{PRE_LIBRARY}}

var ASSERTIONS = true;

// All functions here should be maybeExported from jsifier.js

/** @type {function(string, boolean=, number=)} */
function intArrayFromString(stringy, dontAddNull, length) {
  var len = length > 0 ? length : lengthBytesUTF8(stringy)+1;
  var u8array = new Array(len);
  var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length);
  if (dontAddNull) u8array.length = numBytesWritten;
  return u8array;
}

function intArrayToString(array) {
  var ret = [];
  for (var i = 0; i < array.length; i++) {
    var chr = array[i];
    if (chr > 0xFF) {
      if (ASSERTIONS) {
        assert(false, ‘Character code ‘ + chr + ‘ (‘ + String.fromCharCode(chr) + ‘)  at offset ‘ + i + ‘ not in 0x00-0xFF.‘);
      }
      chr &= 0xFF;
    }
    ret.push(String.fromCharCode(chr));
  }
  return ret.join(‘‘);
}

Module["intArrayFromString"] = intArrayFromString;
Module["intArrayToString"] = intArrayToString;
var setTempRet0 = Runtime.setTempRet0, getTempRet0 = Runtime.getTempRet0;

Module.asmGlobalArg = { "Math": Math, "Int8Array": Int8Array, "Int16Array": Int16Array, "Int32Array": Int32Array, "Uint8Array": Uint8Array, "Uint16Array": Uint16Array, "Uint32Array": Uint32Array, "Float32Array": Float32Array, "Float64Array": Float64Array, "NaN": NaN, "Infinity": Infinity };

Module.asmLibraryArg = { "abort": abort, "assert": assert, "enlargeMemory": enlargeMemory, "getTotalMemory": getTotalMemory, "abortOnCannotGrowMemory": abortOnCannotGrowMemory, "abortStackOverflow": abortStackOverflow, "setTempRet0": setTempRet0, "getTempRet0": getTempRet0, "DYNAMICTOP_PTR": DYNAMICTOP_PTR, "tempDoublePtr": tempDoublePtr, "ABORT": ABORT, "STACKTOP": STACKTOP, "STACK_MAX": STACK_MAX, "gb": gb, "fb": fb };
// EMSCRIPTEN_START_ASM
var asm = (/** @suppress {uselessCode} */ function(global, env, buffer) {
‘almost asm‘;

  var HEAP8 = new global.Int8Array(buffer);
  var HEAP16 = new global.Int16Array(buffer);
  var HEAP32 = new global.Int32Array(buffer);
  var HEAPU8 = new global.Uint8Array(buffer);
  var HEAPU16 = new global.Uint16Array(buffer);
  var HEAPU32 = new global.Uint32Array(buffer);
  var HEAPF32 = new global.Float32Array(buffer);
  var HEAPF64 = new global.Float64Array(buffer);

  var DYNAMICTOP_PTR=env.DYNAMICTOP_PTR|0;
  var tempDoublePtr=env.tempDoublePtr|0;
  var ABORT=env.ABORT|0;
  var STACKTOP=env.STACKTOP|0;
  var STACK_MAX=env.STACK_MAX|0;
  var gb=env.gb|0;
  var fb=env.fb|0;

  var __THREW__ = 0;
  var threwValue = 0;
  var setjmpId = 0;
  var undef = 0;
  var nan = global.NaN, inf = global.Infinity;
  var tempInt = 0, tempBigInt = 0, tempBigIntS = 0, tempValue = 0, tempDouble = 0.0;
  var tempRet0 = 0;

  var Math_floor=global.Math.floor;
  var Math_abs=global.Math.abs;
  var Math_sqrt=global.Math.sqrt;
  var Math_pow=global.Math.pow;
  var Math_cos=global.Math.cos;
  var Math_sin=global.Math.sin;
  var Math_tan=global.Math.tan;
  var Math_acos=global.Math.acos;
  var Math_asin=global.Math.asin;
  var Math_atan=global.Math.atan;
  var Math_atan2=global.Math.atan2;
  var Math_exp=global.Math.exp;
  var Math_log=global.Math.log;
  var Math_ceil=global.Math.ceil;
  var Math_imul=global.Math.imul;
  var Math_min=global.Math.min;
  var Math_max=global.Math.max;
  var Math_clz32=global.Math.clz32;
  var abort=env.abort;
  var assert=env.assert;
  var enlargeMemory=env.enlargeMemory;
  var getTotalMemory=env.getTotalMemory;
  var abortOnCannotGrowMemory=env.abortOnCannotGrowMemory;
  var abortStackOverflow=env.abortStackOverflow;
  var setTempRet0=env.setTempRet0;
  var getTempRet0=env.getTempRet0;
  var tempFloat = 0.0;

// EMSCRIPTEN_START_FUNCS

function stackAlloc(size) {
  size = size|0;
  var ret = 0;
  ret = STACKTOP;
  STACKTOP = (STACKTOP + size)|0;
  STACKTOP = (STACKTOP + 15)&-16;
  if ((STACKTOP|0) >= (STACK_MAX|0)) abortStackOverflow(size|0);

  return ret|0;
}
function stackSave() {
  return STACKTOP|0;
}
function stackRestore(top) {
  top = top|0;
  STACKTOP = top;
}
function establishStackSpace(stackBase, stackMax) {
  stackBase = stackBase|0;
  stackMax = stackMax|0;
  STACKTOP = stackBase;
  STACK_MAX = stackMax;
}

function setThrew(threw, value) {
  threw = threw|0;
  value = value|0;
  if ((__THREW__|0) == 0) {
    __THREW__ = threw;
    threwValue = value;
  }
}

function _get_start() {
 var label = 0, sp = 0;
 sp = STACKTOP;
 return ((gb + (0) | 0)|0);
}
function _get_end() {
 var $0 = 0, $1 = 0, label = 0, sp = 0;
 sp = STACKTOP;
 $0 = HEAP32[(gb + (16384) | 0)>>2]|0;
 $1 = ((gb + (0) | 0) + ($0<<2)|0);
 return ($1|0);
}
function _generate_array($0) {
 $0 = $0|0;
 var $1 = 0, $10 = 0, $11 = 0, $2 = 0, $3 = 0, $4 = 0, $5 = 0, $6 = 0, $7 = 0, $8 = 0, $9 = 0, label = 0, sp = 0;
 sp = STACKTOP;
 STACKTOP = STACKTOP + 16|0; if ((STACKTOP|0) >= (STACK_MAX|0)) abortStackOverflow(16|0);
 $1 = $0;
 $2 = 0;
 while(1) {
  $3 = $2;
  $4 = $1;
  $5 = ($3|0)<($4|0);
  if (!($5)) {
   break;
  }
  $6 = $2;
  $7 = $2;
  $8 = ((gb + (0) | 0) + ($7<<2)|0);
  HEAP32[$8>>2] = $6;
  $9 = $2;
  $10 = (($9) + 1)|0;
  $2 = $10;
 }
 $11 = $1;
 HEAP32[(gb + (16384) | 0)>>2] = $11;
 STACKTOP = sp;return;
}
function runPostSets() {
 var temp = 0;
}

// EMSCRIPTEN_END_FUNCS

  return { runPostSets: runPostSets, establishStackSpace: establishStackSpace, stackSave: stackSave, stackRestore: stackRestore, _get_end: _get_end, setThrew: setThrew, stackAlloc: stackAlloc, _generate_array: _generate_array, _get_start: _get_start };
})
// EMSCRIPTEN_END_ASM
(Module.asmGlobalArg, Module.asmLibraryArg, buffer);

var real_setThrew = asm["setThrew"]; asm["setThrew"] = function() {
  assert(runtimeInitialized, ‘you need to wait for the runtime to be ready (e.g. wait for main() to be called)‘);
  assert(!runtimeExited, ‘the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)‘);
  return real_setThrew.apply(null, arguments);
};

var real__generate_array = asm["_generate_array"]; asm["_generate_array"] = function() {
  assert(runtimeInitialized, ‘you need to wait for the runtime to be ready (e.g. wait for main() to be called)‘);
  assert(!runtimeExited, ‘the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)‘);
  return real__generate_array.apply(null, arguments);
};

var real__get_start = asm["_get_start"]; asm["_get_start"] = function() {
  assert(runtimeInitialized, ‘you need to wait for the runtime to be ready (e.g. wait for main() to be called)‘);
  assert(!runtimeExited, ‘the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)‘);
  return real__get_start.apply(null, arguments);
};

var real__get_end = asm["_get_end"]; asm["_get_end"] = function() {
  assert(runtimeInitialized, ‘you need to wait for the runtime to be ready (e.g. wait for main() to be called)‘);
  assert(!runtimeExited, ‘the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)‘);
  return real__get_end.apply(null, arguments);
};
var setThrew = Module["setThrew"] = asm["setThrew"];
var _generate_array = Module["_generate_array"] = asm["_generate_array"];
var runPostSets = Module["runPostSets"] = asm["runPostSets"];
var _get_start = Module["_get_start"] = asm["_get_start"];
var _get_end = Module["_get_end"] = asm["_get_end"];

var NAMED_GLOBALS = {  };
for (var named in NAMED_GLOBALS) {
  Module[‘_‘ + named] = gb + NAMED_GLOBALS[named];
}
Module[‘NAMED_GLOBALS‘] = NAMED_GLOBALS;
;

Runtime.registerFunctions([], Module);

// === Auto-generated postamble setup entry stuff ===

__ATPRERUN__.push(runPostSets);

if (runtimeInitialized) {
  // dlopen case: we are being loaded after the system is fully initialized, so just run our prerun and atinit stuff now
  callRuntimeCallbacks(__ATPRERUN__);
  callRuntimeCallbacks(__ATINIT__);
} // otherwise, general dynamic linking case: stuff we added to prerun and init will be executed with the rest of the system as it loads

  // {{MODULE_ADDITIONS}}

  return Module;
});

  往后用Binaryen这一步总是不成功。而后经过我不断探索,发现了另一个神器:WebAssembly Explorer。他是一个在线的wasm编译器,能够很完美编译出我们想要的wasm,而且没有胶水代码。

  这里C代码的环境最好选择C99,这样编译出来的函数名跟你C模块中的函数名是一致的,否则会有一些不一样。

  然后我们可以在页面使用这个模块:

<html>
  <head>
    <meta charset="UTF-8">
    <title>Game of Life</title>
  </head>
  <body>
    <canvas id="game"></canvas>
    <script>
        // 通过fetch获取wasm模块
        fetch(‘./test.wasm‘).then(function (response) {
            return response.arrayBuffer();
        }).then(function (bytes) {
            // 初始化内存,1个单位代表64kb=65536byte
            var memory = new WebAssembly.Memory({initial: 1, maximum: 1});
            WebAssembly.instantiate(bytes, {
                env: {
                    // memory 实例
                    // memory: memory,
                    // table 实例
                    table: new WebAssembly.Table({
                        initial: 0,
                        maximum: 0,
                        element: ‘anyfunc‘
                    }),
                    // 以下都是编译生成的wasm所需要的变量,不需要可以直接传0
                    abortStackOverflow: function () {},
                    DYNAMICTOP_PTR: 0,
                    tempDoublePtr: 0,
                    ABORT: 0,
                    STACKTOP: 0,
                    STACK_MAX: 0,
                    gb: 0,
                    fb: 0,
                    memoryBase: 0,
                    tableBase: 0
                },
                global: {
                    NaN: NaN,
                    Infinity: Infinity
                }
            }).then(function (results) {
                // 得到编译后的wasm模块实例
                var module = results.instance.exports;
                // 调用模块函数,生成从0到99的数组
                module.generate_array(100);
                // 通过slice偏移量得到最终的生成的数组
                var generatedArray = new Int32Array(module.memory.buffer).slice(module.get_start() >> 2, module.get_end() >> 2);
                console.log(generatedArray);
            });
        });
    </script>
  </body>
</html>

  然后呢我们在控制台得到如下结果:

  这里需要注意的是,如果你按照百度文章里面的代码来写,你是跑不出这个结果,为什呢,我觉得百度这位同学估计也没理解好WebAssembly的memory这个概念。

 var generatedArray = new Int32Array(memory.buffer).slice(module._get_start() >> 2, module._get_end() >> 2);

  这行是它的文章中的代码,这里使用的memory.buffer根本不是wasm这个模块module的内存。

  这里应当使用的是module.memory.buffer!!!!!!!!!!!!!!!

  这里应当使用的是module.memory.buffer!!!!!!!!!!!!!!!

  这里应当使用的是module.memory.buffer!!!!!!!!!!!!!!!

  因为这个问题,把我折腾到两点,果然不能全信百度。但终归来讲还是要谢谢这篇文章,解了我对WebAssembly的一个疑惑。

WebAssembly的兼容性

  总的来说这个新特性的发展前景是比较好的,头一次由所有的浏览器厂商都打成一致意见。而且未来还会让我们在里面操作DOM。目前阶段来说,看下图喽

参考资料

下面就是我搜集的跟WebAssembly相关的一些比较好的资料(作为一个技术我最烦那些只讲技术历史不讲实际内容的文章)

设计文档

https://github.com/WebAssembly/design

webassembly和webworker

https://stackoverflow.com/questions/47083951/how-to-use-webassembly-wasm-code-in-a-web-worker

wasm webworker js性能测试比较案例

https://tech.foodora.com/webassembly/

https://github.com/eliamaino-fp/webassembly-js/issues

wasm社区

http://webassembly.org/docs/semantics/

emscripten

https://kripken.github.io/emscripten-site/docs/api_reference/fetch.html

传递js数组作为wasm函数的参数

https://stackoverflow.com/questions/41875728/pass-a-javascript-array-as-argument-to-a-webassembly-function

wasm example

https://github.com/mdn/webassembly-examples

另外wasm例子

https://github.com/Hanks10100/wasm-examples

wasm MDN

https://developer.mozilla.org/en-US/docs/WebAssembly/Concepts

wasm实践——segment fault

https://segmentfault.com/a/1190000008402872#articleHeader20

wasm canvas demo

https://github.com/cunzaizhuyi/wasm-canvasDemo

时间: 2024-12-26 15:22:57

让你的Javascript计算性能提升70%的相关文章

回归基础: JavaScript 变量提升

from me: javascript的变量声明具有hoisting机制,它是JavaScript一个基础的知识点,也是一个比较容易犯错的点,平时在开发中,大大小小的项目都会遇到. 它是JavaScript引擎在执行的时候,把所有变量的声明都提升到当前作用域的最前面. 当然了,函数声明也是可以被提升的.然后,函数表达式却没有提升. 原文:Back to Basics: JavaScript Hoisting 译文:回归基础: JavaScript 变量提升 变量声明是所有的编程语言中最基础部分之

JavaScript 变量提升

变量提升(Hoisting):在ES6之前,函数声明和变量声明总是被JavaScript解释器隐式地提升(hoist)到包含他们的作用域的最顶端. 注意: 1. JavaScript 仅提升声明,而不提升初始化.2. ES6 中不存在变量提升的概念. 1. 变量提升 变量未声明: function fn () { console.log(name); } fn(); // 报错: ReferenceError: name is not defined 变量在使用后声明: function fn

JavaScript变量提升及作用域

今天在知乎看前端面试题的时候,看到这样的问题,发现自己懂的真的是太少了,看了给的例子,所以写一下自己的理解. 首先放一段代码: var v= "hello JavaScript"; alert(v); 很明显,这样的是会弹出对话框: 将alert(v)写到一个函数中: var v= "hello JavaScript"; function test(){ alert(v); }; test(); 这样弹出的结果肯定也是和第一个一样:那么下面这种方式输出的结果是什么?

javascript水平提升

学习javascript分几个阶段,没入门,入门初学者,中级水平,高级水平,ppt水平. 1,没入门的如何学习? 我当初是先学jquery,有css和html基础,有css基础看jq的语法很简单,就是选择符,jq的api懂高中英语就够了,猜也猜的出来意思,然后下载基本jq的chm手册,对着挨个看一遍.(这个挨个看一遍的意思就是一行一句的逐个看一遍,看不懂的别死扣,先看完再说). 看完一遍之后,心中有数,哦,原来有个ajax函数可以访问网络资源,有个success可以写回调方法,超时怎么办?几个一

关于JavaScript变量提升的理解

废话不说,直接上代码(这是在JavaScript面对对象编程指南上面看到的一个例子) var a=123; function f(){ alert(a); var a=1; alert(a); } f(); 书上的解释是这样的:当JavaScript执行过程进入新函数时,这个函数内被声明的所有变量都会被移动导到函数最开始的地方.这种现象叫做提升.且被提升的只有变量的声明. 上面这个例子可以等价于: var a=123;function f(){ var a; alert(a); a=1; ale

对javascript变量提升跟函数提升的理解

在写javascript代码的时候,经常会碰到一些奇怪的问题,例如: console.log(typeof hello); var hello = 123;//变量 function hello(){//函数声明 } console.log(typeof hello); var hello = function(){//函数表达式 } console.log(typeof hello);//function number function 对于为什么会是这样的一个结果:function numb

Web性能优化系列:10个JavaScript性能提升的技巧

由 伯乐在线 - Delostik 翻译,黄利民 校稿.未经许可,禁止转载!英文出处:jonraasch.com.欢迎加入翻译小组. Nicholas Zakas是一位 JS 大师,Yahoo! 首页的前端主程.他是<高性能 Javascript>的作者,这本书值得每个程序员去阅读. 当谈到 JS 性能的时候,Zakas差不多就是你要找的,2010年六月他在Google Tech Talk发表了名为<Speed Up Your Javascript>的演讲. 但 Javascrip

javascript变量提升详解

js变量提升 对于大多数js开发者来说,变量提升可以说是一个非常常见的问题,但是可能很多人对其不是特别的了解.所以在此,我想来讲一讲. 先从一个简单的例子来入门: a = 2; var a; console.log(a); 你觉得以上的代码会输出什么?是输出undefined吗?如果是按照程序的自上而下执行的话,那么这一段代码确实是输出undefined.然而,javascript并不是严格的自上而下执行的语言. 这一段代码的输出结果是2,是不是感到很意外?为什么会这样呢?这个问题的关键就在于变

【译】学习JavaScript中提升、作用域、闭包的终极指南

这似乎令人惊讶,但在我看来,理解JavaScript语言最重要和最基本的概念是理解执行上下文.通过正确学习它,你将很好地学习更多高级主题,如提升,作用域链和闭包.考虑到这一点,究竟什么是"执行上下文"?为了更好地理解它,我们首先来看看我们如何编写软件. 编写软件的一种策略是将代码分解为单独的部分.虽然这些"部分"有许多不同的名称(功能,模块,包等),但它们都是为了一个目的而存在 - 分解和管理应用程序的复杂性.现在,不要像编写代码的人那样思考,而是根据JavaScr