javascript 基础知识分享

56 阅读34分钟

一、javascript 组成

javascript关键点:类型、对象、函数、原型、原型链、继承、词法作用域、作用域链、执行上下文、执行上下文栈、this、闭包、垃圾回收、事件循环

1.1 应用方向

表单动态校验(密码强度检测);(初衷)

网页特效(尽量不用,影响性能);

服务端开发(Node.js);

桌面开发(electron);

App(Cordova);

控制硬件-物联网(Ruff);

游戏开发(coocs2d-js);

二、javascript 是什么

2.1 由什么组成

JavaScript = ECMAScript + 文档对象模型(DOM)+ 浏览器对象模型(BOM)

ECMAScript:语言本身,由语法、类型、语句、关键字、保留字、操作符、全局对象组成

文档对象模型(DOM):操作 HTML 的 API

浏览器对象模型(BOM):操作 浏览器的 API,如 Window、Screen、Location、History、Navigator、弹出框、Timing、Cookies

2.2 特点

基于原型继承,即每个对象拥有一个原型对象,对象又以其原型为模板,从原型继承方法和属性。原型对象也是对象,也拥有原型,并从它的原型继承方法和属性,如此类型,形成原型链。

函数是一等公民,不仅能像对象一样使用,还拥有函数传值、作为返回值返回。

函数作用域,函数作用域为词法作用域,可形成闭包。

三、javascript 数据类型

1、语法

2、变量和数据类型

3、关键字和保留字

4、操作符

5、语句

6、对象

3.1 数据类型

原始类型(值类型): number、string、null、undefined、boolean、symbol(es6)、bigint(es10)

非原始类型(引用类型): Object、Array、定型数组(typed array)、Date、RegExp、Function、基本包装类型(String、Number、Boolean)、单体内置对象(Global、Math)、ES6新增引用类型(Map、WeakMap、Set 、WeakSet)

3.1.2 类型判断

1、typeof

  • 作用:判断值类型
  • 两个特例:null返回object, new Function 返回function
  • 返回值:string

2、instanceof

  • 检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
  • 返回值:boolean

3、constructor

  • 通过每个类型上的constructor函数判断
  • 两个特例:null和undefined,没有constructor
  • 返回值:function 在终端显示: [Function: 类型]

4、Object.prototype.toString.call(source)

  • 作用:真正做到对类型的检测
  • 返回值:字符串 在终端显示:[object 类型]
// 类型检测函数
const typeList = ["Null", "Undefined", "String", "Number", "Boolean", "Object", "Array", "Function", "Date", "RegExp", "Error", "Map", "Set", "WeakSet", "WeakMap"];
function getType (source) {
    const target = Object.prototype.toString.call(source);
    for (let type of typeList) {
        if (target == "[object " + type + "]")
            return type.toLocaleLowerCase();
    }
}

3.1.3 原始值(primitive value)

不可更改和无法分解的基本数据类型,包括数字、布尔值、字符串、null、undefined和Symbol。

// vue2.6中
function isPrimitive (value: any): boolean % checks{
    return (
        typeof value == "string" ||
        typeof value == "number" ||
        typeof value == "symbol" ||
        typeof value == "boolean"
    )
}

3.2 基本包装类型

基本类型number、string、boolean有其自己的包装类对象,在使用的时候后台会自动调用。

示例

// 字面量创建的字符串默认会带String上的方法
let str1 = "hello world";
console.log(str.toUpperCase());  // ===> HELLO WORLD

高程四的解释

// 1、创建一个 String 类型的实例;
// 2、调用实例上的特定方法
// 3、销毁实例

let s1 = new String("some text")
let s2 = s1.substring(2)
s1 = null

四、javascript 原始数据(基本类型)

4.1 字符串类型

字符串一旦创建,不能修改,只能替换

function findIndex(str, findstr) {
    var index = str.indexOf(findstr);
    var newIndex = [];
    var num = 0;
    while (index !== -1) {
        num++;
        newIndex.push(index);
        index = str.indexOf(findstr, index + 1);
    }
    newIndex.join();
    return '出现' + num + '次' + ',分别在' + newIndex;
}

4.2 数字类型

4.2.1 简单使用

let a = 1245;
 a.toFixed(2); // 保留几位小数
 a.toLocaleString(); // 转换成字符串
 a.valueOf();  // 获取a的值,

4.2.2 内存分布

符号位决定了一个数的正负,指数部分决定了数值的大小,尾数部分决定了数值的精度。

IEEE 754规定,有效数字第一位默认为1,并且不占用尾数位的52位,所以有效数字长度为 53个二进制位。(有效数字的1位 + 尾数位 52位)

所有数字都是按64位形式存入,不分浮点型和整型,所以在js中,1 === 1.0

  • 符号位S:正负符号位,0表示是正值,1代表是负值。 63位。(1bits)
  • 指数位E:中间的11位存储指数,用来表示次方数。52-62位。 (11bits)
  • 尾数位M:最后的52位尾数,超出的部分自动进一舍零。0-51位。(52bits)

4.2.2.1 存储

整数:

2^53 + 1内存分布。 符号位:0, 指数位:53,尾数位:1(有效数字).0000...0000(52个0)(尾数位)第53位丢失精度

2^53内存分布。 符号位:0, 指数位:53, 尾数位:1(有效数字) .0000...0000(52个0)(尾数位)已丢失一位精度

2^53 - 1内存分布。 符号位:0, 指数位:52,尾数位:1(有效数字).1111...1111(52个1)(尾数位)未丢失精度

2^53 - 2内存分布。 符号位:0, 指数位:52,尾数位:1(有效数字).1111...1110(51个1,1个0)(尾数位)未丢失精度

注:指数和尾数相乘得到数据,所以只要按对应的数据修改尾数和指数就能得到对应的存法,每一个数据都对应唯一的存法。

小数:

0.1 的二进制位:0.00011001100(1100无限循环),科学计数法表述,1.1001 * 2^-4 ,即指数位为-4

0.1 的内存分布,符号位:0, 指数位:-4,尾数位:1(有效数字).1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001(尾数位)

因为第53位是1,二进制逢2进1,所以尾数位变为 1.1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010

转换为十进制为:0.100000000000000005551115123126,出现了精度误差。结果只取前17位,后面的舍掉。即为0.1。

4.2.2.2 运算

在运算时,位运算会自动转化为32位有符号整数。有符号整数使用 31 位表示整数的数值,用第 32 位表示整数的符号,0 表示正数,1 表示负数,所以数值范围为 [-2^31 , 2^31-1] 。对于数值不在 [-2^31 , 2^31-1] 这个范围的数字不操作的情况下,是无符号整型,但是一旦进行了位运算,这个数字就转化成了32位有符号整型。

4.2.3 超出安全范围计算

可以使用bigint类型进行计算

const bigint = BigInt("9007199256741998");
const bigint2 = BigInt("100");
bigint = bigint + bigint2;
console.log(bigint);

4.3 布尔类型

布尔类型的值为1

let a = true;
console.log(a + 1); // ===> 2;
console.log(Boolean(a + 1)) // ===> true;

