nodejs中Buffer对象的静态属性(二)

前面说了下Buffer构造函数的实例化方法,以及Buffer构造函数的源码实现,只知道实例化的方法,不会用也不行么,所以这里再看下实例化对象之后,支持哪些属性和操作。

概总

让我们先看下Buffer构造函数有哪些静态方法,之后再看看实例化之后的对象,有哪些属性和方法,继续使用REPL模式下来看下吧。

直接在nodejs的控制面板中输入下面的信息:


C:\nodejs>node
>Buffer  //用于直接查看Buffer构造函数
{
    [Function:Buffer]
    isEncoding:[Function],
    poolSize:8192,
    compare:[Function:compare],
    isBuffer:[Function:isBuffer],
    byteLength:[Function],
    concat:[Function]
}
//这些属性,在不同版本中,这些Buffer构造函数的静态属性是不同的,比如compare在v0.12.2版本中是存在的,但是在v0.10的版本中,就不存在。

//当时也是看到源码中有compare方法,可是在我原来的nodejs版本中,却没有,果断升级之后,才看到了该属性。


由上述代码中,可以看到,Buffer构造函数中包括的一些Buffer函数上的一些静态方法和属性,在接下来,将对这些方法的概念和使用,进行简单的说明。

Buffer对象静态方法和属性的使用

1:isEncoding方法

isEncoding是用于检测一个字符串是否为一个有效的编码格式字符串,使用方法如下:


Buffer.isEncoding(encoding);


isEncoding方法中,使用一个参数,用于指定需要被检测的字符串,如果该字符串为有效的编码格式字符串,则方法返回true,否则返回false

让我们看下在REPL运行环境下使用isEncoding方法来检测一些字符串是否为有效的编码格式字符串:


>str1 = "utf8";
'utf16le'
>str2 = "utf7";
'utf16e'
>Buffer.isEncoding(str1);
true
>Buffer.isEncoding(str2);
false
>Buffer.isEncoding("ascii");


由此可以看出,isEncoding的方法,只是用来检测,字符串是不是当前所支持的有效编码,看下源码中,isEncoding的实现方法,就可以知道,当前的isEncoding支持哪些编码方式了:


Buffer.isEncoding = function(encoding) {
	//先变成字符串,然后变小写字符
  switch ((encoding + '').toLowerCase()) {
    case 'hex':
    case 'utf8':
    case 'utf-8':
    case 'ascii':
    case 'binary':
    case 'base64':
    case 'ucs2':
    case 'ucs-2':
    case 'utf16le':
    case 'utf-16le':
    case 'raw':
      return true;

    default:
      return false;
  }
};


由上述代码中isEncoding的源码,也可以看出,nodejs中,支持的有效编码有:hexutf8utf-8asciibinarybase64ucs2ucs-2utf16leutf-16leraw。对于其中每种编码方式的差别,好吧,我是不知道的~~~

2:poolSize属性

该属性是用于控制生成内存区间大小的一个参数,默认值为8 * 1024,使用方法如下:


Buffer.poolSize = 10000;
//这里属于对该属性进行赋值


该属性是在实例化Buffer构造函数时使用的,把该属性放在Buffer对象的静态属性中,是为了能按照个人的需求自定义每次生成内存区大小,可以根据不同的业务,或者不同的应用,定义更适合的值。

该属性可读写。

3:isBuffer方法

isBuffer方法,是用于判断一个对象是否为Buffer对象的,使用方法如下:


Buffer.isBuffer(obj);


isBuffer方法中,需要一个参数,用于指定需要被判断的对象,如果对象为Buffer对象,方法返回true,否则返回false

继续在REPL模式下,看一下实示例:


>Buffer.isBuffer("111");
false
>Buffer.isBuffer([1,2,3]);
false
>Buffer.isBuffer(new Buffer([1,2,3]));
true


由此可见,只有使用Buffer实例化之后的Buffer对象,通过使用isBuffer函数,才会返回true,其他的不论是字符串,数组,对象,都返回false

继续看下isBuffer方法在nodejs中的源码中是如何实现的:


Buffer.isBuffer = function isBuffer(b) {
  return util.isBuffer(b);
};

