实现以下方法,建议配合this指向、原型与原型链食用更佳(点击链接了解相关知识)
1实现Instanceof(判断某个构造函数的prototype属性是否出现在了该对象原型链上)
function myInstanceof(obj, constructor) {
let left = Object.getPrototypeOf(obj);
let right = constructor.prototype;
while (true) {
if (left == null) return false;
if (left === right) return true;
left = Object.getPrototypeOf(left);
}
}
原型链是由对象及它的proto属性一级一级构成的,所以只需判断constructor.prototype是否等于原型链上某个对象的proto。如果最后等于null,说明遍历到了原型链尽头了,就肯定没有出现在原型链上了。
2实现new操作符
new操作符先创建了一个空对象
设置原型,将对象的原型设置为构造函数的prototype属性(让这个对象可以使用原型上的方法)
让函数的this指向这个对象,执行构造函数代码(为这个对象添加属性)
判断返回类型,如果是值类型,返回创建的对象,如果是引用类型,返回这个引用类型的对象
function Mynew() {
let newObj = null;
result = null;
constructor = Array.prototype.shift.call(arguments);
if (typeof constructor !== 'function') {
console.error("type error");
return;
}
newObj = Object.create(constructor.prototype);
//给newObj添加函数上的属性并将return结果赋值给result
result = constructor.apply(newObj, arguments);
let flag = result && (typeof result === 'object' || typeof result === 'function');
return flag ? result : newObj;
}
使用:Mynew(构造函数,初始化参数)
行12 13看如下代码,应该可以理解
//判断返回类型,如果是值类型,返回创建的对象(在行13中属于return newObj情况)
function Person(name) {
this.name = name; //默认return undefined,值类型
}
const user = new Person('khkkm');
console.log(user.name) //khkkm
——————————————————————————————————————————————————
//判断返回类型,如果是引用类型,返回这个引用类型的对象(在行13中属于return result情况)
function Person(name) {
this.name = name;
return {
age: 20
}
}
const user = new Person('khkkm');
console.log(user.name) //undefined
console.log(user.age) //20
3 call
call方法的第一个参数也是this的指向,后面传入的是一个参数列表(注意和apply传参的区别)。当一个参数为null或undefined的时候,表示指向window(在浏览器中),和apply一样,call也只是临时改变一次this指向,并立即执行。
Function.prototype.myCall = function (context) {
if (typeof this !== "function") {
console.error("type error");
return;
}
let result = null;
args = [...arguments].slice(1);
context = context || window;
context.fn = this;
result = context.fn(...args);
delete context.fn;
return result;
}
看着上面的解释,应该不难理解
4 apply
apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次。
Function.prototype.myApply = function (context) {
if (typeof this !== 'function') {
console.error("type error");
return;
}
let result = null;
context = context || window;
context.fn = this;
if (arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}
delete context.fn;
return result;
}
看着上面的解释,应该不难理解
5 bind
bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
console.error("type error");
return;
}
let fn = this;
args = [...arguments].slice(1);
return function Fn() {
return fn.apply(this instanceof Fn ? this : context, args.concat(...arguments));
}
};
行9 this instanceof Fn ? this : context 就是优先级:new绑定 > 显示绑定,args.concat(...arguments)则是参数可以分多次传
const func=fn.myBind({name:'khkkm'});
new func();
举例: fn就是随便一个函数,以上为对 this instanceof Fn ? this : context 的解释,如果将Fn(行8)作为了构造函数(如行 15),那么构造函数Fn的this得指向它new出来的实例对象,而不能是用户自定义的context。因为优先级 new绑定 > 显示绑定
6 Object.create(创建一个新对象,使用现有的对象来作为新创建对象的原型)
function MyCreate(proto) {
function Fn() { }
Fn.prototype = proto;
Fn.prototype.constructor = Fn;
return new Fn();
}
修改了构造函数的prototype属性后,记得把它的constructor指回它本身,主要是代码的规范性,别的作用没啥。
最后,希望能对大家有所帮助,祝愿各位都能找到自己满意的工作。
手写系列还会继续更新。。。