JavaScript中的缓冲数组和强类型数组
在做最近的几个demo的时候碰到了ArrayBuffer(缓冲数组)的这样一个东西,所以在此整理一下相关的知识点。
1.什么是ArrayBuffer
你可以把ArrayBuffer当做一个内存的容器,以下代码声明了1kb的内存空间,但是你不可以直接通过数组下标获取的里面的数据。
//声明1kb的内存空间
var buffer = new ArrayBuffer(1024);
console.log(buffer[0]) //..undefined
2.Javascript的强类型数组
强类型数组有三种基本类型:
- Int 整数
- Uint 无符号整数
- Float IEEE754浮点数
根据这些类型MDN给出了JavaScript的以下几个API:
- Int8Array
- Uint8Array
- Uint8ClampedArray
- Int16Array
- Uint16Array
- Int32Array
- Uint32Array
- Float32Array
- Float64Array
基本使用
//创建一个有3个元素的8bit无符号整型
var a=new Uint8Array(3);
var b = new Uint8Array([1,2,3]);
//假如输入一个非数字呢
var c = new Uint8Array([258,1.3,-23,"hello"]);
console.log(a); // Uint8Array(3) [0,0,0,buffer...]
console.log(b); // Uint8Array(3) [1,2,3,buffer...]
console.log(c); // Uint8Array(3) [2,1,233,0,buffer...]
诶,我们可以发现在输入258的时候,范围溢出了uint8的0~255位,输入值就变成了258-255 = 2 了。浮点型直接去了尾数,-23二进制是11101001即得233 ,字符串型变成了0。
2.通过强类型数组来使用ArrayBuffer
var buffer=new ArrayBuffer(2);
var a=new Uint8Array(buffer);
var b=new Int8Array(buffer);
a[0]=100;
a[1]=255;
console.log(a); // Uint8Array(2) [100,255]
console.log(b); //Int8Array(2) [100, -1]
我们发现Int8类型在输入255的时候,溢出了–128 到 127的范围,输入值就变成了-128+255-128 = -1 了。
假如你只学过脚本语言(Python,JavaScript)对数据类型字节数(多少bit位来表达一个数)和范围还没有了解的同学,建议点击链接:TypedArray(MDN),或者随便找一个C/C++的数据类型看一下。
3.Uint8ClampedArray和Uint8Array的区别
就拿上例来说:
var c = new Uint8Array([258,1.3,-23,"hello"]);
console.log(c); // Uint8Array(3) [2,1,233,0,buffer...]
var d = new Uint8ClampedArray([258,1.3,-23,"hello"]);
console.log(d); // Uint8ClampedArray(3) [255,1,0,0,buffer...]
在处理溢出的时候,Uint8ClampedArray将超出255的值归到了255,将负数值全部归为了0。
4.实例分析
一、使用Canvas读取图片像素信息的实例:
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var image = new Image();
image.src = 'test.jpg';
image.onload = function() {
context.drawImage(image, 0, 0, this.width, this.height);
imageData = context.getImageData(0, 0, this.width, this.height).data;
console.log(imageData); //Uint8ClampedArray(1048576) [2, 2, 14, 255, 2, 2, …]
}
可以见得,canvas的api为了提高读取效率也使用了Uint8ClampedArray
二、读取音频高低数据(用作音频可视化)的实例:
var url = "http://localhost/test.mp3"
var xhr = new XMLHttpRequest();
var AudioContext = new AudioContext();
xhr.open("GET", url);
xhr.responseType = "arraybuffer";
xhr.onload = function(data){
musicData = data.target.response;
musicData = AudioContext.decodeAudioData(musicData,function(buffer){
var analyser = AudioContext.createAnalyser(buffer);
var arr = new Uint8Array(analyser.frequencyBinCount);
var bufferSource = AudioContext.createBufferSource();
bufferSource.connect(analyser);
analyser.connect(AudioContext.destination);
bufferSource.buffer = buffer;
bufferSource.start();
function v(){
//analyser.getByteFrequencyData 要求传入一个Uint8Array,并且我们让它不断的更新
audio = analyser.getByteFrequencyData(arr);
console.log(arr);
requestAnimationFrame(v);
}
requestAnimationFrame(v);
});
}
xhr.send();
上传一张结果图:
三、使用二进制数据加密前端资源文件url链接:
<body>
<audio id="audio" controls></audio>
</body>
<script type="text/javascript">
var url = "http://localhost/test.mp3"
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "arraybuffer";
xhr.onload = function(){
var arrayBuffer = new Uint8Array(this.response);
// 把资源文件转成blob用用户不可以直接下载使用的格式来转化,起到加密的作用
var blob = new Blob([arrayBuffer], {type: 'mine'});
var url = window.URL.createObjectURL(blob);
document.getElementById('audio').src = url;
console.log(url);
}
xhr.send();
</script>
上传一张结果图:
四、bitmap(位图)
问题:访客统计问题,10万个访客需要验证是它否存在也许用数组就可以容易实现了,那么当你有50亿个访客的时候呢。
这是一个后端常见的访客统计问题,当然,JavaScript也可以实现bitmap啦。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>bitmap</title>
</head>
<body>
<input type="text" placeholder="input a number" id="number">
<button id="set">添加</button>
<button id="isset">验证是否存在</button>
<button id="reset">删除</button>
<p id="answer"></p>
</body>
<script>
function Bitmap(bytes){
// >> 5 相当于 bytes / 32
// 10000000 / 32 * 4 / 1024 / 1024 约等于 12.5 MB
var buffer = new ArrayBuffer(bytes >> 5 + 1)
this.map = new Uint32Array(buffer)
}
Bitmap.prototype.set = function(num){
let ind = num >> 5;
let pos = num % 32;
this.map[ind] |= (1 << pos);
}
Bitmap.prototype.isSet = function(num){
let ind = num >> 5;
let pos = num % 32;
let flag = false;
if (this.map[ind] & (1 << pos)) flag = true;
return flag;
}
Bitmap.prototype.reSet = function(num){
let ind = num >> 5;
let pos = num % 32;
this.map[ind] &= ~(1 << pos)
}
var bitmap = new Bitmap