手动实现bind函数

JavaScript010

手动实现bind函数,第1张

为什么要自己去实现一个bind函数?

所以,为了理想主义和世界和平(所有浏览器上都能随心所欲调用它),必要的时候需要我们自己去实现一个bind。那么,一个bind函数需要具备什么功能呢?

绑定this、定义初始化参数是它存在的主要意义和价值。MDN对它的定义如下:

鉴于这两个核心作用,我们可以来实现一个简单版看看:

由于arguments是类数组对象,不拥有数组的slice方法,所以需要通过call来将slice的this指向arguments。 args就是调用bind时传入的初始化参数(剔除了第一个参数oThis)。将args与绑定函数执行时的实参arguments通过concat连起来作为参数传入,就实现了bind函数初始化参数的效果。

bind函数的另外一个也是最主要的作用:绑定this指向,就是通过将调用bind时的this(self)指向指定的oThis来完成。这样当我们要使用bind绑定某个对象时,执行绑定函数,它的this就永远固定为指定的对象了~

到这里,我们已经可以用上面的版本来使用大部分场景了。但是~

但是,这种方案就像前面说的,它会永远地为绑定函数固定this为指定的对象。如果你仔细看过MDN关于bind的描述,你会发现还有一个情况除外:

我们可以通过一个示例来试试看原生的bind对于使用new的情况是如何的:

试验结果发现,obj.name依然是lily而没有变成tom,所以就像MDN描述的那样,如果绑定函数cat是通过new操作符来创建实例对象的话,this会指向创建的新对象tom,而不再固定绑定指定的对象obj。

而上面的简易版却没有这样的能力,它能做到的只是永久地绑定指定的this(有兴趣的朋友可以在控制台使用简易版bind试下这个例子看看结果)。这显然不能很好地替代原生的bind函数~

那么,如何才能区分绑定函数有没有通过new操作符来创建一个实例对象,从而进行分类处理呢?

我们知道 检测一个对象是否通过某个构造函数使用new实例化出来的最快的方式是通过 instanceof :

A instanceof B //验证A是否为B的实例

那么,我们就可以这样来实现这个bind:

假设我们将调用bind的函数称为C,将fBound的prototype原型对象指向C的prototype原型对象(上例中就是self),这样的话如果将fBound作为构造函数(使用new操作符)实例化一个对象,那么这个对象也是C的实例,this instanceof self就会返回true。这时就将self指向新创建的对象的this上就可以达到原生bind的效果了(不再固定指定的this)。否则,才使用oThis,即绑定指定的this。

但是这样做会有什么影响?将fBound的prototype原型对象直接指向self的prototype原型对象,那么当修改fBound的prototype对象时,self(上述C函数)的prototype对象也会被修改!!考虑到这个问题,我们需要另外一个function来帮我们做个中间人来避免这个问题,我们看看MDN是怎么实现bind的。

MDN针对bind没有被广泛支持的兼容性提供了一个实现方案:

发现了吗,和上面经过改造的方案相比,最主要的差异就在于它定义了一个空的function fNOP,通过fNOP来传递原型对象给fBound(通过实例化的方式)。这时,修改fBound的prototype对象,就不会影响到self的prototype对象啦~而且fNOP是空对象,所以几乎不占内存。

其实这个思路也是YUI库如何实现继承的方法。他的extend函数如下:

最后一步是将Child的constructor指回Child。

实现一个原生的函数,最重要的是理清楚它的作用和功能,然后逐一去实现它们包括细节,基本上就不会有问题~

这里用到的一些关于 prototype instanceof 的具体含义,可以参考阮一峰老师的 prototype 对象 ,相信对你理解JavaScript的原型链和继承会有帮助~

原文链接: https://lvdingjin.github.io/tech/2018/06/05/achieve-bind.html

博客园整理了一下,有好的面试题欢迎大家发在评论区哟

1. 闭包

2. 数组去重

3. 原型和原型链

4. call,apply,bind三者的区别?

5. 请介绍常见的 HTTP 状态码(至少五个)

6. 深浅拷贝

7. 实现(5).add(3).minus(2)输出6

8. null和undefined区别

9. MVC和MVVC?

10. Vue生命周期

11. Vue数据双向绑定原理

12. Vue组件传参

13. 说说各浏览器存在的兼容问题

14. router和route

15. active-class属于Vue哪一个modules,有什么作用

16. v-if和v-show

17. computed和watch有什么区别

