关注公众号,每天都能领外卖红包
1、概念
搬自MDN - 实例对象的原型链指向它构造方法的原型。原型使用prototype访问,原型链用__proto__访问,虽然__proto__更大主流浏览器都支持,但MDN更推荐使用以下方法进行原型链读写,为了方便讲解文章使用__proto__来举例。
// get __proto__
Object.getPrototypeOf();
// set __proto__
Object.setPrototypeOf();
关于原型和原型链,举个简单的例子
const object = new Object();
上述代码可知object是被Object构造出来的实例对象,Object是构造出object的构造方法,所以根据概念可以得出实例对象object的原型链指向构造方法Object的原型
object.__proto__ === Object.prototype; // true
// or
Object.getPrototypeOf(object) === Object.prototype; // true
2、new
new做了什么,再从MDN中搬下概念:
-
创建一个空的简单JavaScript对象(即{});
-
为步骤1新创建的对象添加属性__proto__,将该属性链接至构造函数的原型对象 ;
-
将步骤1新创建的对象作为this的上下文 ;
-
如果该函数没有返回对象,则返回this。
手动实现一个:
const myNew = (constructor, ...params) => {
const obj = Object.create(null);
obj.__proto__ = constructor.prototype;
const result = constructor.call(obj, ...params);
return typeof result === 'object' ? result : obj;
};
测试一下
function Person(type) { this.type = type || 'person'; }
const student = myNew(Person, 'student');
console.log(student);
打印
再测试一下js自带的new
function Person(type) { this.type = type || 'person'; }
const student = new Person('student');
console.log(student);
结果相同
3、Object.create(null)
const obj1 = Object.create(null);
const obj2 = new Object();
const obj3 = {};
比较常见创建对象方法有以上三种,Object.create(null)创建的是一个不包含原型链的纯净空对象,而第二种和第三种都会创建原型链,第三种可以理解成第二种的语法糖,也更为常见,分别打印他们结果
第一种:
第二种:
第三种,结果和第二种相同:
对象中属性的规则为当前对象如果找不到该属性则会顺着原型链,去一层层向上寻找,直到null,下面代码为例
Object.prototype.a = 1;
const obj = {};
console.log(obj.a); // 1
所以如果你的项目对性能有很大的要求,建议不要使用类似下面的代码来获取属性值,因为当对象属性a不存在的时候会试图遍历更高层的原型属性,直到null,像下面这样写性能会差些
const a = obj.a || 2;
你可以使用Object.create(null)创建一个没有原型链的纯净对象,或者使用Object.hasOwnProperty,他只会去访问当前对象属性不会去遍历原型链
const a = obj.hasOwnProperty('a') ? obj.a : 2;
4、继承
先说说ES6中的class关键字,如下
class Person {
construstor() { this.type = 'person'; }
getType() { return this.type; }
}
形如java中的class语法,但他只是个语法糖,原理仍然是个对象,相当于
function Person() {
this.type = 'perosn';
}
Person.prototype.getType= function() { return this.type; }
class继承也很简单,和java一样使用extends和super
class Student extends Person {
construstor() {
super();
this.type = 'student';
}
}
const student = new Student();
// student
console.log(student.type);
// student
console.log(student.getType());
extends、super和class一样也是语法糖,其核心操作为 - extends使实例对象可以访问到继承的对象属性和原型属性,super就是调用一下要继承的构造方法,并且将this指向他,实现一个简易版的单继承extends和super
// extends,也可以使用Object.create形式实现
function myExtends(a, b) {
a.prototype.__proto__ = b.prototype;
}
// super
function mySuper(a, b, ...params) {
a.call(b, ...params);
}
function Student() {
// 调用父类构造方法
mySuper(Person, this);
this.type = 'student';
}
// Student继承Person
myExtends(Student, Person);
const student = new Student();
// student
console.log(student.type);
// student
console.log(student.getType());
测试打印结果相同
顺便提一句,js中所谓在子类中对父类方法的重写,只是因为新重写的方法在原型链中离实例对象更近,所以调用优先级更高
5、instanceof
执行a instanceof b后,会判断a的原型链是否指向b的原型,如果不是会一直顺着原型链往上进行比较,一直到null
student instanceof Student // true
student instanceof Person // true
student instanceof Object // true
我们可以手动实现一个
function myInstanceof(a, b) {
let result = a;
while(result.__proto__) {
result = result.__proto__;
if (result === b.prototype) return true;
}
return null;
}
测试打印,结果相同
student instanceof Student // true
student instanceof Person // true
student instanceof Object // true
6、结语
周末抽空巩固记录一下基础知识,以上的内容如有错误请及时指出
可以关注我的公众号,都是原创技术文章,一起进步吧!