js知识点汇总

76 阅读16分钟

1. 概念

1. 改变 & 不改变数组方法

1. 改变原数组
    + pop
    + push
    + reverse
    + shift / unshift
    + sort
    + splice

2. 不改变原数组
    + concat
    + join
    + slice
    + JSON.parse / JSON.stringfy
    + indexOf / lastindexOf
    + filter
    + reduce
    + map
    + [...arr]

2. slice splice区别

// splice(start, count, item1, item2, itemN)
let fruits = ["Banana", "Orange", "Apple", "Mango"];
let ret = fruits.splice(2, 1, "Lemon", "Kiwi");

console.log(fruits) //["Banana", "Orange", "Lemon", "Kiwi", "Mango"]
console.log(ret) //["Apple"]

// slice(start, end)
var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var citrus = fruits.slice(1,3);

console.log(citrus) // [Orange,Lemon]

3. for of & for in

1. `for...in` 便利数组或对象属性
2. `for in` 得到对象key 或 字符串下标
3. `for of``forEach` 一样, 可以直接得到值
4. `for of` 不能用于对象, 可以遍历string, 和 0xFFFF码点
    // 遍历对象
    const obj = {
        a: 1,
        b: 2,
        c: 3
    }
    for (let i in obj) {
        console.log(i)    //输出 : a   b  c
    }
    for (let i of obj) {
        console.log(i)    //输出: Uncaught TypeError: obj is not iterable 报错了
    }
​
​
   // 遍历数组
   const arr = ['a', 'b', 'c']
   // for in 循环
   for (let i in arr) {
       console.log(i)         //输出  0  1  2
   }
   
   // for of
   for (let i of arr) {
       console.log(i)         //输出  a   b   c
   }

4. event loop => 宏任务 & 微任务

宏:
    + script
    + setTimeout / setInterval / setImmediate(node)
        ⚠️ setTimeout延迟时间: 也就是说小于1ms的值,默认使用1ms, 超过5层, 最小间隔 4ms
        
    + ajax
    + 事件绑定
    + requestAnimationFrame(浏览器)
    + I / O
    + UI render(浏览器)

微:
    + new Promise的then catch
    + process.nextTick(node)
    
1. 事件循环由宏任务和在执行宏任务期间产生的所有微任务组成, 完成当下的宏任务后, 会立刻执行所有在此期间入队的微任务.
2. 区分了微任务和宏任务后,本轮循环中的微任务实际上就是在插队, 这样微任务中所做的状态修改, 在下一轮事件循环中也能得到同步.
3. 宏任务队列只有一个,而每一个宏任务都有一个自己的微任务队列, 每轮循环都是由一个宏任务+多个微任务组成.
4. w3c新规则: 存放定时器的队列是延时队列,存放用户交互操作的是交互队列, 还有浏览器一定要准备好的微任务队列, 优先级是最高的.

5. 原始数据类型

+ undefined
+ null
+ boolean
+ string
+ number
+ object
+ symbol(es6)

2. 方法

1. Array

2. Object

Object.defineProperty

   配置项:
           writable:       是否可重写
           value:          当前值
           get:            读取时内部调用的函数
           set:            写入时内部调用的函数
           enumerable:     是否可以遍历
           configurable:   是否可再次修改配置项
let person = {
    name: '码农',
    age: 18,
};

Object.defineProperty(person, 'sex', {
    value: '男', //设置属性值
    enumerable: false, //控制属性是否可以枚举,默认值是true
    writable: true, //控制属性是否可以被修改,默认值是true
    configurable: true, //控制属性是否可以被删除,默认值是true
});

// 可以用getOwnPropertyDescriptors方法查看对象属性配置项
console.log(Object.getOwnPropertyDescriptors(person)); 

3. this

1. 区别:
    + bind 调用后触发
    + apply 跟参数, 为一个数组
    