4.4 undefined类型

变量未定义,值为undefined

4.5 null类型

变量被定义,值为null

4.6 基本类型转换

4.6.1 转换为字符串

变量.toString()  //
String(变量)      //
num+''            // +进行隐式转换

4.6.2 转换为数字型

parseInt('1.3pxrem')    // parseInt(变量)变量必须以数字开头,得到的是整数
parseFloat('1.3pxrem')   // parseFloat(变量) 变量必须以数字开头,得到的是浮点数
Number('100')              //
'100' - 2                  // -*/可以进行隐式转换

4.6.3 转换成布尔型

Boolean(变量)    

4.7 基本类型和引用类型区别

基本类型存储在栈中,值拷贝

引用类型存储在堆中,地址拷贝

// 基本类型
var str1 = "hello world";
var str2 = str1;
str2 = "hello world2";
console.log(str1);  // ===> hello world;
console.log(str2);  // ===> hello world2;


// 引用类型
var obj = {
    name: "zhangsan",
    age: 18
}
var obj2 = obj;
obj.name = "lisi";
obj2.age = 21;
console.log(obj);  // ===> { name: "lisi", age: 21 };
console.log(obj2); // ===> { name: "lisi", age: 21 };

五、javascript 非原始数据(容器)

ES5:Date Math Array ReExp Object;

ES6: Map Set

第三方:immutable 库的容器

5.1 Array 数组对象

5.1.1 数组翻转

function reverse(arr) {
    if (arr instanceof Array) {
        var newArr = [];
        for (var i = arr.length - 1; i >= 0; i--) {
            newArr.push(arr[i]);
        }
        return newArr;
    }
    else {
        return 'error 这个参数必须是数组';
    }
}

判断类型两种方法

arr instanceof Array;    // 运算符
Array.isArray(arr)      // 内置对象提供的方法

5.1.2 数组添加删除

arr = new Array(1, 2);
arr.push(参数);                // 末尾添加数组元素
arr.unshift(参数);              // 开头添加数组元素
arr.pop();                          // 删除结尾数组元素
arr.shift();                      // 删除开头数组元素

5.1.3 数组排序

// 翻转数组方法
var arr = [1, 2, 3, 88, 78, 13];
arr.reverse();
console.log(arr);

// 冒泡排序方法
arr.sort(function (a, b) {
    return a - b;  // 升序
    return b - a;  // 降序
})

5.1.4 数组索引

var arr = [1, 2, 3, 1];
arr.indexOf(1);         // 只返回找到的第一个元素的索引

5.1.5 数组去重(重点案例)

function unique(arr) {
    var newArr = [];
    for (var i = 0; i < arr.length; i++) {
        if (newArr.indexOf(arr[i]) === -1) {
            newArr.push(arr[i])
        }
    }
    return newArr;
}

5.1.6 数组转换字符串

var arr = [];
arr.join(' ');        // 括号里加分隔符

5.2 Map 对象

ES6提供Map对象

5.3 Math 对象

不是构造函数,不需要new一个对象来使用

常用方法:

返回随机整数

代码演示

/* 随机整数代码 */
function getRandom(min,max) {
    return Math.floor(Math.random() * (max - min +1))+min;
}

5.4 Date 对象

是一个构造函数,需要new一个对象来使用

/* 不传参数,返回系统当前时间 */
var date = new Date();
console.log(date);
/* 数字型参数,返回月份比传入参数月份大1 */
var date1 = new Date(2020,6,16);
console.log(date1);
/* 字符串参数,返回传入时间 */
var date2 = new Date('2020-6-16 20:29:30');
console.log(date2)

常用方法:

格式化当前年月日期

代码演示:

function getNow(){
    var date = new Date();                 // 创建date对象
    var year = date.getFullYear();            //  获取当前年份
    var month = date.getMonth() + 1;       // 获取当前月份 ,月份从0开始,所以要加1
    var dates = date.getDate();             // 获取当前日期 
    var day = date.getDay();                 // 获取星期几 周日是0
    var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
    return '今天是:' + year + '年' + month + '月' + dates + '日' + arr[day]
} 

返回当前的时分秒

代码演示:

function getTime() {
    var time = new Date();
    var h = time.getHours();    // 获取当前小时
    h = h < 10 ? '0' + h : h;
    var m = time.getMinutes();      // 获取当前分钟
    m = m < 10 ? '0' + m : m;
    var s = time.getSeconds();     // 获取当前秒钟
    s = s < 10 ? '0' + s : s;
    return h + ':' + m + ':' + s;
}

获得当前距离1970.1.1总毫秒数(时间戳)

/* 1、通过valueOf()  getTime() */ 
var date = new Date();
    date.getTime();  
    date.valueOf();
/* 2、简单写法 (最常用)*/
var date = +new Date();
/* h5新增写法 */
Date.now();

倒计时案例

算法分析:

代码演示:

function countDown(time) {
    var nowTime = +new Date();                                  // 获取当前时间时间戳
    var userInputTime = +new Date(time);                    // 获取用户输入时间时间戳
    var times = (userInputTime - nowTime) / 1000;        // 剩余时间秒数
    var d = parseInt(times / 60 / 60 / 24);                     // 剩余时间天数
    d = d < 10 ? '0' + d : d;
    var h = parseInt(times / 60 / 60 % 24);                    // 剩余小时
    h = h < 10 ? '0' + h : h;
    var m = parseInt(times / 60 % 60);                          // 剩余分
    m = m < 10 ? '0' + m : m;
    var s = parseInt(times % 60);                                  // 剩余秒
    s = s < 10 ? '0' + s : s;
    return d + '天' + h + '时' + m + '分' + s + '秒'
}

5.5 List(immutable)

  1. 与深拷贝相比,Immutable最大程度上共用了内存,这与深拷贝是有很大程度上的不同。
    共用内存: 使用setIn去修改Immutable对象时,未被修改的节点依旧共享。

  2. 适用于纯函数式编程。纯函数编程就是输入一样,输出必定一样。

  3. 可以通过Immutable对象去判断是否要更新组件,这样可以提高性能,避免不必要的渲染。

5.6 Map(immutable)

5.6.1 插值和修改(和获取值用法相同)

set:设置数据

const { Map, List } = Immutable;

// Map用法
const map = Map(); //  <====> Map({});

// 设置值
const newMap = map.set("key", "value");
console.log(newMap.get("key"));
console.log(map.get("key"));

setIn:设置嵌套数据(好用)

const { Map, List } = Immutable;

// Map用法
const map = Map({
    a: Map({
        b: Map({
            c: Map({
                d: "嵌套测试"
            })
        })
    })
});

// 设置嵌套值,嵌套层数任意  map.setIn(['a'])  <===>  map.set('a');
const newMap = map.setIn(["a", "b", "c", "d"], "修改嵌套数据");

// ↑等价于↓
const newMap = map.set("a", Map({
    b: Map({
        c: Map({
            d: "修改嵌套数据"
        })
    })
}))

update:修改数据

const { Map, List } = Immutable;

const newMap = map.update("e", v => Map({
    f: "111"
}));

