【前端】js和ts基础

代码调试方式:直接在html文件中插入debugger;代码行

1.JS和TS的区别

1.1 JS
  1. CommonJS
    1. 语句和执行
    2. var a = 1+3程序的执行单位为行(line),
      1. var a = 1+3; var b = 'abc'
      2. var a=1+3
      3. typeof运算符
    3. 语句statement:以一个分号结尾,可以没有任何内容
      1. 赋值语句:右边是表达式(会返回值, expression)、存在于堆,左边是栈里的对其引用
      var a = 1+3;
      
      1. 多个语句可以写于一行
      ;;; //3个空语句
      
      1. 表达式不需要分号结尾
        1. typeof 运算符
        typeof undefined
        // undefined
        
        1. instanceof 运算符
          1. function f() {}
          2. typeof f
        2. Object.prototype.toString 方法(toString作用返回一个对象的字符串,返回类型字符串)
          1. 数值:不同数据类型的Object.prototype.toString方法返回值如下
          [object Number] // 数值
          [object String] // 字符串
          [object Boolean] // 布尔值
          [object undefined] // 
          [object Null] // 
          [object Arguments] // arguments对象
          [object Function] // 
          [object Error] //
          [object Date] //
          [object RegExp] //
          // 看到一个值到底能分什么类型
          Object.prototype.toString.call(2) //
          Object.prototype.toString.call('') // 
          Object.prototype.toString.call(true)
          Object.prototype.toString.call(undefined)   
          // 在此基础上,专门判断某种类型数据的方法;写一个比typeof
          var type = function(o) {
           var s = Object.prototype.toString.call(o);
           return s.match(/\[object(.*?)\]/)[1].toLowerCase();
          }
          ['Null', 'Undefined', 'Object', 'Array', 'String', 'Number', 'Boolean', 'Function', 'RegExp'].forEach(function(t) {
           type['is' + t] = function(o) {
              return type(o) === t.toLowerCase();
           }
          });
          type.isObject({}) // true
          type.isNumber(NaN)
          type.isRegExp(/abc/)
          
          1. typeof undefined
          Object.prototype.toLocaleString()// 方法与toString的返回结果
          var obj = {};
          obj.toString(obj) //"[object object]"
          obj.toLocaleString(obj) // "[object object]"
          
          1. 应用:判断数据类型
          var obj = {} // 
          obj.toString() // '[object object]',其中第二个Object表示该值的构造函数
          // 由于实例对象会自定义toString方法,覆盖Object.prototype.toString,
          obj.toLocaleString(obj)
          // 方法的主要作用是留一个接口,让不同对象实现自己版本的toLocaleString
          var person = {
             toString: function() {
                return 'Henry Norman Bethune';
             }
             toLocaleString: function() {
                return '白求恩';
             }
          }
          person.toString()
          person.toLocaleString() 
          
          1. 数据
          {
             
          }
          
          1. 1
      2. typeof运算符
        1. typeof运算符:
          typeof 123 // number
          typeof ''
          
        2. instanceof运算符:
        3. Object.prototype.toString方法:
    4. 变量提升
  2. ESMoudle
1.2 TS
1.2.1 TS概述

TypeScript 是JavaScript 的超集(ts是微软开发的开源编程语言),即包含JavaScript 的所有元素,能运行JavaScript 的代码,并扩展了JavaScript 的语法

1)TypeScript 引入了 JavaScript(初始)没有的“类”概念
2)TypeScript 中引入了模块的概念,可以把声明、数据、函数和类封装在模块中。
3)js没有重载概念,ts有可以重载
4)ts增加了接口interface、泛型、类、类的多态、继承等
5)ts对比js基础类型上,增加了 void/never/any/元组/枚举/以及一些高级类型
6)js有的类型:undefined、boolean类型、number类型、string类型、object(array类型、null),js是动态类型、变量能随时更改

var a = 1; a = 'hello';
/**
 * 重新声明已存在的变量无效
 *  */ 
var x = 1; 
var x; // 无效
x//1
/**
 * 赋值会覆盖
 */
var x = 2;
x //2

 ts新增的类型:tuple类型(元组类型)、enum类型(枚举类型)、any类型(任意类型)
  a)Symbol类型:
Symbol是JS中一种新的基本数据类型,引自ES6标准。是不可变且唯一的数据类型,能创建独一无二的键(key),其描述符相同也不相等
① 遍历带有Symbol的对象,结果Symbol没有出现在遍历结果中