// 小白对象
let xiaobai = {
  name: "小白",
  pet: "splider"
}

// 小杨对象
let xiaoyang = {
  name: '小杨',
  pet: 'pig',
  say: function (...args) {
    console.log(this)
    console.log(`${this.name}有一只${this.pet}宠物 to say${args[0]} and ${args[1]}`)
  }
}

xiaoyang.say.call(xiaobai, 'fff', 'ddd')
xiaoyang.say.apply(xiaobai, ['fff', 'ddd'])
xiaoyang.say.bind(xiaobai, 'fff', 'ddd')() // 调用后触发

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};
​
// ES5的写法 -- 改变this指向
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

4. reduce

var arr = [1, 2, 3, 4];
var sum = arr.reduce(function (prev, cur, index, arr) {
    console.log(prev, cur, index, arr);
    return prev + cur; // return后的值赋给prev, 此处可以认为 prev = prev + cur
}, 22); // 第二个参数, 实际为index == 0 的prev 赋值为 initValue
console.log(arr, sum);
​
// 1. 计算每个元素出现的次数
let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
let nameNum = names.reduce((pre, cur) => {
    if (cur in pre) {
        pre[cur]++;
    } else {
        pre[cur] = 1;
    }
    console.log(pre, cur);
    return pre; // 若prev是复杂数据类型, return 后 pre元素相累计为一个新的复杂数据类型
}, {});
console.log(nameNum); //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}// 2. 数组去重
let reArr = [1, 2, 3, 4, 4, 1];
let newArr = arr.reduce((pre, cur) => {
    if (!pre.includes(cur)) {
        return pre.concat(cur);
    } else {
        return pre;
    }
}, []);
console.log(reArr); // [1, 2, 3, 4]// 3. 数组扁平化
let flatArr = [
    [0, 1],
    [2, 3],
    [4, [5, 6, 7, [9, 11, [33, 23, [1, 2, 3, 4]]]]],
];
const newFlatArr = function (arr) {
    return arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) ? newFlatArr(cur) : cur), []);
};
console.log(newFlatArr(flatArr)); //[0, 1, 2, 3, 4, 5, 6, 7]// 4. 对象里属性求和
var result = [
    {
        subject: 'math',
        score: 10,
    },
    {
        subject: 'chinese',
        score: 20,
    },
    {
        subject: 'english',
        score: 30,
    },
];
​
var resSum = result.reduce(function (prev, cur) {
    return cur.score + prev;
}, 0);
console.log(resSum); //60

5. slice

1. slice 不会改元素, 只会返回一个浅复制了原数组中的元素和一个新数组.
2. 如果该元素是个 对象引用 (不是实际的对象), slice 会拷贝这个对象引用到新的数组里, 两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变.
var arr = [];
let arrayLike = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
}
for (var i = 0; i < arrayLike.length; i++) {
  arr.push(arrayLike[i]);
}
​
console.log('原理=>', arr);
​
// 等价写法
// 这里可以改变this的call出现了,假如我用call将arguments把this给slice
// slice会得到具有长度属性的对象,就实现了对象转数组的
console.log('快捷=>', [].slice.call(arrayLike));

3. ES6

1. 类

class Person {

    constructor(name, age) {
	// 通过new创建对象时, 实际上就是在调用类的构造函数
	console.log('constructor!!')
	// 在构造函数中, 可以通过this来引用当前对象
	this.name = name
	this.age = age
    }
   
    // 方法
    sayHi() {
	console.log('hi I am lily')
    }

    // this
    /*
		类中所有代码都会在严格模式中执行
    */
    fn() {
	console.log('-->', this)
    }
}

const p = new Person('lily', 18) // 创建对象
p.sayHi() // 调用方法

console.log(p) // {name: 'lily', age: 18}

console.log(p.fn()) // Person {}
const test = p.fn

2. ...

