NodeJS 集成了 V8 作为 JS 引擎。利用 V8 暴露出来的 API 可以高效地和 NodeJS 进行通信,从而编写自己需要的 native 模块。实际上,NodeJS 底层很大程度上也是直接使用了 Chrome V8 所暴露的API。

一、基本概念

  • 内存机制

1、V8 中 JS 数据类型都是由 V8 内部的内存机制进行管理的
2、V8是由C++编写的,所以在JS中声明一个变量,本质上对应一个C++变量,C++这边也可以操作这个变量
3、V8垃圾回收的前提是变量既没有被JS引用,也没有被对应的C++引用
4、新生代内存区分为两个 Semispace: From 和 To,内存分配从 From 进行,回收时,检查 From 区存活的对象并复制到To空间中,然后 From 和 To 角色互换
5、当新生代中的对象经过多次垃圾回收后仍然存活,则其可以进行对象晋升到老生代内存区
6、老生代内存区使用标记清除(容易产生内存碎片)或者标记整理(效率稍低,但不会产生碎片)管理内存

  • 基本数据类型

1、隔离实例 Isolate,即一个 V8 实例,本身不执行 JS,也没有 JS 环境的执行上下文

  1. // 实例参数
  2. v8::Isolate::CreateParams create_params;
  3. // 创建 v8 实例
  4. v8::Isolate *isolate = v8::Isolate::New(create_params)

不过,在进行 NodeJS 的 C++ 扩展时不需要手动创建 v8 实例,因为以及处于 v8 环境中了,直接获取 NodeJS 环境所使用的实例即可:

  1. void Method(const v8::FunctionCallbackInfo<v8::Value>& args)
  2. Isolate * isolate = args.GetIsolate();

2、上下文 Context,用来定义 JS 执行环境的一个对象,在创建时需要和 v8 实例 isolate 绑定

  1. v8::Isolate * isolate = ...
  2. v8::Local<v8::Context> context = v8::Context::New(isolate);

开发 NodeJS 的 C++ 扩展时通常不需要这个对象
3、脚本 Script,包含一段已经编译好的 JS 脚本对象,编译时需要和一个上下文 context 绑定

  1. v8::Local<v8::Context> context = ...
  2. v8::Local<v8::String> source = 一段 JS 代码
  3. // 绑定上下文并编译
  4. v8::Local<v8::Value> script = v8::Script::Compile(context,source).ToLocalChecked();
  5. // 执行脚本
  6. v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();

二、句柄和句柄作用域

1、句柄和引用的区别:v8 在进行垃圾回收的时候,会对对象进行多次移动,一旦移动了对象,该对象的引用就变成了游离的,即失去了与该对象的联系。但是句柄仍然可以正确找到该对象
2、常用句柄:本地句柄(v8::Local),持久句柄(v8::Persistent),待实本地句柄(v8::MaybeLocal)
3、本地句柄存在于栈内存,其生命周期由其所在的句柄作用域决定。当句柄作用域对象被删除的时候,在该作用域内创建的句柄所指向的对象会被垃圾回收(没有被其它地方引用)
4、本地句柄基本操作:
a、创建 New

  1. Local<Number> Number::New(Isolate* isolate,double value)

b、清除 Clear,即设置为空句柄

  1. Local<Number> number = Number::New(isolate,123);
  2. number.Clear()

c、是否为空 IsEmpty

  1. Local<Number> number = Number::New(isolate,123);
  2. if(number.IsEmpty()){
  3. }

d、类型转换 As/Cast,As 是实例函数,Cast 是静态函数

  1. Local<Number> number = Local<Number>::Cast(handle); // 假设 handle 是 Local<Value>句柄
  2. Local<Number> number = handle.As<Number>();

5、持久句柄,对应于JS中堆内存中的引用,可通过 SetWeak 使持久句柄变为弱持久句柄,当一个JS对象的引用只剩下弱持久句柄时,v8 垃圾回收就会触发一个回调
6、持久句柄还可分为:唯一持久句柄(v8::UniquePersistent)和一般持久句柄(v8::Persistent),一般持久句柄需要显示调用 Reset 方法才能将其清除
7、持久句柄基本操作
持久句柄通常是通过本地句柄升格而成。
a、创建 Persistent

  1. Local<Number> local = Number::New(isolate,123);
  2. Persistent<Number> persistent = Persitent(local)
  3. Persistent<Number> persistent = Persitent(isolate,local)

b、清除 Clear 和判空 IsEmpty
c、置为弱持久句柄实例函数 SetWeak 和 取消弱持久句柄实例函数 ClearWeak
d、标记为独立持久句柄 MarkIndependent,独立持久句柄何以在新生代回收时被回收,而非独立句柄则不行,在开发C++扩展时,一般和 SetWeak 成对出现,因为通常是期望弱持久句柄尽早被回收

  1. persistent_handle.SetWeak(test,Callback,WeakCallbackType::kParameter);
  2. persistent_handle.MarkIndependent();

e、是否为弱句柄 IsWeak,是否为独立 IsIndependent
8、代实本地句柄 MaybeLocal。当一个函数返回这种句柄,说明返回值可能为空,需要做判空处理,如果要拿到真实本地句柄,需要调用 ToLocalChecked 方法

  1. Local<Value> x = some_value;
  2. MaybeLocal<String> s = some_value.ToString();
  3. // 判空
  4. if(!s.IsEmpty()){
  5. Local<String> s_local = s.ToLocalChecked();
  6. }