//util模块中的isBuffer方法
function isBuffer(arg) {
  return arg instanceof Buffer;
}
exports.isBuffer = isBuffer;


isBuffer方法,是使用的util模块中的isBuffer方法,至于util中的isBuffer方法呢,判断方法,使用的是instanceof进行判断的。

4:byteLength方法

byteLength方法是为了计算一个指定字符串的字节数,使用方法如下:


Buffer.byteLength(str,[encoding]);


byteLength方法,使用两个参数,第一个参数是必须的参数,用于指定需要计算字节数的字符串,第二个参数为可选的,用于指定按什么编码方式进行计算,默认编码方式为utf8

继续看下示例:


>a = "我喜爱Nodejs";
"我喜爱Nodejs"
>a.length
9
>Buffer.byteLength(a);
15
>Buffer.byteLength(a,"utf16le");
18
>Buffer.byteLength(a,"raw");
'raw' (array of integers) has been removed. Use 'binary'
9
>Buffer.byteLength(a,"hex");


由此可见,对于同一个字符串,不同的编码格式,编码之后形成的byte数是不同的,也就说明了,相同的内容,经过不同的编码,保存到内存中时,所需要的内存区域大小时不同的。

上面的raw的编码方式,提示我已经被移除~~~这个就可能会受到nodejs版本的影响了吧。

看下byteLength的源码实现:


Buffer.byteLength = function(str, enc) {
  var ret;
  str = str + '';
  //把str参数转换为一个字符串
  
  switch (enc) {
    case 'ascii':
    case 'binary':
    case 'raw':
      ret = str.length;
      break;
    case 'ucs2':
    case 'ucs-2':
    case 'utf16le':
    case 'utf-16le':
      ret = str.length * 2;
      break;
    case 'hex':
      ret = str.length >>> 1;
      break;
    default:
      ret = internal.byteLength(str, enc);
  }
  
  //根据不同的编码类型,返回一个长度值
  return ret;
};

从该源码中,就可以看出,上述示例中,是如何根据不同的编码方式计算长度值的了。


5:concat方法

concat方法用于将几个Buffer对象结合创建为一个新的Buffer对象,使用方法如下:


Buffer.concat(list,[totalLength]);


concat方法中,支持两个参数,其中第一个参数为必须指定的参数,参数值为一个放了多个Buffer对象的数组,concat方法将把其中所有的Buffer对象链接创建一个Buffer对象。

第二个参数为可选参数,用于指定被创建的Buffer对象的总长度,当省略该参数时,被创建的Buffer的长度为第一个参数中,所有Buffer对象的的长度的总和。

其中:

如果第一个参数为空数组或者第二个参数值等于0,那么concat方法返回一个长度为0的Buffer对象。

如果第一个参数值数组中只有一个Buffer对象,那么concat方法直接返回该Buffer对象。

如果第一个参数值数组中拥有一个以上的Buffer对象,那么从插头方法返回被创建的Buffer对象。

看下REPL模式下的示例:


>str1 = new Buffer("我");
<Buffer e6 88 91>
>str2 = new Buffer("爱");
<Buffer e7 88 b1>
>str3 = new Buffer("Nodejs");
<Buffer 4e 6f 64 65 6a 73>
>buf = Buffer.concat([str1,str2,str3]);
<Buffer e6 88 91 e7 88 b1 4e 6f 64 65 6a 73>
>buf.length
12
>buf.toString();
"我爱Nodejs"


OK,这里就先说到这里,看下源码中的concat是如何实现的,方便更彻底的了解concat的功能。


Buffer.concat = function(list, length) {
  if (!util.isArray(list)){
    //如果第一个参数不为数组,则抛出一个“类型异常”
	throw new TypeError('list argument must be an Array of Buffers.');
  }

  
  if (util.isUndefined(length)) {
  //如果第二个参数没有定义,则~~把length定义为list数组中所有数组的长度之和
    length = 0;
    for (var i = 0; i < list.length; i++)
      length += list[i].length;
  } else {
	//否则,把输入的length属性变成数字
    length = length >>> 0;
  }

  //如果第一个参数数组的长度为0,则返回一个长度为0的buffer对象
  if (list.length === 0)
    return new Buffer(0);
  else if (list.length === 1)
    return list[0];
  //如果第一个数组参数的长度为1,则直接返回该buffer对象

  //否则,初始化一个长度为length的buffer对象
  var buffer = new Buffer(length);
  var pos = 0;
  //通过for循环,把第一个参数中的所有数据,
  //初始化到新生成的这个buffer对象中去
  for (var i = 0; i < list.length; i++) {
    var buf = list[i];
    buf.copy(buffer, pos);
    pos += buf.length;
  }

  //返回新生成的buffer对象。
  return buffer;
};


