目录
实现一个_instance方法,判断对象obj是否是target的实例
实现一个myNew方法,接收一个构造函数以及构造函数的参数,返回构造函数创建的实例对象
寄生组合式继承:调用一次父类构造函数+通过object()复制原型
原型链相关
原型链(prototype chain)是 JavaScript 中面向对象编程的一个重要概念,用于实现对象的继承和共享属性。每个函数(构造函数)都有一个 prototype
属性,指向一个对象,这个对象称为原型对象。这个原型对象包含了所有实例共享的属性和方法。
当我们使用构造函数创建一个新对象时,新对象会有一个隐式的原型属性(__proto__
),指向构造函数的原型对象。这样,新对象就可以通过原型链访问到构造函数原型对象上的属性和方法。
如果我们继续查找 __proto__
属性,可以找到一个叫做 Object.prototype
的对象,它是所有对象的原型。如果继续查找 __proto__
属性,会找到 null
,表示原型链的结束。
这就形成了一个原型链的连接,从新对象的 __proto__
属性可以一直向上查找到 Object.prototype
,然后再查找到 null
。这种连接方式让所有对象都可以继承 Object.prototype
的属性和方法,并且可以通过原型链实现对象的继承和共享属性。
手写instanceof
实现一个_instance方法,判断对象obj是否是target的实例
思路:instanceof的原理是基于原型链的概念,通过遍历对象的原型链,检查原型链中的某个原型是否等于目标构造函数的
prototype
属性。如果找到匹配,则返回true
,否则返回false
。
instanceof使用场景:我们通常用typeof判断基本类型、symbol、function。对于对象的具体类型,通常用instanceof来判断,比如判断Map、Set、Array、Date类型。
function _instanceof(obj, target) {
//instanceof只检测对象
if (typeof obj != "object" || obj == null) {
return false;
}
let proto = Object.getPrototypeOf(obj); //拿到对象的原型
// let proto = obj.__proto__;
while (proto) {
if (proto == target.prototype) {
//原型上找到了target
return true;
}
proto = Object.getPrototypeOf(proto);
// proto = proto.__proto__;
}
return false;
}
可以通Object.getPrototypeOf()方法拿到对象/构造函数原型的原型
也可以使用__proto__两个下划线proto拿到原型
测试
对Array、Set、Map类型进行测试
console.log(_instanceof(null, Array));
console.log(_instanceof([], Array)); //判断数组
console.log(_instanceof({}, Array));
console.log(_instanceof({}, Object)); //普通对象
const set = new Set();
console.log(_instanceof(set, Set)); //判断Set
const map = new Map();
console.log(_instanceof(map, Map)); //判断Map
const date = new Date();
console.log(_instanceof(date, Date)); //判断Date
手写new的过程
实现一个myNew方法,接收一个构造函数以及构造函数的参数,返回构造函数创建的实例对象
思路:
- 创建一个对象obj,使用
constructor.prototype
作为其原型。- 使用
apply
方法改变构造函数constructor
的this
指向为新对象obj
,并将args
传递给constructor
。- 如果
constructor
返回一个对象,则返回constructor
返回的对象;否则返回newObj
。
//第一个参数构造函数,第二个通过...拿到的args参数
function myNew(constructor, ...args) {
const obj = Object.create(constructor.prototype);
let res = constructor.apply(obj, args); //使用apply绑定this,传args类数组对象,执行constructor构造函数方法
// let res = constructor.call(obj, ...args);
return typeof res === "object" ? res : obj; //构造函数如果没有返回值,返回obj;如果有返回值,返回res
}
在myNew方法中调用constructor方法,需要显示通过call或apply改变this。让方法找调用的对象obj。
测试myNew方法
测试myNew方法,打印person实例,调用person方法
// 测试
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function () {
console.log(this.age);
};
let person = myNew(Person, "三月的一天", 18);
console.log(person);
person.say();
手写类的继承
在过去,JavaScript 中实现类的继承有多种方式,包括原型链继承、构造函数继承、组合继承、原型式继承等。这些方式各有优缺点,需要开发人员根据具体情况进行选择和使用。
但是,随着 ES6 的普及和使用,开发人员可以更多地关注使用
class
和extends
实现继