`...` 展开语法本质是是“浅拷贝”——它只会复制一层, 这使得它的执行速度很快, 但是也意味着当你想要更新一个嵌套属性时, 你必须得多次使用展开语法.

3. 函数

1. rest函数:
    + rest函数实际上是 ... 的相反用法.
    
2. 箭头函数:
    1. 箭头函数没有arguments.
    2. 箭头函数没有自己的this.
    3. 剪头函数中的this无法通过call(), apply(), bind()修改.
    4. 箭头函数无法作为构造函数使用.
    5. pipeline 管道机制, 前一个函数的输出是后一个函数的输入.
    
3. 尾调用:
    + 尾调用(Tail Call), 就是指某个函数的最后一步是调用另一个函数.
    
4. 柯里化:
    + 将多参数的函数转换成单参数的形式, 希望函数功能尽可能单一.
    
// 1. rest
function demo(...params) {
    console.log(params)
}
demo(1, 2, 3, 4) // [1, 2, 3, 4]

// 2. pipeline 例子
const pipeline = (...funcs) =>
  val => funcs.reduce((a, b) => b(a), val);
const plus1 = a => a + 1;
const mult2 = a => a * 2;
console.log(pipeline(plus1, mult2)(5)) // 12

// 3. 尾调用
// eg
function f(x){
  return g(x);
}

// 尾递归1 -- 阶乘
function factorial(n, total = 1) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

// 尾递归2  -- Fibonacci函数的实现
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
  if( n <= 1 ) {return ac2};
  return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}

// 4. 柯里化
// eg
const fn = a => b => c => a + b + c;
console.log(fn(1)(2)(3))

5. 数组

1. Array.from:
    + 伪数组类型转为真正的数组 Set, Map, arguments

2. Array.of:
    + 将一组值转为数组
    
3. copyWithin:

4. find & findIndex:
    + findeIndex, 类似 indexOf方法, 可以识别NaN.

5. entries & keys & values:
    + entries 对整个键值对遍历.

6. includes:
    + Array.prototype.includes 与 字符串 includes方法类似.

7. flat & flatMap:
    + flatMap 相当于对元素组每个成员执行一个map函数, 再对返回值执行flat方法.
    + `flatMap()` 方法还可以有第二个参数,用来绑定遍历函数里面的 `this`
    
    
// 1. Array.from
let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// ES5的写法 -- 改变this指向
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

// 2. Array.of
// eg
Array.of(3, 11, 8) // [3,11,8]

// 实现原理
function ArrayOf () {
  return [].slice.call(arguments);
}

// 3. copyWithin
<!---->

// 4. find & findIndex

// 5. entries & keys & values
for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

// 6. includes

// 7. flat & flatMap
// eg
[1, 2, [3, 4]].flat() // 默认拉平一层
// [1, 2, 3, 4]

[1, 2, [3, [4, 5]]].flat(2) // 拉平2层
// [1, 2, 3, 4, 5]

[1, [2, [3]]].flat(Infinity) // 全部拉平
// [1, 2, 3]

// eg -- flagMap
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]

arr.flatMap(function callback(currentValue[, index[, array]]) {
  // ...
}, [thisArg])

// flatMap eg
const studioList = [{
  Authorized: "2",
  CompanyType: "1",
  Id: "3729",
  Name: "阿我打完的",
  ServiceProviderId: "6",
  TenantId: "1",
},
{
  Authorized: "1",
  CompanyType: "1",
  Id: "3134",
  Name: "纳税统计-启用时间202101无期初",
  ServiceProviderId: "6",
  TenantId: "1",
},
{
  Authorized: "1",
  CompanyType: "1",
  Id: "427",
  Name: "美丽人生工作室",
  ServiceProviderId: "6",
  TenantId: "1",
},
{
  Authorized: "1",
  CompanyType: "1",
  Id: "410",
  Name: "凭证测试专用2",
  ServiceProviderId: "6",
  TenantId: "1",
}]


