插件 Addons 是动态链接的共享对象。他提供了 C/C++ 类库能力。这些API比较复杂,他包以下几个类库:

  • V8 JavaScript, C++ 类库。用来和 JavaScript 交互,比如创建对象,调用函数等等。在 v8.h 头文件中 (目录地址deps/v8/include/v8.h),线上地址online

  • libuv,C 事件循环库。等待文件描述符变为可读,等待定时器,等待信号时,会和 libuv 打交道。或者说,如果你需要和 I/O 打交道,就会用到 libuv。

  • 内部 Node 类库。 其中最重要的类 node::ObjectWrap,你会经常派生自它。

  • 其他的参见 deps/

Node 已经将所有的依赖编译成可以执行文件,所以你不必当心这些类库的链接问题。

以下所有例子可以在download 下载,也许你可以从中找一个作为你的扩展插件。

Hello world

现在我们来写一个 C++ 插件的小例子,它的效果和以下 JS 代码一致:

  1. module.exports.hello = function() { return 'world'; };

创建 hello.cc 文件:

  1. // hello.cc
  2. #include <node.h>
  3. using namespace v8;
  4. void Method(const FunctionCallbackInfo<Value>& args) {
  5. Isolate* isolate = Isolate::GetCurrent();
  6. HandleScope scope(isolate);
  7. args.GetReturnValue().Set(String::NewFromUtf8(isolate, "world"));
  8. }
  9. void init(Handle<Object> exports) {
  10. NODE_SET_METHOD(exports, "hello", Method);
  11. }
  12. NODE_MODULE(addon, init)

注意:所有的 Node 插件必须输出一个初始化函数:

  1. void Initialize (Handle<Object> exports);
  2. NODE_MODULE(module_name, Initialize)

NODE_MODULE 之后的代码没有分号,因为它不是一个函数 (参见node.h)。

module_name 必须和二进制文件名字一致 (后缀是 .node)。

源文件会编译成 addon.node 二进制插件。 为此我们创建了一个很像 JSON 的 binding.gyp 文件, 它包含配置信息,这个文件用node-gyp编译。

  1. {
  2. "targets": [
  3. {
  4. "target_name": "addon",
  5. "sources": [ "hello.cc" ]
  6. }
  7. ]
  8. }

下一步创建一个 node-gyp configure 工程,在平台上生成这些文件。

创建后,在build/文件夹里拥有一个 Makefile (Unix 系统) 文件或者 vcxproj 文件(Windows 系统)。 接着调用 node-gyp build 命令编译,生成 .node 文件。 这些文件在 build/Release/ 目录里。

现在,你能在 Node 工程中使用这些 2 进制扩展插件,在 hello.js 中声明require之前编译的hello.node:

  1. // hello.js
  2. var addon = require('./build/Release/addon');
  3. console.log(addon.hello()); // 'world'

更多的信息请参考https://github.com/arturadib/node-qt

插件模式

下面是一些 addon 插件的模式,帮助你开始编码。v8 reference 文档里包含 v8 的各种接口,Embedder's Guide这个文档包含各种说明,比如 handles, scopes, function templates, 等等。

在使用这些例子前,你需要先用 node-gyp 编译。创建binding.gyp 文件:

  1. {
  2. "targets": [
  3. {
  4. "target_name": "addon",
  5. "sources": [ "addon.cc" ]
  6. }
  7. ]
  8. }

将文件名加入到 sources 数组里就可以使用多个 .cc 文件,例如 :

  1. "sources": ["addon.cc", "myexample.cc"]

准备好 binding.gyp 文件后, 你就能配置并编译插件:

  1. $ node-gyp configure build

函数参数