updateIn:修改嵌套数据

const { Map, List } = Immutable;

const newMap = map.updateIn(["e"], v => Map({
    f: "111"
}));

5.6.2 删除值

delete

deleteAll

5.6.3 clear

清空所有返回值

const { Map, List } = Immutable;

// Map用法
const map = Map({
    a: Map({
        b: Map({
            c: Map({
                d: "嵌套测试"
            })
        })
    })
});


const newMap = map.clear();
console.log(newMap); //  ===> Map {}

5.6.4 merge

合并两个Map数据,merge 参数的map会覆盖前面的map

const map1 = Map({
    x: 1,
    y: 2
})

const map2 = Map({
    y: 2222,
    z: 1
})


const map4 = map1.merge(map2);

添加合并规则

const map1 = Map({
    x: 1,
    y: 2
})

const map2 = Map({
    y: 2222,
    z: 1
})


// oldVal是 两个Map中共有属性 map1中的值, newVal是 两个Map中共有属性 map2中的值
const map4 = map1.mergeWith((oldVal, newVal) => { console.log(oldVal); return newVal + "!!!" }, map2);
console.log(map4);

5.6.6 toJS、toJSON、toObject

toJS 返回深拷贝对象

toJSON 返回浅拷贝对象

toObject 返回浅拷贝对象

// 转换为对象 
const map = Map({
    a: Map({
        b: Map({
            c: "1"
        })
    })
})


const map2 = map.toJS();
const map3 = map.toJSON();
const map4 = map.toObject(); // toObject没有效果

// 打印没有区别,未知问题
console.log(map2);
console.log(map3);
console.log(map4);

5.6.7 toArray

转换为数组

// 转换为数组
const map = Map({
    a: 1,
    b: 2,
    c: 3
})

const map4 = map.toArray();
console.log(map4);

六、JavaScript 类(对象)

String、Number、Boolean、Array、Function、Date、RegExp、Error 等等以各自特性成一构造函数

6.2 属性和方法

JavaScript 对象可以从一个称为原型的对象里继承属性。对象的方法通常是继承的属性。这种”原型式继承“(prototypal inheritance)是 JavaScript 的核心特征

// 实例继承自 Object.prototype, 而不是 Object
let obj = { name: "zhangsan", age: 18 };
console.dir(obj);

6.2.1 静态方法

  • Object.assign():通过复制一个或多个对象来创建一个新的对象
  • Object.create():使用指定的原型对象和属性创建一个新对象
  • Object.defineProperty():给对象添加一个属性并指定该属性的配置
  • Object.defineProperties():给对象添加多个属性并分别指定它们的配置
  • Object.entries():返回给定对象自身可枚举属性的 [key, value] 数组
  • Object.keys():返回一个包含所有给定对象自身可枚举属性名称的数组
  • Object.values():返回给定对象自身可枚举值的数组

6.2.2 实例属性

  • Object.prototype.constructor:一个引用值,指向 Object 构造函数
  • Object.prototype.__proto__:指向一个对象,当一个 object 实例化时,使用该对象作为实例化对象的原型

6.2.3 实例方法

  • Object.prototype.hasOwnProperty():返回一个布尔值,用于表示一个对象自身是否包含指定的属性,该方法并不会查找原型链上继承来的属性

    • hasOwnProperty 就能检测出,它能区别自身属性与继承属性
  • Object.prototype.isPrototypeOf():返回一个布尔值,用于表示该方法所调用的对象是否在指定对象的原型链中

  • Object.prototype.toString():返回一个代表该对象的字符串。

  • Object.prototype.valueOf():返回指定对象的原始值

6.3 创建对象

6.3.1 三种方法

有三种方法。对象直接量、关键字 new、 Object.create 函数来创建对象

// 等价
let obj1 = new Object();
let obj2 = {};
let obj3 = Object.create(Object.prototype);

6.3.2 object.create

函数实现

function create (proto, propertiesObject) {
    if (typeof proto !== 'object' && typeof proto !== 'null') {
        throw new TypeError('Object prototype may only be an Object or null');
    }

    var obj = {};
    Object.setPrototypeOf(obj, proto);

    if (propertiesObject !== undefined) {
        Object.defineProperties(obj, propertiesObject);
    }

    return obj;
};

6.3.3 用法

第二个参数与 Object.defineProperties() 一致

var obj1 = Object.create({name: 'johan', age: 23}) // obj1 继承了属性name 和 age
var obj2 = Object.create(null) // obj2 没有任何属性和方法
var obj3 = Object.create(Object.prototype) // 相当于 {} 和 new Object()

6.3.4 三种方法区别

Object.create(null):没有继承任何属性和方法

变量 = {} 与 new Object(): 继承了Object.prototype对象的属性和方法

6.4 构造函数和原型

6.4.1 构造函数

new作用:

1、申请内存

2、this指向新对象

3、执行函数代码为新对象添加属性和方法

4、返回新对象

6.4.2 原型对象 (protopyte)

通过同一个构造函数创建的对象方法是共享的

某一个构造函数都有一个prototype属性。通过在其上定义方法和属性,创建的对象共享prototype上的方法和属性

6.4.3 对象原型(__ proto __)

new 构造函数().proto_ 指向构造函数的prototype对象。是一个非标准属性,不能使用。

构造函数.prototype.name <====> new 构造函数().name

6.4.5 构造函数、实例、原型对象之间的关系

构造函数.prototype.consturctor -----> 构造函数

new 构造函数().proto_.constructor -----> 构造函数.prototype

构造函数.prototype ---> 构造函数的原型对象

6.4.6 原型链

构造函数.prototype ----> 基类.prototype。

构造函数中有一个原型,使用构造函数构造的对象中有一个对象原型指向构造函数的原型对象,构造函数的原型对象的构造器指向构造函数,这个原型对象上有一个对象原型指向基类的原型对象。

6.4.7 成员查找机制

对象自身属性 ----> 原型对象上查找 ----> 原型对象的父级原型对象查找 ----> 查到Object为止。

6.4.8 扩展内置对象方法

可以给对象自定义一个内置方法,给数组添加一个求和的方法

Array.prototype.sum = function () {
    var sum = 0;
    for (var i = 0; i < this.length; i++) {
        sum += this[i];
    }
    return sum;
}
  • 不能给Array.prototype重新赋值,会覆盖掉原来自带的方法,而且会报错

6.4.9 构造函数方法和prototype方法

1、prototype上的属性不会被初始化

function A(name) {
    this.name = name;
}
A.prototype.test = "hello world";
let a = new A("zhangsan");
console.log(JSON.stringify(a));
// 结果===> {"name":"zhangsan"}


function B(name) {
    this.name = name;
    this.test = "hello world";
}
let b = new B("zhangsan");
console.log(JSON.stringify(b)); 
// 结果===> {"name":"zhangsan","test":"hello world"}

6.4.10 构造函数添加值

function test(){}

test.cid = 1;
console.log("cid = ", test.prototype.constructor.cid);  // ===> 1
console.log("cid = ", new test().constructor.cid);  // ===> 1
console.log("cid = ", new test().cid);  // ===> undefined

