高级js学习7 prototype 继承 微任务宏任务以及promsie

Posted lin-fighting

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高级js学习7 prototype 继承 微任务宏任务以及promsie相关的知识,希望对你有一定的参考价值。

prototype重定向

function Fn()
Fn.prototype.say = () => 
Fn.prototype = new Fn()

重定向有几个问题

  • 1 重定向后prototype指向的对象可能不具备constructor对象,必须自己手动配置。
  • 2 可能导致浏览器默认开启的原型对象上的方法丢失。解决:重定向的对象最好和之前的原型对象做一个合并处理。

Fn.prototype = Object.assing(, Fn.prototype, new Fn())


原型重定向的优点:

  • 1方便批量向原型更新方法
  • 2 原型继承的方案就是基于原型重定向完成的。

手撕call bind apply

call的作用可以改变函数的this,并执行。

const a = d:1
var d = 2
function D()
	console.log(this.d)

D() // 2 this指向window
D.call(a) // 1. this指向了a
D.apply(a) // 1
const test = D.bind(a)
test() // 1 this指向了a

原理:
首先D基于_proto _ 找到了Function.prototype.call方法。将call方法执行。
实现

function _call(obj, ...args)
	const a = Symbol('obj')
	obj[a] = this //让obj短暂的拥有了这个办法并且执行。
	const result = obj[a](...args)
	delete obj[a]
	return result

apply跟call一样,不同的是入参的区别,apply第二个参数必须是数组,他会一个个传给fn。

function apply(obj, args)
	if(!Array.isArray(args))
		return;
	
	const a = Symbol('obj')
	obj[a] = this //让obj短暂的拥有了这个办法并且执行。
	const result = obj[a](...args)
	delete obj[a]
	return result

bind是将函数的this改变之后,并传入对应的参数之后,返回一个新的函数,不会执行。

functuion bind(obj, ...args)
    const self = this
	return function(...args2)
			self().call(fn,...args,...args2)




js中的四种检测

typeof xx
xx instance X
constructor
Obhect.prototype.toString.call(xx)

typeof

typrof [value] => 字符串,包含类型。
局限性:
typeof null => 'object'
typeof 对象,除了function检测出来时'function',其他都是'object'

因为:typeof检测类型的机制是,按照计算机底层存储的二进制来进行检测的。
以000开始的对象,而null在计算机的存储都是。
而所有对象的存粗二进制前三个值都是0。所以null也会被判断为object。
好处:简单,速度。
特点:只能识别原始类型和function。

instanceof

因为typeof的局限性,所以instanceof来充当壮丁了。因为instance本身不是用来判断类型的,而是用来判断实例是否是某个类的。
但是instanceof不能用来检测原始值类型。

[] instanceof Array // true
[] instanceof Object // true
 instanceof Array // false
 instance Object //true
new Number(1) instanceof Number // true

因为数组也是对象,只不过是特殊的对象。
Array.prototype._ proto _ = Object.prototype.
原理:按照原型链检测,只要当前检测的构造函数(他的原型对象),出现在实例的原型上(原型链),检测结果就是true。
实现:

f instanceof Fn =>
第一步:
先判断Fn上有没有这个属性 Symbol.hasInstance
有的话将f作为参数执行 Fn[Symbol.hasInstance](f),
将其结果返回作为instaceof的结果。
f instanceof Fn === Fn[symbol.hasInstance(f)]
  • 在函数中,Function.prototype就有这个Symbol.hasInstance属性,
    并且,如果直接修改如 Fn[Symbol.hasInstace] =()=>不会成功
    只能通过es6的写法,
class Fnstatic [Symbol.hasInstace]()

去修改才会成功
所以所有的函数都会有这个属性

局限性:
可能可以通过prototype来修改原型,所以判断的值也有可能有误。

  • 实现instanceof函数

        const _instanceof = function _instanceof(obj, Ctor)
            let proto = Object.getPrototypeOf(obj)
            //只要原型链上有Ctor这个类的原型
            while(proto)
                if(proto === Ctor.prototype)
                    return true
                
                proto = Object.getPrototypeOf(proto)
            
            return false
        

instancof的本质就是判断Ctor的类的原型是否在obj的原型链上。

constructor

constructor也是被拉下临时顶替的,因为它能弥补instancof的不足。

const arr = []
arr.constructor === Array //true
arr.constructor === Object //false

//还有
const Fn = 
const fn = new Fn()
fn.constructor === Object //false
Fn.prototype = 
const fn1 = new Fn()
f1.constructor === Object //true, 
//他的constructor是通过._proto_去Object.prototype找的。

但是constructor的修改更加容易,所以更加容易出错。

Object.prototype.toString.call(val)

最完美的。他的值是

'[object String|Number|Array|Object...]'
//第二个就是类型,首字母大写。

大部分内置类都有自己的toString,用来转换为字符串, 但是Object.prototype.toString是用来检测数据类型的,返回值中包含自己所属的构造函数的信息。

  • 用call的原因是让所有类型都可以i使用Object.prototype上的toString,里面的this指向谁就检测谁。
  • 但是当实例对象拥有Symbol.toStringtag属性,救返回该属性值。

深浅拷贝

JSON.parse(JSON.string())