看了源码中的逻辑,以及注释,应该能深刻的彻底的了解到concat的一些功能了吧,Ok,不再多说。

6:compare属性

compare方法,用于判断两个Buffer对象的相对位置,使用方法如下:


Buffer.compare(a,b);


compare方法中,支持两个参数,这两个参数都必须是Buffer的实例,并且都是必须的,如果只使用一个参数,则会在执行时,抛出一个异常。

返回值是按照两个buffer对象中,每一个元素数值的大小对比的,对比两个buffer对象中相同位置的元素大小,如果前一个buffer中的元素值大于第二个buffer对象中相同位置的元素值,则返回1,否则返回-1,如果一直到结束,所有的元素都相同,则返回0。

OK,下面就做个测试验证前面的说法,在REPL调试模式下:


C:\nodejs>node
>buf1 = new Buffer([1,2,3]);
<Buffer 01 02 03>
>buf2 = new Buffer([2,2,3]);
<Buffer 02 02 03>
>Buffer.compare(buf1,buf2);
-1
//因为buf1[0]   buf2[0]
>buf2[0]=1
1
>Buffer.compare(buf1,buf2);
0
//因为这个时候,buf1和buf2的元素值是完全相同的
>buf2[0] = -1
-1
//修改buf2的第一个元素值为-1,继续比较
>Buffer.compare(buf1,buf2);
-1
//为什么呢?这里分明buf1[0] > buf2[0]了啊?
>buf2
<Buffer ff 02 03>
//看下的buf2的值,就明白了,因为-1的值,被保存到内存中,是以ff保存的~~
>buf2[0]
255
//所以,在数组中,保存为负值时,是需要注意的
//这个时候,想要读取为-1的值,只能使用buf实例中的一些方法了
//比如我这里需要读取8位有符号整数,这些属性在下一篇中统一说明。
>buf.readInt8(0)
-1


前面的示例中,也把对应的一些关系都说明了一下,不过,对比的元素也只是相同长度的元素,那么如果是两个buffer的长度不同呢?这个有兴趣的可以自己测试一下,依然符合我们之前说的那个逻辑。

如果对JS很熟悉,会不会尝试一下,如果Buffer的原始是一个Object对象呢?反正我是试了一下,Buffer元素不能是对象,只能是一些基本的数字字符串等。

ObjectArray等对象,undefinedNaNnull等,都是不能被赋值到Buffer对象中去的,如果尝试使用这些给buffer中的元素赋值,那么buffer中对应的元素会被置为0,而truefalse是可以先被转换为10,对buffer元素赋值的。

不过这些意义就不大了,了解即可。

看完了上面的测试示例,那么看下buffer中源码是如何实现该compare方法的:


Buffer.compare = function compare(a, b) {
  if (!(a instanceof Buffer) ||
      !(b instanceof Buffer))
    throw new TypeError('Arguments must be Buffers');

  return internal.compare(a, b);
};


这里的internal是何许的东东,在源码中找了一下,并没有找到对应的源码,所以这里对该部分的源码就不再多说了,当我们知道了前面的示例中,出现不同结果的原因之后,其实conpare的处理逻辑,也基本上,就可以自己实现了。

总结

本篇想要说的Buffer对象的静态属性和方法,其实也不需要如此多的篇幅,这里只是想要把源码一起看看,按照一句话的说法就是,如果想要学到更深的层次,要“知其然,知其所以然”,做到最深层次的了解,对于学习,对于使用都是必不可少的,而且在学习源码的时候,还可以学到一些技巧,这些技巧一般都是在书中学不到的东西。

本文地址:http://www.zhangyunling.com/?p=398

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>