6.5 继承

6.5.1 call方法

// 1、调用函数
function fn(x, y){
    console.log('123');
    console.log(x + y)
    console.log(this)
}
var o = {
    name : 'zhangsan'
}
fn.call()

// 2、改变this指向,并且向函数中传递参数 
// 将函数this的指向改为指向o对象
fn.call(o, 1, 2)
  • 谁调用修改谁的this指向

6.5.2 继承属性

// 父构造函数
function Father(uname, age){
    this.uname = uname;
    this.age = age
}
// 子构造函数
function Son(uname, age){
    // 调用父构造函数并将父构造函数的this指向子构造函数,
    Father.call(this, uname, age)
    this.score = 100
}
var son = new Son('pink', 18)

6.5.3 继承方法

// 推荐使用
// 令子构造函数原型对象指向父构造函数创建的实例对象
// 父构造函数创建的实例对象的__proto__又会指向父构造函数的prototype
Son.prototype = new Father();
// 给Son.prototype赋值后,需要将constructor函数指回原来的构造函数
Son.prototype.constructor = Son;


// 不推荐使用:
// Son.prototype = Father.prototype  也可以指向父构造函数的prototype,
// 但是如果子prototype中添加方法,父prototype也会添加方法
Son.prototype.exam = function () {
    console.log('孩子要考试');
}

6.5.4 继承属性和方法(好用)

function VueFather (_name) {
    this.name = _name
    this.func = function () {
        return "func test ...."
    }
}

function Vue (options) {
    Object.setPrototypeOf(Vue.prototype, new VueFather("zhangsan"));
    this._init(options);
}

Vue.prototype._init = function (options) {
    const vm = this;
    console.log(Object.getPrototypeOf(this).name);
    console.log(Object.getPrototypeOf(this).func());
}

new Vue()

七、javascript(ES6)

7.1 类和对象

class Father {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    sum(x, y) {
        console.log(this.x + this.y);
    }
}

// extends 继承
class Son extends Star {
    constructor(x, y) {
        // super调用父类的构造函数
        super(x, y)
        // 这样写是不对的,this指向不同的对象,父类中的this.x与子类中的this.x是不同的
        // this.x = x
        // this.y = y
    }
    say() {
        // super调用父类中的方法
        super.sum()
    }
}

// 测试
var son = new Son(1, 2)
son.sum()

7.2 ES5-数组方法

7.2.1 forEach

arr = [1, 2, 3]
// 1、遍历数组
arr.forEach(function (value, index, array) {
    console.log(value);   // 数组当前项的值
    console.log(index);   // 数组当前项索引号
    console.log(array);   // 数组对象本身
});

7.2.2 filter

// 返回一个新数组对象,把所有满足条件的元素返回到新数组对象中
arr = [1, 2, 3]
var newArr = arr.filter(function (value, index) {
    return value >= 2;
})
console.log(newArr);

7.2.3 some

// 查找数组中是否有满足条件的元素 
// 返回一个布尔值,并且查到第一个满足的元素就停止循环
arr = [1, 2, 3]
var s = arr.some(function (value) {
    return value == 2;
})
console.log(s);

7.2.4 foreach方法和some方法区别

some方法中出现return true会终止迭代

foreach方法中如果出现return不会终止迭代

some方法在某些情况下效率会高一些

7.3 ES5-字符串方法

7.3.1 trim

字符串对象.trim()

7.3 ES5-对象方法

7.3.1 Object.keys

obj = {
    name: 'pink',
    age:18,
}

// 返回对象的key值 // 返回一个新数组
var nerArr = Object.keys(obj)

7.3.2 defineProperty方法添加修改对象

function proxy (obj, target, key) {
    // obj[key] 代理到 obj[target][key]
    Object.defineProperty(obj, key, {
        value: 1000,
        // 默认false, 不能被重写
        writable: false,
        // 默认false, 不能被 for ... in 和 Object.keys 枚举
        enumerable: false,
        // 默认false, 不能被可以被删除或不可以重新设置特性
        configurable: false,
        get () {
            return obj[target][key];
        },
        set (newValue) {
            if (newValue == obj[target][key]) {
                return;
            }
            obj[target][key] = newValue;
        }
    })
}

let obj = {
    __data: {
        name: 'zhangsan',
        age: 18
    }
}
proxy(obj, "__data", "name");
proxy(obj, "__data", "age");

console.log(obj.name);      // ===> zhangsan
console.log(obj.age);       // ===> 18

7.4 严格模式

7.4.1 开启严格模式

// 为某个js脚本开启严格模式
'use strict'
// 为立即执行函数开启严格模式
(function(){
    'use strict'  
}())
// 为某个函数开启严格模式
function(){
    'use strict'
}

7.4.2 严格模式下this指向问题

全局作用域函数:this指向window;严格模式-全局作用域函数:this指向undefined

构造函数不加new也可以调用,当做普通函数,this指向全局,严格模式不加new会报错

new实例化构造函数指向创建的实例对象

定时器this指向window

事件对象指向调用者

7.5 ES6-变量声明

7.5.1 let

// 1、在大括号中使用let声明的变量才具有块级作用域
if (true) {
    let a = 2
}
console.log(a)

// 2、let声明的变量没有变量提升,必须先声明在使用
console.log(a);
let a = 1;

// 3、暂时性死区特性
var a = 2;
if (true) {
    console.log(a)
    let a = 4
}

7.5.2 const

// 1、在大括号中使用const声明的常量具有块级作用域
if (true) {
    const a = 2;
    if(true){
        const a = 3;
        console.log(a)
    }
    console.log(a)
}
console.log(a)

// 2、const常量声明必须赋初始值
const a = 1;

// 3、常量值赋值后不可更改
// 内存地址是不可更改的,
const a = 2;
a = 3 // 会报错
const arr = [1,2]
// 修改地址内的值,不会报错
arr[0] = 2  // 不会报错
// 重新给数组赋值会改变内存地址,所以会报错
arr = [2,3] // 会报错

7.6 ES6-解构赋值

7.6.1 数组解构

let arr1 = [1,2,3]
let [a,b,c] = arr1

7.6.2 对象解构

let obj = {
    name: 'pink', age: 18
}

// 取同名字段
let { name, age } = obj
console.log(name);
console.log(age);

// 同名字段自定义映射名
let { name: myname, age: myage } = obj
console.log(myname);
console.log(myage);

7.7 ES6-箭头函数

7.7.1 箭头函数使用

const f = (a, b) => a + b

7.8 ES6-剩余参数

const sum = (...args) => {
    let total = 0
    args.forEach(item => total += item)
    return total
}
console.log(sum(1, 2, 3));

7.9 ES6-扩展运算符

7.9.1 扩展运算符的使用

let arr = ['a','b','c']
console.log(...arr)

7.10 ES6-数组方法

7.10.1 find函数

var arr = [{
    id: 1,
    name: '老王'
}, {
    id: 2,
    name: 'pink'
}]
var a = arr.find(value => value.id == 1)
console.log(a);

7.10.2 findIndex函数

查找出第一个符合条件的元素,返回值是元素的引用

