背景
html
近年来,前端技术突飞猛进,前端已经不只仅是网页,更多的开始由狭义向广义发展。 前后涌现出了具有后端能力的node,具有移动开发能力的react native,具有游戏渲染能力的cocos2d-js,以及iOS上的热修复技术JSPatch等等新技术。 咋一看,几乎各个端都被JavaScript攻陷,大有一统江湖之势。 究竟,JavaScript如何作到上天入地无所不能?JavaScript真的能一统江湖吗?前端
领域vue
表明技术html5
web前端node
各种MVVM框架,recat,angular,vue...react
故事要从JavaScript的由来讲起。android
乱世出英雄:git
JavaScript的诞生github
背景:web
1995年SUN开发了Java技术,这是第一个通用软件平台。Java拥有跨平台、面向对象、泛型编程的特性,普遍应用于企业级Web应用开发和移动应用开发。Java也伴随着互联网的迅猛发展而发展,逐渐成为重要的网络编程语言。名闻遐迩。
1994年Netscape公司成立,并推出了本身的浏览器的免费版本 Netscape Navigator,很快就占有了浏览器市场。到了 1995 年,微软公司开始加入,并很快发布了本身的 Internet Explorer 1.0。
1995年,当时在Netscape就任的Brendan Eich(布兰登·艾克),正为Netscape Navigator 2.0浏览器开发的一门名为LiveScript的脚本语言,后来Netscape与Sun Microsystems组成的开发联盟,为了让这门语言搭上Java这个编程语言“热词”,将其临时更名为“JavaScript”,往后这成为大众对这门语言有诸多误解的缘由之一。
JavaScript最初受Java启发而开始设计的,目的之一就是“看上去像Java”,所以语法上有相似之处,一些名称和命名规范也借自Java。但JavaScript的主要设计原则源自Self和Scheme。JavaScript与Java名称上的近似,是当时Netscape为了营销考虑与SUN达成协议的结果。
因此,JavaScript和Java其实没有半毛钱关系。
JavaScript推出后在浏览器上大获成功,微软在不久后就为Internet Explorer 3.0浏览器推出了JScript,以与处于市场领导地位的Netscape产品同台竞争。JScript也是一种JavaScript实现,这两个
JavaScript语言版本在浏览器端共存意味着语言标准化的缺失,对这门语言进行标准化被提上了日程,在1997年,由Netscape、SUN、微软、宝蓝等公司组织及我的组成的技术委员会在ECMA(欧洲计算机制造商协会)肯定定义了一种名叫ECMAScript的新脚本语言标准,规范名为ECMA-262。JavaScript成为了ECMAScript的实现之一。ECMA-262 第五版,便是ES5。
ECMA-262,包括ES5, ES6等是一个标准,JavaScript是ECMAScript的一个实现。
完整的JavaScript实现应包含三个部分:
名称
描述
ECMAScript
(语言核心)
便是基本语法,代码块,做用域,数据类型等。
在网景导航2.0和IE 3.0出现以后的几年间,网景和微软公司不停的发布新版本的浏览器,支持更多的新功能。自此拉开了浏览器之战的序幕。这场浏览器之战到如今还在继续,如下一张图看清楚过程。
从浏览器之战能够看出,各家浏览器比拼的大体两个方面视觉体验(渲染排版)和速度(脚本运行)。
因此一个完整的浏览器组成,至少包含两个部分:
浏览器
组成部分
描述
排版引擎
(内核)
全称是Layout engine,也称为浏览器内核(web browser engine)、页面渲染引擎(rendering engine)或样版引擎)是一种软件组件,负责获取标记式内容(如HTML、XML及图像文件等等)、整理信息(如CSS及XSL等),并将排版后的内容输出至显示器或打印机。全部网页浏览器、电子邮件客户端以及其它须要根据表示性的标记语言(Presentational markup)来显示内容的应用程序都须要排版引擎。
JavaScript引擎
JavaScript引擎是一个专门处理JavaScript脚本的虚拟机,通常会附带在网页浏览器之中。常见的有SpiderMonkey,V8等。
补充一个市面常见浏览器的内核和JavaScript引擎搭配:
浏览器名称
排版引擎
(内核)
JavaScript引擎
Google Chrome
使用webkit排版,借鉴Safari和Firefox部分红果
V8
其余JavaScript引擎,Rhino,由Mozilla基金会管理,开放源代码,彻底以Java编写,能够看作SpiderMonkey的Java版。 注意:webkit不仅仅只是一个排版引擎,webkit = 排版引擎 + JavaScript引擎。 > 因此,JavaScript是动态语言,它的运行都是基于JavaScript引擎,引擎大都是由静态语言实现C++、Java、and so on。JavaScript的能力也是由引擎赋予。无论是浏览器环境中是window,亦或是node环境中的process,均是由引擎提供。 (番外:Mozilla的人不知道为啥特别喜欢猴子,常常以猴子命名技术,因此看到带Monkey的,十有八九估计是他们搞的。)
诺曼底登录:
JAVASCRIPT BINDING/BRIDGE 桥接技术
在浏览器环境中,DOM、BOM、window对象、setTimeout/setInterval,alert,console等方法均不是JavaScript自身具有的能力,而是浏览器native实现,而后经过JavaScript引擎注入到JS运行的全局上下文中,供JS使用。 鉴别方式,在调试器console中打出来,带有[native code]的便是:
讲道理:
一、JavaScript运行 → 依赖于JavaScript引擎 ← 浏览器集成了JavaScript引擎,同时经过JavaScript引擎注入native代码工JS脚本使用
二、发散一下思惟,只要有JavaScript引擎,就能运行JS脚本,无论有没有浏览器!只是缺乏浏览器提供的alert,window等方法。
三、既然浏览器能够往JavaScript引擎中注入代码,赋予JS脚本在网页中特殊的能力,同理咱们能够本身集成JavaScript引擎,本身定义本身的方法往JavaScript引擎中注入,赋予JS更多更强的自定义能力!
注入的关键是:值类型相互对应,Obj映射class的一个实例,function映射一个句柄或者引用
JavaScript
C++
Java
number
int/long/float/double
int/long/float/double
JAVASCRIPT数值型中的坑
JavaScript内部,全部数字都是以64位浮点数形式储存,即便整数也是如此。因此,1与1.0是相同的,是同一个数。
这就是说,在JavaScript语言的底层,根本没有整数,全部数字都是小数(64位浮点数)。容易形成混淆的是,某些运算只有整数才能完成,此时JavaScript会自动把64位浮点数,转成32位整数,而后再进行运算。因为浮点数不是精确的值,因此涉及小数的比较和运算要特别当心。尽可能避免使用JavaScript作精准计算和密集计算。
根据国际标准IEEE 754,JavaScript浮点数的64个二进制位,从最左边开始,是这样组成的。
*第1位:符号位,0表示正数,1表示负数
*第2位到第12位:储存指数部分
*第13位到第64位:储存小数部分(即有效数字) 符号位决定了一个数的正负,指数部分决定了数值的大小,小数部分决定了数值的精度。 IEEE 754规定,有效数字第一位默认老是1,不保存在64位浮点数之中。也就是说,有效数字老是1.xx...xx的形式,其中xx..xx的部分保存在64位浮点数之中,最长可能为52位。所以,JavaScript提供的有效数字最长为53个二进制位(64位浮点的后52位+有效数字第一位的1)。
内部表现公式:(-1)^符号位 * 1.xx...xx * 2^指数位
精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-(253-1)到253-1,均可以精确表示。 而大部分的后端语言,C++、Java、Python等的long型都是能够支持到64位,所以long型数据从后端语言传给JavaScript会发生低位截断。遇到这种状况通常使用String处理,如须要在JavaScript中作long型计算,须要自行实现计算器。
有了自行往JavaScript引擎中注入的想法,接下来就是分析可行性。 大部分是JavaScript引擎是使用C++编写,若是本身的程序使用的是C++能够很方便的进行注入,若是是OC,可使用OC和C++混编的形式。 其余语言怎么破? 要在一门静态语言上与动态语言JavaScript相互调用,最便捷的方式是找到一个这门语言实现的JavaScript引擎(开源),直接进行集成,注入。若是没有,则须要使用多一层桥接,把这门语言的接口暴露给C++,再由C++实现的JavaScript引擎将接口注入供JavaScript使用。
服务端集成思路&实践:
nodeJS中的桥接
咱们都知道nodeJS,可是nodeJS的运行依赖于Google的V8 引擎,V8是C++实现,底层使用C++实现底层功能,好比网络,数据库IO,对外暴露一个构造器接口注入到上下文中,注意此处暴露的只是一个构造器接口而不是一个建立完的实例。而后实现了一个require的hook函数。当使用require加载一个JS模块时,跟网页中使用AMD 的require并没有异样,当使用require加载系统库,既是C++的模块时,会调用暴露出来的构造器接口,获得一个实例对象。无论是装载JS模块仍是装载C++模块,获得的均可以看作是一个Module Object,node会将装载完的模块缓存到binding_cache中,下次在别处的代码中使用require装载模块时,就会先去binding_cache中查找,若是找到了则返回该module object,若是没找到再执行上面的装载流程。 这就是node的基本原理:C++封装底层操做,经过V8注入,使得JS脚本有网络和IO能力。
基于Spring的桥接
以上说到的几个都是C++层面的应用,那么经典的Java怎么玩?是否是Java就必须是静态语言的玩法,没有办法像C++之类的,可使用JS的动态特性? 固然不是。这个时候,咱们须要提及前面介绍过的一个JS引擎 Rhino,Rhino是彻底由Java编写,可想而知,Rhino几乎就是为Java应用而生的。 用法是这样:
一、首先在咱们的Java应用中集成Rhino;
二、全部的IO操做,网络操做等,都封装成service,并提供增删改查,setter && getter等多种方法
三、经过spring,把这些service bean注入到Rhino中;
四、把业务逻辑写到JS代码中,JS代码调用多个已注入的Java service处理业务逻辑,拼装数据返回!
好处:修改业务逻辑不须要修改Java代码,也就是不须要从新编译和部署,只须要刷新下跑在Rhino中的JS代码便可。以往Java应用的一个痛点是部署,须要从新编译,打包,部署重启服务器,如今以这种形式开发,能够达到服务端的热更新和热部署。既能够享有Java服务的稳定性和可靠性,又能够享有JS的灵活性。 这种技术和用法在差很少十年前就有过,前EMC的工程师基于EMC著名的商业产品Documentum,设计了一套Java开源的中小企业CMS系统Alfresco,在该系统中实现了这种技术,这种技术基于spring,叫作spring-surf,作了一个胶水层。能够看作小十年前的node吧。
Demo,使用spring-surf框架的系统中一个webscript模块。
一、categorynode.get.xml定义URL拦截器和权限控制;
二、.get指明是处理GET请求,RESTful;
三、在categorynode.get.js中调用已注入的Java Bean处理业务逻辑;
四、若为网页请求返回.html.ftl,若为Ajax,返回.json.ftl;
(此处配套使用的是FreeMarker模板引擎)
categorynode.get.desc.xml
categorynode.get.js
categorynode.get.html.ftl
categorynode.get.json.ftl
移动端集成思路&实践:
React Native中的桥接
React Native目前也是异常火爆,RN程序的运行依赖于Facebook的RN框架。在iOS、Android的模拟器或是真机上,React Native使用的是JavaScriptCore引擎,也就是Safari所使用的JavaScript引擎。可是在iOS上JavaScriptCore并无使用即时编译技术(JIT),由于在iOS中应用无权拥有可写可执行的内存页(于是没法动态生成代码),在安卓上,理论上是可使用的。JavaScriptCore引擎也是使用C++编写,在iOS和安卓中,JavaScriptCore都作了一层封装,能够无须关心引擎和系统桥接的那一层。iOS/Android系统经过JavaScriptCore引擎将定制好的各类原生组件注入,如:listview,text等。
Cocos2d-JS中的桥接
cocos2dx是游戏开发中很是经常使用的游戏渲染引擎,有一系列的产品,如:cocos2dx(C++),cocos2d-lua(lua), cocos2d-js(JavaScript)等多个产品。其中最新退出的是cocos2dx的JS版本的cocos2d-js,编写游戏渲染特效代码相比于C++和lua很是方便。对于作须要常常更新的渲染场景,C++是静态语言,每次修改都须要从新编译才能运行,显然是不合适的。天然也就想到了脚本语言,lua和js,二者有些相似,都是动态语言,只须要集成一个运行引擎,提供一个运行的容器便可运行,同时经过引擎注入底层方法供脚本调用便可。lua好处是精简,语法精简,引擎页很小很精简,因此不可避免的代码量会比js多,同时学习成本比较高。js的好处是有ECMAScrtpt的核心,语法比较丰富,同时有支持一些高级属性。在cocos2d-js中,cocos2dx(C++)集成了SpiderMonkey(C++)做为JS运行引擎,中间作了一个胶水层既是JS Binding,经过引擎注入了一个cc的全局对象,映射的是底层C++的一个单例C++实例。表面上写的是JS代码,实际上操做的是底层的C++。cocos2d-js是代码能够运行在多种环境中,当运行的网页环境中时,使用的是cocos2d-html5引擎,底层操做的是canvas;当运行在客户端上时,使用的是cocos2dx引擎,底层操做的是C++,再由C++去操控openGL作绘制和渲染。提供相同的API,对开发者几乎是透明无差别的,开发者只须要关注实现效果便可。达到一套代码,多端运行(网页端,客户端)。
JSPatch技术中的桥接
JSPatch是目前比较流行的iOS上的热修复技术,JSPatch 能作到经过 JS 调用和改写 OC 方法最根本的缘由是 Objective-C 是动态语言,OC 上全部方法的调用/类的生成都经过 Objective-C Runtime 在运行时进行,咱们能够经过类名/方法名反射获得相应的类和方法。JSPatch 的基本原理就是:JS 传递字符串给 OC,OC 经过 Runtime 接口调用和替换 OC 方法。
关键技术之一是 JS 和 OC 之间的消息互传。JSPatch里包含了,一个JS引擎JavaScriptCore(Safari,React Native用的同款)。用到了 JavaScriptCore 的接口,OC 端在启动 JSPatch 引擎时会建立一个 JSContext 实例,JSContext 是 JS 代码的执行环境,能够给 JSContext 添加方法,JS 就能够直接调用这个方法。本质上就是经过JavaScriptCore引擎注入,暴露OC的方法供JS调用来实现动态修改OC的反射。
Demo,iOS热更新,热修复:
一、集成JavaScriptCore引擎;
二、经过引擎,桥接JS和OC;
三、经过JS修改OC反射。
详细的JSPatch技术介绍请移步:https://github.com/bang590/JSPatch/wiki
关于JavaScript引擎:
在iOS 或 android 上可以运行的JavaScript 引擎有4个:JavaScriptCore,SpiderMonkey,V8,Rhino。下面这个表格展现各个引擎在iOS 和 Android 的兼容性。
JavaScript引擎
iOS
Android
JavaScriptCore
Interpreter only(仅解释器模式)
Interpreter and JIT(解释器模式和即时编译模式)
由于iOS平台不支持JIT即时编译,而V8只有JIT模式,因此V8没法在iOS平台使用(越狱设备除外,想体验iOS JIT的同窗能够自行越狱)。 因此,目前能够作到横跨iOS和Android双平台的JS引擎,只有两款,便是SpiderMonkey和JavaScriptCore。 JavaScript引擎会受不少东西影响,好比交叉编译器的版本、引擎的版本和操做系统的种类等。
至于如何选择,能够参考:《Part I: How to Choose a JavaScript Engine for iOS and Android Development》
至此,JavaScript从立足于前端,到征战全端的逆袭之路,能够总结为“携引擎以令天下”。 不足之处,还请各位看官轻拍~