从以下模式中解释了如何从 JavaScript 函数中读取参数,并返回结果。仅需要一个addon.cc文件:

  1. // addon.cc
  2. #include <node.h>
  3. using namespace v8;
  4. void Add(const FunctionCallbackInfo<Value>& args) {
  5. Isolate* isolate = Isolate::GetCurrent();
  6. HandleScope scope(isolate);
  7. if (args.Length() < 2) {
  8. isolate->ThrowException(Exception::TypeError(
  9. String::NewFromUtf8(isolate, "Wrong number of arguments")));
  10. return;
  11. }
  12. if (!args[0]->IsNumber() || !args[1]->IsNumber()) {
  13. isolate->ThrowException(Exception::TypeError(
  14. String::NewFromUtf8(isolate, "Wrong arguments")));
  15. return;
  16. }
  17. double value = args[0]->NumberValue() + args[1]->NumberValue();
  18. Local<Number> num = Number::New(isolate, value);
  19. args.GetReturnValue().Set(num);
  20. }
  21. void Init(Handle<Object> exports) {
  22. NODE_SET_METHOD(exports, "add", Add);
  23. }
  24. NODE_MODULE(addon, Init)

可以用以下的 JavaScript 代码片段测试:

  1. // test.js
  2. var addon = require('./build/Release/addon');
  3. console.log( 'This should be eight:', addon.add(3,5) );

回调Callbacks

你也能传 JavaScript 函数给 C++ 函数,并执行它。 在 addon.cc 中:

  1. // addon.cc
  2. #include <node.h>
  3. using namespace v8;
  4. void RunCallback(const FunctionCallbackInfo<Value>& args) {
  5. Isolate* isolate = Isolate::GetCurrent();
  6. HandleScope scope(isolate);
  7. Local<Function> cb = Local<Function>::Cast(args[0]);
  8. const unsigned argc = 1;
  9. Local<Value> argv[argc] = { String::NewFromUtf8(isolate, "hello world") };
  10. cb->Call(isolate->GetCurrentContext()->Global(), argc, argv);
  11. }
  12. void Init(Handle<Object> exports, Handle<Object> module) {
  13. NODE_SET_METHOD(module, "exports", RunCallback);
  14. }
  15. NODE_MODULE(addon, Init)

注意,这个例子中使用了 Init() 里的 2 个参数,module对象是第二个参数。它允许 addon 使用一个函数完全重写 exports

可以用以下的代码来测试:

  1. // test.js
  2. var addon = require('./build/Release/addon');
  3. addon(function(msg){
  4. console.log(msg); // 'hello world'
  5. });

对象工厂

addon.cc 模式里,你能用 C++ 函数创建并返回一个新的对象,这个对象所包含的 msg 属性是由createObject() 函数传入:

  1. // addon.cc
  2. #include <node.h>
  3. using namespace v8;
  4. void CreateObject(const FunctionCallbackInfo<Value>& args) {
  5. Isolate* isolate = Isolate::GetCurrent();
  6. HandleScope scope(isolate);
  7. Local<Object> obj = Object::New(isolate);
  8. obj->Set(String::NewFromUtf8(isolate, "msg"), args[0]->ToString());
  9. args.GetReturnValue().Set(obj);
  10. }
  11. void Init(Handle<Object> exports, Handle<Object> module) {
  12. NODE_SET_METHOD(module, "exports", CreateObject);
  13. }
  14. NODE_MODULE(addon, Init)

使用 JavaScript 测试:

  1. // test.js
  2. var addon = require('./build/Release/addon');
  3. var obj1 = addon('hello');
  4. var obj2 = addon('world');
  5. console.log(obj1.msg+' '+obj2.msg); // 'hello world'

工厂模式

这个模式里展示了如何创建并返回一个 JavaScript 函数,它是由 C++ 函数包装的 :

  1. // addon.cc
  2. #include <node.h>
  3. using namespace v8;
  4. void MyFunction(const FunctionCallbackInfo<Value>& args) {
  5. Isolate* isolate = Isolate::GetCurrent();
  6. HandleScope scope(isolate);
  7. args.GetReturnValue().Set(String::NewFromUtf8(isolate, "hello world"));
  8. }
  9. void CreateFunction(const FunctionCallbackInfo<Value>& args) {
  10. Isolate* isolate = Isolate::GetCurrent();
  11. HandleScope scope(isolate);
  12. Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, MyFunction);
  13. Local<Function> fn = tpl->GetFunction();
  14. // omit this to make it anonymous
  15. fn->SetName(String::NewFromUtf8(isolate, "theFunction"));
  16. args.GetReturnValue().Set(fn);
  17. }
  18. void Init(Handle<Object> exports, Handle<Object> module) {
  19. NODE_SET_METHOD(module, "exports", CreateFunction);
  20. }
  21. NODE_MODULE(addon, Init)

