erp-erp外包-北京erp外包

搜 索

Search:erp


js代码 es6二进制数组,js二进制数组

ES6 二进制数组 二进制数组(ArrayBuffer对象、TypedArray视图和DataView视图)是JavaScript操作二进制数据的一个接口。这些对象早就存在,属于独立的规格(2ES6 二进制数组二进制数组(ArrayBuffer对象、TypedArray视图和DataView视图)是JavaScript操作二进制数据的一个接口。这些对象早就存在,属于独立的规格(2021年2月发布),ES6将它们纳入了ECMAScript规格,并且增加了新的方法。这个接口的原始设计目的,与WebGL项目有关。所谓WebGL,就是指浏览器与显卡之间的通信接口,为了满足JavaScript与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式。文本格式传递一个32位整数,两端的JavaScript脚本与显卡都要进行格式转化,将非常耗时。这时要是存在一种机制,可以像C语言那样,直接操作字节,将4个字节的32位整数,以二进制形式原封不动地送入显卡,脚本的性能就会大幅提升。二进制数组就是在这种背景下诞生的。它很像C语言的数组,允许开发者以数组下标的形式,直接操作内存,大大增强了JavaScript处理二进制数据的能力,使得开发者有可能通过JavaScript与操作系统的原生接口进行二进制通信。二进制数组由三类对象组成。(1)ArrayBuffer对象:代表内存之中的一段二进制数据,可以通过“视图”进行操作。“视图”部署了数组接口,这意味着,可以用数组的方法操作内存。(2)TypedArray视图:共包括9种类型的视图,比如Uint8Array(无符号8位整数)数组视图, Int16Array(16位整数)数组视图, Float32Array(32位浮点数)数组视图等等。(3)DataView视图:可以自定义复合格式的视图,比如第一个字节是Uint8(无符号8位整数)、第二、三个字节是Int16(16位整数)、第四个字节开始是Float32(32位浮点数)等等,此外还可以自定义字节序。简单说,ArrayBuffer对象代表原始的二进制数据,TypedArray视图用来读写简单类型的二进制数据,DataView视图用来读写复杂类型的二进制数据。TypedArray视图支持的数据类型一共有9种(DataView视图支持除Uint8C以外的其他8种)。数据类型字节长度含义对应的C语言类型Int818位带符号整数signed charUint818位不带符号整数unsigned charUint8C18位不带符号整数(自动过滤溢出)unsigned charInt16216位带符号整数shortUint16216位不带符号整数unsigned shortInt32432位带符号整数intUint32432位不带符号的整数unsigned intFloat32432位浮点数floatFloat64864位浮点数double注意,二进制数组并不是真正的数组,而是类似数组的对象。很多浏览器操作的API,用到了二进制数组操作二进制数据,下面是其中的几个。File APIXMLHttpRequestFetch APICanvasWebSocketsArrayBuffer对象概述ArrayBuffer对象代表储存二进制数据的一段内存,它不能直接读写,只能通过视图(TypedArray视图和DataView视图)来读写,视图的作用是以指定格式解读二进制数据。ArrayBuffer也是一个构造函数,可以分配一段可以存放数据的连续内存区域。var buf = new ArrayBuffer(32);上面代码生成了一段32字节的内存区域,每个字节的值默认都是0。可以看到,ArrayBuffer构造函数的参数是所需要的内存大小(单位字节)。为了读写这段内容,需要为它指定视图。DataView视图的创建,需要提供ArrayBuffer对象实例作为参数。var buf = new ArrayBuffer(32);var dataView = new DataView(buf);dataView.getUint8(0) // 0上面代码对一段32字节的内存,建立DataView视图,然后以不带符号的8位整数格式,读取第一个元素,结果得到0,因为原始内存的ArrayBuffer对象,默认所有位都是0。另一种TypedArray视图,与DataView视图的一个区别是,它不是一个构造函数,而是一组构造函数,代表不同的数据格式。var buffer = new ArrayBuffer(12);var x1 = new Int32Array(buffer);x1[0] = 1;var x2 = new Uint8Array(buffer);x2[0]= 2;x1[0] // 2上面代码对同一段内存,分别建立两种视图:32位带符号整数(Int32Array构造函数)和8位不带符号整数(Uint8Array构造函数)。由于两个视图对应的是同一段内存,一个视图修改底层内存,会影响到另一个视图。TypedArray视图的构造函数,除了接受ArrayBuffer实例作为参数,还可以接受普通数组作为参数,直接分配内存生成底层的ArrayBuffer实例,并同时完成对这段内存的赋值。var typedArray = new Uint8Array([0,1,2]);typedArray.length // 3typedArray[0] = 5;typedArray // [5, 1, 2]上面代码使用TypedArray视图的Uint8Array构造函数,新建一个不带符号的8位整数视图。可以看到,Uint8Array直接使用普通数组作为参数,对底层内存的赋值同时完成。ArrayBuffer.prototype.byteLengthArrayBuffer实例的byteLength属性,返回所分配的内存区域的字节长度。var buffer = new ArrayBuffer(32);buffer.byteLength// 32如果要分配的内存区域很大,有可能分配失败(因为没有那么多的连续空余内存),所以有必要检查是否分配成功。if (buffer.byteLength === n) {// 成功} else {// 失败}ArrayBuffer.prototype.slice()ArrayBuffer实例有一个slice方法,允许将内存区域的一部分,拷贝生成一个新的ArrayBuffer对象。var buffer = new ArrayBuffer(8);var newBuffer = buffer.slice(0, 3);上面代码拷贝buffer对象的前3个字节(从0开始,到第3个字节前面结束),生成一个新的ArrayBuffer对象。slice方法其实包含两步,第一步是先分配一段新内存,第二步是将原来那个ArrayBuffer对象拷贝过去。slice方法接受两个参数,第一个参数表示拷贝开始的字节序号(含该字节),第二个参数表示拷贝截止的字节序号(不含该字节)。如果省略第二个参数,则默认到原ArrayBuffer对象的结尾。除了slice方法,ArrayBuffer对象不提供任何直接读写内存的方法,只允许在其上方建立视图,然后通过视图读写。ArrayBuffer.isView()ArrayBuffer有一个静态方法isView,返回一个布尔值,表示参数是否为ArrayBuffer的视图实例。这个方法大致相当于判断参数,是否为TypedArray实例或DataView实例。var buffer = new ArrayBuffer(8);ArrayBuffer.isView(buffer) // falsevar v = new Int32Array(buffer);ArrayBuffer.isView(v) // trueTypedArray视图概述ArrayBuffer对象作为内存区域,可以存放多种类型的数据。同一段内存,不同数据有不同的解读方式,这就叫做“视图”(view)。ArrayBuffer有两种视图,一种是TypedArray视图,另一种是DataView视图。前者的数组成员都是同一个数据类型,后者的数组成员可以是不同的数据类型。目前,TypedArray视图一共包括9种类型,每一种视图都是一种构造函数。Int8Array:8位有符号整数,长度1个字节。Uint8Array:8位无符号整数,长度1个字节。Uint8ClampedArray:8位无符号整数,长度1个字节,溢出处理不同。Int16Array:16位有符号整数,长度2个字节。Uint16Array:16位无符号整数,长度2个字节。Int32Array:32位有符号整数,长度4个字节。Uint32Array:32位无符号整数,长度4个字节。Float32Array:32位浮点数,长度4个字节。Float64Array:64位浮点数,长度8个字节。这9个构造函数生成的数组,统称为TypedArray视图。它们很像普通数组,都有length属性,都能用方括号运算符([])获取单个元素,所有数组的方法,在它们上面都能使用。普通数组与TypedArray数组的差异主要在以下方面。TypedArray数组的所有成员,都是同一种类型。TypedArray数组的成员是连续的,不会有空位。TypedArray数组成员的默认值为0。比如,new Array(10)返回一个普通数组,里面没有任何成员,只是10个空位;new Uint8Array(10)返回一个TypedArray数组,里面10个成员都是0。TypedArray数组只是一层视图,本身不储存数据,它的数据都储存在底层的ArrayBuffer对象之中,要获取底层对象必须使用buffer属性。构造函数TypedArray数组提供9种构造函数,用来生成相应类型的数组实例。构造函数有多种用法。(1)TypedArray(buffer, byteOffset=0, length?)同一个ArrayBuffer对象之上,可以根据不同的数据类型,建立多个视图。// 创建一个8字节的ArrayBuffervar b = new ArrayBuffer(8);// 创建一个指向b的Int32视图,开始于字节0,直到缓冲区的末尾var v1 = new Int32Array(b);// 创建一个指向b的Uint8视图,开始于字节2,直到缓冲区的末尾var v2 = new Uint8Array(b, 2);// 创建一个指向b的Int16视图,开始于字节2,长度为2var v3 = new Int16Array(b, 2, 2);上面代码在一段长度为8个字节的内存(b)之上,生成了三个视图:v1、v2和v3。视图的构造函数可以接受三个参数:第一个参数(必需):视图对应的底层ArrayBuffer对象。第二个参数(可选):视图开始的字节序号,默认从0开始。第三个参数(可选):视图包含的数据个数,默认直到本段内存区域结束。因此,v1、v2和v3是重叠的:v1[0]是一个32位整数,指向字节0~字节3;v2[0]是一个8位无符号整数,指向字节2;v3[0]是一个16位整数,指向字节2~字节3。只要任何一个视图对内存有所修改,就会在另外两个视图上反应出来。注意,byteOffset必须与所要建立的数据类型一致,否则会报错。var buffer = new ArrayBuffer(8);var i16 = new Int16Array(buffer, 1);// Uncaught RangeError: start offset of Int16Array should be a multiple of 2上面代码中,新生成一个8个字节的ArrayBuffer对象,然后在这个对象的第一个字节,建立带符号的16位整数视图,结果报错。因为,带符号的16位整数需要两个字节,所以byteOffset参数必须能够被2整除。如果想从任意字节开始解读ArrayBuffer对象,必须使用DataView视图,因为TypedArray视图只提供9种固定的解读格式。(2)TypedArray(length)视图还可以不通过ArrayBuffer对象,直接分配内存而生成。var f64a = new Float64Array(8);f64a[0] = 10;f64a[1] = 20;f64a[2] = f64a[0] + f64a[1];上面代码生成一个8个成员的Float64Array数组(共64字节),然后依次对每个成员赋值。这时,视图构造函数的参数就是成员的个数。可以看到,视图数组的赋值操作与普通数组的操作毫无两样。(3)TypedArray(typedArray)TypedArray数组的构造函数,可以接受另一个TypedArray实例作为参数。var typedArray = new Int8Array(new Uint8Array(4));上面代码中,Int8Array构造函数接受一个Uint8Array实例作为参数。注意,此时生成的新数&#
标签:

js代码 es6数值的扩展,js数值的扩展

ES6 数值的扩展 二进制和八进制表示法 ES6提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。 0b111110111 === 503 // true 0oES6 数值的扩展二进制和八进制表示法ES6提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。0b111110111 === 503 // true0o767 === 503 // true从ES5开始,在严格模式之中,八进制就不再允许使用前缀0表示,ES6进一步明确,要使用前缀0o表示。// 非严格模式(function(){console.log(0o11 === 011);})() // true// 严格模式(function(){'use strict';console.log(0o11 === 011);})() // Uncaught SyntaxError: Octal literals are not allowed in strict mode.如果要将0b和0o前缀的字符串数值转为十进制,要使用Number方法。Number('0b111')// 7Number('0o10')// 8Number.isFinite(), Number.isNaN()ES6在Number对象上,新提供了Number.isFinite()和Number.isNaN()两个方法。Number.isFinite()用来检查一个数值是否为有限的(finite)。Number.isFinite(15); // trueNumber.isFinite(0.8); // trueNumber.isFinite(NaN); // falseNumber.isFinite(Infinity); // falseNumber.isFinite(-Infinity); // falseNumber.isFinite('foo'); // falseNumber.isFinite('15'); // falseNumber.isFinite(true); // falseES5可以通过下面的代码,部署Number.isFinite方法。(function (global) {var global_isFinite = global.isFinite;Object.defineProperty(Number, 'isFinite', {value: function isFinite(value) {return typeof value === 'number' && global_isFinite(value);},configurable: true,enumerable: false,writable: true});})(this);Number.isNaN()用来检查一个值是否为NaN。Number.isNaN(NaN) // trueNumber.isNaN(15) // falseNumber.isNaN('15') // falseNumber.isNaN(true) // falseNumber.isNaN(9/NaN) // trueNumber.isNaN('true'/0) // trueNumber.isNaN('true'/'true') // trueES5通过下面的代码,部署Number.isNaN()。(function (global) {var global_isNaN = global.isNaN;Object.defineProperty(Number, 'isNaN', {value: function isNaN(value) {return typeof value === 'number' && global_isNaN(value);},configurable: true,enumerable: false,writable: true});})(this);它们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,非数值一律返回false。isFinite(25) // trueisFinite("25") // trueNumber.isFinite(25) // trueNumber.isFinite("25") // falseisNaN(NaN) // trueisNaN("NaN") // trueNumber.isNaN(NaN) // trueNumber.isNaN("NaN") // falseNumber.parseInt(), Number.parseFloat()ES6将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。// ES5的写法parseInt('12.34') // 12parseFloat('123.45#') // 123.45// ES6的写法Number.parseInt('12.34') // 12Number.parseFloat('123.45#') // 123.45这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。Number.parseInt === parseInt // trueNumber.parseFloat === parseFloat // trueNumber.isInteger()Number.isInteger()用来判断一个值是否为整数。需要注意的是,在JavaScript内部,整数和浮点数是同样的储存方法,所以3和3.0被视为同一个值。Number.isInteger(25) // trueNumber.isInteger(25.0) // trueNumber.isInteger(25.1) // falseNumber.isInteger("15") // falseNumber.isInteger(true) // falseES5可以通过下面的代码,部署Number.isInteger()。(function (global) {var floor = Math.floor,isFinite = global.isFinite;Object.defineProperty(Number, 'isInteger', {value: function isInteger(value) {return typeof value === 'number' && isFinite(value) &&value > -9007199254740992 && value < 9007199254740992 &&floor(value) === value;},configurable: true,enumerable: false,writable: true});})(this);Number.EPSILONES6在Number对象上面,新增一个极小的常量Number.EPSILON。Number.EPSILON// 2.220446049250313e-16Number.EPSILON.toFixed(20)// '0.00000000000000022204'引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围。我们知道浮点数计算是不精确的。0.1 + 0.2// 0.300000000000000040.1 + 0.2 - 0.3// 5.551115123125783e-175.551115123125783e-17.toFixed(20)// '0.00000000000000005551'但是如果这个误差能够小于Number.EPSILON,我们就可以认为得到了正确结果。5.551115123125783e-17 < Number.EPSILON// true因此,Number.EPSILON的实质是一个可以接受的误差范围。function withinErrorMargin (left, right) {return Math.abs(left - right) < Number.EPSILON;}withinErrorMargin(0.1 + 0.2, 0.3)// truewithinErrorMargin(0.2 + 0.2, 0.3)// false上面的代码为浮点数运算,部署了一个误差检查函数。安全整数和Number.isSafeInteger()JavaScript能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。Math.pow(2, 53) // 90071992547409929007199254740992// 90071992547409929007199254740993// 9007199254740992Math.pow(2, 53) === Math.pow(2, 53) + 1// true上面代码中,超出2的53次方之后,一个数就不精确了。ES6引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限。Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1// trueNumber.MAX_SAFE_INTEGER === 9007199254740991// trueNumber.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER// trueNumber.MIN_SAFE_INTEGER === -9007199254740991// true上面代码中,可以看到JavaScript能够精确表示的极限。Number.isSafeInteger()则是用来判断一个整数是否落在这个范围之内。Number.isSafeInteger('a') // falseNumber.isSafeInteger(null) // falseNumber.isSafeInteger(NaN) // falseNumber.isSafeInteger(Infinity) // falseNumber.isSafeInteger(-Infinity) // falseNumber.isSafeInteger(3) // trueNumber.isSafeInteger(1.2) // falseNumber.isSafeInteger(9007199254740990) // trueNumber.isSafeInteger(9007199254740992) // falseNumber.isSafeInteger(Number.MIN_SAFE_INTEGER - 1) // falseNumber.isSafeInteger(Number.MIN_SAFE_INTEGER) // trueNumber.isSafeInteger(Number.MAX_SAFE_INTEGER) // trueNumber.isSafeInteger(Number.MAX_SAFE_INTEGER + 1) // false这个函数的实现很简单,就是跟安全整数的两个边界值比较一下。Number.isSafeInteger = function (n) {return (typeof n === 'number' &&Math.round(n) === n &&Number.MIN_SAFE_INTEGER <= n &&n <= Number.MAX_SAFE_INTEGER);}实际使用这个函数时,需要注意。验证运算结果是否落在安全整数的范围内,不要只验证运算结果,而要同时验证参与运算的每个值。Number.isSafeInteger(9007199254740993)// falseNumber.isSafeInteger(990)// trueNumber.isSafeInteger(9007199254740993 - 990)// true9007199254740993 - 990// 返回结果 9007199254740002// 正确答案应该是 9007199254740003上面代码中,9007199254740993不是一个安全整数,但是Number.isSafeInteger会返回结果,显示计算结果是安全的。这是因为,这个数超出了精度范围,导致在计算机内部,以9007199254740992的形式储存。9007199254740993 === 9007199254740992// true所以,如果只验证运算结果是否为安全整数,很可能得到错误结果。下面的函数可以同时验证两个运算数和运算结果。function trusty (left, right, result) {if (Number.isSafeInteger(left) &&Number.isSafeInteger(right) &&Number.isSafeInteger(result)) {return result;}throw new RangeError('Operation cannot be trusted!');}trusty(9007199254740993, 990, 9007199254740993 - 990)// RangeError: Operation cannot be trusted!trusty(1, 2, 3)// 3Math对象的扩展ES6在Math对象上新增了17个与数学相关的方法。所有这些方法都是静态方法,只能在Math对象上调用。Math.trunc()Math.trunc方法用于去除一个数的小数部分,返回整数部分。Math.trunc(4.1) // 4Math.trunc(4.9) // 4Math.trunc(-4.1) // -4Math.trunc(-4.9) // -4Math.trunc(-0.1234) // -0对于非数值,Math.trunc内部使用Number方法将其先转为数值。Math.trunc('123.456')// 123对于空值和无法截取整数的值,返回NaN。Math.trunc(NaN);// NaNMath.trunc('foo');// NaNMath.trunc();// NaN对于没有部署这个方法的环境,可以用下面的代码模拟。Math.trunc = Math.trunc || function(x) {return x < 0 ? Math.ceil(x) : Math.floor(x);};Math.sign()Math.sign方法用来判断一个数到底是正数、负数、还是零。它会返回五种值。参数为正数,返回+1;参数为负数,返回-1;参数为0,返回0;参数为-0,返回-0;其他值,返回NaN。Math.sign(-5) // -1Math.sign(5) // +1Math.sign(0) // +0Math.sign(-0) // -0Math.sign(NaN) // NaNMath.sign('foo'); // NaNMath.sign();// NaN对于没有部署这个方法的环境,可以用下面的代码模拟。Math.sign = Math.sign || function(x) {x = +x; // convert to a numberif (x === 0 || isNaN(x)) {return x;}return x > 0 ? 1 : -1;};