js小整理

偶尔过一遍js知识

1.typeof

  • 对于基本类型除了null都可以显示正确的类型
  • 对于对象,除了函数都会显示object
  • 对于null会显示object

可以使用Object.prototype.toString.call(xxx)获得正确类型返回类似[Object Type]的字符串

2.类型转换

  • 转Boolean

    条件判断除了undefined,null,false,NaN,’’,0,-0 其他所有值都转为true

  • 对象转基本类型:首先会调用valueOf然后调用toString。并且这两个方法可以重写。

    Symbol.toPrimitive 也可以重写,该方法在转基本类型时调用优先级最高。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let num = {
valueOf() {
return 0;
},
toString() {
return '1';
},
[Symbol.toPrimitive]() {
return 2;
}
}
num
// {valueOf: ƒ, toString: ƒ, Symbol(Symbol.toPrimitive): ƒ}
1+num
// 3
'1'+num
// '12'
  • 四则运算符:加法运算时,其中一方时字符串类型就会把另一个也转化为字符串类型。

    其他运算只要其中一方是数字,那么另一方就转为数字。

    1
    2
    [1,2] + [2,1] // '1,22,1'
    // 加法会触发三种类型转换:将值转换为原始值,转换为数字,转换为字符串

3.原型

prototype

基本上所有的函数都有这个属性,除了Function.prototype.bind()创建的对象

  • 声明一个函数时,这个属性就被自动创建了

  • 这个属性的值是一个对象(也就是原型),只有一个属性constructor

  • constructor对应构造函数

    1
    2
    function Fn() {}
    Fn.prototype // {constructor: ƒ}

    constructor是一个共有且不可枚举的属性。一旦我们改变了函数的prototype,那么这个对象就没有这个属性了。

    1
    2
    Fn.prototype = {a: 1}
    Fn.prototype // {a: 1}

    constructor属性的作用

    • 让实例对象知道是什么函数构造了它
    • 如果想给某类库中的构造函数增加一些自定义的方法,就可以通过xx.constructor.method来扩展

proto

这是每个对象都有的隐式原型,指向创建该对象的构造函数原型

因为在js中没有类的概念,为了实现类似继承的方式,通过 proto 将对象的原型联系起来组成原型链,得以让对象可以访问到不属于自己的属性。

  • 实例对象 proto 的 产生: 使用new操作符时,生成的实例对象拥有 proto __属性

    1
    2
    function Fn() {}
    // 这个函数是Function的实例对象 function是个语法糖 内部调用了 new Function

总结

  • Object是所有对象的爸爸,所有对象都可以通过 proto 找到它
  • Function是所有函数的爸爸,同上
  • Function.prototype和Object.prototype是两个特殊的对象,他们由引擎创建
  • 除了以上两个特殊对象,其他对象都是通过构造器new出来的
  • 函数的prototype是一个对象也就是原型
  • 对象的 proto 指向原型, proto __将对象和原型链接起来组成原型链

4.New

  • new的过程
    • 新生成一个对象
    • 链接到原型
    • 绑定this
    • 返回新对象
1
2
3
4
5
6
7
8
9
10
11
12
13
// 调用new时发生的四件事,试着实现一个new
function create() {
// 创建一个空对象
let obj = new Object()
// 获得构造函数 也就是传进来的第一个参数
let Con = [].shift.call(arguments)
// 链接到原型
obj.__proto__ = Con.prototype
// 绑定this执行构造函数
let result = Con.apply(obj, arguments)
// 确保new出来的是一个对象
return typeof result === 'object' ? result : obj
}
  • new 运算符优先级

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function Fn() {
    return this
    }
    Fn.getName = function () {
    console.log('1')
    }
    Fn.prototype.getName = function () {
    console.log('2')
    }
    new Fn.getName() // 1
    new Fn().getName() // 2

    -------------执行顺序-------------
    new (Fn.getName())
    (new Fn()).getName()

5.Instanceof

可以正确判断对象的类型,内部机制是通过判断对象的原型链中是不是能找到类型的prototype.

6.this

  • 有对象调用: 指向调用它的对象
  • 没有对象调用: 指向window
  • new出来的实例: 指向实例
  • call apply bind 改变指向传入的对象
  • 箭头函数没有this,this指向取决于箭头函数外第一个不是箭头函数的函数

7.闭包

函数A返回一个函数B,并且B中使用了函数A的变量,函数B就被称为闭包

8.深拷贝浅拷贝

一个变量赋值一个对象,那么两者的值引用同一个,其中一方改变,另一个相应改变。实际业务重要解决这个问题

1.浅拷贝 assign

1
2
3
4
5
6
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1

扩展运算符 …

1
2
3
4
5
6
let a = {
age: 1
}
let b = {...a}
a.age = 2
console.log(b.age) // 1

浅拷贝只能解决第一层问题,如果接下去的值里还有对象,就需要深拷贝

2.深拷贝 JSON.parse(JSON.stringify(object))

局限

  • 忽略undefined
  • 不能序列化函数
  • 不能解决循环引用的对象

如果有以上三种情况 可以使用lodash的深拷贝函数

9.防抖和继承

防抖多次执行变为最后一次执行,节流每隔一段时间执行,不频繁执行

10.继承

ES5 Object.create()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Super() {}
Super.prototype.getNumber = function() {
return 1
}

function Sub() {}
let s = new Sub()
Sub.prototype = Object.create(Super.prototype, {
constructor: {
value: Sub,
enumerable: false,
writable: true,
configurable: true
}
})
// 以Sub为构造函数以Super为原型

浏览器小结

1.Event loop

js执行过程中产生执行环境,这些执行环境会被顺序的加入到执行栈中。如果遇到异步代码,会被挂起并加入到Task队列中。一旦执行栈为空,Event Loop 就会从Task队列中拿出需要执行的代码并放入执行栈中执行

  • 不同的任务源会被分配到不同的Task队列中,任务源可以分为

    • 微任务(microtask) ES6中称为jobs 包括:

      • process.nextTick
      • promise
      • Object.observe
      • MutationObserver
    • 宏任务(macrotask)ES6中称为task

      • script
      • setTimeout
      • setInterval
      • setImmediate
      • I/O
      • UI rendering

Event loop顺序

  • 执行同步代码,这属于宏任务
  • 执行栈为空,查询是否有微任务需要执行
  • 执行所有微任务
  • 必要的话渲染UI
  • 然后开始下一轮Event loop,执行红任务中的异步代码

2.渲染机制

  • 处理html并构建dom树
  • 处理css构建cssdom树
  • 将dom和cssdom合并成一个渲染树
  • 根据渲染树的布局,计算每个节点的位置
  • 调用GPU绘制, 合成图层,显示在屏幕上

3.重绘(Repaint)和回流(Reflow)

  • 重绘是当前节点需要更改外观而不会影响布局,比如改变color就称为重绘

  • 回流是布局或者几何属性需要改变

    回流必定发生重绘,重绘不一定引发回流。回流所需的成本比重绘高的多,改变深层次的节点很有可能导致父节点一些了回流。

  • 导致性能问题的几个点

    • 改变 window 大小
    • 改变字体
    • 添加或删除样式
    • 文字改变
    • 定位或者浮动
    • 盒模型

参考链接: https://yuchengkai.cn/docs/zh/frontend/

------ 本文结束------
0%