const obj = {
   a: 'a',
   b: 'b',
   c: 'c',
   [Symbol('a')]: '我是symbol'
}
for (let key in obj) {
   cosole.log('obj.' + key + '=我是' + obj[key])
}
// 直接定义新属性或修改现有,语法规则obj, prop, descriptor
// obj目标对象,prop属性名称,descriptor描述符
Object.defineProperty(obj,      
'enumerableTrue', {
      value: 42,
      writable: false
   }
)
//  
var obj = Object.defineProperties({}, {
   p1: {value: 1, enumerable: true},
   p2: {}
})

② Symbol主要用于对象属性的唯一标识。对象中Symbol作为属性名,定义
③ 字符串String的声明方式var str = ‘str’,Symbol声明类似,调用构造函数Symbol()

const mySymbol = Symbol() // 创建Symbol
// 使用Symbol作为对象属性名
const obj = {}
const mySymbol = Symbol()
obj[mySymbol] = 'Symbol属性'
console.log(obj[mySymbol]) // 输出

// 描述符
const mySymbol = Symbol('描述符')
console.log(mySymbol.toString()) // 输出
// 内置属性
const mySymbol = Symbol.iterator
const arr = [1, 2, 3]
const iterator = arr[mySymbol]() 
console.log(iterator.next()) // 输出:{value:1, done:false}
// 这是张三的代码
(function(){
   let a = 10
   console.log(a)
})()
// 这是李四的代码
let sayBye = new Function('console.log("bye bye!!")')
sayBye()

// 函数的不定参(可变参)使用

② ES5对象属性名均为字符串,易造成属性名的冲突。若使用他人提供的对象,但想为这个对象添加新方法(mixin模式)
  Symbol函数不能使用new命令,
③ Symbol的使用场景

const name = Symbol('name')
const age = Symbol('age')
const person = {
   [name]: 'John',
   [age]: 30
}

④ 内置属性

const mySymbol = Symbol.iterator
const arr = [1,2,3] 
const iterator = arr[mySymbol]()
console.log(iterator.next()) // 输出: {value:1, done:false}

④ 使用Symbol具以下优势
a. 属性名的唯一性:防止属性被意外访问
b. 防止属性被意外访问:避免属性名冲突问题。多个Symbol值使用相同描述符,依然不同属性名
c. 扩展对象的功能:通过自定义Symbol,能为对象添加自定义行为,如迭代器、比较器等;因此能更灵活地扩展对象,使其具备特定行为
d. 安全性提升:Symbol属性不被常规的属性遍历方法访问,能在一定程度上提升安全性,防止属性被意外修改
⑤ 使用Symbol的缺点
a. 无法遍历:Symbol属性名,无法通过常规的属性遍历方法(如for…in)获取,若要遍历,则不能使用Symbol作为属性名
b. 内存泄漏:因Symbol创建的属性唯一,一旦创建后无法被销毁或垃圾回收机制回收。若大量使用Symbol创建,会造成内存泄漏的问题
c. 可调试性差:Symbol属性值在控制台输出时,没有明确标识,不易调试和查看对象的具体属性
d. 不可序列化:Symbol值不能被JSON.stringify()序列化,也不能传给其他线程或进程

1.2.2 TS提取接口属性考试解答

2. JS