数组对象.findIndex((value,index) => {})

7.10.3 includes函数

查找数组元素中是否包含某一个元素,返回值是布尔值

数组对象.includes((value,index) => {})

7.10.4 map函数

let nums: string[] = ['1', '2', '3']

// let rst: number[] = nums.map((item: string) => parseInt(item))
console.log(rst) // ===>结果:[1,NaN, NaN] // ---> 结果:[1,2,3]

// map的callback函数有三个参数,正在遍历的元素, 元素索引(index), 原数组本身(array)。
// parseInt有两个参数,string和radix(进制)。只传入parseInt的话,
// map callback会自动忽略第三个参数array。而index参数不会被忽略。而不被忽略的index(0,1,2)就会被parseInt当做第二个参数。
let rst: number[] = nums.map(parseInt)
console.log(rst) // ===>结果:[1,NaN, NaN]

7.10.5 reduce函数

1、直接使用

let values = [1, 2, 3, 4, 5];
values = values.map(item =>  item * 2 );
let result = values.reduce((prev, curr) => {
    // prev 是上一次返回值, 初始值为1, 可以通过init设置
    console.log(prev, curr);
    return prev + cur;  // 返回的值当做下一次prev的值
})
console.log(result);

2、设置初始值

let values = [1, 2, 3, 4, 5];
values = values.map(item =>  item * 2 );
let result = values.reduce((prev, curr) => {
    // prev 是上一次返回值, 初始值为1, 可以通过init设置
    console.log(prev, curr);
    return prev + cur;  // 返回的值当做下一次prev的值
}, 10) // 设置初始值
console.log(result); // result = 25 最后一次回调返回值

// 打印
// 10, 1
// 11, 2
// 13, 3
// 16, 4
// 20, 5

7.11 ES6-字符串方法

7.11.1 字符串新增特性

// 可以解析变量
var name = 'zhangsan'
var sayHello = `hello, 我的名字叫${name}`
// 可以调用函数
f = () => 123
console.log(`keyidiaoyonghanshu${f()}`);

7.11.2 startWith方法

查看以什么字符开头,返回一个布尔值

var str = '12345'
str.startWith('123')

7.11.3 endWith方法

查看以什么字符结尾,返回一个布尔值

var str = '12345'
str.startWith('45')

7.11.4 repeat方法

将原字符串重复n次

var a = 'hello'
console.log(a.repeat(3))

7.12 ES6-Set数据结构

7.12.1 利用Set数据结构将数组去重

Set数据结构是一个构造函数

var arr = new Set([1,2,3,4,2,5,3])
arr = [...arr]

7.12.2 Set中的方法

var set = new Set()
// 添加,返回Set结构本身
set.add(1)
console.log(set);
// 删除某个值,返回一个布尔值,表示是否成功
set.delete(1)
// 判断是否有某个值,返回一个布尔值
set.has(1)
// 清除所有成员,没有返回值
set.clear()

7.12.3 可以遍历

var set = new Set([a,b,c])
set.forEach(value => console.log(value))
  • Set构造函数传递数组参数,返回 {将传递数组参数序列化}
  • Set构造函数传递变量参数,返回 {第一个参数}

7.13 ES6-判断运算符

7.13.1 ?? 非空运算符

如果第一个参数不是 null/undefined,将返回第一个参数,否则返回第二个参数。

//  || 运算符
let prevMoney = 1
let currMoney = 0
let noAccount = null
let futureMoney = -1
function moneyAmount(money) {
  return money || `账户未开通`
}
console.log(moneyAmount(prevMoney)) // => 1
console.log(moneyAmount(currMoney)) // => 账户未开通
console.log(moneyAmount(noAccount)) // => 账户未开通
console.log(moneyAmount(futureMoney)) // => -1

// ===============================================// 
// ?? 运算符
let prevMoney = 1
let currMoney = 0
let noAccount = null
let futureMoney = -1
function moneyAmount(money) {
  return money ?? `账户未开通`
}
console.log(moneyAmount(prevMoney)) // => 1
console.log(moneyAmount(currMoney)) // => 0
console.log(moneyAmount(noAccount)) // => 账户未开通
console.log(moneyAmount(futureMoney)) // => -1

7.13.2 ??= 空赋值运算符

仅当值为 null 或 undefined 时,此赋值运算符才会赋值。

let x = null
let y = 5
console.log(x ??= y) // => 5

7.13.2 ?. 链判断运算符

判断这个对象(travelPlans)下的(tuesday)下的(location)是否为null或者undefined,当其中一链为null或者undefined时就返回undefined,这样即使中间缺少一个属性也不会报错,双问号后面接的就是默认值。

let travelPlans = {
  destination: 'DC',
  monday: {
    location: 'National Mall',
    budget: 200,
    host: null
  }
}

let res = travelPlans?.tuesday?.location ?? "locahost"; // => locahost
let res2 = travelPlans?.host; // => undefined

travelPlans?.tuesday?.location <==> travelPlans && travelPlans.tuesday && travelPlans.tuesday.location; 

7.13.2 ?: 三元运算符

value > 0 ? 1 : 0; 

7.14 ES6-Promise

7.14.1 回调函数

函数参数中有函数的函数,那个函数参数就是回调函数

function getMsg(callback) {
    setTimeout(function () {
        callback({
            Msg: '213456'
        })
    }, 2000)
}

getMsg(function (data) {
    console.log(data);
})

异步API不能通过返回值拿到结果,需要使用回调函数以参数传递方式拿到返回值

7.14.2 回调地狱

// 依次读取三个文件
const fs = require('fs')
fs.readFile('./1.txt', 'utf8', (err, doc) => {
    console.log(doc);
    fs.readFile('./2.txt', 'utf8', (err, doc) => {
        console.log(doc);
        fs.readFile('./3.txt', 'utf8', (err, doc) => {
            console.log(doc)
        })
    })
})

7.14.3 promise构造函数

解决回调地狱问题

const fs = require('fs')
let promise = new Promise((resolve, reject) => {
    // 读取文件操作,异步API
    fs.readFile('./1.txt', 'utf8', (err, doc) => {
        if (err !== null) {
            // reject为出错执行函数
            reject(err)
        }
        else {
            // resolve为成功执行函数
            resolve(doc)
        }
    })
})
// 
promise.then(doc => {
    console.log(doc);
})
// 
    .catch(err => {
        console.log(err);
    })

在创建构造函数时,将异步API由回调函数的方式传入

然后使用resolve函数和reject函数传出

7.14.4 封装读取多个文件

const fs = require('fs')

function p(filepath) {
    return new Promise((resolve, reject) => {
        fs.readFile(filepath, 'utf8', (err, doc) => {
            // 成功执行的函数
            resolve(doc)
        })
    })
}
// 链式编程
p('./1.txt').then(r1 => {
    console.log(r1);
    // 返回下一个then执行对象
    return p('./2.txt')
}).then(r2 => {
    console.log(r2);
    return p('./3.txt')
}).then(r3 => {
    console.log(r3);
})

7.14.5 Promise.all(iterable)