测试:

  1. // test.js
  2. var addon = require('./build/Release/addon');
  3. var fn = addon();
  4. console.log(fn()); // 'hello world'

包装 C++ 对象

以下会创建一个 C++对象的包装MyObject,这样 他就能再 JavaScript 中用 new 实例化。首先在addon.cc中准备主要模块:

  1. // addon.cc
  2. #include <node.h>
  3. #include "myobject.h"
  4. using namespace v8;
  5. void InitAll(Handle<Object> exports) {
  6. MyObject::Init(exports);
  7. }
  8. NODE_MODULE(addon, InitAll)

接着在 myobject.h 创建包装,它继承自 node::ObjectWrap:

  1. // myobject.h
  2. #ifndef MYOBJECT_H
  3. #define MYOBJECT_H
  4. #include <node.h>
  5. #include <node_object_wrap.h>
  6. class MyObject : public node::ObjectWrap {
  7. public:
  8. static void Init(v8::Handle<v8::Object> exports);
  9. private:
  10. explicit MyObject(double value = 0);
  11. ~MyObject();
  12. static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
  13. static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args);
  14. static v8::Persistent<v8::Function> constructor;
  15. double value_;
  16. };
  17. #endif

myobject.cc 中实现各种暴露的方法,通过给构造函数添加 prototype 属性来暴露 plusOne 方法:

  1. // myobject.cc
  2. #include "myobject.h"
  3. using namespace v8;
  4. Persistent<Function> MyObject::constructor;
  5. MyObject::MyObject(double value) : value_(value) {
  6. }
  7. MyObject::~MyObject() {
  8. }
  9. void MyObject::Init(Handle<Object> exports) {
  10. Isolate* isolate = Isolate::GetCurrent();
  11. // Prepare constructor template
  12. Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
  13. tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject"));
  14. tpl->InstanceTemplate()->SetInternalFieldCount(1);
  15. // Prototype
  16. NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne);
  17. constructor.Reset(isolate, tpl->GetFunction());
  18. exports->Set(String::NewFromUtf8(isolate, "MyObject"),
  19. tpl->GetFunction());
  20. }
  21. void MyObject::New(const FunctionCallbackInfo<Value>& args) {
  22. Isolate* isolate = Isolate::GetCurrent();
  23. HandleScope scope(isolate);
  24. if (args.IsConstructCall()) {
  25. // Invoked as constructor: `new MyObject(...)`
  26. double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
  27. MyObject* obj = new MyObject(value);
  28. obj->Wrap(args.This());
  29. args.GetReturnValue().Set(args.This());
  30. } else {
  31. // Invoked as plain function `MyObject(...)`, turn into construct call.
  32. const int argc = 1;
  33. Local<Value> argv[argc] = { args[0] };
  34. Local<Function> cons = Local<Function>::New(isolate, constructor);
  35. args.GetReturnValue().Set(cons->NewInstance(argc, argv));
  36. }
  37. }
  38. void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) {
  39. Isolate* isolate = Isolate::GetCurrent();
  40. HandleScope scope(isolate);
  41. MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder());
  42. obj->value_ += 1;
  43. args.GetReturnValue().Set(Number::New(isolate, obj->value_));
  44. }

测试:

  1. // test.js
  2. var addon = require('./build/Release/addon');
  3. var obj = new addon.MyObject(10);
  4. console.log( obj.plusOne() ); // 11
  5. console.log( obj.plusOne() ); // 12
  6. console.log( obj.plusOne() ); // 13

包装对象工厂

当你想创建本地对象,又不想在 JavaScript 中严格的使用 new 初始化的时候,以下方法非常实用。

  1. var obj = addon.createObject();
  2. // instead of:
  3. // var obj = new addon.Object();

