你有遇见过给bind返回的函数做new操作的场景,本篇主要讲述的就是实现一下兼容new操作的bind写法,顺便学习一下new操作符,为大家提供下参考。
大家可以去看下关于 JS 中 bind 方法的实现的文章,并给出了实现:
Function.prototype.myBind = function(thisArg, ...prefixArgs) { const fn = this; return function(...args) { return fn.call(thisArg, ...prefixArgs, ...args); } }
但没有处理通过 new 创建实例的情况。
我们没考虑过给 bind 返回的函数做 new 操作的场景,主要就是这种情况极少遇见。
但有时还是会出现,在实现一下兼容 new 操作的 bind 写法,顺便学习一下 new 操作符。
这里我推荐先看下前一篇文章:《前端面试题:手写 bind》。
new
我们先学习一下 new 操作符。
new 用于通过函数来创建一个对象实例,在很多语言中都能看到。
JS 的函数,除了可以是普通函数,比如:
function sum(a, b) { return a + b; }
还可以是构造函数,只需要在构造时在它前面加一个 new:
function Person(name, age) { this.name = name; this.age = age; } const person = new Person('前端西瓜哥', 100) // Person {name: '前端西瓜哥', age: 100}
new 创建一个新对象,做了下面几件事:
创建一个空对象{};
空对象的原型属性__proto__指向构造函数的原型对象Person.prototype;
函数中的 this 设置为这个空对象;
如果该函数不返回一个对象,就返回这个 this,否则返回这个对象。
判断函数是否通过 new 被调用
怎么判断一个函数正在被 new 操作符调用?
答案是使用 instanceof 判断 this 是否为当前函数的实例,即this instanceof Fn为 true,表示在通过 new 构建实例。
先看下实例:
function Person() { if (this instanceof Person) { console.log('通过 new 构建实例'); } else { console.log('普通调用') } } Person() // 输出:普通调用 new Person() // 输出:通过 new 构建实例
在 Vuejs 的源码,你会看到下面代码,这里也用到了这个技巧。
function Vue(options) { if (__DEV__ && !(this instanceof Vue)) { warn('Vue is a constructor and should be called with the `new` keyword') } this._init(options) }
你在开发环境如果不通过 new 来使用 Vue 对象,会在控制台提示你要通过 new 来调用 Vue。
new 和 bind
如果我们 new 的是 Function.prototype.bind 返回的新函数,会发生什么事情?
function Person(name, age) { this.name = name; this.age = age; } const BoundPerson = Person.bind(null, '前端西瓜哥'); const boundPerson = new BoundPerson(100); // Person {name: '前端西瓜哥', age: 100} boundPerson.__proto__ === Person.prototype // true
结果等价于直接去 new 原始函数。
不同的是,仍旧可以进行参数的预置。可以看到,构造函数的第一个参数,在调用 bind 的时候就提前设置为 '前端西瓜哥'。
实现完整的 bind
完整实现如下:
Function.prototype.myBind = function(thisArg, ...prefixArgs) { const fn = this; const boundFn = function(...args) { // 通过 new 使用当前函数 if (this instanceof boundFn) { return new fn(...prefixArgs, ...args); } // 普通的方法调用当前函数 return fn.call(thisArg, ...prefixArgs, ...args); } boundFn.prototype = fn.prototype; return boundFn; }
这里我通过this instanceof boundFn来判断是否用了 new,如果是,就直接 new 原始函数然后返回,记得带上 bind 预置好的参数。
和原本一样(参照《前端面试题:手写 bind》)。
boundFn.prototype = fn.prototype;这个可写可不写,只是让 bind 返回的新函数的 prototype 指向原函数的 prototype。
如果是原生 bind 返回的函数,它是没有 protoype 属性的,可以认为它是一种特别的函数,而我们实现的 bind 返回的却是一个普通函数,所以并不能完全模拟的。
如果你追求完美的实现,可以研读一下Function.prototype.bind的标准:
然后再看看知名的core.js库中对 bind 的实现:
其中核心实现为:
// `Function.prototype.bind` method implementation // https://tc39.es/ecma262/#sec-function.prototype.bind module.exports = Function.bind || function bind(that /* , ...args */) { var F = aCallable(this); var Prototype = F.prototype; var partArgs = arraySlice(arguments, 1); var boundFunction = function bound(/* args... */) { var args = concat(partArgs, arraySlice(arguments)); return this instanceof boundFunction ? construct(F, args.length, args) : F.apply(that, args); }; if (isObject(Prototype)) boundFunction.prototype = Prototype; return boundFunction; };
这里有更多的细节:
这里判断了 this 是否为函数类型,不是函数会报错;
F.prototype 需要是一个对象或函数,才能赋值给新函数;
使用了普通函数和 arguments,这是为了兼容 ES5。
结尾
手写 bind,需要我们学习更多知识:
bind 的详尽用法:包括改变 this、预置参数、new 的表现;
闭包的使用:保存一些私有变量;
通过原型链的方式(this instanceof boundFn)判断是否通过 new 调用当前函数;
使用 call 在执行时改变函数的 this 指向。
以上就是全部内容,欢迎大家继续关注后续更多精彩内容。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/128375.html
...时。 那么与节流函数的区别直接看这个动画实现即可。 手写简化版: // 防抖函数 const debounce = (fn, delay) => { let timer = null; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); }, delay); }; }; 适用...
手写路径导航 实现一个new操作符 实现一个JSON.stringify 实现一个JSON.parse 实现一个call或 apply 实现一个Function.bind 实现一个继承 实现一个JS函数柯里化 手写一个Promise(中高级必考) 手写防抖(Debouncing)和节流(Throttling) 手写一个JS深拷...
最近准备初级前端面试,发现有很多手写实现什么的,例如什么手写实现bind,promise。手写ajax,手写一些算法。翻阅了很多书籍和博客。 这里做一个总结改进,算是对我后面大概为期一个月找工作的准备。 手写实现bind() ...
...链是啥? JavaScript原型与原型链 7.说一说js的闭包是啥?手写一个闭包的案例 JavaScript 闭包 8.说一说promise是啥? 可以参考一下两篇文章: 面试精选之Promise 解读Promise内部实现原理 9.你能不能手写一个promise? 我感觉这简直是变态...
... 本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言: Linux中素有“万物皆文件,一切皆IO”的说法。前面几讲手撕了CGI网关服务器、echo回显服务器、discard服务的代...
阅读 39·2023-01-02 11:07
阅读 26·2023-01-02 10:55
阅读 16·2022-12-25 21:03
阅读 19·2022-12-25 20:37
阅读 32·2022-12-16 17:52
阅读 27·2022-12-16 17:30
阅读 30·2022-12-16 17:25
阅读 24·2022-12-06 22:13