(1) 只有p1、p2、p3、p4的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3、p4的返回值组成一个数组,传递给p的回调函数。 (2) 只要p1、p2、p3、p4之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

const p1 = 'p1-ok';
const p2 = Promise.resolve('p2-ok');
const p3 = new Promise((resolve) => setTimeout(resolve, 3000, 'p3-ok'));
const p4 = Promise.reject('p4-err');

Promise.all([p1, p2, p3, p4])
    .then(resolves => {
    // p4 为rejected, 不会进入then
    resolves.forEach(resolve => {
        console.log(resolve);
    })
})
    .catch(err => {
    console.log(err); // p4-err
})

7.14.6 Promise.race(iterable)

Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的回调函数。

const p5 = new Promise(resolve => setTimeout(resolve, 500, "five"));
const p6 = new Promise((resolve, reject) => setTimeout(reject, 100, "six"));

Promise.race([p5, p6])
.then(resolve => console.log("resolve", resolve)) // 不执行 状态首先改为了reject
.catch((err) => console.log("err", err))    // 执行

7.14.7 实例方法-finally

不管是fulfilled 还是 rejected都会执行的方法

const promise = new Promise((resolve, reject) => resolve(1));
promise.then(res => console.log(res)).finally(() => console.log("finally 函数, 不管状态是什么都会执行"));

7.14.9 异步函数(async/await)(ES7)

对生成器函数在处理异步问题时的一种“进化”或者说“高级封装”,有效地降低了异步编程的复杂性,提升了代码可读性和编写效率

async function p1() {
    throw 777
    return 123
}
async function p2() {
    return 543
}
async function p3() {
    return 835
}

async function run() {
    let r1 = await p1()
    let r2 = await p2()
    let r3 = await p3()
    console.log(r1, r2, r3);
}
run()

示例

const fs = require('fs')
// 改造现有异步函数api,让其返回promise对象,从而支持异步函数语法
const promisify = require('util').promisify;
// 调用promisify方法改造异步api,让其返回promise对象
const readFile = promisify(fs.readFile)

async function run() {
    // await 后跟promise对象,返回值为resolve函数中的参数
    let r1 = await readFile('./1.txt', 'utf8')
    let r2 = await readFile('./2.txt', 'utf8')
    let r3 = await readFile('./3.txt', 'utf8')
    console.log(r1, r2, r3);
};

run()
const fs = require('fs')
let r1 = fs.readFileSync('./1.txt', 'utf8')
let r2 = fs.readFileSync('./2.txt', 'utf8')
let r3 = fs.readFileSync('./3.txt', 'utf8')
console.log(r1, r2, r3);

7.15 ES6-Proxy

// Proxy
let target: any = { a: 1 };
let proxy = new Proxy(target, {
    /**
     * @param target: 监听的对象
     * @param key: 获取的索引
     * @param receiver: 接收对象
    */
    get(target: any, key: string | number | symbol, receiver: any) {
        console.log("get.target", target);
        console.log("get.key", key);
        console.log("get.receiver", receiver);
        return target[key];
    },
    /**
     * @param target: 监听的对象
     * @param key: 被修改值的索引
     * @param newValue: 修改后的值
     * @param receiver: 接收对象
    */
    set(target: any, key: string | number | symbol, newValue: any, receiver: any): boolean {
        console.log("set.target", target);
        console.log("set.key", key);
        console.log("set.newValue", newValue);
        console.log("set.receiver", receiver);
        return Reflect.set(target, key, newValue, receiver);
    }
})

console.log(proxy.a); // 执行get

proxy.a = 11
console.log(proxy.a); // 执行set

7.16 ES6-Symbol

生成全局唯一的值,Symbol中参数相同,生成的值也不相同

let test1 = Symbol('a')
let test2 = Symbol('a')
test1 == test2 ===> false

7.17 ES6-异常处理

7.1 Throw、try、catch基本使用

function sum (num1, num2) {
    if (typeof num1 !== "number" || typeof num2 !== "number") {
        throw "parameters is error type~"
    }
    return num1 + num2
}

// try ... catch ...  捕获异常,如果没有捕获程序不会向下执行
try {
    sum("fwq", "fwq");
}
catch (exception) {
    console.log(exception);
}

console.log("fwq");

7.18 导入包的路径信息

<script type="module" src="my_module.mjs">
    console.log(import.meta);    
</script>

7.19 ES6-装饰器

7.19.1 不带参数

// 装饰器:
// 装饰属性(或方法)时:
// target为被装饰属性的类对象,key为被装饰属性名字符串,descriptor为被装饰属性key的属性描述符对象
// 装饰类时:
// target为类,key与descriptor都是undefined

function readOnly(target, key, descriptor) {
    console.log('target:', target, typeof target);
    console.log('key:', key, typeof key);
    console.log('desc:', descriptor, typeof descriptor);
    if (typeof key === 'string') { // 作为属性或方法装饰器时,key为字符串
        if (typeof descriptor.value === 'function') { // 作为方法装饰器
            console.log('~~~ decorator of method');
            return descriptor.value;
        } else { // 作为属性装饰器
            return {
                ...descriptor,
                writable: false // 覆盖为false
            }
        }
    } else { // 作为类装饰器
        console.log('~~~ decorator of cls');
        return target;
    }
}

class Oatmeal {
    @readOnly viscosity = 20; // 属性装饰器
    constructor(flavor) {
        this.flavor = flavor;
    }
}

var oatmeal = new Oatmeal('Brown Sugar Cinnamon');
// oatmeal.viscosity = 30; // 已设为只读,修改将报错
console.log(oatmeal);

console.log('\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~');

@readOnly
class A {
    constructor() {
        this.a = 111;
    }
};

var a = new A();
console.log(a.a);

console.log('\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~');

class B {
    constructor() {
        this.b = 222;
    }
    @readOnly
    show() {
        console.log(this.b);
    }
}

var b = new B();
b.show();

7.19.2 带参数

// 带参数的装饰器
function decorator (name) {
    return function (target, key, descriptor) {
        console.log(name);
        console.log(target);
        console.log(key);
        console.log(descriptor);
    }
}
 
class C {
    @decorator("zhangsan")
    test () {
        console.log("test function");
    }
}

let c = new C();
c.test();

7.19.3 自制

// 类似python的装饰器
function decorator (fn) {
    return function () {
        console.log("--------innerFunction--------");
        fn();
        console.log("--------innerFunction--------");
    }
}

function test () {
    console.log("test function");
}
decorator(test)();

7.20 ES6-this问题

7.20.1 call方法

改变函数的this指向并执行函数

function func(this: any, age){
    console.log(this);
}

let obj = {
    a: 1,
    b: 2
}

// 测试
func.call(obj); // ==> { a: 1, b: 2 }

7.20.1.1 手动实现

Function.prototype.mycall = function (context, ...args) {
    context = context === undefined || context === null ? globalThis : Object(context);

    const fn = this;

    // 在context中建立一个 key值唯一的函数,在调用完之后把键值删除
    const key = Symbol();

    // 使用代理设置fn
    Object.defineProperty(context, key, {
        enumerable: false,
        value: fn
    })

    const result = context[key](...args);
    delete context[key];
    return result;
}