addon.cc 中注册 createObject 方法:

  1. // addon.cc
  2. #include <node.h>
  3. #include "myobject.h"
  4. using namespace v8;
  5. void CreateObject(const FunctionCallbackInfo<Value>& args) {
  6. Isolate* isolate = Isolate::GetCurrent();
  7. HandleScope scope(isolate);
  8. MyObject::NewInstance(args);
  9. }
  10. void InitAll(Handle<Object> exports, Handle<Object> module) {
  11. MyObject::Init();
  12. NODE_SET_METHOD(module, "exports", CreateObject);
  13. }
  14. NODE_MODULE(addon, InitAll)

myobject.h 中有静态方法 NewInstance,他能实例化对象 (它就像 JavaScript 的 new):

  1. // myobject.h
  2. #ifndef MYOBJECT_H
  3. #define MYOBJECT_H
  4. #include <node.h>
  5. #include <node_object_wrap.h>
  6. class MyObject : public node::ObjectWrap {
  7. public:
  8. static void Init();
  9. static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args);
  10. private:
  11. explicit MyObject(double value = 0);
  12. ~MyObject();
  13. static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
  14. static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args);
  15. static v8::Persistent<v8::Function> constructor;
  16. double value_;
  17. };
  18. #endif

这个实现方法和 myobject.cc 类似:

  1. // myobject.cc
  2. #include <node.h>
  3. #include "myobject.h"
  4. using namespace v8;
  5. Persistent<Function> MyObject::constructor;
  6. MyObject::MyObject(double value) : value_(value) {
  7. }
  8. MyObject::~MyObject() {
  9. }
  10. void MyObject::Init() {
  11. Isolate* isolate = Isolate::GetCurrent();
  12. // Prepare constructor template
  13. Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
  14. tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject"));
  15. tpl->InstanceTemplate()->SetInternalFieldCount(1);
  16. // Prototype
  17. NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne);
  18. constructor.Reset(isolate, tpl->GetFunction());
  19. }
  20. void MyObject::New(const FunctionCallbackInfo<Value>& args) {
  21. Isolate* isolate = Isolate::GetCurrent();
  22. HandleScope scope(isolate);
  23. if (args.IsConstructCall()) {
  24. // Invoked as constructor: `new MyObject(...)`
  25. double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
  26. MyObject* obj = new MyObject(value);
  27. obj->Wrap(args.This());
  28. args.GetReturnValue().Set(args.This());
  29. } else {
  30. // Invoked as plain function `MyObject(...)`, turn into construct call.
  31. const int argc = 1;
  32. Local<Value> argv[argc] = { args[0] };
  33. Local<Function> cons = Local<Function>::New(isolate, constructor);
  34. args.GetReturnValue().Set(cons->NewInstance(argc, argv));
  35. }
  36. }
  37. void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) {
  38. Isolate* isolate = Isolate::GetCurrent();
  39. HandleScope scope(isolate);
  40. const unsigned argc = 1;
  41. Handle<Value> argv[argc] = { args[0] };
  42. Local<Function> cons = Local<Function>::New(isolate, constructor);
  43. Local<Object> instance = cons->NewInstance(argc, argv);
  44. args.GetReturnValue().Set(instance);
  45. }
  46. void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) {
  47. Isolate* isolate = Isolate::GetCurrent();
  48. HandleScope scope(isolate);
  49. MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder());
  50. obj->value_ += 1;
  51. args.GetReturnValue().Set(Number::New(isolate, obj->value_));
  52. }

测试:

  1. // test.js
  2. var createObject = require('./build/Release/addon');
  3. var obj = createObject(10);
  4. console.log( obj.plusOne() ); // 11
  5. console.log( obj.plusOne() ); // 12
  6. console.log( obj.plusOne() ); // 13
  7. var obj2 = createObject(20);
  8. console.log( obj2.plusOne() ); // 21
  9. console.log( obj2.plusOne() ); // 22
  10. console.log( obj2.plusOne() ); // 23

传递包装对象