/**
 * 目标格式:
 *  [
      {
        text: '公司名称',
        value: '公司ID'
      }
    ]
 */

// filter + map
const filterList1 = studioList.filter(r => r.Authorized == '1').map(i => ({ text: i.Name, value: i.Id }))

// flatMap
const filterList2 = studioList.flatMap(r => r.Authorized == '1' ? { text: r.Name, value: r.Id } : [])


6. 对象

1. Object.is:
    + 比较两值是否相等
    
2. Object.keys & Object.getOwnPropertyNames:
    + same: 都是返回自身的属性,不会返回原型链上的.
    + diff: Object.keys()返回可枚举的,Object.getOwnPropertyNames()返回所有的.
    
3. Object.assign:
    1. 当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝.
    2. 如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性 (同名属性替换).
    3. 如果该参数不是对象,则会先转成对象,然后返回.
    
4. super:
    1. Object.setPrototypeOf(obj, params) -- 将属性写在对象原型链上
    2. Object.getPrototypeOf(params) -- 从对象原型链上的属性
    
5. ?. (链判断运算符) ?? (Null判断运算符):

6. Object.keys & Object.values & Object.entries:

7. Object.fromEntries:

// 1. Object.is
Object.is(+0, -0) // false
Object.is({}, {}) // false
Object.is(NaN, NaN) // true

// 3. Object.assign
// 3.1. 对象合并
var target = {name:'带你飞',age:16}
var source = {age:18}
var result = Object.assign(target,source)
console.log(result,target===result); // {name: '带你飞', age: 18} true

// 3.2. 原始类型封装为对象
var source1 = "abc";
var source2 = true;
var source3 = 10;
 
var result = Object.assign({}, source1, null, source2, undefined, source3); 
// 原始类型会被包装,null 和 undefined 会被忽略。
// 注意,只有字符串的包装对象才可能有自身可枚举属性。
console.log(result); // {0: 'a', 1: 'b', 2: 'c'}

// 3.3. 将数组认为是对象
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]

// 4. super
// super.foo指向原型对象proto的foo方法,但是绑定的this却还是当前对象obj,因此输出的就是world
const proto = {
  x: 'hello',
  foo() {
    console.log(this.x);
  },
};
const obj = {
  x: 'world',
  foo() {
    super.foo();
  }
}
Object.setPrototypeOf(obj, proto);
obj.foo() // "world"

// 5. ?. ??
const obj = {
    x: 'hello',
    y: 'world',
    zero: 0,
}

// ?. 类似三目 判断属性是否存在
let objP = obj?.x || 'default'
console.log(objP) // hello

// delete 存在则删除
delete obj?.x
console.log(obj) // {y: 'world'}

// ?? 类似|| 只有null才生效, false 和 0 不生效
let objZ = obj?.zero ?? true
console.log(objZ) // true

// 6. Object.keys & Object.values & Object.entries
// keys -- 键
// values -- 值
// entries -- 键值对 (单项数组形式)

let { keys, values, entries } = Object;
let objEn = { a: 1, b: 2, c: 3 };
for (let key of keys(objEn)) {
    console.log(key); // 'a', 'b', 'c'
}
for (let value of values(objEn)) {
    console.log(value); // 1, 2, 3
}
for (let [key, value] of entries(objEn)) {
    console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}
for (let item of entries(objEn)) {
    console.log(item); // ['a', 1], ['b', 2], ['c', 3]
}

// 7. Object.fromEntries
// 例一
const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);
Object.fromEntries(entries)
// { foo: "bar", baz: 42 }

// 例二
const map = new Map().set('foo', true).set('bar', false);
Object.fromEntries(map)
// { foo: true, bar: false }


7. Set & Map

set中 NaN 是相同的, 两个空对象 是不同的

1. Set.prototype.constructor:
    + 构造函数,默认就是`Set`函数.
    