缺点:属性值symbol,undefined,functiion会丢失,属性值是Error或者正则会转为对象,属性值是bigInt报错,属性值是Date的,转化为字符串,再重新转为对象,结果还是字符串。原理:把json字符串转划为对象,浏览器会重新分配内存,实现深拷贝。
如:


可以自己手写一个函数递归遍历来实现深拷贝。

前端开发中的同步异步概念

基础可以看js中的eventloop
这里再记录一些细节。

渲染进程:

  • GUI渲染线程,渲染页面 & 绘制图形
  • JS引擎线程,解析和执行js代码跟GUI渲染线程互斥。
  • 事件触发线程:监听事件触发
  • 定时器触发线程:监听定时器计时
  • 异步http请求线程:基于http从服务器获取数据
  • Webscoker

异步编程实现机制:

  • 多线程机制
  • eventloop事件循环机制

  • js是“单线程的”,浏览器只分配一个js引擎线程来解析和执行js代码。js中大部分操作都是同步的,少部分操作,结合Eventloop机制,实现了异步处理。

异步任务:

  • 异步微任务 microtask:
    promise.then,async,await, queueMicork(手动创建微任务),process.nextTick,MutationObserver(监听dom)…
  • 异步宏任务 macrotask:
    定时器,事件绑定,fetch/ajax,node的setImmediate,script整个代码块…

js底层运行机制。
所有的代码都会在执行栈中(主线程,js引擎)执行,然后还有

  • WebApiS 任务监听队列,所有的异步任务都需要在这个队列中进行监听,监听何时执行。

  • 还有一个EventQueue,,任务/事件等待队列。

  • 当异步任务在监听队列当中检测到已经可以执行了,就会把执行的任务挪到任务等待队列(EventQueue)。

  • 当主线程执行完同步代码之后,再按照优先级,依次从任务等待队列中找到对应的异步任务,把它放到主线程中去执行。然后执行完继续来任务等待队列中找。这就是事件循环机制eventloop。优先级顺序就是:先找可以执行的微任务,再找可执行的宏任务。优先级相同,谁先进队就先执行谁。队列的特点也是先进先出。

微任务,宏任务。

EventQueue有两个队列,一个微任务队列,一个宏任务队列。

setTimeout(()=>,2000)
for(let i = 0; i< 99999; i++)
Promise.then(()=>console.log(123))

当js引擎执行到这行代码的时候,会将其放入webApis队列,并且浏览器开启一个定时器监听线程,开始计时!(这个时间需要大概5-7ms完成)
然后同步代码到循环,循环没多久,setTimeout就到时间了,但是js是单线程的,只能同时做一件事情,所以把可执行的setTimeout放入EventQueue中。
遇到Promise.then的时候,将它放入EventQueue的微任务队列。

Promise

实现原理可以了解手撕promise

async await

实现原理可以了解 async

async函数中,await是异步的微任务,遇到await之后,函数体外的代码会继续执行,函数体内await之后的代码会暂停执行,把他们当作微任务,放置在EventQueue的微任务队列中,相当于promise.then。

面试题:

  • 首先a执行,打印’all-start’,
  • 然后遇到await testA(),执行testA函数。打印 “estA- start”。await下面的代码会放入微任务执行,等待await后的promise返回成功。所以继续执行同步代码,打印"中间穿插"
  • 等到2s后,await后面的promise返回成功,打印’testA end’
  • 然后打印 “test常量 testA’
  • 接着遇到await testB(),执行testB(),打印 testB strat
  • 后面的代码放入微任务中。1s过后,打印testB end
  • 然后打印test常量 testB

    结果正确。

类的继承

类的继承,封装,多态。

  • 封装: 实现某个功能的代码进行封装处理,后期想实现这个功能,直接调用函数即可完成“低耦合,高内聚“
  • 多态:方法名相同,参数个数不同会认为是多个方法(重载)
  • 继承 子类继承父类的方法。

原型继承

直接让子类的prototype指向new Parent()

function F()
function S()
S.prototype = new F()
S.prototype.constructor = S

直接让S.prototyoe指向了F的实例,那么S的实例可以通过原型链找到F上的prototype的方法属性。
特点:并没有copy一份父类的方法,只是建立父类与子类的原型的关系。子类实例可以通过原型链,_ proto _去获取。赋予父类私有的属性,会变成子类共有的,不可以传值给父类的构造函数。

call继承,把父类当作普通方法,去初始化对象。

function F()
	this.f = 200

function S()
	F.call(this) //通过父类去初始化this
	this.s =. 300

特点:父类的原型跟子类完全没关系,

两者结合 寄生组合继承

function F()
function S()
F.call(this)

S.prototype = Object.create(F.prototype)//创建一个新的空对象,proto指向F,并且作为S的prototype。
S.prototype.constructor = S

es6 继承

使用extends加上super,一旦使用extends,还编写了constructor,就必须super。他的原理类似于call继承。
extends+super类似于寄生组合继承。

以上是关于高级js学习7 prototype 继承 微任务宏任务以及promsie的主要内容,如果未能解决你的问题,请参考以下文章

[JS]事件循环怎么处理宏任务与微任务

20230515学习笔记——js中的同步任务与异步任务,宏任务与微任务

正常任务(宏任务)和微任务

js 宏任务,微任务

JS事件循环机制(宏任务,微任务)

JS事件循环机制(宏任务,微任务)