除了包装并返回 C++ 对象,你可以使用 Node 的 node::ObjectWrap::Unwrap 帮助函数来解包。在下面的 addon.cc 中,我们介绍了一个 add() 函数,它能获取2个 MyObject对象:

  1. // addon.cc
  2. #include <node.h>
  3. #include <node_object_wrap.h>
  4. #include "myobject.h"
  5. using namespace v8;
  6. void CreateObject(const FunctionCallbackInfo<Value>& args) {
  7. Isolate* isolate = Isolate::GetCurrent();
  8. HandleScope scope(isolate);
  9. MyObject::NewInstance(args);
  10. }
  11. void Add(const FunctionCallbackInfo<Value>& args) {
  12. Isolate* isolate = Isolate::GetCurrent();
  13. HandleScope scope(isolate);
  14. MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>(
  15. args[0]->ToObject());
  16. MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>(
  17. args[1]->ToObject());
  18. double sum = obj1->value() + obj2->value();
  19. args.GetReturnValue().Set(Number::New(isolate, sum));
  20. }
  21. void InitAll(Handle<Object> exports) {
  22. MyObject::Init();
  23. NODE_SET_METHOD(exports, "createObject", CreateObject);
  24. NODE_SET_METHOD(exports, "add", Add);
  25. }
  26. NODE_MODULE(addon, InitAll)

介绍 myobject.h 里的一个公开方法,它能在解包后使用私有变量:

  1. // myobject.h
  2. #ifndef MYOBJECT_H
  3. #define MYOBJECT_H
  4. #include <node.h>
  5. #include <node_object_wrap.h>
  6. class MyObject : public node::ObjectWrap {
  7. public:
  8. static void Init();
  9. static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args);
  10. inline double value() const { return value_; }
  11. private:
  12. explicit MyObject(double value = 0);
  13. ~MyObject();
  14. static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
  15. static v8::Persistent<v8::Function> constructor;
  16. double value_;
  17. };
  18. #endif

myobject.cc 的实现方法和之前的类似:

  1. // myobject.cc
  2. #include <node.h>
  3. #include "myobject.h"
  4. using namespace v8;
  5. Persistent<Function> MyObject::constructor;
  6. MyObject::MyObject(double value) : value_(value) {
  7. }
  8. MyObject::~MyObject() {
  9. }
  10. void MyObject::Init() {
  11. Isolate* isolate = Isolate::GetCurrent();
  12. // Prepare constructor template
  13. Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
  14. tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject"));
  15. tpl->InstanceTemplate()->SetInternalFieldCount(1);
  16. constructor.Reset(isolate, tpl->GetFunction());
  17. }
  18. void MyObject::New(const FunctionCallbackInfo<Value>& args) {
  19. Isolate* isolate = Isolate::GetCurrent();
  20. HandleScope scope(isolate);
  21. if (args.IsConstructCall()) {
  22. // Invoked as constructor: `new MyObject(...)`
  23. double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
  24. MyObject* obj = new MyObject(value);
  25. obj->Wrap(args.This());
  26. args.GetReturnValue().Set(args.This());
  27. } else {
  28. // Invoked as plain function `MyObject(...)`, turn into construct call.
  29. const int argc = 1;
  30. Local<Value> argv[argc] = { args[0] };
  31. Local<Function> cons = Local<Function>::New(isolate, constructor);
  32. args.GetReturnValue().Set(cons->NewInstance(argc, argv));
  33. }
  34. }
  35. void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) {
  36. Isolate* isolate = Isolate::GetCurrent();
  37. HandleScope scope(isolate);
  38. const unsigned argc = 1;
  39. Handle<Value> argv[argc] = { args[0] };
  40. Local<Function> cons = Local<Function>::New(isolate, constructor);
  41. Local<Object> instance = cons->NewInstance(argc, argv);
  42. args.GetReturnValue().Set(instance);
  43. }

测试:

  1. // test.js
  2. var addon = require('./build/Release/addon');
  3. var obj1 = addon.createObject(10);
  4. var obj2 = addon.createObject(20);
  5. var result = addon.add(obj1, obj2);
  6. console.log(result); // 30