2. Set.prototype.size:
    + 返回`Set`实例的成员总数.
    
3. Set.prototype.add(value):
    + 添加某个值,返回 Set 结构本身.
    
4. Set.prototype.delete(value):
    + 删除某个值,返回一个布尔值,表示删除是否成功.
    
5. Set.prototype.has(value):
    + 返回一个布尔值,表示该值是否为`Set`的成员.
    
6. Set.prototype.clear():
    + 清除所有成员,没有返回值.
    
    
const map = new Map()

let params1 = ['a']
let params2 = ['a']

map.set(params1, 111).set(params2, 222).set('aaa', 'aaa') // 可链式, 对象做键同值不同址

console.log(map.size) // 2

console.log(map.get(params1)) // 111

console.log(map.has(['a']), map.has(params1)) // false true

//map.delete('aaa') // 删除 'aaa' => 'aaa'
//map.clear() // 清空所有

// map.keys()
console.log(map.keys()) // MapIterator {Array(1), Array(1), 'aaa'}
for (let key of map.keys()) {
  console.log(key) // ['a'] ['a'] 'aaa' (三次循环)
}

// map.values()
console.log(map.values()) // MapIterator {111, 222, 'aaa'}
for (let val of map.values()) {
  console.log(val) // 111 222 'aaa' (三次循环)
}

// map.entries()
console.log(map.entries()) // MapIterator {Array(1) => 111, Array(1) => 222, 'aaa' => 'aaa'}
for (let [key, val] of map.entries()) {
  console.log(key, val) // ['a'] 111; ['a'] 222; 'aaa' 'aaa'
}

// 拓展运算符...
console.log([...map.keys()])
// [1, 2, 3]
console.log([...map.values()])
// ['one', 'two', 'three']
console.log([...map.entries()])
// [[1,'one'], [2, 'two'], [3, 'three']]
console.log([...map])
// [[1,'one'], [2, 'two'], [3, 'three']]

8. Promise

1. then
function runAsync () {
  return new Promise(function (resolve, reject) {
    //做一些异步操作
    setTimeout(function () {
      console.log('执行完成');
      resolve('随便什么数据');
    }, 1000);
  });
}

runAsync().then(v => {
  console.log(v)
  return runAsync()
}).then(v => {
  console.log(v)
  return runAsync()
}).then(v => {
  console.log(v)
  return runAsync()
})


执行完成 // 1000
随便什么数据 // 1000
执行完成 // 2000
随便什么数据 // 2000
执行完成 // 3000
随便什么数据 // 3000
执行完成 // 4000
2. resolve & reject
function getNumber () {
  return new Promise(function (resolve, reject) {
    //做一些异步操作
    setTimeout(function () {
      var num = Math.ceil(Math.random() * 10); //生成1-10的随机数
      console.log('num', num)
      if (num <= 5) {
        resolve(num);
        reject('数字刚刚好') // 执行完resolve 不会继续执行 reject
      }
      else {
        reject('数字太大了');
        resolve(num); // 同理 执行完 reject 不会执行 resolve
      }
    }, 1000);
  });
}

getNumber()
  .then(
    function (data) {
      console.log('resolved');
      console.log(data);
    },
    function (reason, data) {
      console.log('rejected');
      console.log(reason);
    }
  );

// eg1: resolve情况
num 4
resolved
4

// eg2: reject情况
num 10
rejected
数字太大了
3.  catch
function getNumber () {
  return new Promise(function (resolve, reject) {
    //做一些异步操作
    setTimeout(function () {
      var num = Math.ceil(Math.random() * 10); //生成1-10的随机数
      console.log('num', num)
      if (num <= 5) {
        resolve(num);
        reject('数字刚刚好') // 执行完resolve 不会继续执行 reject
      }
      else {
        reject('数字太大了');
        resolve(num); // 同理 执行完 reject 不会执行 resolve
      }
    }, 1000);
  });
}