2.1 JS的基本语法
  1. JS的7种基本数据类型:boolean, number, string, undefined(声明后未赋值), null, bigint, symbol
    1. js原始数据类型:undefined, null, boolean, number, string
      1. 含义不同
        1. null:“无”的对象,即变量缺少指向;即将来指向一个对象
        2. undefined:“无”的原始值,变量声明但没赋值时,值为undefined
      2. 非相等运算符
        1. 原始类型值
        2. true>false
        3. ‘cat’>‘Cat’: true
          1. ‘大’>‘小’ // false
          2. 原始类型值
            1. 5>‘4’ // true
            2. true
      3. 类型不同
        1. typeof操作符:检测null返回object
        2. typeof检测undefined:返回undefined
    2. 引用数据:对象,数组,函数(Object, Array, Function, Data
    3. bigint(ES2020引入,比number范围更大), symbol(ES6引入,表示独一无二,解决属性名冲突)
    4. 引用数据类型:Object对象;包括普通Object、Function、Array、Date、RegExp、Math
    5. 显式转换(隐式-使用“+”):NaN不能转为数字(如’pink’)
      1. 转为数据Number(): e.g., Number(‘123’)
      2. 转为整数parseInt():e.g., parseInt(‘12.3px’)输出12,而parseFloat(‘12.95px’)输出12.95
      // 案例:将秒数转为时间
      let secondNum
      h = parseInt(sccondNum/(60*60)%24) // 避免超过24小时
      m = parseInt(sccondNum/60%60) // 避免超过1小时
      s = parseInt(sccondNum%60)
      h = h<10? '0'+h : h
      m = h<10? '0'+m : m
      s = s<10? '0'+s : s
      console.log(h,':',m,':',s)
      
      1. 转为浮点数parseFloat()
  2. typeof,instanceof和?
    1. typeof和instanceof的区别
      1. typeof
        1. typeof在基础数据中(除"null"外都能判断);对引用类型,除 function 外,都会返回 “object”
      2. 作用点和底层逻辑
        1. 作用点:typeof判断基础数据类型,
        2. 底层逻辑:
      3. 返回值:string类型
        1. typeof(typeof(undefined)) -> "string"
          1. typeof alert === 'object' 在IE6,7,8中,宿主对象是对象而非函数
          2. typeof /[0-9]/ == 'function'
          3. typeof /[0-9]/ == 'function'
        2. 未定义的变量不报错,返回"undefined"
        3. 遗留的bugtypeof(null) -> "object"
      4. 无法区别数组与普通对象:typeof([]) -> "object"
      5. 区别
        1. typeof返回一个变量的基本类型,instanceof是布尔值
        2. instanceof准确判断复杂引用数据类型,但不能正确判断基础
        3. typeof存在弊端,虽然能判断基础数据类型(除null),但引用类型中,除function外其他无法判断
        4. 通过检测数据类型,采用Object.prototype.toString, 调用该方法,统一返回格式"[object Xxx]"的字符串
    2. js如何判断字符串为json格式
    // 不靠谱
    function isJSON(str){
       if(typeof(str) == "object"){
          try{
             JSON.parse(str)
             return true
          }
          catch(err){
             console.log(err)
             return false
          }
       }
    } // 有JSON.parse('123')('{}')('"foo"')等很多例外
    // 靠谱
    function isJSON_Correct(str){
       if(typeof(str) == "object"){
          try{
             let obj = JSON.parse(str)
             if(typeof(obj) == "object" && obj){
                return true
             } else {
                return false
             }
          }catch(err){
             console.log('error:'+str+'!'+err)
             return false
          }
       }
       console.log('not a string')
    }
    
    1. 1
  3. 输入输出
    JS的执行顺序:
    1)按HTML文档执行JS语句;
    2)弹窗提示alert()和prompt()会跳过页面先被执行
