不知道有没有跟我一样每次面试前都要去现找一些题去看的小伙伴,基于这一痛点呢我想还是总结一下,这样显得更有效率也更加系统!写这篇文章也是为了准备面试所以也会尽可能写的详细一点
一、手写new方法
使用new生成对象实例总共经历了以下四个步骤:
- 创建一个新的对象;
- 将新对象的
__proto__
属性指向构造函数的prototype
; - 执行构造函数,并把this指向新创建的对象;
- 若有返回值则判断是否为引用类型,如果是则返回引用类型的值,否则返回新创建的对象;
js
function mynew(){
// 创建一个新的对象
const obj = {};
// 从参数中获取构造函数
const func = [].shift.call(arguments);
obj.__proto__ = func.prototype;
// 执行构造函数,并把this指向新创建的对象
const res = func.apply(obj,[...arguments])
// 判断返回值类型如果为引用类型则返回对应的引用类型的值,否则返回新创建的对象
return res instanceof Object ? res : obj
}
// demo
function Person(name,age){
this.name = name;
this.age = age;
// return {name:'邓紫棋',age:18}
}
Person.prototype.fn = function(){
console.log("我可以陪你去流浪~~~")
}
const p = mynew(Person,'薛之谦',20) // {name:'薛之谦',age:20}
p.fn(); // 我可以陪你去流浪~~~ 如果Person中return的是引用类型则没有fn方法
二、手写instanceof方法
instanceof是用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
js
function myInstanceof(obj,constructor){
// 如果是传入的是基本数据类型直接返回false
if(typeof obj != 'object' || obj == null)return false;
let proto = obj.__proto__;
//
while(true){
// 已经查到了最顶层
if(proto == null)return false;
// 说明在原型链上
if(proto == constructor.prototype)return true;
// 再查找到顶层对象之前依然没有找到则继续向上查找
proto = proto.__proto__;
}
}
console.log(myInstanceof({},Object)); // true
console.log(myInstanceof({},Array)); // false
三、手写call、apply、bind方法
三个方法的作用相同:改变函数运行时的this
值。
三个方法的对比:
call
和bind
方法的参数是一个参数列表,apply
方法的参数是一个数组call
和apply
方法会立即执行函数,bind
会返回一个新的函数,需要手动调用此函数才会获得执行结果
call
js
// 为了让所有函数都能调用到我们自定义的方法,需要把方法定义到Function的原型上
// myCall方法中的this指向fn函数,thisArg指向的是目标对象
Function.prototype.myCall = function(thisArg,...args){
// 如果传入的目标对象是undefined或null,就将this指向全局对象
thisArg = thisArg || window
// 目的是让fn成为目标对象的方法来运行,这样this便指向了目标对象(核心思路:根据谁调用函数this就指向谁的原则)
thisArg.f = this;
// 运行这个方法并传入剩余参数
let result = thisArg.f(...args)
// 返回值同fn原函数一样
return result;
/**
* 到这里call的基本功能就完成了,但还存在一些问题:
* 目标对象上会永远存在我们自定义的f属性,并且如果多个函数调用这个方法,而目标对象也相同,
则存在目标对象的f属性被覆盖的可能
* 我们可以通过以下两种方式解决:
* 1、使用Symbol数据类型来定义对象的唯一属性名
* 2、使用delete操作符删除对象中的某个属性
* (一下代码与上面没有关联)
*/
// 如果传入的目标对象是undefined或null,就将this指向全局对象
thisArg = thisArg || window
// 生成唯一属性名,解决覆盖的问题
const prop = Symbol();
// 注意这里不能使用.
thisArg[prop] = this;
// 运行这个方法,传入剩余参数,同样不能用.
let result = thisArg[prop](...args);
// 运行完删除属性
delete thisArg[prop]
return result;
}
// demo
function fn(name,age){
console.log(name,age); // '薛之谦',20
return '我的心愿是世界和平!'
}
let p = fn.myCall(obj,'薛之谦',20)
console.log(p); // 我的心愿是世界和平!
apply
apply和call的实现思路一样,只是传参方式不同
js
Function.prototype.myApply = function (thisArg, args) {
thisArg = thisArg || window;
// 判断是否接收参数,若未接收参数,替换为[]
args = args || []
const prop = Symbol();
thisArg[prop] = this;
let result = thisArg[prop](...args);
delete thisArg[prop];
return result;
}
未完待续。。。。。。
时间就像海绵里的水,挤一挤总会有的!