// then中回调函数只有一个参数, 默认参数是resolve回调函数
// 若有reject, 没有第二个参数, 则被catch承接
// 遇到 reject(没有第二个参数), err情况 都会直接执行到catch
getNumber()
  .then(function (data) {
    console.log('resolved');
    console.log(data);
    // console.log(somedata); //此处的somedata未定义
    return getNumber()
  })
  .then(function (data) {
    console.log('resolved');
    console.log(data);
    return getNumber()
  })
  .then(function (data) {
    console.log('resolved');
    console.log(data);

    return getNumber()
  })
  .catch(function (reason) {
    console.log('rejected catch');
    console.log(reason);
  });

// eg1: 没有执行到catch
num 5 // 1000
resolved // 1000
5 // 1000
num 4 // 2000
resolved // 2000
4 // 2000
num 5 // 3000
resolved // 3000
5 // 3000
num 3 // 4000

// eg2: 执行过程中 执行到reject
num 1 // 1000
resolved // 1000
1 // 1000
num 9 // 2000
rejected catch // 2000
数字太大了 // 2000

// eg3: 首次执行 执行到reject
num 10 // 1000
rejected catch // 1000
数字太大了 // 1000

// eg4: 放开console.log(somedata), 数字太大 先执行reject, 直接中断
num 8 // 1000
rejected catch // 1000
数字太大了 // 1000

// eg5: 放开console.log(somedata), 数字正常 先执行resolve, 再执行catch
num 3 // 1000
resolved // 1000
3 // 1000
rejected catch // 1000 (同步) 
somedata is not defined // 1000 (同步)
4.all & race
function runAsync1 () {
  return new Promise(function (resolve, reject) {
    //做一些异步操作
    setTimeout(function () {
      console.log('异步任务1执行完成');
      resolve('随便什么数据1');
    }, 1000);
  });
}
function runAsync2 () {
  return new Promise(function (resolve, reject) {
    //做一些异步操作
    setTimeout(function () {
      console.log('异步任务2执行完成');
      resolve('随便什么数据2');
    }, 1000);
  });
}
function runAsync3 () {
  return new Promise(function (resolve, reject) {
    //做一些异步操作
    setTimeout(function () {
      console.log('异步任务3执行完成');
      resolve('随便什么数据3');
    }, 1000);
  });
}

// all----------------------------
// all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的
// 等全部执行完
Promise
  .all([runAsync1(), runAsync2(), runAsync3()])
  .then(function (results) {
    console.log(results);
  });

// eg1: all
异步任务1执行完成
异步任务2执行完成
异步任务3执行完成
(3) ['随便什么数据1', '随便什么数据2', '随便什么数据3']

// race----------------------------
// race 是 第一个执行完的 执行回调 然后其他继续执行 (回调后并不会像catch那样中断其他任务)
Promise
  .race([runAsync1(), runAsync2(), runAsync3()])
  .then(function (results) {
    console.log('first ok ', results);
  });

// eg2: race
异步任务1执行完成
first ok  随便什么数据1
异步任务2执行完成
异步任务3执行完成

// race demo----------------------------
//请求某个图片资源
function requestImg () {
  return new Promise(function (resolve, reject) {
    var img = new Image();
    img.onload = function () {
      resolve(img);
    }
    img.src = '../../img/img01.png'; //  right
    // img.src = 'xxxxxxx'; // fault
  });
}

//延时函数,用于给请求计时
function timeout () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      reject('图片请求超时');
    }, 5000);
  });
}

Promise
  .race([requestImg(), timeout()])
  .then(function (results) { // 成功请求到返回 (立刻)
    console.log('right', results);
  })
  .catch(function (reason) { // 报错返回 (5000ms后)
    console.log('fail', reason);
  });


// eg3: race success
right <img src=​"../​../​img/​img01.png">​