// 输出到HTML文档
document.write('嘿嘿') //或其他DOM语句
// e.g., 输出到HTML表格
let age = 19
document.write('
   <table>
      <tr>
         <th>猜猜你多大?</th>
      </tr>
      <tr>
         <td>${age}</td>
      </tr>
   </table>
')
// 控制台打印输出
console.log('控制台输出成功')
// 输入语句1-弹窗
// 核心-实现成功:prompt('看到了吗')
let zero = prompt('来个字符')
let a = +prompt('来个数字') //prompt前加+能把变量转化为数字型
2.2 JS的字符串和数组
  1. 模板字符串
// 数字嵌入字符串
let age = 200
document.write('我今年${age}岁了')
  1. 数组和操作:数组支持顺序存取,如arr[0]
let arr = [1,2,3,4,5,6]
let length = 0
let elem
// 增加
length = arr.push() // 加到末尾,返回长度
length = arr.unshift() // 加到开头,返回长度
// 删除
elem = arr.pop() // 删除末尾,返回删除元素
elem = arr.shift() // 删除开头,返回删除元素
arr.splice(0,1) // 删除指定元素/个数,()中是左属性是操作开始的下标、右属性是删除的个数
arr.splice(0, arr.length, ...(arrB)) // 删除再拼接arrB
arr.slice(0,1) // splice会修改原数组arr,而slice不会
// 修改,查询:arr[idx]
2.3 JS的函数和对象
  1. 函数
/// 1. 普通函数
// 声明
function func1(){
   // 返回多值-通过数组存储
   return [value1,value2]
}
// 执行
let arr = func1()
/// 2. 匿名函数:通过将匿名函数赋值给一个变量来调用,称为函数表达式
// 声明
let fn = function(){

}
// 执行
fn()
/// 3. 立即执行函数
// 方法1
(function(形参{函数体}))(实参)
// 方法2
(function(形参{函数体})(实参))
  1. 函数的形式参数和实际参数
    1. 函数名括号中的参数成为形参
      其中,带默认参数的函数,实参与行参按左至右的顺序匹配
    int getMax (int i1, int i2, int i3=100) {
       if (i1<i2) {
          i1 = i2
       }
       return i1>i3 ? i1 : i3
    }
    
    1. 未出现函数调用,函数的形参不分配内存单元。只有在被调用时,形参才分配内存单元,接受实参传来的值
  2. 编译时,分配到另一个入口
#include <stdio.h>
typedef void (*FN_NOTIFY)(const char *pszMsg)
void Notify1()
iRand = int(rand())
if(iRand%9==0) {
   sprintf(szBuf, "%d", iRand)
   pFn(szBuf)
}
int main() {
   FN_NOTIFY pFn;
   pFn = Notify1;
   Process(pFn);
   pFn = Notify2;
   Process(pFn);
   return 0;
}
  1. 其他使用
// Attention: 0-9的数字补0,使用三元表达式
let num = prompt('数字:')
num = num < 10 ? 0+num : num // 隐式转换
console.log('结果:'+num)
  1. 对象:可理解为无序的数据集合
    1)创建
// 1)对象字面量
var obj1 = {
   name: "Jack",
   age: 26
}

// 2)Object构造函数
var obj2 = new Object()
obj2.name = "Jack"
obj2.age = 26 

// 3)构造函数
function Test(name, age){
    this.name = name
    this.age = age
    this.say = function(){
        console.log('我能说话')
    }
}
var obj3 = new Test('Jack', 26)
var obj4 = new Test('Rose', 25)

2)自定义对象

// 模板
let obj = {
   key:value,
   method:function
}
// 例子
let per = {
   uname:'Tony',
	age: 18,
   sayHi: function (形参){
      document.write('Hi~')
   }
}

// 访问属性
// 对象.属性名
person.age
// 对象['属性名']
person['age']

// 访问方法
// 对象.方法名
person.sayHi()

// 修改属性
person.age=17
// 新增属性
person.score=98
// 删除属性
delete person.score
// 用for in循环语句可遍历对象属性:0,1,..不可存取
// 跳过不可遍历的属性。会遍历继承属性
for(let k in person) {
   console.log(k)
   // toString属性

}
// 2. 
Object.defineProperty(obj, 'enumerableTrue', {
   value: 'enumerableTrue',
   enumerableTrue: true
})
// 3. 
Object.defineProperty(obj, 'enumerableTrue', {
   value: 'not-enumerableTrue',
   enumerableTrue: false
})
// 
for(let key in obj) {
   console.log()
}

3)内置对象
JavaScript提供的对象,包含各种属性和方法给开发调用
如document.write()、consolo.log()都属于内置对象

/// Math对象
// 1. 随机数random:随机生成一个大于等于0并且小于1的浮点数
Math.random() 
// 1.1 产生1-10的随机数
let rand1 = Math.floor(Math.random() * 10 + 1) // 注意范围和下限
// 1.2 不相邻整数中取一个随机数,如2和4中选
let rand2 = Math.random() < 0.5? 2 : 4
// 1.3 不相邻的多整数中产生一个随机数:结合函数参数数组
function selectFromMess(){
   return arguments[Math.floor(Math.random()*arguments.length)]
}
let rand3 = selectFromMess(1,6,8)
let box = document.querySelector('#box')
// 2. 向上取整ceil
Math.ceil(num) // 如Math.ceil(-11.2)=-11
// 3. 向下取整floor
Math.floor(num) // 如Math.ceil(11.2)=11
// 4. 最大数max
Math.max(1,2,3)
// 5. 最小数min
Math.min(1,2,3)
// 6. 幂运算pow
Math.pow(x, a) // 球x的a次幂
// 7. 绝对值abs
Math.abs(num)
// 8. 四舍五入取整
Math.round(num) // 先加0.5再向下取整,如Math.round(-11.6)=-12,Math.round(-11.5)=-11
// 9. 三角函数
Math.sin()
Math.cos()
// 10. 平方根
Math.sqrt(num) // num是数字或者可以通过隐式转换来的(如字符串类型)
// 11. eval()函数
// 11.1 
即使严格模式下,eval可以读写作用域的变量
(function f(){

})
// 11.2 eval的别名调用:不利于引擎优化执行速度。更麻烦的是,引擎在静态分析的阶段,分辨执行的是eval
var m = eval; // m:eval别名
m('var x = 1');
x // 1
// 为了保证eval的别名不影响代码优化,其内全是全部作用域
var a = 1;
function f() {
   var a = 2;
   var e;
}
eval.call(null, '...') // eval别名调用形式五花八门
// eval别名调用即使函数中,无论是作用域还是全局作用域;如果非直接调用,都属于别名
window.eval('...') 
(1, eval)

