node开发要点
1、常用前端技术:
html5
css3
jQuery
2、js高级话题:
面向对象
闭包
作用域
设计模式
3、其他后端技术:
mongodb
redis
apache
nignix
mysql
4、node内置核心库:
全局对象
时事件
流
网络
子程序
5、常用知名第三方库:
async
express
koa
6、node高级话题:
异步
部署
性能调优
异常调试
node事件循环机制
* javaScript高级话题(面向对象、作用域、闭包、设计模式)
面向对象
1、常用js类定义的方法?
构造函数原型法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 function Person () {
this.name = 'pinocchio';
}
Person.prototype.sayName = function () {
alert(this.name);
; }
var person = new Person ();
person.sayName();
//对象创建:
var Person = {
name: 'pinocchio',
sayName: function () {
alert(this.name);
}
};
var person = Object.create(Person);
person.sayName();
2、js类继承的方法?
原型链法(Person是Animal的子集,即子类):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 function Animal () {
this.name = 'animal';
}
Animal.prototype.sayName = function () {
alter(this.name);
}
function Person () {}
Person.prototype = Animal.prototype; //人继承动物
Person.prototype = constructor = 'Person'; //更新构造函数为人
属性复制法(单纯复制,两者无继承关系):
function Animal () {
this.name = 'animal';
}
Animal.prototype.sayName = function () {
alter(this.name);
};
function Person () {}
//复制动物所有属性到人
for (prop in Animal.prototype) {
Person.prototype[prop] = Animal.prototype[prototype];
}
//更新构造函数为人
Person.prototype.constructor = 'Person';
构造器应用法:
function Animal () {
this.name = 'animal';
}
Animal.prototype.sayName = function () {
alter(this.name);
};
function Person () {
//apply,call,bind方法都行,有细微区别
Animal.call(this);
}
3、js类多重继承的方法怎么实现?
通过类继承里面的属性复制法来实现,可以继承所有父类的prototype属性
作用域
4、js里作用域是什么样?
函数作用域,
1
2
3
4
5
6
7
8
9var globalVar = 'global var';
function test() {
alert(globalVar); // undefined, 因为globalVar在本函数内被重定义了,导致全局失效,这里使用函数内的变量值,可是此时还没定义
var globalVar = 'overrided var'; // globalVar在本函数内被重定义
alert(globalVar); // overrided var
}
alert(globalVar); // global var,使用全局变量
5、js中this指的是什么?
this指的是对象本身
6、apply,call,bind有什么区别?
apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
三者第一个参数都是this要指向的对象,也就是想指定的上下文;
三者都可以利用后续参数传参;
apply,call是直接执行函数调用,bind是绑定,执行需要再次调用.apply和call的区别是apply接受数组作为参数,而call是接受逗号分隔的无限多个参数列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Person () {}
Person.prototype.sayName() {
alert(this.name);
}
//普通对象,不是Person实例
var obj = {
name: 'pinocchio'
};
//apply
Person.prototype.sayName.apply(obj, [param1, param2, param3]);
//call
Person.prototype.sayName.call(obj, param1, param2, param3);
//bind,先绑定,再执行
var sn = Person.prototype.sayName.bind(obj);
sn([param1, param2, param3]);
sn(param1, param2, param3);
7、caller,callee和arguments分别是什么?
1
2
3
4
5
6
7
8
9
10
11
12
caller,callee之间的关系就像是employer和employee之间的关系,就是调用与被调用的关系,二者返回的都是函数对象引用.arguments是函数的所有参数列表,它是一个类数组的变量.
function parent(param1, param2, param3) {
child(param1, param2, param3);
}
function child() {
console.log(arguments); // { '0': 'mqin1', '1': 'mqin2', '2': 'mqin3' }
console.log(arguments.callee); // [Function: child]
console.log(child.caller); // [Function: parent]
}
parent('mqin1', 'mqin2', 'mqin3');
闭包
8、什么是闭包?闭包有什么用处?
通俗的说,闭包就是作用域范围,因为js是函数作用域,所以函数就是闭包.全局函数的作用域范围就是全局,所以无须讨论.更多的应用其实是在内嵌函数,这就会涉及到内嵌作用域,或者叫作用域链。
这就会带来另外一个问题,什么时候引用结束?如果不结束,就会一直占用内存,引起内存泄漏.不用的时候就引用设为空。
9、defineProperty, hasOwnProperty, propertyIsEnumerable都是做什么用的?
Object.defineProperty(obj, prop, descriptor)用来给对象定义属性,有value,writable,configurable,enumerable,set/get等.hasOwnProerty用于检查某一属性是不是存在于对象本身,继承来的
父亲的属性不算.propertyIsEnumerable用来检测某一属性是否可遍历,也就是能不能用for..in循环来取到.
设计模式
10、js常用设计模式的实现思路,单例,工厂,代理,装饰,观察者模式等
1) 单例: 任意对象都是单例,无须特别处理
var obj = {name: 'michaelqin', age: 30};
2) 工厂: 就是同样形式参数返回不同的实例
1
2
3
4
5
6
7
8
9
10
11
12
13
function Person() { this.name = 'Person1'; }
function Animal() { this.name = 'Animal1'; }
function Factory() {}
Factory.prototype.getInstance = function(className) {
return eval('new ' + className + '()');
}
var factory = new Factory();
var obj1 = factory.getInstance('Person');
var obj2 = factory.getInstance('Animal');
console.log(obj1.name); // Person1
console.log(obj2.name); // Animal1
3) 代理: 就是新建个类调用老类的接口,包一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Person() { }
Person.prototype.sayName = function() { console.log('michaelqin'); }
Person.prototype.sayAge = function() { console.log(30); }
function PersonProxy() {
this.person = new Person();
var that = this;
this.callMethod = function(functionName) {
console.log('before proxy:', functionName);
that.person[functionName](); // 代理
console.log('after proxy:', functionName);
}
}
var pp = new PersonProxy();
pp.callMethod('sayName'); // 代理调用Person的方法sayName()
pp.callMethod('sayAge'); // 代理调用Person的方法sayAge()
4) 观察者: 就是事件模式,比如按钮的onclick这样的应用.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function Publisher() {
this.listeners = [];
}
Publisher.prototype = {
'addListener': function(listener) {
this.listeners.push(listener);
},
'removeListener': function(listener) {
delete this.listeners[listener];
},
'notify': function(obj) {
for(var i = 0; i < this.listeners.length; i++) {
var listener = this.listeners[i];
if (typeof listener !== 'undefined') {
listener.process(obj);
}
}
}
}; // 发布者
function Subscriber() {
}
Subscriber.prototype = {
'process': function(obj) {
console.log(obj);
}
}; // 订阅者
var publisher = new Publisher();
publisher.addListener(new Subscriber());
publisher.addListener(new Subscriber());
publisher.notify({name: 'michaelqin', ageo: 30}); // 发布一个对象到所有订阅者
publisher.notify('2 subscribers will both perform process'); // 发布一个字符串到所有订阅者
11、列举数组相关的常用方法
push/pop, shift/unshift, split/join, slice/splice/concat, sort/reverse, map/reduce, forEach, filter
12、列举字符串相关的常用方法
indexOf/lastIndexOf/charAt, split/match/test, slice/substring/substr, toLowerCase/toUpperCase
node核心内置库类(事件, 流, 文件, 网络)
node概览:
1、node架构
主要分为三层,应用app >> V8及node内置架构 >> 操作系统. V8是node运行的环境,可以理解为node虚拟机.
node内置架构又可分为三层: 核心模块(javascript实现) >> c++绑定 >> libuv + CAes + http.
2、node的核心模块
EventEmitter, Stream, FS, Net, 全局对象
node全局对象:
1、node有哪些全局对象?
process、 console、 Buffer
2、process有哪些方法?
process.stdin, process.stdout, process.stderr, process.on, process.env, process.argv, process.arch, process.platform, process.exit
3、console有哪些常用方法?
console.log/console.info, console.error/console.warning, console.time/console.timeEnd, console.trace, console.table
4、node有哪些定时功能?
setTimeout/clearTimeout, setInterval/clearInterval, setImmediate/clearImmediate, process.nextTick
5、node中的事件循环是什么样子的?
process.nextTick >> setImmidate >> setTimeout/SetInterval
6、node中的Buffer如何应用?
Buffer是用来处理二进制数据的,比如图片,mp3,数据库文件等.Buffer支持各种编码解码,二进制字符串互转
EventEmitter:
1、什么是EventEmitter?
EventEmitter是node中一个实现观察者模式的类,主要功能是监听和发射消息,用于处理多模块交互问题.
2、如何实现一个EventEmitter?
主要分三步:定义一个子类, 调用构造函数, 继承EventEmitter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var util = require('util'),
EventEmitter = require('event').EventEmitter;
//构造函数
function MyEmitter () {
EventEmitter.call(this);
}
//继承
util.inherits(MyEmitter, EventEmitter);
var em = new MyEmitter();
//接受事件
em.on('hello', function(data) {
console.log('收到事件hello的数据:', data);
});
em.emit('hello', 'EventEmitter传递消息真方便');
3、EventEmitter有哪些典型应用?
模块之间传递消息
回调函数内外传递消息
处理流数据
观察者模式发射触发机制
4、怎么捕获EventEmitter的错误事件?
监听error事件即可,若有多个EventEmitter,可用domain来统一处理。
1
2
3
4
5
6
7
8
9
10
11
12var domain = require('domain');
var myDomain = domain.create();
//接受事件并打印
myDomain.on('error', function(err){
console.log('domain接受到的事件', err);
});
myDomain.run(function() {
var emitter1 = new MyEmitter();
emitter1.emit('error', '错误事件来自emitter1');
var emitter2 = new MyEmitter();
emitter2.emit('error', '错误事件来自emitter2');
});
5、EventEmitter中的newListenser事件有什么用处?
newListener可以用来做事件机制的反射,特殊应用,事件管理等.当任何on事件添加到EventEmitter时,就会触发newListener事件,基于这种模式,我们可以做很多自定义处理.
1
2
3
4
5
6
7
8
9
var emitter3 = new MyEmitter();
emitter3.on('newListener', function(name, listener) {
console.log("新事件的名字:", name);
console.log("新事件的代码:", listener);
setTimeout(function(){ console.log("我是自定义延时处理机制"); }, 1000);
});
emitter3.on('hello', function(){
console.log('hello node');
});
Stream
stream是基于事件EventEmitter的数据管理模式.由各种不同的抽象接口组成,主要包括可写,可读,可读写,可转换等几种类型.
1、Stream有什么好处?
非阻塞式数据处理提升效率,片段处理节省内存,管道处理方便扩展
2、Stream有哪些典型应用?
文件处理、网络交换、数据转换、音视频处理
3、怎么捕获Stream的错误事件?
监听error事件、方法同EventEmitter
4、哪些常用Stream,分别什么时候用?
Readable为可被读流,在座位数据源时使用;
Writeable为可被读流,在作为输出元时使用;
Duplex为读写流,作为输入源接受被写入,同时又作为输入源被后面的流读出;
Transform机制和Duplex一样,都是双向流,区别时Transfrom只需要实现一个函数_transfrom(chunk, encoding, callback);
而Duplex需要分别实现_read(size)函数和_write(chunk, encoding, callback)函数.
5、实现一个Writable Stream?
三步走:1)构造函数call Writable 2) 继承Writable 3) 实现_write(chunk, encoding, callback)函数
```
var Writeable = require('stream').Writable;
var util = require('util');
//构造函数
function MyWriteable(options) {
Writeable.call(this, options);
}
//继承Writeable
util.inherits(MyWriteable, Writable);
MyWritable.prototype._write = function(chunk, encoding, callback) {
// 此处可对写入的数据进行处理
console.log("被写入的数据是:", chunk.toString());
callback();
};
// stdin作为输入源,MyWritable作为输出源
process.stdin.pipe(new MyWritable());
```
node高级话题(异步, 部署, 性能调优, 异步调试)
1、node中的异步与同步怎么理解?
node是单线程的,异步是通过一次次的事件循环来实现的。
同步则是阻塞式的io,在高并发环境下会有很大的性能问题,所以同步一般只在基础框架的启动时使用,如:加载配置文件、初始化程序
2、哪些方法可以进行异步流程控制?
1)多层嵌套回调
2)单独为回调写函数,函数里再回调
3)第三方框架:async、q、promise
3、怎么绑定node程序到80端口?
1)sudo
2)Apache/nginx代理
3)操作系统的firewall iptables端口绑定
4)第三方库up
4、有哪些方法可以让node程序遇到错误后自动重启?
1) runit
2) forever
3) nohup npm start &
4)up -watch -port 80 server.js
5、怎么充分利用多个cpu?
1个cpu运行一个node程序
6、怎样调节node执行单元的内存大小?
用--max-old-space-size 和 --max-new-space-size 来设置 v8 使用内存的上限
7、有哪些常用方法可以防止程序崩溃?
1) try-catch-finally
2) EventEmitter/Stream error事件处理
3) domain统一控制
4) jshint静态检查
5) jasmine/mocha进行单元测试
8、怎样调试node程序?
1) 监听错误事件req.on('error', function(){}), 适用EventEmitter存在的情况;
2) Promise.then.catch(error),适用Promise存在的情况
3) try-catch,适用async-await和js运行时异常,比如undefined object