-
Notifications
You must be signed in to change notification settings - Fork 0
Description
原型
- 构造函数、原型、实例的关系
- 原型链
- instanceof
- 如何实现一个 instanceof
构造函数创建对象
在 JavaScript 中创建对象的方式通常是字面量或构造函数的方式. 下面通过构造函数来创建对象:
function Student(name, age) {
this.name = name;
this.age = age;
}
const studentObj1 = new Student('张三', 28);
console.log(studentObj1);
上面很简单, 通过构造函数 Student
来创建 studentObj1
对象. 接着我们讲下主要的内容:
prototype
在 JavaScript 中每个函数有一个原型对象属性 prototype
. 也就是函数可以通过下面访问去访问原型对象.
function Student() {}
console.log(Student.prototype); // {}
__proto__
同样在 JavaScript 每个对象(除了null)中包含一个 __proto__
属性, 指向的是生成它的构造函数的原型. 这句话有点绕, 我们结合上面代码来理解:
function Student(){}
const a = new Student();
console.log(a.__proto__ === Student.prototype); // true
注意:
proto 属性只是部分浏览器中存在该属性(非标准属性), 主要是方便上面去查看. proto 是其对象内部属性
[[Prototype]]
的引用, 如果要获取原型对象可以通过ES5中提供的Object.getPrototypeOf(obj)
标准方法替代.
constructor
前面说到 prototype
指向的是函数的原型. 有没有能说明原型对象与函数关联的属性? constructor
.
// 结合上面案例
function Student(){}
console.log(Student.prototype.constructor === Student); // true
通过上面我们可以知道的构造函数、原型、实例之间的关系. 如下图:
原型链
用大白话讲原型的原型就形成了原型链
. 前面讲到 JavaScript 中每个对象都有原型对象, 那么原型对象的原型是谁?
function Student(name, age) {
this.name = name;
this.age = age;
}
const studentObj1 = new Student('张三', 28);
console.log(studentObj1.__proto__); // studentObj1.__proto__ === Student.prototype
下面获取原型对象的原型(p):
//
console.log(Student.prototype.__proto__ === studentObj1.__proto__.__proto__)
console.log(studentObj1.__proto__.__proto__); // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
前面提及原型对象与构造函数关系, 如果要想知道原型对象(p)是谁创建可以通过 constructor
属性:
console.log(studentObj1.__proto__.__proto__.constructor); // [Function: Object]
也就是说明原型对象(p)通过构造函数 Object
创建生成的. 按照构造函数、原型、实例之间的关系, 那么下面的关系同样成立:
console.log(studentObj1.__proto__.__proto__ === Object.prototype)
console.log(studentObj1.__proto__.__proto__.constructor == Object)
依此此类推, 那么 Object.prototype
指向的原型对象的原型对象是什么?
console.log(Object.prototype.__proto__); // null
其也是原型链的未端. 上面关系汇聚如下这个图:
对象属性的查找方式
实例对象与原型通过对象内部 [[Prototype]]
关联着, 那么属性查找时跟原型及原型链有关系? 还是基于上面的案例:
function Student(name, age) {
this.name = name;
this.age = age;
}
Student.prototype.getName = function(){
return this.name;
}
const studentObj1 = new Student('张三', 28);
console.log(studentObj1.name); // '张三'
console.log(studentObj1.getName()); // '张三'
上面基于前面案例作了细微调整, 在原型对象 Student.prototype
添加一个方法 getName
, 然后通过实例去访问这个方法.
从代码层面可以看到, 在 Student
的实例对象中不存在 getName
方法, 但是当用实例去访问 getName
时是可以访问到. 这说明访问实例对象某个属性时, 当实例对象上不存在时会从其原型对象上去查找.
也就是说当实例对象中包含与原型对象中属性时, 则原型对象上的属性会被遮蔽
.
function Student(name, age) {
this.name = name;
this.age = age;
this.getName = function(){
return "实例上的方法";
}
}
Student.prototype.getName = function(){
return this.name;
}
const studentObj1 = new Student('张三', 28);
console.log(studentObj1.name); // '张三'
console.log(studentObj1.getName()); // '实例上的方法'
instanceof
前面知道构造函数与原型对象关系(prototype)、 原型对象与构造函数关系(constructor)、 实例与原型对象的关系(proto), 在 JavaScript 怎么判断实例是不是由某个构造函数的实例? instanceof
function Student(name, age) {
this.name = name;
this.age = age;
}
const studentObj1 = new Student('张三', 28);
console.log(studentObj1 instanceof Student); // true
再来看看下面的例子:
console.log(studentObj1 instanceof Object); // true
从输出结果来看, studentObj1
也是 Object
的实例, 为什么? 回答问题就要知道 instanceof 的背后原理:
只要能在操作符左侧(studentObj1)的原型链上, 能找到右侧构造的原型(Object.prototype), 那么则返回
true
.
那回顾讲解原型链, 看看 Object.prototype
是不是存在 studentObj1
的原型链上.
知道其原理, 那么可以自己实现 instanceof
操作符的功能:
const getPrototypeOf = Object.getPrototypeOf;
const instanofFun = function(left, right) {
if (left === null) {
return false;
}
const proto = getPrototypeOf(left);
if (proto === right.prototype) {
return true;
}
return instanofFun(proto,right)
}
function Student(name, age) {
this.name = name;
this.age = age;
}
const studentObj1 = new Student('张三', 28);
console.log(instanofFun(studentObj1, Student)); // true
console.log(instanofFun(studentObj1, Object)); // true
console.log(instanofFun(studentObj1, Array)); // false
上面通过简单的递归, 完成相应的功能. 这里说句题外话, 编写一个递归有两个因素: 递归英子(递归条件) 、递归体(循环的内容)
总结
-
原型对象、实例对象、构造函数之间的关系.
- 构造函数与原型对象关系(
prototype
) - 原型对象与构造函数关系(
constructor
) - 实例与原型对象的关系(
__proto__
)
- 构造函数与原型对象关系(
-
原型链: 原型的原型构成的关系我们称为
原型链
. -
对象属性查找方式: 先从自身查找、找不到则从原型链上查找
-
自身实例属性会遮蔽原型上的同名属性,这中方式应用很广, 比如 函数、数组都实现了
toString
方法. -
instanof 的使用以及实现原理.