var arr = ['a', 'b', 'c]
arr[0] = 'a';
arr[1] = 'b';
var arr [{a:1}, [1,2,3], function(){return true;}]
arr[0] // Object {a:1}
arr[2] // function(){return true;}
// arr数组
var arr = ['a', 'b', 'c'];
['a', 'b', 'c'].length //3
[].length = -1
[].length = Math.pow(2, 32)
[].length = 'abc'
// 数组
var a = [];
a['p'] = 'abc';
a.length
a[2.1] = 'abc';
a.length
// for循环
for(var i=0; i<a.Length; i++) {
   console.log(a[i]);
}
// while循环
var i = 0;
colors.forEach(function(color){
   console.log(color)
})
var a = [undefined, undefined]
a.forEach(function(x,i) {
   console.log(i)
});
Object.keys(a)
var obj = {

}
/// arguments对象:JS没有重载函数(函数/方法名称相同,但参数列表不同)的功能,但arguments能模拟重载;

/// Attention!arguments对象不能显式创建,只有函数开始时才可用
(function(a,b,c){
   console.log(arguments) //arguments.length实参个数,arguments.callee引用函数本身-与形参一一映射
   console.log(arguments[0]) //支持数组引用 
   return n + arguments.callee(n-1) // 实现递归
})(1,2,3,4)
// 
function max(){
   if(){

   }
}
// 
const arr = [{name: 'wfly'}, {name: 'fnn'}]
for (let key in arr) {
   console.log('obj.' + key + '= 我是' + JSON.stringify(arr[key]))
}
let box = document.querySelector('#box')
box.onclick = function() {
   console.log('我是匿名函数')
}
box.onclick = null
// 
for (let key in testProtoTypeObj) {
   console.log('testProtoTypeObj' + key + '=我是' + testProtoTypeObj[key])
}
console.log('====')
// 
for (let key in testProtoTypeObj) {
   if (testProtoTypeObj.hasOwnProperty(key)) {
      console.log('testProtoTypeObj.'+key+'=我是' + testProtoTypeObj[key])
   }
}
console.log(testProtoTypeObj) // 
2.4 JS的其他知识
  1. 其他
    1. 作用域:若函数内,变量未声明直接赋值,也会当全局变量看。不推荐(变量提升)
    2. 逻辑短路:&&(一假则假,因此前假后不执行)和||(一真则真,因此前真、后不执行)
    3. LHS和RHS引用:L和R表示在赋值操作符的左右侧
      1. RHS:console.log(a),找到变量的值、不修改
      2. LHS:a=2,找到变量的容器并赋值
      3. 区分的意义:js引擎在用这两种方式查询未定义的变量时会有截然不同的结果。若执行RHS在作用域中找不到所需的变量,引擎抛出ReferenceError(错误信息xx if not defined);非严格模式下,而LHS若在顶层/全局作用域中也找不到目标变量,全局作用域中就会创建一个具有该名称的变量(严格模式中报错)
  2. JS的4种暴露方式:无论何种方式,暴露的都是对象
  3. 默认暴露
    设计的导出
  ```js
  const a = []
  export default a
  ```
  导入
  ```js
  // 简写
  import a from './a' 
  // 详细
  import {defualt as a} from './a'
  ```
  1. 分别暴露
    导出
  ```js
  export const a=[] // 系统会自动转成对象,变成{a:[]}
  ```
  导入
  ```js
  import {a} from './a'        
  ```
  1. 统一暴露:和分别暴露的写法一样
    导出
  ```js
  const a = []
  const b = {}
  export {a,b}
  ```
  导入
  ```js
  import {a, b} from '/xxx'
  ```
  1. 暴露导出
  ```js
  export {default as xx} from '/xxx'
  ```
  可以理解为`import xxx from '/xxx` + `export xxx`,先导入再导出
  **【应用场景】**:
  **step 1. 将本暴露** 如果 
  **step 2. elseif:**
  **step 3. if:**  
  1. 数据保存位置
    1. 分类和保存
      1. 原始数据保存:栈内存,直接存储在栈(stack)中的简单数据段,空间小、大小稳定,被频繁访问
      2. 引用数据保存:引用数据在堆(heap)中,空间大、大小不稳定
        1. 引用数据类型:Object, Array, Function, Date和Math
        2. 栈中存了一个地址表示堆的引用 - JS解释器寻找引用值,会先检索栈再从堆中获得实体
        3. 堆是无序存储,可根据引用直接获取
      3. 关于复制
        1. 引用数据类型复制时,系统会为新变量在中分配存储,即指向堆中的同一对象
        2. let copyObj = obj,copyObj和obj具有相同的地址值。当修改 obj 或 copyObj 任意一个值时都会引起实际对象的值被修改
    2. 堆栈的区别
      1. 内存中,
        1. 栈由os自动分配、释放;堆由开发人员自主分配和释放,若不主动释放,程序结束时浏览器回收
        2. 栈存储执行上下文和函数调用的数据(调用函数->执行上下文:参数、局部变量及返回地址);堆存储动态佩服的数据,如对象和函数
          1.
      2. 数据结构时,栈是运算受限的线性表、只能在表的一端进行插入删除(该端是栈底),入栈push、出栈pop;堆是一种优先队列
    3. 垃圾回收机制(GC,Garbage Collection):管理不用的内存对象,并进行自动内存释放
      1. 全局和局部:全局的生命周期较长,回收器只能在程序结束时进行内存回收;局部:较短,一旦函数执行完毕,垃圾回收器会立即识别并回收
      2. 定义:Garbage Collection(GC),程序工作过程中产生很多垃圾,是不用的内存或之前用过、以后不再用 -> 内存占用高,轻则影响性能,重则崩溃
        1. GC:负责回收,对前端而言GC较为无感
        2. 高级语言会自带GC(Java、Python、JS),不是所有的都有 → C、C++等无
        3. 定期找出,而后释放
      3. 算法 - 可达性分析Reachability Analysis、通过检查是否仍然可被访问 确定是否为垃圾对象,那些以某种方式可访问/可用的值
        1. 被保证存储在内存中
        2. 反之不可访问需回收
      4. 算法策略
        1. 标记清除算法(Mark-Sweep)
          1. JS里算法最常用,大多数浏览器
          2. 两个阶段
            1. 标记阶段:活动对象上标记;
              1. 执行GC(标记清除算法)时,要从出发点遍历内存中所有对象去打标记 -> 对象,包括但不限于全局window对象、文档DOM树等
              2. 方法
                1. 当变量进入执行时,反转某位(通过一个二进制字符来标记)
                2. 维护进入环境变量和离开环境变量这两个列表
            2. 清除:将非活动对象/没有标记的销毁
              1. 垃圾收集器运行时会给内存中所有变量都加个标记,设内存中所有对象都是垃圾,全为0
              2. 从各个根开始遍历,将不是垃圾的节点改成1
              3. 清理标记为0的垃圾,销毁并回收所占内存空间
              4. 最后,将内存中对象标记修改为0,等待下一轮垃圾回收
          3. 优缺点
            1. 优点:实现较简单,可以用0/1
            2. 缺点
              1. 清除后,剩余对象内存位置不变
            2. 新建对象分配内存大小size,由于空闲内存间断、不连续,对空闲内存列表进行一次单向遍历找出大于等于size的块才能分配 1. 寻找合适的块 1. First-fit:大于等于size的块立即返回 2. Best-fit:遍历整个空闲列表,返回大于等于size的最小分块 3. Worst-fit:遍历空闲,找到最大分块,然后切成两部分, 2. 其中,Worst-fit的空闲利用率看起来最合理,但切分会造成更多碎片,不推荐使用。对于First-fit和Best-fit来说,考虑到分配速度和效率First-fit 1. 内存碎片化:空闲内存块 2. 分配速度慢:使用First-fit策略,操作仍是O(n)操作。最坏每次都遍历
          4. 算法
            1. 垃圾收集器在运行时给内存中所有变量都加上一个标记
            2. 各个根对象开始遍历,将不是垃圾的节点改成1
            3. 清理所有标记为0,销毁并回收它们占用的内存空间
            4. 将内存中对象标记修改为0,等下一轮垃圾回收
          5. COM对象
            1. let ele = document.getElementById('"xxx")
            2. ``
        2. 引用计数算法(Reference Counting):早先的垃圾回收算法
          1. 将“对象是否不需要”定义为“对象有没有其他的引用”,若没有引用指向该对象(零引用),对象将被垃圾回收机制回收
          2. 将“对象是否不再需要”简化为“对象有没有其他对象引用到它”
            1. 若无,则对象被垃圾回收机制回收
              1. 对象Object
              2. 数组Array
              3. 函数Function
            2. 声明一个变量且将引用类型赋值给该变量的时候这个值的引用次数为1
              1. 同一个值又被赋给另一个变量,则引用数+1
              2. 该变量的值被其他覆盖,则引用数-1
            3. 当值的应用次数为0,则没有变量在使用
            let a = new Object() // 此对象的引用计数为1(a引用)
            let b = a // 此对象的引用计数为2(a,b引用)
            a = null 
            b = null // 对象的引用次数
            ... // GC回收此对象
            
          3. 遇到的问题
            1. 循环引用:对象A有一个指针指向对象B
            function test() {
               let A = new Object() // 此对象的引用计数为1
               let B = new Object() 
               A.b = B
               B.a = A
            }
            
            1. 函数test执行完成后,对象A和B要被清理,但使用引用计数不会被清理,因为引用数量不会为0
            // COM对象
            let ele = document.getElementById("xxx")
            let obj = new Object()
            // 造成循环引用
            obj.ele = ele
            ele.obj = obj
            // 切断引用关系
            obj.ele = null
            ele.obj = null
            
            1. 引用计数算法
              1.
            2. 优缺点
              1. 优点:
              2. 缺点:需要计数器
          function test(){
            let A = new Object()
            let B = new Object()
            A.b = B
            B.a = A
          }
          
          1. 安装Chrome浏览器
            1. 开发者工具
        3. V8对GC的优化:V8-新生代+老生代
          1. 分代式垃圾回收
            1. 新生代:将新、小、存活时间短,一小块内存频率高的快速清理
              1. 新生代对象 = 使用+空闲区
                1. 使用区写满时,要执行垃圾清理
                2. 活动对象做标记
                  1. 新生代垃圾回收器:对使用区中的活动对象做标记
                  2. 标记完成后要进行:标记完成后将使用区的活动对象复制进空闲区并进行x序
                  3. 进入垃圾清理阶段
                  4. 非活动对象占用的空间清理掉
                  5. 进行角色互换
              2. Cheney算法:堆内存一分为二,处于使用状态的空间是使用区,闲置状态的空间是空闲区
              3. 新生代垃圾回收器:Scavenge算法进行垃圾回收,主要采用复制式方法即Cheney算法
            2. 老生代:大、老、存活时间长的对象,很少检查
            3. 新老生代回收机制及频率不同,因此出现很大程度提高了垃圾回收机制的效率
              1. V8堆内存,新生代(1-8M)和老生代
              2. 存活时间较长/常驻内存的对象
              3. 老生代垃圾回收器
          2. 浏览器
            1. 基于标记清除算法
            2. 新生代(1-8M)+老生代
              1.
          3. 新lao
      5. 通用
        1.
        2.
      6. V8(Google开源的JavaScript和WebAssembly引擎,用C++编写)
    4. js全局变量
      
      
    5. 1
2.5 异常处理
  1. try, catch, finally使用:捕获同步代码块中的异常,如操作未定义变量或调用不存在的函数等
    1. 使用代码
    // 使用 try-catch 处理同步代码块中的异常
    function example1() {
       try {
          // 可能抛出的异常代码块
          const res = func()
       } catch (e) {
          console.error(e)
       } finally { // 在try或catch块结束后都会进入
    
       }
    }
    
  2. Promise,then的异步操作:不能用try/catch替换
    1. 使用代码
    // 使用 Promise.catch() 处理异步操作中的异常
    function example2() {
       somePromiseFunction()
          .then(result => {
             // 处理 Promise 成功的结果
    
          })
          .catch(error => {
             console.error(error.message); // 处理 Promise 失败的原因:会在Promise抛出任何错误时执行,并在promise链条上终止
          })
    }
    
    1. Promise
      1. Promise的基本使用

        1. Promise是一个类,执行的时候要传递一个执行器进去,之后立即执行。是JS进行异步编程新的解决方案
        2. Promise对象的三种状态 - 状态一旦确定就不变
          1. resolve 接受:
          2. reject 拒绝:
          3. pending 等待:
        3. Promise的基本流程
        1. 具体表达
          1. 语法上:Promise是构造函数
          2. 功能:Promise对象用于封装一个异步并获取结果
        2. 执行器接收两个参数:
          1. 使用Promise构造函数
          const myPromise = new Promise((resolve, reject)=>{
             setTimeout(()=>{
                resolve('foo')
             }, 300)
          })
          myPromise.then((value)=>{
             console.log(value,'value')
          }).catch((err)=>{
             console.log(err)
          })
          console.log(myPromise, 'myPromise')
          
          1. 基于回调的API转为基于Promise的API
          function myAsyncFunction(url) {
             return new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest()
                xhr.open('GET',url)
                xhr.onload=() => resolve(xhr.responseText)
                xhr.onerror=() => reject(xhr.statusText)
                xhr.send()
             })
          }
          
          1. Promise.all()
          const promise1 = Promise.resolve(3)
          const promise2 = 42
          const promise3 = new Promise((resolve, reject) => {
             setTimeout(resolve, 100, 'foo')
          })
          const promise4 = Promise.all([1,2,3,Promise.resolve(444)])
          Promise.all([promise1, promise2, promise3]).then((val)=>{
             console.log(val)
          })
          async funtion getPrice() {
             const [choise, prices] = await 
          }
          
          1. Promise.allSettled(),静态方法将一个Promise可迭代对象作为输入,返回一个单独的Promise
          Promise.allSettled(promises).then((res) => {
             results.forEach((res) => {
                console.log(res.status)
             })
          })
          
          1. 1
        3. 执行Promise
          1. 构造函数:Promise构造函数同步执行,then异步
        4. 为什么使用Promise
          1. 指定回调的方式更灵活:在请求发出甚至结束后指定回调
          2. 支持链式,解决回调地狱
      2. 手写Promise
        代码如下。

      // 4. 定义状态常量
      const PENDING = 'pending'
      const FULFILLED = 'fulfilled'
      const REJECTED = 'rejected'
      
      // 1. 创建MyPromise类
      class MyPromise implements Promise {
          // 2. 构造函数Constructor,执行
         constructor(executor) {
            // 13. Promise实现捕获错误
            try {
               executor(this.resolve, this.reject)
            } catch (e) {
               this.reject(e)
            }
         } 
         status = PENDING
         // 6. MyPromise定义value和reason,储存执行器成功和失败的返回值
         value = null
         reason = null
         // 9. 实现then方法多次调用添加多个处理函数,初始化回调为数组依次执行
         successCallback = []
         failCallback = []
         // 3. 定义resolve和reject
         resolve = value => {
            if(this.status !== PENDING) {
               return
            }
            // 改变当前状态
            this.status = REJECTED
            // 保存返回值
            this.reason = reason
            // 执行失败回调
            while () {
               this.failCallback.shift()(this.reason)
            }
         }
         // 
         reject = reason => {
            if(this.status !== PENDING)
               return
            this.status = REJECTED
         }
      }
      // 2.
      
      1. 1
    2. Then
  3. 使用async&await处理异常
    1. 回调地狱
    function AsyncTask() {
       asyncFuncA(function(err, resultA){
          if(err) return cb(err);
          asyncFuncB(function(err, resultB){
             if(err) return cb(err);
             asyncFuncC(function(err, resultC){
                   if(err) return cb(err);
                   // And so it goes....
             })
          })
       })
    }
    
    1. 使用Promise优化
    function AsyncTask(cb) {
       asyncFuncA.then(AsyncFuncB)
       .then(AsyncFuncC)
       .then(AsyncFuncD)
       .then(data => cb )
    }
    

你可能感兴趣的:(javascript,前端,开发语言)