// 测试
function asyncFunc (...args) {
    console.log(this);
    console.log(args);
}

let obj = {
    a: 1,
    b: 2
}

asyncFunc.mycall(obj);

7.20.2 apply方法

改变函数的this指向并执行函数,将第二个参数传递给arguments

arr = [1, 2, 3, 4, 5];
var max = Math.max.apply(Math, arr);
console.log(max);

7.20.2.1 手动实现

Function.prototype.myapply = function (context, argArray) {
    context = context === null || context === undefined ? globalThis : Object(context);

    var fn = this;
    var key = Symbol();
    Object.defineProperty(context, key, {
        enumerable: false,
        value: fn
    })

    const result =  context[key](...argArray);
    delete context[key];
    return result;
}

7.20.3 bind方法

bind方法不会调用函数,返回的是原函数修改this指向后的新函数

var o = {
    name :'zhangsan'
}

function fn(this: any, a, b){
    console.log(this);
}

const f = fn.bind(o, a, b);
f(); // ==> { name: "zhangsan" };

7.20.3.1 手动实现

key值问题:每次调用bind新函数都会重新创建还是在调用bind方法时创建固定的key

Function.prototype.mybind = function (context, ...args) {
    context = context === undefined || context === null ? globalThis : Object(context);

    const fn = this;

    // 在context中建立一个 key值唯一的函数,在调用完之后把键值删除
    const key = Symbol();

    // 使用代理设置fn
    Object.defineProperty(context, key, {
        enumerable: false,
        value: fn
    })

    return function () {
        const result = context[key](...args);
        delete context[key];
        return result;
    }
}


// 测试
function asyncFunc (...args) {
    console.log(this);
    console.log(args);
}

let obj = {
    a: 1,
    b: 2
}

const bindFunc = asyncFunc.mybind(obj, 4, 123);
bindFunc();

八、javascript 二进制数据流

  • Blob (binary large object)对象:Blob 对象表示二进制数据,可以通过调用 Blob 构造函数创建。Blob 对象可以通过 FileReader 对象读取为原始数据流。
  • ArrayBuffer 对象:ArrayBuffer 对象表示一块内存区域,其中存储了二进制数据。可以通过调用 ArrayBuffer 构造函数创建。ArrayBuffer 对象可以通过TypedArray 类型转换为各种类型的 TypedArray 对象,如 Uint8Array、Int16Array 等。
  • ReadableStream 对象:ReadableStream 对象表示一个可读的数据流,可以通过调用 ReadableStream 构造函数创建。可以通过控制流的读取来获取二进制数据。
  • DataView 对象:DataView 对象表示一个指向 ArrayBuffer 对象的视图,可以通过调用 DataView 构造函数创建。DataView 对象可以通过指定视图的起始位置和长度来读取 ArrayBuffer 对象中的二进制数据。
  • URL.createObjectURL() 方法:该方法可以将 Blob 对象或 DataURL 对象转换为 URL 对象,可以通过该 URL 对象创建一个可读的二进制数据流。

8.1 Blob

二进制大对象,接口定义

declare var Blob {
    new(blobParts?: BlobPart[], options?: BlobPropertyBag): Blob;
}

interface BlobPropertyBag {
    type?: string;
    ending?: "native" | "transparent";
}

8.1.1 文件下载

request("posttesturl").then(res => {
    const blob = new Blob([res]);
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.style.display = "none";
    link.herf = url;
    link.setAttribute("download", "测试文件.txt");
    document.body.append(link);
    link.click();
})

8.2 File

是特殊类型的Blob, Blob的属性和方法都可以用于File对象

接口定义

interface File extends Blob {
    readonly lastModified: number; // 最后修改时间,时间戳
    readonly name: string; // 文件名
    readonly webkitRelativePath: string; // 
};

declare var File: {
    prototype: File;
    new(fileBits: BlobPart[], fileName: string, options?: FilePropertyBag): File;
};

8.2.1 文件上传

<input style="display: none" type="file" />

<script>
    const file = document.querySelector("input");
    file.addEventListener("change", (e) => {
        const uploadFiles = e.target.files;
        console.log("file = ", uploadFiles[0]); // 单个文件,类型为File
    })
</script>

8.2.2 拖拽上传

<div id="drop-zone"></div>
<script>
    const dropZone = document.getElementById("drop-zone");

    // 阻止默认事件,将文件夹显示到页面上
    dropZone.ondragover = (e) => {
        e.preventDefault();
    }

    dropZone.ondrop = (e) => {
        e.preventDefault();
        const files = e.dataTransfer.files;
        console.log(files)
    }
</script>

8.2.3 构造函数

const buf = new ArrayBuffer(10);
const u8 = new Uint8Array(buf);
u8.set([3, 124, 23, 5, 23, 23, 3, 2, 1]);

const file = new File(u8, "file test");

8.3 FileReader

FileReader 可以将 Blob 读取为不同的格式。

  • readAsArrayBuffer: Blob ===> ArrayBuffer
  • readAsBinaryString: Blob ===> 读取文件的原始二进制数据
  • readAsDataURL: Blob ===> data: URL 格式的 Base64 字符串
  • readAsText: Blob ===> 读取的文件内容

8.3.1 常用API

<input type="file" name="" id="">
<script>
    const input = document.querySelector("input");
    const reader = new FileReader();
    input.addEventListener("change", (e) => {
        reader.readAsArrayBuffer(e.target.files[0]);
        reader.readAsBinaryString(e.target.files[0]);
        reader.readAsDataURL(e.target.files[0]);
        reader.readAsText(e.target.files[0]);
    })

    reader.onload = (e) => {
        console.log(e.target.result);
    }

    reader.onprogress = (e) => {
        // loaded 已读取量 ; total 需读取总量
        if (e.loaded && e.total) {
            const percent = (event.loaded / event.total) * 100;
            console.log(`上传进度: ${Math.round(percent)} %`);
        }
    });
</script>

8.4 ArrayBuffer

ArrayBuffer 的内容不能直接操作,只能通过 DataView 对象或 TypedArrray 对象来访问。

TypedArray视图和 DataView视图的区别:主要是字节序,前者的数组成员都是同一个数据类型,后者的数组成员可以是不同的数据类型。

blob和ArrayBuffer区别:blob 作为整体文件,适合用于传输,如果要对二进制数据进行操作时(修改某一段数据),可以使用ArrayBuffer

接口定义

declare var ArrayBuffer {
    readonly prototype: ArrayBuffer; // 实例对象上的属性和API
    new(byteLength: number): ArrayBuffer;
    isView(arg: any): arg is ArrayBufferView; // 是否是视图对象,ArrayBuffer不是视图对象
};

interface ArrayBuffer {
    readonly byteLength: number;
    slice(begin: number, end?: number): ArrayBuffer; // 
}

8.4.1 TypedArray

数组和类型化数组区别

  • 类型化数组的元素都是连续的,不会为空;

  • 类型化数组的所有成员的类型和格式相同;
  • 类型化数组元素默认值为 0;
  • 类型化数组本质上只是一个视图层,不会存储数据,数据都存储在更底层的 ArrayBuffer 对象中。