// eg4: race fail
GET http://127.0.0.1:5500/img/img1.png 404 (Not Found)
promise.js:188 fail 图片请求超时 // 5000

9. class

1.  getter & setter
class MyClass {
  constructor () {
    this.con_var = 'constructor中的变量'
    this.con_fun = function () {
      return 'constructor中的方法'
    }
    this.self2 = this.self2.bind(this)
  }
  params () {
    return 'this is params'
  }
  get prop () {
    return 'this is prop'
  }
  set prop (value) {
    console.log('setter', value)
  }
  var1 = '定义的变量直接属于类本身方法'
  self1 = () => {
    return '箭头函数或者在constructor中重绑this指向会属于类本身方法而非prototype中属性'
  }
  self2 () {
    return 'this中重新指向(同时也会在prototype中隐式属性中保留)'
  }
  self3 () {
    return '普通方法会在prototype中隐式属性显示'
  }
}

let my = new MyClass()

my.attr = 'set attr 属性' // 最后attr属性值以get方法return为准
console.log(my)
console.log(my.attr, my.__proto__.attr) // this is params this is params

// getPrototypeOf定义的变量 / 方法 都在prototype的显式属性
// 直接定义的变量 / 方法 则在实例的显式属性中
my.out_var1 = 'out定义实例变量1'
Object.getPrototypeOf(my).out_var2 = 'out定义实例变量2 (prototype中)'
my.out_fun1 = function () {
  return 'out定义实例方法1'
}
my.out_fun2 = () => {
  return 'out定义实例方法2'
}
Object.getPrototypeOf(my).out_fun3 = function () {
  return 'out定义实例方法3 (prototype中)'
}
Object.getPrototypeOf(my).out_fun4 = () => {
  return 'out定义实例方法4 (prototype中)'
}
2. 属性表达式
var funName = 'outTing'
class AA {
  constructor (val) {
    this.name = val
    this.description = 'aaaaaa'
  }
  [funName] () {
    return 'prototype中的隐式属性'
  }
}

// 直接赋值
let person1 = new class {
  constructor (name) {
    this.name = name;
  }
  sayName () {
    console.log(this.name);
  }
}('张三');
person1.sayName(); // "张三"
console.log(person1)
3. 静态方法static
// 静态

4. 封装

1. 时间

时间戳格式化

    function timestampToTime(timestamp) {
        // 时间戳为10位需*1000,时间戳为13位不需乘1000
        var date = new Date(timestamp * 1000);
        var Y = date.getFullYear() + '-';
        var M =
            (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
        var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
        var h = date.getHours() + ':';
        var m = date.getMinutes() + ':';
        var s = date.getSeconds();
        return Y + M + D + h + m + s;
    }
    console.log(new Date().getTime());
    console.log(timestampToTime(1670145353)); //2022-12-04 17:15:53

2. obj 按key排序

    function objKeySort(arys) {
        //先用Object内置类的keys方法获取要排序对象的属性名,再利用Array原型上的sort方法对获	取的属性名进行排序,newKey是一个数组
        var newKey = Object.keys(arys).sort();
        //console.log('newKey='+newKey);
        var newObj = {}; //创建一个新的对象,用于存放排好序的键值对
        for (var i = 0; i < newKey.length; i++) {
            //遍历newKey数组
            newObj[newKey[i]] = arys[newKey[i]]; //向新创建的对象中按照排好的顺序依次增加键值对
        }
        return newObj; //返回排好序的新对象
    }

3. 深拷贝

JSON.stringfy 方法无法拷贝属性值为undefined的项

//使用递归的方式实现数组、对象的深拷贝
function deepClone(obj) {
    let objClone = Array.isArray(obj) ? [] : {};
    if (obj && typeof obj === 'object') {
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                //判断ojb子元素是否为对象,如果是,递归复制
                if (obj[key] && typeof obj[key] === 'object') {
                    objClone[key] = deepClone(obj[key]);
                } else {
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}