9、句柄作用域是一个维护一堆句柄的容器,当一个句柄作用域对象的构析函数被调用时,这个作用域内创建的句柄都会被垃圾回收,EscapableHandleScope 除外
10、句柄作用域以 HandleScope 或者 EscapableHandleScope 存在于栈内存中
11、句柄作用域常见操作

  1. Isolate::Scope isolate_scope(isolate);
  2. // 在栈中创建一个句柄作用域
  3. HandleScope handle_scope(isolate);
  4. // 创建可逃句柄作用域
  5. EscapableHandleScope scope(isolate);
  6. scope.Escape(..)

12、可通过 * 运算符获取句柄对应的 v8 数据

三、模版

v8 中的模版是JS函数或者对象的模具,可以通过模具将 C++ 函数或者数据结构包成JS的函数或对象

  • 函数模版 FunctionTemplate ```cpp void Method(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); args.GetReturnValue().Set(String::NewFromUtf8(isolate, “this is a function”)); }

void init(Local exports) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); // 创建函数模版,绑定 Method 方法 Local t = FunctionTemplate::New(isolate, Method); // 获取函数模版实例 Local fn = t->GetFunction(); Local name = String::NewFromUtf8(isolate, “funcCreateByTemplate”); // 设置函数名称 fn->SetName(name); // 挂载到 module.exports exports->Set(name, fn); }

  1. 常用API
  2. ```cpp
  3. // 创建 New
  4. Local<FunctionTemplate> t = FunctionTemplate::New(isolate, Method);
  5. // 设置类名
  6. t->SetClassName(String::NewFromUtf8(""ClassName));
  7. // 设置参数数量
  8. t->SetLength(2)
  9. // 实例化
  10. t->GetFunction()
  • 对象模版 ObjectTemplate ```cpp // 作为函数模板的原型 PrototypeTemplate Local tpl = FunctionTemplate::New(isolate, Constructor); tpl->SetClassName(String::NewFromUtf8(isolate, “TestClass”)); Local proto = tpl->PrototypeTemplate(); proto->Set(String::NewFromUtf8(isolate, “get”), FunctionTemplate::New(isolate, ClassGet)); exports->Set(String::NewFromUtf8(isolate, “TestClass”), tpl->GetFunction());

// 作为对象模板创建对象 NewInstance Local fun = FunctionTemplate::New(isolate); fun->SetClassName(String::NewFromUtf8(isolate, “TestClass2”)); Local obj_tpl = ObjectTemplate::New(isolate, fun); obj_tpl->Set(String::NewFromUtf8(isolate, “num”), Number::New(isolate, 233)); Local array = Array::New(isolate, 10); for(int i = 0; i < 10; i++) { array->Set(Number::New(isolate, i), obj_tpl->NewInstance()); } exports->Set(String::NewFromUtf8(isolate, “array”), array);

// 设置函数体 SetCallAsFunctionHandler Local obj_with_func_tpl = ObjectTemplate::New(isolate); obj_with_func_tpl->Set(String::NewFromUtf8(isolate, “cat”), String::NewFromUtf8(isolate, “南瓜饼”)); obj_with_func_tpl->Set(String::NewFromUtf8(isolate, “dog”), String::NewFromUtf8(isolate, “蛋花汤”)); obj_with_func_tpl->SetCallAsFunctionHandler(Func); exports->Set(String::NewFromUtf8(isolate, “func”), obj_with_func_tpl->NewInstance());

  1. 对象模版还有访问器、拦截器、内置字段等功能,非常强大,这些暂时先不深究了。
  2. <a name="vuc8A"></a>
  3. ### 四、常用数据类型
  4. - Value
  5. 所以数据类型的父类,当不确定一个数据的类型时就可以用 Value 代替,有两大类APIIs... To... 用于类型判断和类型转换,如 xx->IsString(),xx->ToString()。注意 To.. 系列函数返回的是待实本地句柄
  6. - String
  7. 常用APILength(),Utf8Length(),NewFromUtf8() 用于 char * StringUtf8Value() 用于 String char * , 该函数可能会失败
  8. - Nubmer
  9. - Integer
  10. - Int32
  11. - Uint32
  12. 以上四个类型都是 v8 中有效的和数字有关的数据类型,有静态方法 New 和成员函数 Value
  13. - Boolean
  14. 静态方法 New 和成员函数 Value
  15. - Object
  16. 创建 New,操作 Set,Get,Delete
  17. - Function
  18. 作为普通函数调用 Call,作为类实例化 NewInstance
  19. ```cpp
  20. Isolate* isolate = args.GetIsolate();
  21. Local<Context> context = isolate->GetCurrentContext();
  22. Local<Array> array = args[0].As<Array>();
  23. Local<Function> func = args[1].As<Function>();
  24. Local<Array> ret = Array::New(isolate, array->Length());
  25. Local<Value> null = v8::Null(isolate);
  26. Local<Value> a[3] = { Local<Object>(), null, array };
  27. for(uint32_t i = 0; i < array->Length(); i++)
  28. {
  29. a[0] = array->Get(i);
  30. a[1] = Int32::New(isolate, i);
  31. MaybeLocal<Value> v = func->Call(context, null, 3, a);
  32. ret->Set(i, v.ToLocalChecked());
  33. }
  34. args.GetReturnValue().Set(ret);
  • Array

创建 New,操作 Set,Get,长度 Length

  • FunctionCallbackInfo

参数个数 Length,[i]获取第i个参数,This() 得到函数this,Holder() 函数调用时的this,IsConstructorCall() 是否用 new 方式调用,GetIsolate() 获取当前 Isolate,GetReturnValue() 获取返回值对象

  • ReturnValue

Set() 设置返回值;SetNull();SetUndefined();SetEmptyString()