18.Vue 组件中 data 为什么必须是函数

19. vue中子组件调用父组件的方法

20. vue中 keep-alive 组件的作用

21. vue中如何编写可复用的组件?

22. Vue 如何去除url中的 #

23. Vue 中 key 的作用

24. Vue 中怎么自定义指令

25. Vue 中怎么自定义过滤器

26. NextTick 是做什么的

27. Vue 组件 data 为什么必须是函数

28. 计算属性computed 和事件 methods 有什么区别

29. scoped(死够扑的)

30. vue如何获取dom?

31. promise

32. vue常用指令有哪些

33.vue-loader是什么?使用和用途?

34.css样式局部化,如何让css只在当前组件起作用?scss和stytus样式穿透

闭包指有权访问另一个函数中变量的函数,

MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

B站视频: https://www.bilibili.com/video/BV1YJ411R7ap?from=search&seid=18157596230752413126

https://www.jianshu.com/p/9c3547450a52

https://search.bilibili.com/all?keyword=%E5%8E%9F%E5%9E%8B%E9%93%BE%20%E9%BB%91%E9%A9%AC

都是用来改变this指向的

call和apply都是function原型上的方法,每一个函数作为function的实例都可以调用这两个方法,而这两个方法都是用来改变this指向的

一般情况下this指向其调用者()

fun.call(thisArg,arg1,ary2,...)

主要作用可以实现继承

调用函数,改变this指向

继承

fun.apply(thisArg,[argsArray])

作用:调用函数,改变函数内部this指向

参数必须是数组

apply主要应用于借助数学对象等

bind()方法不会调用函数,但可以改变函数内部this指向

fun.bind(thisArg,arg1,arg2,...)

thisArg:在fun函数运行时指定的this值

arg1,arg2:传递的其他参数

返回由指定的this值和初始化参数改造的原函数拷贝

call的性能要比apply好一些,尤其当传递参数超过3个,后期开发可多用call

时间测试

状态码是由 3 位数组成,第一个数字定义了响应的类别,且有五种可能取值:

1xx:指示信息–表示请求已接收,继续处理。

2xx:成功–表示请求已被成功接收、理解、接受。

3xx:重定向–要完成请求必须进行更进一步的操作。

4xx:客户端错误–请求有语法错误或请求无法实现。

5xx:服务器端错误–服务器未能实现合法的请求。

阮一峰: http://www.ruanyifeng.com/blog/2014/03/undefined-vs-null.html

MVC 是后端中的概念

MVVC中 是前端概念

最终实现V和M数据的同步,因此开发者只需关注业务逻辑,不需要手动操作Dom,mvvm是vue的核心

这两个不同的结构可以看出两者的区别,他们的一些属性是不同的。

active-class 属于vue-router的样式方法

当routerlink标签被点击时将会应用这个样式

使用有两种方法

routerLink标签内使用

在使用时会有一个Bug

首页的active会一直被应用

解决办法

为了解决上面的问题,还需加入一个属性exact,类似也有两种方式:

在router-link中写入exact

在路由js文件,配置active-class

还可以不用exact这种方法去解决,例如

首页

路由中加入重定向

computed

computed是计算属性,也就是计算值,它更多用于计算值的场景

computed具有缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时重新调用对应的getter来计算

computed适用于计算比较消耗性能的计算场景

watch

watch更多的是[观察]的作用,类似于某些数据的监听回调,用于观察props $emit或者本组件的值,当数据变化时来执行回调进行后续操作

无缓存性,页面重新渲染时值不变化也会执行

小结

当我们要进行数值计算,而且依赖于其他数据,那么把这个数据设计为computed

如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化。

去这里看一下​blog.csdn.net

vue-router 默认使用 hash 模式,所以在路由加载的时候,项目中的 url 会自带 #。如果不想使用 #, 可以使用 vue-router 的另一种模式 history

new Router({

mode: 'history',

routes: [ ]

})

需要注意的是,当我们启用 history 模式的时候,由于我们的项目是一个单页面应用,所以在路由跳转的时候,就会出现访问不到静态资源而出现 404 的情况,这时候就需要服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面

具体参考 官方API

参考 官方文档-自定义指令

过滤器也同样接受全局注册和局部注册

具体可参考官方文档 深入响应式原理

我们可以将同一函数定义为一个 method 或者一个计算属性。对于最终的结果,两种方式是相同的

不同点:

让css只在当前组件中起作用