Node.js C/C++ 插件的更多相关文章

  1. 10最好用的Node.js工具、插件和资料库

    每一个称职的程序员都应该拥有一套极好的工具来提高自己的工作效率.在Livecoding.tv 上,那里的程序员分享了10个他们认为是最好用的工具.插件和资料库.据说,以下的这10个工具是使用Node. ...

  2. ubuntu 安装 nvm 管理Node.js 以及vim 插件增强

    安装curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bashsource ~/.bashr ...

  3. 实用的 Node.js 教程,工具和资源

    这里分享一批实用的实用的 Node.js 教程,工具和资源. Node.js是一个建立在Chrome之上的JavaScript运行时平台,可方便地构建快速,可扩展的网络应用程序.Node.js使用事件 ...

  4. 如何安装node.js支持插件

    在eclipse插件中,node.js插件中比较知名的是nodeclipse. 从HBuilder6.3起,工具-插件安装,可直接选择nodeclipse插件安装.安装完毕后重启HBuilder新建n ...

  5. node.js安装及grunt插件,如何进行脚本压缩

    http://gruntjs.com/pluginshttp://gruntjs.com/getting-startedhttp://gruntjs.com/configuring-tasks#glo ...

  6. KoaHub平台基于Node.js开发的Koa 连接支付宝插件代码信息详情

    KoaHub平台基于Node.js开发的Koa 链接支付宝插件代码信息详情 easy-alipay alipay payment & notification APIs easy-alipay ...

  7. node.js零基础详细教程(7.5):mongo可视化工具webstorm插件、nodejs自动重启模块Node Supervisor(修改nodejs后不用再手动命令行启动服务了)

    第七章 建议学习时间4小时  课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑. ...

  8. 在开发node.js中,关于使用VS2013插件出现一直读取资源的问题

    情况描述: 1.安装了VS2013: 2.安装了VS开发node.js的插件; 3.打开以前的工程文件,有的可以打开,有的打不开.而且打不开的始终停留在读取资源的界面.很痛苦的.等半天都没有反应.到底 ...

  9. 十一、Node.js监听代码改动自动重启node插件supervisor

    我们慢慢地发现,每次我们稍微改变js代码都需要重启服务才能在浏览器显示新的效果,很麻烦,这里我们可以通过npm命令安装supervisoror插件,安装方法如下 之前章节我们知道安装了nodejs就会 ...

随机推荐

  1. 归档(NSKeyedArchiver)的使用

    归档的使用,是归于使用保存数据,但是一些简单的数据,如数组,字典等基本的数据类型,往往不使用在归档中,归档和plist以及UserDefaults最大的区别就在于,前者可以存放自定义的数据类型,而后两 ...

  2. spark2.1:使用df.select(when(a===b,1).otherwise(0))替换(case when a==b then 1 else 0 end)

    最近工作中把一些sql.sh脚本执行hive的语句升级为spark2.1版本,其中遇到将case when 替换为scala操作df的方式实现的问题: 代码数据: scala> import o ...

  3. Struts(十一):OGNL表达式(二)

    Map栈 :request,session,application的一个属性值或一个请求参数的值. 若想访问ContextMap里的某个对象的属性,可以使用以下几种之一: #object.proper ...

  4. angularJs-route路由详解

    本篇基于ng-route来讲下angular中的路由,路由功能主要是 $routeProvider服务 与 ng-view 实现. ng-view的实现原理,是根据路由的切换,动态编译html模板-- ...

  5. Java 局部变量、实例变量、类变量(静态变量)区别

    1. 局部变量: 局部变量是类的方法中的变量: 2. 实例变量: 实例变量也是类中独立于方法之外的变量,不过没有static修饰,也叫 对象变量 3. 类变量(静态变量): 类变量是类中独立于方法之外 ...

  6. transition和animation做动画(css动画二)

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! translate:平移:是transform的一个属性: transform:变形:是一个静态属性,可以 ...

  7. ps图层的基本使用

    图层的使用 图层的基本使用一:复制,选择多个,背景图上添加图片,同时移动多个图层 复制图层:图层里的内容位置会变化,而拷贝的图层,图层里的位置不变,跟原来的图层一样 选择多个图层:shift选中多个图 ...

  8. 开源协议瞎扯淡,什么是 MIT 协议?[转]

    图片来源:http://ruby-china.org/topics/15979

  9. (hdu-4280)Island Transport~测试网络流模板速度~要加挂才能过啊

    Problem Description In the vast waters far far away, there are many islands. People are living on th ...

  10. [LeetCode] Reverse Words in a String III 翻转字符串中的单词之三

    Given a string, you need to reverse the order of characters in each word within a sentence while sti ...