常见类型化数组:Uint8Array、Uint16Array、Uint32Array

接口定义

interface Uint8ArrayConstructor {
    readonly prototype: Uint8Array;
    new(length: number): Uint8Array;
    new(array: ArrayLike<number> | ArrayBufferLike): Uint8Array;
    new(buffer: ArrayBufferLike, byteOffset?: number, length?: number): Uint8Array;

    /**
     * 数组中每个元素大小
     */
    readonly BYTES_PER_ELEMENT: number;

    /**
     * 从一组元素中返回一个新数组。
     * @param items要包含在新数组对象中的一组元素
     */
    of(...items: number[]): Uint8Array;

    /**
     * 从类似数组或可迭代对象创建数组。
     * @param 转换为数组的类似数组或可迭代对象
     */
    from(arrayLike: ArrayLike<number>): Uint8Array;

    /**
     * 从类似数组或可迭代对象创建数组
     */
    from<T>(arrayLike: ArrayLike<T>, mapfn: (v: T, k: number) => number, thisArg?: any): Uint8Array;

}
declare var Uint8Array: Uint8ArrayConstructor;

8.4.1.1 简单使用

const buf = new ArrayBuffer(10);
const view = Uint8Array(buf);
view[0] = 1;
view[1] = 1;
view[2] = 1;
console.log(view.length); // 10
console.log(view.byteLength); // 10
console.log(view.buffer); // ArrayBuffer对象

8.4.2 DataView

ArrayBuffer对象的各种TypedArray视图,是用来向网卡、声卡之类的本机设备传送数据

DataView视图的设计目的,是用来处理网络设备传来的数据,所以大端字节序或小端字节序是可以自行设定的

8.4.2.1 读写数据

const buffer = new ArrayBuffer(5);
const view = new DataView(buffer);
view.setUint8(0, 11); // 向哪个地址写入数据
view.setUint8(1, 11);
view.setUint8(2, 11);

view.getInt8(1); // 从第一个字节读取一个1字节数据
view.getInt16(1); // 从第一个字节读取一个2字节数据

console.log(view.getInt8(1)); // 0b
console.log(view.getInt16(1)); // 0b0b
console.log(view.buffer); // [0b, 0b, 0b, 00, 00]

8.5 Object URL

Blob URL

const fileInput = document.getElementById("fileInput");
const preview = document.getElementById("preview");

fileInput.onchange = (e) => {
    const objUrl = URL.createObjectURL(new File([""], "filename"));
    console.log(objUrl);
    preview.src = objUrl;
    URL.revokeObjectURL(objUrl); // 释放内存
};

8.6 Base64

Base64 是一种基于64个可打印字符来表示二进制数据的表示方法。Base64 编码普遍应用于需要通过被设计为处理文本数据的媒介上储存和传输二进制数据而需要编码该二进制数据的场景。这样是为了保证数据的完整并且不用在传输过程中修改这些数据。

8.6.1 API

console.log(btoa("hello world")); // 编码
console.log(atob(`aGVsbG8gd29ybGQ=`)); // 解码

8.6.2 DataURL

canvas的toDataURL和 FileReader的readAsDataURL 生成 base64 编码格式

canvas.toDataURL();
reader.readAsDataURL(e.target.files[0]);
reader.onload = (e) => console.log(e.target.result);

8.10 常见数据流转换

ArrayBuffer → blob

const arrayBufferToBlob = (data: ArrayBuffer): Blob => new Blob([new Uint8Array(data)]);

ArrayBuffer → base64

const arrayBufferToBase64 = (data) => btoa(String.fromCharCode.apply(null, new Uint8Array(data)));

base64→ blob

const base64ToBlob = (data, sliceSize, contentType = "text/plain") => {
    const byteChars = atob(data);
    const byteArrays = [];

    sliceSize = sliceSize || byteChars.length; // 默认为 base64解码后的长度

    for (let i = 0; i < byteChars.length; i += sliceSize) {
        const slice = byteChars.slice(i, i + sliceSize);
        const byteNumbers = new Array(slice.length);
        for (let i = 0; i < slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }
        const byteArray = new Uint8Array(byteNumbers);
        byteArrays.push(byteArray);
    }

    const blob = new Blob(byteArrays, { type: contentType });
    return blob;
}

blob→ ArrayBuffer

const blobToArrayBuffer = (data) => {
    return new Promise((resolve, reject) => {
        data.arrayBuffer().then(buf => {
            resolve(buf);
        })
    })
}

blob→ base64

const blobToBase64 = (data) => {
    return new Promise((resolve, reject) => {
        blobToArrayBuffer(data).then((res) => {
            const buf = res;
            resolve(arrayBufferToBase64(buf));
        });
    })
}

blob→ Object URL

const url = (data) => URL.createObjectURL(data);

九、javascript 语法

9.1 运算符

9.1.1 短路运算(重点)

表达式1 && 表达式2      // 1为真 结果为2 ;
表达式1 || 表达式2      // 1为真 结果为1 ;

9.1.2 优先级

图片转存失败,建议将图片保存下来直接上传

9.2 分支循环

9.2.1 分支

if分支语句

if (条件表达式) {
    // 表达式为true执行的语句
} 
else {
    // 表达式为false执行语句
}

三元表达式

/* 条件表达式?  表达式1:表达式2;*/
/* 如果表达式为真 ,则返回1的值;如果表达式为假,则执行表达式2的值 */
var result = 1 > 0 ? 10 : 20;

switch分支语句

switch(表达式) {
    case value:
        // 执行语句;
        break;
    case value2:
        // 执行语句;
         break;
    case value3:
        // 执行语句;
         break;
    default// 都不匹配执行语句
}

9.2.2 循环

for循环

for(var i=0; i<10; i++){
    // 循环体
}

双重for循环

常见双重循环代码演示:

/* i是行数;j是列数 */
/* 行数递增;列数递减 */
for (var i = 0; i < 10 ; i++){
    for (var j = i; j < 10; j++){

    }
}
/* 行数递增;列数递增 */
for (var i = 0; i < 10; i++) {
    for (var j = 0; j <= i; j ++) {

    }
}

while循环

while (条件表达式) {
    // 执行语句
}

do-whlie循环

/* 先执行一次循环体,在执行表达式 */
do {
    // 循环体
} while (条件表达式)

9.3 函数

9.3.1 函数声明

/* 第一种声明方法 */
function 函数名(形参1,形参2...){
   // 函数体
}
函数名(实参)

/* 第二种声明方法*/
var a = function() {
    // 函数体
}
a()

9.3.2 函数返回值

函数如果没有返回值,返回undifind

function 函数名(形参1,形参2...){
   // 函数体
    return 返回值;
}
result = 函数名(实参)

9.3.3 arguments

function a() {
    /* arguments中存储的是调用函数是传递过来的所有实参,是一个列表*/
    /* 每个函数都内置了arguments对象 */
    console.log(arguments.length); 
} 
a(1,2,3,4)