简介
手撕常用源码为面试中必备的技能,欢迎各位大佬一起探讨指正~
实现一个new源码
首先了解new对实例化构造函数时内部实现了啥:
- 创建一个控对象
- 将构造函数的原型对象添加到这个对象的隐形属性__proto__
- 执行构造函数当函数返回对象的时候,返回对象
- 当函数返回非对象的时候返回这个ob j
具体代码实现逻辑如下
// es5写法
function newp(Con,...args){
const obj = Object.create(Con.prototype);
const res = Con.apply(obj, args)
return res instanceof Object ? res : obj
}
// es3
function newP(){
const obj = new Object();
const args = Array.prototype.slice.call(arguments, 1);
const Con = Array.prototype.shift.call(arguments);
obj.__proto__ = Con.prototype;
const res = Con.apply(obj, args);
return res instanceof Object ? res : obj;
}
实现Object.create
上面new的例子在es5的写法中用到了Object.create的写法,因此我们了解到Object.create的作用是创建一个新对象,使用现有的对象作为新创建对象的prototype。:
- 先生成一个空对象
- 将传入的对象添加到这个对象的隐形属性__proto__中
- 最后的生成的对象的原型对象的constructor属性要指向创建的对象
具体代码实现逻辑如下
function create(object){
const obj = new Object();
obj.__prototype = object;
obj.__prototype.constructor = obj;
return obj
}
实现instanceof
学习过原型链的都知道判断一个对象是否在某个原型链上,以及判别引用类型的方法不能用typeof, 而是instanceof 那么手撕一波:
// 1. 检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
// 2. Object.getPrototypeOf(obj(要返回其原型的对象)) 方法返回指定对象的原型(内部[[Prototype]]属性的值)。
// 3. 返回值:给定对象的原型。L.__proto__.__proto__ 直到 proto 为 null,则返回 null 。
// 4. 函数中的while(true){return} 不会无限循环,当出发return结束循环
function instanceofP(L, R){
if(typeof(L) !== 'object'){
return false
}
let l = L.getPrototypeOf(L);
const r = R.prototype;
while(l !== null){
if(l === r){
return true
}
l = l.getPrototypeOf(l)
}
}
bind实现
bind做了啥
- bind是Function原型链中的Function.prototype的一个属性,
- 它是一个函数,修改this指向,合并参数传递给原函数,返回值是一个新的函数。
- bind返回的函数, 可以通过new调用,返回原函数的实例,但是不改变this指向
- 指向了new生成的全新对象。内部模拟实现了new操作符。
/* eslint-disable no-extend-native */
Function.prototype.bind3 = function () {
if (!(this instanceof Function)) {
throw new Error('is not a function');
}
const func = this;
const args = Array.prototype.slice.call(arguments, 1);
const Content = Array.prototype.shift.call(arguments);
return function F() {
const argp = Array.prototype.slice.call(arguments);
const Arg = args.concat(argp);
const selfp = this;
// this instanceof f 正常情况下这种情况返回的是纯粹函数,this指向的是全局当用new时this指向的时F, 否则指向的原来的构造函数的原型对象
if (selfp.__proto__ === F.prototype) {
return new func(...Arg);
}
return func.apply(Content, Arg);
};
};
call实现
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。这是也是利用函数this指向对象的套路同apply
// 参数你也可以指定(self, ... agrs)我这里用arguments只是单纯为了装b
Function.prototype.callp = function () {
// 这里还是我想玩一下原型链的骚操作
// 可以用!(this.instanceof Function),或者typeof(this) === 'function
if (this.__proto__ !== Function.prototype) {
throw new Error('is not function');
}
const args = Array.prototype.slice.call(arguments, 1);
const content = this;
// 为什么用shift, 问就是为了装b
const self = Array.prototype.shift.call(arguments) || {};
self.fuc = content;
const res = self.fuc(...args);
// 这里一定要记的删掉this中添加的函数属性
delete self.fuc;
return res;
};
apply实现
apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)提供的参数, 返回修改this指向的函数结果, 利用this指向(obj)特性实现apply源码
/* eslint-disable no-extend-native */
// apply改变函数this指向, 返回改变指向的函数的结果
Function.prototype.applyp = function (self, args) {
if (!(this instanceof Function)) {
throw new Error('is not function');
}
const content = this;
const selfp = self || {};
selfp.content = content;
const res = self.content(...args);
// 一定要将添加到修改函数this指向中的函数去掉
delete self.content;
return res;
};
结束语
如果各位大佬觉得还不错请给个素质3连,欢迎各位大佬提出更好的写法~