高级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 Fn{static [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类似于寄生组合继承。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderlin_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值