大家好,我是前端西瓜哥。
之前写了一篇关于 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 用于通过函数来创建一个对象实例,在很多语言中都能看到。
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 创建一个新对象,做了下面几件事:
怎么判断一个函数正在被 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 的是 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 的时候就提前设置为 '前端西瓜哥' 。
完整实现如下:
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 的标准:
https://tc39.es/ecma262/#sec-function.prototype.bind。
然后再看看知名的 core.js 库中对 bind 的实现:
https://github.com/zloirock/core-js/blob/cafe9ecf2b384385f8b8d1da0047e44586fff2dc/packages/core-js/internals/function-bind.js#L23-L33。
其中核心实现为:
// `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;
};
这里有更多的细节:
手写 bind,现在想来并不简单,需要掌握好很多知识点:
来源:https://www.toutiao.com/article/7118393414034571815
new 是构造函数生成实例的命令, ES6为 new 命令引入了 new.target属性。这个属性用于确定构造函数是怎么调用的。在构造函数中, 如果一个构造函数不是通过 new操作符调用的, new.target会返回 undefined。
new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。假设Test是一个构造函数,通常在创建对象的实例时,要使用new,eg:test = new Test() , 那么在调用new的时候,发生了什么呢?
1. 创建一个类的实例:创建一个空对象obj,然后把这个空对象的__proto__设置为Person.prototype(即构造函数的prototype);2. 初始化实例:构造函数Person被传入参数并调用,关键字this被设定指向该实例obj;3. 返回实例obj。
new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。 ——(来自于MDN)new 创建的实例有以下 2 个特性:1、访问到构造函数里的属性;2、访问到原型里的属性
new 运算符 创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。比较好奇new运算符的原理,了解了下,分享给大家。自己写一个New()方法,
首先我们来看一下实例化一个对象做了浏览器做了什么事情,new的四步操作:1. 创建一个空对象2. 设置空对象的__proto__属性继承构造函数的prototype属性,也就是继承构造函数的原型对象上的公有属性和方法
那么什么是this,new,bind,call,apply呢?这些你都用过吗?掌握这些内容都是基础中的基础了。如果你不了解,那还不赶快去复习复习,上网查阅资料啥的!通过call,apply,bind可以改变this的指向
理解new对象过程,需要提前了解原型及原型链的相关知识;我们都知道,JS当中创建对象使用的是原型设计模式,即使用new操作符调用构造函数,这里先看一个例子:
对于创建一个对象来说,更推荐使用字面量的方式创建对象(无论性能上还是可读性)。因为你使用 new Object() 的方式创建对象需要通过作用域链一层层找到 Object
上面的代码,我们首先创建了一个函数,如果是用面向对象的说法就是创建了一个Function类的实例,如果直接执行这个函数,那它就是一个普通的函数,如果用new执行,则这个函数被称为一个自定义的类。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!