《WebKit 技术内幕》学习之十(3): 插件与JavaScript扩展

3 JavaScript引擎的扩展机制

3.1 混合编程

        混合编程由来已久,因为浏览器能力的不足,特别是以前的浏览器甚至不支持内嵌视频和音频等技术,所以导致需要Flash等插件来扩展网页的能力。当然Flash插件是由第三方提供的,大家都可以使用。还有一种使用场景,那就是网页的开发者在使用HTML/JS/CSS开发网页的时候,发现能力不足,希望使用传统语言例如C/C++来开发一些库,这些库可以被网页调用,这样来满足应用的要求,这里称之为混合编程。

        从上面的介绍可以看出,NPAPI和PPAPI也能够提供混合编程的能力,也就是说,开发者能够将一些本地代码提供的能力提供给Web开发者,示例代码10-4描述了使用PPAPI技术的混合编程。

示例代码10-4 使用插件机制来扩展JavaScript的功能

    <script>var exam = null;window.onload = function() {exam = document.getElementById("example");exam.addEventListener('message', handleMessage, false);}function handleMessage(message) {… // handle results here}function function1() {if (exam) exam.postMessage("function1");}function function2() {if (exam) exam.postMessage("function2");}</script><embed id="example" type="application/x-example"/>class MyInstance : public pp::Instance {public:virtual bool HandleMessage(const pp::Var& var_message) {if (var_message == "function1") {…} else if (var_message == "function2") {…}} };

        从上面实例中可以看到它们的工作方式,两个JavaScript函数“function1”和“function2”可以被调用,这两个函数的实现通过C++代码来完成,因为PPAPI和NPAPI插件能够调用任何系统接口,所以开发者甚至能够将任何能力提供给JavaScript代码调用。

        从某种程度来说,使用插件机制来扩展JavaScript接口有个明显的缺陷,就是需要在DOM树中加入一个“embed”节点,而且需要提前完成插件的加载和初始化(这在上面的示例代码中没有很好地体现)。但是从技术上来讲,开发者可以通过插件机制在JavaScript引擎中注入一些JavaScript对象和方法,使得这些JavaScript对象和方法能够调用本地代码才能提供的能力,这的确是一种不错的扩展JavaScript引擎方式。

3.2 JavaScript扩展机制

        下面看看V8引擎和JavaScriptCore引擎是如何提供机制来扩展JavaScript引擎的能力,也就是如何使用本地代码来扩充引擎中的对象和函数。V8提供了两种方式,第一种是JavaScript绑定,第二种是V8的Extension机制,而JavaScriptCore提供了JavaScript绑定。

3.2.1 JavaScript绑定

        WebKit中使用IDL来定义JavaScript接口(对象和方法),但是它又稍微不同于IDL的标准,对它作了一些改变,以适应WebKit的需要。如果开发者需要定义新的接口,那么需要完成以下一些步骤。

        首先,当然需要定义新的接口文件,示例代码10-5是一个简单的IDL文件,它定义一个新的接口,该接口名为MyObj,它包含一个属性和一个函数,该接口属于模块“mymodule”。根据这里的定义,如果开发者需要在JavaScript代码使用它们,方式是“mymodule.MyObj.myAttr”和“mymodule.MyObj.myMethod()”,看起来非常直观和容易理解。

示例代码10-5 一个简单的IDL文件

    module mymodule {interface [InterfaceName=MyObject] MyObj {readonly attribute long myAttr;DOMString myMethod(DOMString myArg);};}

        当开发者完成接口的定义之后,需要生成JavaScript引擎所需的绑定文件,该文件其实是按照引擎定义的标准接口为基础,来实现具体的接口类,WebKit提供了工具能够帮助开发者自动生成所需要的绑定类,根据引擎的不同和引擎开发语言的不同,可能有不同的结果,主要包括为JavaScriptCore和V8生成的绑定文件,以V8引擎为例,使用下面的命令就能够为该IDL文件生成结果。

    perl generate-bindings.pl MyObj.pl --generator=V8 --outputDir=./out

        它会生成两个绑定文件V8MyObj.h和V8MyObj.cpp,这些绑定文件就是将JavaScript引擎的调用转成具体的实现类的调用,示例代码10-6就是V8MyObj.cpp文件中的一个部分节选,主要是一个属性和方法的C++代码转接代码。

        在V8MyObj.cpp中,还需要很多其他桥接的代码,它们都是辅助注册桥接的函数到V8引擎的。具体的做法是将示例代码10-6中的两个函数和它们的信息,放入一个下面的数组中。

    {"myAttr", MyObjV8Internal::myAttrAttrGetter,0,0 /* no data */,static_cast<v8:: AccessControl>(v8::DEFAULT),static_cast<v8::PropertyAttribute> (v8::None), 0 /* on instance */}

之后将通过V8的注册函数V8DOMConfiguration::configureTemplate,将这些信息注册到引擎中去,有兴趣的读者可以自行根据上面的命令来生成该文件并理解这些辅助代码。

示例代码10-6 为V8引擎生成的绑定函数

    static v8::Handle<v8::Value> myAttrAttrGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info){INC_STATS("DOM.MyObj.myAttr._get");MyObj* imp = V8MyObj::toNative(info.Holder());return v8Integer(imp->myAttr(), info.GetIsolate());}static v8::Handle<v8::Value> myMethodCallback(const v8::Arguments& args){INC_STATS("DOM.MyObj.myMethod");if (args.Length() < 1)return throwNotEnoughArgumentsError(args.GetIsolate());MyObj* imp = V8MyObj::toNative(args.Holder());EXCEPTION_BLOCK(g*, myArg, V8g::HasInstance(MAYBE_MISSING_PARAMETER(args, 0, DefaultIsUndefined)) ? V8g::toNative(v8::Handle<v8::Object>::Cast(MAYBE_MISSING_PARAMETER(args, 0, DefaultIsUndefined))) : 0);return v8String(imp->myMethod(myArg), args.GetIsolate());}

        绑定文件当然需要调用开发者具体实现部分的代码。根据规则,本例需要包含开发者实现的MyObj.h文件和MyObj类,示例代码10-7就是所要实现的类,开发者只需要将两个函数实现即可被JavaScript引擎所调用。

