对于js原型链的一次回顾
__proto__
和prototype
是什么?
__proto__
指向的地方叫 隐式原型,也就是它构造对象的显式原型(prototype
)- 原型链就是
__proto__
的路径 - 所有原型链的终点都为
Object.prototype.__proto__
, 即为null
- 函数是Function构造函数的实例, 对象是Object构造函数的实例:
Object.__proto__ === Function.prototype
Function.__proto__ === Function.prototype
- 对象、函数实例的prototype本质都是对象 由
new Object()
创建:Function.prototype.__proto__
和Object.prototype.__proto__
都为Object.prototype
其实可以看到,只要按照第一点的原则,后面的规则都解释的通
练习
var F = function() {};
Object.prototype.a = function() {
console.log('a');
};
Function.prototype.b = function() {
console.log('b');
}
var f = new F();
f.a();
f.b();
F.a();
F.b();
答
根据前面的知识,我们先梳理一下原型链(f.__proto__
)
f --__proto__-> F.Prototype --__proto__-> Object.prototype
所以,这个整个链路都没有经过Function.prototype 所以,f.b()
将会报错
f.a(); // a
f.b(); // f.b is not a function
F.a(); // a
F.b(); // b
那么Function.prototype
在哪条路径上呢?
F.__proto__ -> Function.prototype
例子
这个列子解释了引入了继承后, 完整原型链的关系
解释一下
- f是Fo的实例对象,它会有一个
__proto__
属性指向Fo的原型对象prototype
- Fo继承自Foo,其prototype是Foo的实例,会有一个__proto__属性指向Foo的的原型prototype
- Foo继承自顶层对象Object,其prototype是Object的实例,会有一个__proto__属性指向Object.prototype
- Object.prototype已经是最顶层的对象,其__proto__属性指向null
const Foo = function (name, sex) {
this.name = name
this.sex = sex
}
Foo.prototype.showName = function () {
console.log(this.name)
}
const Fo = function (name) {
Foo.call(this, name)
}
Fo.prototype = Object.create(Foo.prototype)
Fo.prototype.constructor = Fo
const f = new Fo('Fo')
f.showName() // Fo
f.__proto__ === Fo.prototype // true
f.__proto__.__proto__ === Foo.prototype // true
f.__proto__.constructor === Fo // true
foo.prototype.__proto__ === Object.prototype
相关知识
instanceof 原理
A instanceof B
这个关键字的主要作用是:判断B的prototype是否在A的原型链上
手写实现
const myInstanceOf = (obj, target) => {
if (!obj) return false
// const proto = obj.__proto__ 非Es6标准属性,建议使用下面的getPrototypeOf
const proto = Object.getPrototypeOf(obj)
if (proto === target.prototype) {
return true
}
return myInstanceOf(proto, target)
}
const a = ['test']
console.log("final res", myInstanceOf(a, Array))
继承
js的继承主要考察的是对于原型链的切换 下面给出一种相对比较完善的寄生组合式继承方式
function Person(name) {
this.name = name
this.getName = function() {
console.log('name:', this.name)
}
}
Person.prototype.listen = function(){
console.log('listen')
}
function Student(name, grade) {
Person.call(this, name)
this.grade = grade
}
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student
Student.prototype.say = function() {
console.log("my name is: ", this.name)
}
const s = new Student('leo', '1')
s.getName()
s.listen()
s.say()
console.log(s instanceof Person)
实现一个new
首先,new做了哪些事? 篇幅有限,不再举例子分析,直接给出结论:
- 得到一个新的Object的实例
- 实例的方法this指向这个实例本身
- 每个实例的__proto__指向构造函数的原型对象
- 当构造函数return 一个Object/Function/Array/Date/RegExp/Error的实例,new操作符得到的就是return的结果
const myNew = function (constructor){
if (typeof constructor !== "function") {
throw "need a constructor function"
}
const newObj = Object.create(constructor.prototype)
const otherParams = Array.from(arguments).slice(1)
const retObj = constructor.call(newObj, ...otherParams)
if (typeof retObj === 'function' || typeof retObj === 'object') {
return retObj
}
return newObj
}
const A = function(name) {
this.name = name
return () => {
console.log('my name:', this.name)
}
}
const a = myNew(A, "name a")
a()
function B(name) {
this.name = name
this.getName = () => {
console.log(this.name)
}
}
const b = myNew(B, "name b")
b.__proto___ === B.prototype // true
b.getName() // "name b"