示例代码10-7 MyObj的实际实现类——MyObj.h

    Class MyObj {public:v8::Handle<v8::Value>  myAttr() {…}v8:: Handle<v8::Value>  myMethod(const v8::Arguments& args) {…}};

        JavaScript绑定机制的一个问题就是它需要和JavaScriptCore引擎或者是V8引擎一起编译,也就是说这些本地文件同引擎源代码一起编译生成本地代码,而不能在引擎执行之后动态地注入这些本地代码,这限制了它的使用场景,因为Web开发者需要能够在引擎中动态注入本地代码来提供一些新对象和函数,以被JavaScript代码直接调用。

3.2.2 V8扩展

        除了JavaScript绑定机制之外,V8引擎另外又提供一种能够动态扩展的机制,它无须跟V8引擎一起编译,因而有很大的灵活性。

        它的基本原理是提供一个基类“Extension”和一个全局的注册函数,对于想扩展JavaScript接口的开发者而言,只需要两个步骤即可以完成扩展JavaScript能力,如示例代码10-8所描述的过程。

示例代码10-8 使用V8的extension来注入新函数

    class MYExtension : public v8::Extension {public:MYExtension() : v8::Extension("v8/My", "native function my();") {}virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(v8::Handle<v8::String> name) {// 可以根据name来返回不同的函数return v8::FunctionTemplate::New(MYExtension::MY);}static v8::Handle<v8::Value> MY(const v8::Arguments& args) {//  Do sth herereturn v8::Undefined();}};MYExtension extension;RegisterExtension(&extension);

        第一个步骤是基于Extension基类构建一个它的子类,并实现它的重要虚函数,那就是GetNativeFunction,根据参数name来决定返回实现函数。第二个步骤就是创建一个该子类的对象,并通过注册函数将该对象注册到V8引擎,这样当JavaScript调用“my”函数的时候就能够找到并执行响应的函数。

        从示例代码10-8可以看出,它只是调用V8的接口来注入新函数,所以这是一种动态扩展机制,非常的方便,但是缺点是,理论上性能可能没有JavaScript绑定机制高效,因而只是在一些对性能要求不高的应用场景才会被使用到。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/641722.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【FINEBI】finebi中常用图表类型及其适用场景

柱状图&#xff08;Bar Chart&#xff09;&#xff1a; 比较不同类别或组之间的数量差异&#xff1a;柱状图可以用于比较不同产品、地区、时间段等的销售额、市场份额等。 显示不同时间段的数据变化&#xff1a;通过绘制柱状图&#xff0c;可以观察到销售额、网站流量等随时间…

前端JavaScript篇之实现有序数组原地去重方法有哪些?

目录 实现有序数组原地去重方法有哪些&#xff1f;方法一&#xff1a;使用 Set 数据结构代码实现&#xff1a;思路说明&#xff1a; 方法二&#xff1a;使用双指针遍历代码实现&#xff1a;思路说明&#xff1a; 实现有序数组原地去重方法有哪些&#xff1f; 在 JavaScript 中…

机器学习神器:Sklearn详解

引言 Sklearn (全称 Scikit-Learn) 是基于 Python 语言的机器学习工具。它建立在 NumPy, SciPy, Pandas 和 Matplotlib 之上&#xff0c;里面的 API 的设计非常好&#xff0c;所有对象的接口简单&#xff0c;很适合新手上路。 在 Sklearn 里面有六大任务模块&#xff1a;分别是…

怎样的安全数据交换系统 可以支持信创环境?

首先&#xff0c;我来看看&#xff0c;什么是安全数据交换系统&#xff1f;安全数据交换系统是一种专门设计用于在不同网络环境之间安全传输数据的技术解决方案。它确保数据在传输过程中的完整性、机密性和可用性&#xff0c;同时遵守相关的数据保护法规和行业标准。 那么&…

透明拼接屏显示:技术与应用

在当今的数字化时代&#xff0c;显示技术已成为我们日常生活和工作中的重要组成部分。透明拼接屏作为一种新型的显示技术&#xff0c;以其独特的透明设计和灵活的拼接特性&#xff0c;正逐渐在各个领域得到广泛应用&#xff0c;尼伽小编&#xff0c;将深入探讨透明拼接屏显示的…

灵感无限!12个设计师最爱的网站推荐,覆盖UX、网页设计和国外设计精华

即时设计资源广场 即时设计资源广场是中国优秀的UI设计网站&#xff0c;全中文环境&#xff0c;非常适合中国人使用。UI设计网站即时设计资源广场内置阿里、字节、腾讯、京东、谷歌、华为等设计系统&#xff0c;3000多个UI组件库&#xff0c;每月更新数百个高质量模板&#xf…

websocket服务端本地部署

文章目录 1. Java 服务端demo环境2. 在pom文件引入第三包封装的netty框架maven坐标3. 创建服务端,以接口模式调用,方便外部调用4. 启动服务,出现以下信息表示启动成功,暴露端口默认99995. 创建隧道映射内网端口6. 查看状态->在线隧道,复制所创建隧道的公网地址加端口号7. 以…

空气净化器or宠物空气净化器?五款猫用空气净化器优质推荐!

作为一个养猫家庭的主人&#xff0c;每天都要面对清理猫砂盘的挑战&#xff0c;这种令人难以形容的气味实在让人难以忍受。尤其是家里有小孩和老人&#xff0c;他们可能会出现过敏性鼻炎等问题&#xff0c;而抵抗力较差的人更容易受到影响。此外&#xff0c;换毛季节到来时&…

性能优化(CPU优化技术)-NEON指令介绍

「发表于知乎专栏《移动端算法优化》」 本文主要介绍了 NEON 指令相关的知识&#xff0c;首先通过讲解 arm 指令集的分类&#xff0c;NEON寄存器的类型&#xff0c;树立基本概念。然后进一步梳理了 NEON 汇编以及 intrinsics 指令的格式。最后结合指令的分类&#xff0c;使用例…

数据运营项目1

下面是一些注意事项&#xff1a; 10w以上就不要用excel去做了会很卡很慢&#xff0c;可以考虑powerbi&#xff0c;用powerbi解决RFM模型 Powerbi替换时&#xff0c;替换没不写就行了&#xff0c;不是空值 主页分组依据就是拉数据透视表 所有工具都要打上双引号 文本不能做减…

Python + Selenium —— ActionChains动作链!

当你需要执行复杂的操作时&#xff0c;比如将一个元素按住拖动到另一个元素上去&#xff0c;需要移动鼠标然后点击并按下键盘某个按键等等。 当然&#xff0c;在 Web 页面上&#xff0c;这种操作好像比较少。 但是&#xff0c;如果遇到了怎么办呢&#xff1f;这就需要用到 Ac…

【Obsidian】【Git】使用gitee同步/保存obsidian笔记

Obisidian是一款markdown软件&#xff0c;使用它可以方便地记笔记、记录科研日常。然而如果在多个设备上使用obsidian&#xff0c;会牵扯到笔记/vault/仓库同步问题。下面来介绍如何用git管理obsidian。 1.创建gitee账号 略 2.下载Obsidian 略 3.新建git仓库 3.1在gitee上…

保姆版Vps安装灯塔(ARL)

因为灯塔的默认端口为5003&#xff0c;所以我们在安装之前就在防火墙里把我们的5003端口打开 打开端口步骤如下&#xff1a; 1.我们打开控制面板&#xff0c;在控制面板里点击 系统和安全 。如下图&#xff1a; 2.接着点击 Windows Defender防火墙,如下图&#xff1a; 3.再…

Vue 3 + Ts 钩子函数(hooks)的用法,以<script setup lang=“ts“/>语法糖形式 #reactive #ref

reactive <template><div><h2 click"increment">{{ count }}</h2></div> </template><script setup lang"ts"> import { ref, onMounted } from vue// 使用reactive创建响应式数据 const state reactive({cou…

架构篇14:高性能数据库集群-读写分离

文章目录 读写分离原理复制延迟分配机制小结 高性能数据库集群的第一种方式是“读写分离”&#xff0c;其本质是将访问压力分散到集群中的多个节点&#xff0c;但是没有分散存储压力&#xff1b;第二种方式是“分库分表”&#xff0c;既可以分散访问压力&#xff0c;又可以分散…

docker容器快速安装启动ES

1、安装 docker a、使用 Homebrew 安装 brew install --cask --appdir/Applications docker b、手动下载安装 1、安装包下载地址&#xff1a;Install Docker Desktop on Mac | Docker Docs 根据自己的笔记本型号选择&#xff0c;我这边选择的是 intel chip 2、下载安装即可&a…

如何使用 Helm 在 K8s 上集成 Prometheus 和 Grafana|Part 3

在本教程的前两部分&#xff0c;我们分别了解和学习了Prometheus 和 Grafana 的基本概念和使用的前提条件&#xff0c;以及使用 Helm 在 Kubernetes 上安装 Prometheus。 在今天的教程中&#xff0c;我们将为你介绍以下内容&#xff1a; 安装 Grafana&#xff1b;集成 Promethe…

服务器数据恢复—EVA存储raid5硬盘离线的数据恢复案例

服务器数据恢复环境&#xff1a; 某品牌EVA某型号存储&#xff0c;底层是RAID5阵列&#xff0c;划分了若干lun。 服务器故障&分析&#xff1a; 该存储设备中raid5阵列有两块硬盘掉线&#xff0c;存储中的lun丢失。 将故障服务器存储中的所有磁盘编号后取出&#xff0c;硬件…

linux C函数之strdup函数分析和getopt_long()的使用

函数原型&#xff1a; #include <string.h> char *strdup(const char *s); 函数介绍&#xff1a; strdup()函数主要是拷贝字符串s的一个副本&#xff0c;有函数返回值返回&#xff0c;这个副本有自己的内存空间&#xff0c;和s没有关联。strdup函数复制一个字符串&…

三字棋游戏的实现

头文件&#xff1a; #define row 5 #define line 5 #include <stdio.h> #include <stdlib.h> #include <time.h> void initial(char board[row][line]); void print_board(char board[row][line]); void player_move(char board[row][line]); char is_win(c…